line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# RDF::Query::Plan::Join::PushDownNestedLoop |
2
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
=head1 NAME |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
RDF::Query::Plan::Join::PushDownNestedLoop - Executable query plan for nested loop joins. |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
=head1 VERSION |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
This document describes RDF::Query::Plan::Join::PushDownNestedLoop 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::Join> class. |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
=over 4 |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
=cut |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
package RDF::Query::Plan::Join::PushDownNestedLoop; |
22
|
|
|
|
|
|
|
|
23
|
35
|
|
|
35
|
|
181
|
use strict; |
|
35
|
|
|
|
|
67
|
|
|
35
|
|
|
|
|
878
|
|
24
|
35
|
|
|
35
|
|
189
|
use warnings; |
|
35
|
|
|
|
|
65
|
|
|
35
|
|
|
|
|
927
|
|
25
|
35
|
|
|
35
|
|
171
|
use base qw(RDF::Query::Plan::Join); |
|
35
|
|
|
|
|
58
|
|
|
35
|
|
|
|
|
2587
|
|
26
|
35
|
|
|
35
|
|
185
|
use Scalar::Util qw(blessed refaddr); |
|
35
|
|
|
|
|
62
|
|
|
35
|
|
|
|
|
1853
|
|
27
|
35
|
|
|
35
|
|
186
|
use Data::Dumper; |
|
35
|
|
|
|
|
81
|
|
|
35
|
|
|
|
|
2887
|
|
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
###################################################################### |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
our ($VERSION); |
32
|
|
|
|
|
|
|
BEGIN { |
33
|
35
|
|
|
35
|
|
95
|
$VERSION = '2.915_01'; |
34
|
35
|
|
|
|
|
717
|
$RDF::Query::Plan::Join::JOIN_CLASSES{ 'RDF::Query::Plan::Join::PushDownNestedLoop' }++; |
35
|
|
|
|
|
|
|
} |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
###################################################################### |
38
|
|
|
|
|
|
|
|
39
|
35
|
|
|
35
|
|
192
|
use RDF::Query::ExecutionContext; |
|
35
|
|
|
|
|
66
|
|
|
35
|
|
|
|
|
52598
|
|
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
=item C<< new ( $lhs, $rhs, $opt ) >> |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
=cut |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
sub new { |
46
|
31
|
|
|
31
|
1
|
69
|
my $class = shift; |
47
|
31
|
|
|
|
|
57
|
my $lhs = shift; |
48
|
31
|
|
|
|
|
57
|
my $rhs = shift; |
49
|
|
|
|
|
|
|
|
50
|
31
|
50
|
|
|
|
238
|
if ($rhs->isa('RDF::Query::Plan::SubSelect')) { |
51
|
0
|
|
|
|
|
0
|
throw RDF::Query::Error::MethodInvocationError -text => "Subselects cannot be the RHS of a PushDownNestedLoop join"; |
52
|
|
|
|
|
|
|
} |
53
|
|
|
|
|
|
|
|
54
|
31
|
|
100
|
|
|
156
|
my $opt = shift || 0; |
55
|
31
|
100
|
100
|
|
|
246
|
if (not($opt) and $rhs->isa('RDF::Query::Plan::Join') and $rhs->optional) { |
|
|
|
66
|
|
|
|
|
56
|
|
|
|
|
|
|
# we can't push down results to an optional pattern because of the |
57
|
|
|
|
|
|
|
# bottom up semantics. see dawg test algebra/manifest#join-scope-1 |
58
|
|
|
|
|
|
|
# for example. |
59
|
1
|
|
|
|
|
6
|
throw RDF::Query::Error::MethodInvocationError -text => "PushDownNestedLoop join does not support optional patterns as RHS due to bottom-up variable scoping rules (use NestedLoop instead)"; |
60
|
|
|
|
|
|
|
} |
61
|
|
|
|
|
|
|
|
62
|
30
|
|
|
|
|
184
|
my @aggs = $rhs->subplans_of_type('RDF::Query::Plan::Aggregate'); |
63
|
30
|
50
|
|
|
|
104
|
if (scalar(@aggs)) { |
64
|
0
|
|
|
|
|
0
|
throw RDF::Query::Error::MethodInvocationError -text => "PushDownNestedLoop join does not support aggregates in the RHS due to aggregate group fragmentation"; |
65
|
|
|
|
|
|
|
} |
66
|
|
|
|
|
|
|
|
67
|
30
|
|
|
|
|
236
|
my $self = $class->SUPER::new( $lhs, $rhs, $opt ); |
68
|
30
|
|
|
|
|
99
|
return $self; |
69
|
|
|
|
|
|
|
} |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
=item C<< execute ( $execution_context ) >> |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
=cut |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
sub execute ($) { |
76
|
30
|
|
|
30
|
1
|
61
|
my $self = shift; |
77
|
30
|
|
|
|
|
56
|
my $context = shift; |
78
|
30
|
|
|
|
|
110
|
$self->[0]{delegate} = $context->delegate; |
79
|
30
|
50
|
|
|
|
154
|
if ($self->state == $self->OPEN) { |
80
|
0
|
|
|
|
|
0
|
throw RDF::Query::Error::ExecutionError -text => "PushDownNestedLoop join plan can't be executed while already open"; |
81
|
|
|
|
|
|
|
} |
82
|
|
|
|
|
|
|
|
83
|
30
|
|
|
|
|
125
|
my $l = Log::Log4perl->get_logger("rdf.query.plan.join.pushdownnestedloop"); |
84
|
30
|
|
|
|
|
6138
|
$l->trace("executing bind join with plans:"); |
85
|
30
|
|
|
|
|
311
|
$l->trace($self->lhs->sse); |
86
|
30
|
|
|
|
|
340
|
$l->trace($self->rhs->sse); |
87
|
|
|
|
|
|
|
|
88
|
30
|
|
|
|
|
279
|
$self->lhs->execute( $context ); |
89
|
30
|
50
|
|
|
|
123
|
if ($self->lhs->state == $self->OPEN) { |
90
|
30
|
|
|
|
|
65
|
delete $self->[0]{stats}; |
91
|
30
|
|
|
|
|
79
|
$self->[0]{context} = $context; |
92
|
30
|
|
|
|
|
102
|
$self->[0]{outer} = $self->lhs; |
93
|
30
|
|
|
|
|
82
|
$self->[0]{needs_new_outer} = 1; |
94
|
30
|
|
|
|
|
87
|
$self->[0]{inner_count} = 0; |
95
|
30
|
|
|
|
|
137
|
$self->state( $self->OPEN ); |
96
|
|
|
|
|
|
|
} else { |
97
|
0
|
|
|
|
|
0
|
warn "no iterator in execute()"; |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
# warn '########################################'; |
100
|
30
|
|
|
|
|
102
|
$self; |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
=item C<< next >> |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
=cut |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
sub next { |
108
|
89
|
|
|
89
|
1
|
162
|
my $self = shift; |
109
|
89
|
50
|
|
|
|
266
|
unless ($self->state == $self->OPEN) { |
110
|
0
|
|
|
|
|
0
|
throw RDF::Query::Error::ExecutionError -text => "next() cannot be called on an un-open PushDownNestedLoop join"; |
111
|
|
|
|
|
|
|
} |
112
|
89
|
|
|
|
|
189
|
my $outer = $self->[0]{outer}; |
113
|
89
|
|
|
|
|
287
|
my $inner = $self->rhs; |
114
|
89
|
|
|
|
|
161
|
my $opt = $self->[3]; |
115
|
|
|
|
|
|
|
|
116
|
89
|
|
|
|
|
269
|
my $l = Log::Log4perl->get_logger("rdf.query.plan.join.pushdownnestedloop"); |
117
|
89
|
|
|
|
|
1616
|
while (1) { |
118
|
162
|
100
|
|
|
|
419
|
if ($self->[0]{needs_new_outer}) { |
119
|
115
|
|
|
|
|
403
|
$self->[0]{outer_row} = $outer->next; |
120
|
115
|
|
|
|
|
338
|
my $outer = $self->[0]{outer_row}; |
121
|
115
|
100
|
|
|
|
938
|
if (ref($outer)) { |
122
|
90
|
|
|
|
|
202
|
$self->[0]{stats}{outer_rows}++; |
123
|
90
|
|
|
|
|
168
|
my $context = $self->[0]{context}; |
124
|
90
|
|
|
|
|
142
|
$self->[0]{needs_new_outer} = 0; |
125
|
90
|
|
|
|
|
151
|
$self->[0]{inner_count} = 0; |
126
|
90
|
50
|
|
|
|
247
|
if ($self->[0]{inner}) { |
127
|
0
|
|
|
|
|
0
|
$self->[0]{inner}->close(); |
128
|
|
|
|
|
|
|
} |
129
|
90
|
|
|
|
|
128
|
my %bound = %{ $context->bound }; |
|
90
|
|
|
|
|
301
|
|
130
|
90
|
|
|
|
|
392
|
@bound{ keys %$outer } = values %$outer; |
131
|
90
|
|
|
|
|
377
|
my $copy = $context->copy( bound => \%bound ); |
132
|
90
|
|
|
|
|
357
|
$l->trace( "executing inner plan with bound: " . Dumper(\%bound) ); |
133
|
90
|
50
|
|
|
|
8607
|
if ($inner->state == $inner->OPEN) { |
134
|
0
|
|
|
|
|
0
|
$inner->close(); |
135
|
|
|
|
|
|
|
} |
136
|
90
|
|
|
|
|
310
|
$self->[0]{inner} = $inner->execute( $copy ); |
137
|
|
|
|
|
|
|
} else { |
138
|
|
|
|
|
|
|
# we've exhausted the outer iterator. we're now done. |
139
|
25
|
|
|
|
|
91
|
$l->trace("exhausted outer plan in bind join"); |
140
|
25
|
|
|
|
|
205
|
return undef; |
141
|
|
|
|
|
|
|
} |
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
|
144
|
137
|
|
|
|
|
519
|
while (defined(my $inner_row = $self->[0]{inner}->next)) { |
145
|
66
|
|
|
|
|
186
|
$self->[0]{stats}{inner_rows}++; |
146
|
66
|
|
|
|
|
259
|
$l->trace( "using inner row: " . $inner_row->as_string ); |
147
|
66
|
100
|
|
|
|
3902
|
if (defined(my $joined = $inner_row->join( $self->[0]{outer_row} ))) { |
148
|
50
|
|
|
|
|
1105
|
$self->[0]{stats}{results}++; |
149
|
50
|
50
|
|
|
|
169
|
if ($l->is_trace) { |
150
|
0
|
|
|
|
|
0
|
$l->trace("joined bindings: $inner_row â $self->[0]{outer_row}"); |
151
|
|
|
|
|
|
|
} |
152
|
|
|
|
|
|
|
# warn "-> joined\n"; |
153
|
50
|
|
|
|
|
377
|
$self->[0]{inner_count}++; |
154
|
50
|
50
|
|
|
|
208
|
if (my $d = $self->delegate) { |
155
|
0
|
|
|
|
|
0
|
$d->log_result( $self, $joined ); |
156
|
|
|
|
|
|
|
} |
157
|
50
|
|
|
|
|
198
|
return $joined; |
158
|
|
|
|
|
|
|
} else { |
159
|
16
|
|
|
|
|
1159
|
$l->trace("failed to join bindings: $inner_row |><| $self->[0]{outer_row}"); |
160
|
16
|
50
|
|
|
|
2005
|
if ($opt) { |
161
|
0
|
|
|
|
|
0
|
$l->trace( "--> but operation is OPTIONAL, so returning $self->[0]{outer_row}" ); |
162
|
0
|
0
|
|
|
|
0
|
if (my $d = $self->delegate) { |
163
|
0
|
|
|
|
|
0
|
$d->log_result( $self, $self->[0]{outer_row} ); |
164
|
|
|
|
|
|
|
} |
165
|
0
|
|
|
|
|
0
|
return $self->[0]{outer_row}; |
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
} |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
|
170
|
87
|
|
|
|
|
174
|
$self->[0]{needs_new_outer} = 1; |
171
|
87
|
50
|
|
|
|
294
|
if ($self->[0]{inner}->state == $self->OPEN) { |
172
|
87
|
|
|
|
|
311
|
$self->[0]{inner}->close(); |
173
|
|
|
|
|
|
|
} |
174
|
87
|
|
|
|
|
169
|
delete $self->[0]{inner}; |
175
|
87
|
100
|
100
|
|
|
320
|
if ($opt and $self->[0]{inner_count} == 0) { |
176
|
14
|
50
|
|
|
|
57
|
if (my $d = $self->delegate) { |
177
|
0
|
|
|
|
|
0
|
$d->log_result( $self, $self->[0]{outer_row} ); |
178
|
|
|
|
|
|
|
} |
179
|
14
|
|
|
|
|
61
|
return $self->[0]{outer_row}; |
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
} |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
=item C<< close >> |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
=cut |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
sub close { |
189
|
30
|
|
|
30
|
1
|
65
|
my $self = shift; |
190
|
30
|
50
|
|
|
|
105
|
unless ($self->state == $self->OPEN) { |
191
|
0
|
|
|
|
|
0
|
throw RDF::Query::Error::ExecutionError -text => "close() cannot be called on an un-open PushDownNestedLoop join"; |
192
|
|
|
|
|
|
|
} |
193
|
30
|
|
|
|
|
68
|
delete $self->[0]{inner}; |
194
|
30
|
|
|
|
|
67
|
delete $self->[0]{outer}; |
195
|
30
|
|
|
|
|
58
|
delete $self->[0]{needs_new_outer}; |
196
|
30
|
|
|
|
|
67
|
delete $self->[0]{inner_count}; |
197
|
30
|
50
|
33
|
|
|
122
|
if (blessed($self->lhs) and $self->lhs->state == $self->lhs->OPEN) { |
198
|
30
|
|
|
|
|
96
|
$self->lhs->close(); |
199
|
|
|
|
|
|
|
} |
200
|
30
|
|
|
|
|
167
|
$self->SUPER::close(); |
201
|
|
|
|
|
|
|
} |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
=item C<< plan_node_name >> |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
Returns the string name of this plan node, suitable for use in serialization. |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
=cut |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
sub plan_node_name { |
210
|
7
|
|
|
7
|
1
|
16
|
my $self = shift; |
211
|
7
|
100
|
|
|
|
44
|
my $jtype = $self->optional ? 'leftjoin' : 'join'; |
212
|
7
|
|
|
|
|
26
|
return "bind-$jtype"; |
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
=item C<< graph ( $g ) >> |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
=cut |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
sub graph { |
220
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
221
|
0
|
|
|
|
|
|
my $g = shift; |
222
|
0
|
0
|
|
|
|
|
my $jtype = $self->optional ? 'Left Join' : 'Join'; |
223
|
0
|
|
|
|
|
|
my ($l, $r) = map { $_->graph( $g ) } ($self->lhs, $self->rhs); |
|
0
|
|
|
|
|
|
|
224
|
0
|
|
|
|
|
|
$g->add_node( "$self", label => "$jtype (Bind)" . $self->graph_labels ); |
225
|
0
|
|
|
|
|
|
$g->add_edge( "$self", $l ); |
226
|
0
|
|
|
|
|
|
$g->add_edge( "$self", $r ); |
227
|
0
|
|
|
|
|
|
return "$self"; |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
=item C<< explain >> |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
Returns a string serialization of the plan appropriate for display on the |
233
|
|
|
|
|
|
|
command line. |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
=cut |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
sub explain { |
238
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
239
|
0
|
|
|
|
|
|
my $s = shift; |
240
|
0
|
|
|
|
|
|
my $count = shift; |
241
|
0
|
|
|
|
|
|
my $indent = $s x $count; |
242
|
0
|
|
|
|
|
|
my $type = $self->plan_node_name; |
243
|
0
|
|
|
|
|
|
my $stats = ''; |
244
|
0
|
0
|
|
|
|
|
if ($self->[0]{stats}) { |
245
|
0
|
|
|
|
|
|
$stats = sprintf(' [%d/%d/%d]', @{ $self->[0]{stats} }{qw(outer_rows inner_rows results)}); |
|
0
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
} |
247
|
0
|
|
|
|
|
|
my $string = sprintf("%s%s%s (0x%x)\n", $indent, $type, $stats, refaddr($self)); |
248
|
0
|
|
|
|
|
|
foreach my $p ($self->plan_node_data) { |
249
|
0
|
|
|
|
|
|
$string .= $p->explain( $s, $count+1 ); |
250
|
|
|
|
|
|
|
} |
251
|
0
|
|
|
|
|
|
return $string; |
252
|
|
|
|
|
|
|
} |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
package RDF::Query::Plan::Join::PushDownNestedLoop::Left; |
256
|
|
|
|
|
|
|
|
257
|
35
|
|
|
35
|
|
206
|
use strict; |
|
35
|
|
|
|
|
75
|
|
|
35
|
|
|
|
|
805
|
|
258
|
35
|
|
|
35
|
|
178
|
use warnings; |
|
35
|
|
|
|
|
69
|
|
|
35
|
|
|
|
|
1060
|
|
259
|
35
|
|
|
35
|
|
248
|
use base qw(RDF::Query::Plan::Join::PushDownNestedLoop); |
|
35
|
|
|
|
|
64
|
|
|
35
|
|
|
|
|
4100
|
|
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
sub new { |
262
|
0
|
|
|
0
|
|
|
my $class = shift; |
263
|
0
|
|
|
|
|
|
my $lhs = shift; |
264
|
0
|
|
|
|
|
|
my $rhs = shift; |
265
|
0
|
|
|
|
|
|
return $class->SUPER::new( $lhs, $rhs, 1 ); |
266
|
|
|
|
|
|
|
} |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
1; |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
__END__ |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
=back |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
=head1 AUTHOR |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
Gregory Todd Williams <gwilliams@cpan.org> |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
=cut |