File Coverage

blib/lib/RDF/Query/Plan/Minus.pm
Criterion Covered Total %
statement 25 145 17.2
branch 0 36 0.0
condition n/a
subroutine 9 21 42.8
pod 12 12 100.0
total 46 214 21.5


line stmt bran cond sub pod time code
1             # RDF::Query::Plan::Minus
2             # -----------------------------------------------------------------------------
3              
4             =head1 NAME
5              
6             RDF::Query::Plan::Minus - Executable query plan for minus operations.
7              
8             =head1 VERSION
9              
10             This document describes RDF::Query::Plan::Minus version 2.915_01.
11              
12             =head1 METHODS
13              
14             Beyond the methods documented below, this class inherits methods from the
15             L<RDF::Query::Plan> class.
16              
17             =over 4
18              
19             =cut
20              
21             package RDF::Query::Plan::Minus;
22              
23 35     35   180 use strict;
  35         78  
  35         902  
24 35     35   190 use warnings;
  35         71  
  35         971  
25 35     35   182 use base qw(RDF::Query::Plan);
  35         70  
  35         2477  
26              
27 35     35   195 use Log::Log4perl;
  35         75  
  35         267  
28 35     35   1563 use Scalar::Util qw(blessed);
  35         77  
  35         1733  
29 35     35   204 use Time::HiRes qw(gettimeofday tv_interval);
  35         77  
  35         239  
30              
31 35     35   3824 use RDF::Query::Error qw(:try);
  35         82  
  35         268  
32 35     35   4520 use RDF::Query::ExecutionContext;
  35         81  
  35         1497  
33              
34             ######################################################################
35              
36             our ($VERSION);
37             BEGIN {
38 35     35   50715 $VERSION = '2.915_01';
39             }
40              
41             ######################################################################
42              
43             =item C<< new ( $lhs, $rhs ) >>
44              
45             =cut
46              
47             sub new {
48 0     0 1   my $class = shift;
49 0           my $lhs = shift;
50 0           my $rhs = shift;
51 0           my $self = $class->SUPER::new( $lhs, $rhs, @_ );
52            
53 0           my %vars;
54 0           my @lhs_rv = $lhs->referenced_variables;
55 0           my @rhs_rv = $rhs->referenced_variables;
56 0           foreach my $v (@lhs_rv, @rhs_rv) {
57 0           $vars{ $v }++;
58             }
59 0           $self->[0]{referenced_variables} = [ keys %vars ];
60 0           return $self;
61             }
62              
63             =item C<< execute ( $execution_context ) >>
64              
65             =cut
66              
67             sub execute ($) {
68 0     0 1   my $self = shift;
69 0           my $context = shift;
70 0           $self->[0]{delegate} = $context->delegate;
71 0 0         if ($self->state == $self->OPEN) {
72 0           throw RDF::Query::Error::ExecutionError -text => "Minus plan can't be executed while already open";
73             }
74            
75 0           $self->[0]{start_time} = [gettimeofday];
76 0           my @inner;
77 0           $self->rhs->execute( $context );
78 0           while (my $row = $self->rhs->next) {
79             # warn "*** loading inner row cache with: " . Dumper($row);
80 0           push(@inner, $row);
81             }
82 0           $self->lhs->execute( $context );
83 0 0         if ($self->lhs->state == $self->OPEN) {
84 0           $self->[0]{inner} = \@inner;
85 0           $self->[0]{outer} = $self->lhs;
86 0           $self->[0]{inner_index} = 0;
87 0           $self->[0]{needs_new_outer} = 1;
88 0           $self->[0]{inner_count} = 0;
89 0           $self->[0]{count} = 0;
90 0           $self->[0]{logger} = $context->logger;
91 0           $self->state( $self->OPEN );
92             } else {
93 0           warn "no iterator in execute()";
94             }
95             # warn '########################################';
96 0           $self;
97             }
98              
99             =item C<< next >>
100              
101             =cut
102              
103             sub next {
104 0     0 1   my $self = shift;
105 0 0         unless ($self->state == $self->OPEN) {
106 0           throw RDF::Query::Error::ExecutionError -text => "next() cannot be called on an un-open Minus";
107             }
108 0           my $outer = $self->[0]{outer};
109 0           my $inner = $self->[0]{inner};
110            
111 0           my $l = Log::Log4perl->get_logger("rdf.query.plan.minus");
112 0           while (1) {
113 0 0         if ($self->[0]{needs_new_outer}) {
114 0           $self->[0]{outer_row} = $outer->next;
115 0 0         if (ref($self->[0]{outer_row})) {
116 0           $self->[0]{outer_row_vars} = { map { $_ => 1 } $self->[0]{outer_row}->variables };
  0            
117 0           $self->[0]{needs_new_outer} = 0;
118 0           $self->[0]{inner_index} = 0;
119 0           $self->[0]{inner_count} = 0;
120             # warn "got new outer row: " . Dumper($self->[0]{outer_row});
121             } else {
122             # we've exhausted the outer iterator. we're now done.
123             # warn "exhausted";
124 0           return undef;
125             }
126             }
127            
128 0           my $ok = 1;
129 0           while ($self->[0]{inner_index} < scalar(@$inner)) {
130 0           my $inner_row = $inner->[ $self->[0]{inner_index}++ ];
131             # warn "using inner row: " . Dumper($inner_row);
132 0           my @shared = grep { exists $self->[0]{outer_row_vars}{ $_ } } $inner_row->variables;
  0            
133 0 0         if (scalar(@shared) == 0) {
    0          
134 0 0         if ($l->is_trace) {
135 0           $l->trace("no intersection of domains in minus: $inner_row ⋈ $self->[0]{outer_row}");
136             }
137             } elsif (my $joined = $inner_row->join( $self->[0]{outer_row} )) {
138 0 0         if ($l->is_trace) {
139 0           $l->trace("joined bindings in minus: $inner_row ⋈ $self->[0]{outer_row}");
140             }
141             # warn "-> joined\n";
142 0           $self->[0]{inner_count}++;
143 0           $self->[0]{count}++;
144 0           $ok = 0;
145 0           last;
146             } else {
147 0 0         if ($l->is_trace) {
148 0           $l->trace("failed to join bindings in minus: $inner_row ⋈ $self->[0]{outer_row}");
149             }
150             }
151             }
152            
153 0           $self->[0]{needs_new_outer} = 1;
154 0 0         if ($ok) {
155 0           my $bindings = $self->[0]{outer_row};
156 0 0         if (my $d = $self->delegate) {
157 0           $d->log_result( $self, $bindings );
158             }
159 0           return $bindings;
160             }
161             }
162             }
163              
164             =item C<< close >>
165              
166             =cut
167              
168             sub close {
169 0     0 1   my $self = shift;
170 0 0         unless ($self->state == $self->OPEN) {
171 0           throw RDF::Query::Error::ExecutionError -text => "close() cannot be called on an un-open Minus";
172             }
173            
174 0           my $l = Log::Log4perl->get_logger("rdf.query.plan.minus");
175 0           my $t0 = delete $self->[0]{start_time};
176 0           my $count = delete $self->[0]{count};
177 0 0         if (my $log = delete $self->[0]{logger}) {
178 0           $l->debug("logging minus execution statistics");
179 0           my $elapsed = tv_interval ( $t0 );
180 0 0         if (my $sparql = $self->logging_keys->{sparql}) {
181 0 0         if ($l->is_trace) {
182 0           $l->trace("- SPARQL: $sparql");
183 0           $l->trace("- elapsed: $elapsed");
184 0           $l->trace("- count: $count");
185             }
186 0           $log->push_key_value( 'execute_time-minus', $sparql, $elapsed );
187 0           $log->push_key_value( 'cardinality-minus', $sparql, $count );
188             }
189 0 0         if (my $bf = $self->logging_keys->{bf}) {
190 0 0         if ($l->is_trace) {
191 0           $l->trace("- bf: $bf");
192             }
193 0           $log->push_key_value( 'cardinality-bf-minus', $bf, $count );
194             }
195             }
196 0           delete $self->[0]{inner};
197 0           delete $self->[0]{outer};
198 0           delete $self->[0]{inner_index};
199 0           delete $self->[0]{needs_new_outer};
200 0           delete $self->[0]{inner_count};
201 0           $self->lhs->close();
202 0           $self->rhs->close();
203 0           $self->SUPER::close();
204             }
205              
206             =item C<< lhs >>
207              
208             Returns the left-hand-side plan to the join.
209              
210             =cut
211              
212             sub lhs {
213 0     0 1   my $self = shift;
214 0           return $self->[1];
215             }
216              
217             =item C<< rhs >>
218              
219             Returns the right-hand-side plan to the join.
220              
221             =cut
222              
223             sub rhs {
224 0     0 1   my $self = shift;
225 0           return $self->[2];
226             }
227              
228             =item C<< distinct >>
229              
230             Returns true if the pattern is guaranteed to return distinct results.
231              
232             =cut
233              
234             sub distinct {
235 0     0 1   return 0;
236             }
237              
238             =item C<< ordered >>
239              
240             Returns true if the pattern is guaranteed to return ordered results.
241              
242             =cut
243              
244             sub ordered {
245 0     0 1   return 0;
246             }
247              
248             =item C<< plan_node_name >>
249              
250             Returns the string name of this plan node, suitable for use in serialization.
251              
252             =cut
253              
254             sub plan_node_name {
255 0     0 1   my $self = shift;
256 0           return 'minus';
257             }
258              
259             =item C<< plan_prototype >>
260              
261             Returns a list of scalar identifiers for the type of the content (children)
262             nodes of this plan node. See L<RDF::Query::Plan> for a list of the allowable
263             identifiers.
264              
265             =cut
266              
267             sub plan_prototype {
268 0     0 1   my $self = shift;
269 0           return qw(P P);
270             }
271              
272             =item C<< plan_node_data >>
273              
274             Returns the data for this plan node that corresponds to the values described by
275             the signature returned by C<< plan_prototype >>.
276              
277             =cut
278              
279             sub plan_node_data {
280 0     0 1   my $self = shift;
281 0           my $expr = $self->[2];
282 0           return ($self->lhs, $self->rhs);
283             }
284              
285             =item C<< graph ( $g ) >>
286              
287             =cut
288              
289             sub graph {
290 0     0 1   my $self = shift;
291 0           my $g = shift;
292 0           my ($l, $r) = map { $_->graph( $g ) } ($self->lhs, $self->rhs);
  0            
293 0           $g->add_node( "$self", label => "Minus" . $self->graph_labels );
294 0           $g->add_edge( "$self", $l );
295 0           $g->add_edge( "$self", $r );
296 0           return "$self";
297             }
298              
299             1;
300              
301             __END__
302              
303             =back
304              
305             =head1 AUTHOR
306              
307             Gregory Todd Williams <gwilliams@cpan.org>
308              
309             =cut