line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# RDF::Query::Plan |
2
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
=head1 NAME |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
RDF::Query::Plan - Executable query plan nodes. |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
=head1 VERSION |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
This document describes RDF::Query::Plan version 2.916. |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
=head1 METHODS |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
=over 4 |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
=cut |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
package RDF::Query::Plan; |
19
|
|
|
|
|
|
|
|
20
|
35
|
|
|
35
|
|
207
|
use strict; |
|
35
|
|
|
|
|
81
|
|
|
35
|
|
|
|
|
958
|
|
21
|
35
|
|
|
35
|
|
225
|
use warnings; |
|
35
|
|
|
|
|
81
|
|
|
35
|
|
|
|
|
992
|
|
22
|
35
|
|
|
35
|
|
192
|
use Data::Dumper; |
|
35
|
|
|
|
|
76
|
|
|
35
|
|
|
|
|
1731
|
|
23
|
35
|
|
|
35
|
|
193
|
use List::Util qw(reduce); |
|
35
|
|
|
|
|
85
|
|
|
35
|
|
|
|
|
2129
|
|
24
|
35
|
|
|
35
|
|
191
|
use Scalar::Util qw(blessed reftype refaddr); |
|
35
|
|
|
|
|
74
|
|
|
35
|
|
|
|
|
2043
|
|
25
|
35
|
|
|
35
|
|
198
|
use RDF::Query::Error qw(:try); |
|
35
|
|
|
|
|
99
|
|
|
35
|
|
|
|
|
270
|
|
26
|
35
|
|
|
35
|
|
23752
|
use RDF::Query::BGPOptimizer; |
|
35
|
|
|
|
|
111
|
|
|
35
|
|
|
|
|
1117
|
|
27
|
|
|
|
|
|
|
|
28
|
35
|
|
|
35
|
|
23881
|
use RDF::Query::Plan::Aggregate; |
|
35
|
|
|
|
|
116
|
|
|
35
|
|
|
|
|
1085
|
|
29
|
35
|
|
|
35
|
|
22042
|
use RDF::Query::Plan::BasicGraphPattern; |
|
35
|
|
|
|
|
110
|
|
|
35
|
|
|
|
|
1071
|
|
30
|
35
|
|
|
35
|
|
20703
|
use RDF::Query::Plan::Constant; |
|
35
|
|
|
|
|
105
|
|
|
35
|
|
|
|
|
1035
|
|
31
|
35
|
|
|
35
|
|
20970
|
use RDF::Query::Plan::Construct; |
|
35
|
|
|
|
|
113
|
|
|
35
|
|
|
|
|
1108
|
|
32
|
35
|
|
|
35
|
|
20808
|
use RDF::Query::Plan::Distinct; |
|
35
|
|
|
|
|
108
|
|
|
35
|
|
|
|
|
1197
|
|
33
|
35
|
|
|
35
|
|
20770
|
use RDF::Query::Plan::Filter; |
|
35
|
|
|
|
|
113
|
|
|
35
|
|
|
|
|
1227
|
|
34
|
35
|
|
|
35
|
|
23573
|
use RDF::Query::Plan::Join::NestedLoop; |
|
35
|
|
|
|
|
101
|
|
|
35
|
|
|
|
|
1431
|
|
35
|
35
|
|
|
35
|
|
23109
|
use RDF::Query::Plan::Join::PushDownNestedLoop; |
|
35
|
|
|
|
|
89
|
|
|
35
|
|
|
|
|
1556
|
|
36
|
35
|
|
|
35
|
|
20853
|
use RDF::Query::Plan::Limit; |
|
35
|
|
|
|
|
95
|
|
|
35
|
|
|
|
|
1538
|
|
37
|
35
|
|
|
35
|
|
20895
|
use RDF::Query::Plan::Offset; |
|
35
|
|
|
|
|
103
|
|
|
35
|
|
|
|
|
1524
|
|
38
|
35
|
|
|
35
|
|
21824
|
use RDF::Query::Plan::Project; |
|
35
|
|
|
|
|
98
|
|
|
35
|
|
|
|
|
1646
|
|
39
|
35
|
|
|
35
|
|
21339
|
use RDF::Query::Plan::Extend; |
|
35
|
|
|
|
|
112
|
|
|
35
|
|
|
|
|
1783
|
|
40
|
35
|
|
|
35
|
|
21670
|
use RDF::Query::Plan::Quad; |
|
35
|
|
|
|
|
114
|
|
|
35
|
|
|
|
|
1940
|
|
41
|
35
|
|
|
35
|
|
23364
|
use RDF::Query::Plan::Service; |
|
35
|
|
|
|
|
115
|
|
|
35
|
|
|
|
|
2105
|
|
42
|
35
|
|
|
35
|
|
21627
|
use RDF::Query::Plan::Sort; |
|
35
|
|
|
|
|
102
|
|
|
35
|
|
|
|
|
2243
|
|
43
|
35
|
|
|
35
|
|
21943
|
use RDF::Query::Plan::ComputedStatement; |
|
35
|
|
|
|
|
108
|
|
|
35
|
|
|
|
|
2085
|
|
44
|
35
|
|
|
35
|
|
21894
|
use RDF::Query::Plan::ThresholdUnion; |
|
35
|
|
|
|
|
103
|
|
|
35
|
|
|
|
|
2077
|
|
45
|
35
|
|
|
35
|
|
20826
|
use RDF::Query::Plan::Union; |
|
35
|
|
|
|
|
104
|
|
|
35
|
|
|
|
|
2065
|
|
46
|
35
|
|
|
35
|
|
21642
|
use RDF::Query::Plan::SubSelect; |
|
35
|
|
|
|
|
126
|
|
|
35
|
|
|
|
|
2245
|
|
47
|
35
|
|
|
35
|
|
20457
|
use RDF::Query::Plan::Iterator; |
|
35
|
|
|
|
|
108
|
|
|
35
|
|
|
|
|
2100
|
|
48
|
35
|
|
|
35
|
|
21076
|
use RDF::Query::Plan::Load; |
|
35
|
|
|
|
|
107
|
|
|
35
|
|
|
|
|
2139
|
|
49
|
35
|
|
|
35
|
|
21097
|
use RDF::Query::Plan::Clear; |
|
35
|
|
|
|
|
108
|
|
|
35
|
|
|
|
|
2271
|
|
50
|
35
|
|
|
35
|
|
22107
|
use RDF::Query::Plan::Update; |
|
35
|
|
|
|
|
109
|
|
|
35
|
|
|
|
|
2390
|
|
51
|
35
|
|
|
35
|
|
21777
|
use RDF::Query::Plan::Minus; |
|
35
|
|
|
|
|
107
|
|
|
35
|
|
|
|
|
2402
|
|
52
|
35
|
|
|
35
|
|
20974
|
use RDF::Query::Plan::Sequence; |
|
35
|
|
|
|
|
105
|
|
|
35
|
|
|
|
|
2324
|
|
53
|
35
|
|
|
35
|
|
22724
|
use RDF::Query::Plan::Path; |
|
35
|
|
|
|
|
117
|
|
|
35
|
|
|
|
|
2825
|
|
54
|
35
|
|
|
35
|
|
22363
|
use RDF::Query::Plan::NamedGraph; |
|
35
|
|
|
|
|
117
|
|
|
35
|
|
|
|
|
2630
|
|
55
|
35
|
|
|
35
|
|
21221
|
use RDF::Query::Plan::Copy; |
|
35
|
|
|
|
|
109
|
|
|
35
|
|
|
|
|
2533
|
|
56
|
35
|
|
|
35
|
|
21555
|
use RDF::Query::Plan::Move; |
|
35
|
|
|
|
|
107
|
|
|
35
|
|
|
|
|
2568
|
|
57
|
|
|
|
|
|
|
|
58
|
35
|
|
|
35
|
|
238
|
use RDF::Trine::Statement; |
|
35
|
|
|
|
|
76
|
|
|
35
|
|
|
|
|
1135
|
|
59
|
35
|
|
|
35
|
|
206
|
use RDF::Trine::Statement::Quad; |
|
35
|
|
|
|
|
72
|
|
|
35
|
|
|
|
|
1107
|
|
60
|
|
|
|
|
|
|
|
61
|
35
|
|
|
35
|
|
208
|
use constant READY => 0x01; |
|
35
|
|
|
|
|
71
|
|
|
35
|
|
|
|
|
3145
|
|
62
|
35
|
|
|
35
|
|
189
|
use constant OPEN => 0x02; |
|
35
|
|
|
|
|
77
|
|
|
35
|
|
|
|
|
2250
|
|
63
|
35
|
|
|
35
|
|
198
|
use constant CLOSED => 0x04; |
|
35
|
|
|
|
|
70
|
|
|
35
|
|
|
|
|
3760
|
|
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
###################################################################### |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
our ($VERSION, %PLAN_CLASSES); |
68
|
|
|
|
|
|
|
BEGIN { |
69
|
35
|
|
|
35
|
|
110
|
$VERSION = '2.916'; |
70
|
35
|
|
|
|
|
20794
|
%PLAN_CLASSES = ( |
71
|
|
|
|
|
|
|
service => 'RDF::Query::Plan::Service', |
72
|
|
|
|
|
|
|
); |
73
|
|
|
|
|
|
|
} |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
###################################################################### |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
=item C<< new >> |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
=cut |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
sub new { |
82
|
556
|
|
|
556
|
1
|
864
|
my $class = shift; |
83
|
556
|
|
|
|
|
1245
|
my @args = @_; |
84
|
556
|
|
|
|
|
4797
|
return bless( [ { __state => $class->READY }, @args ], $class ); |
85
|
|
|
|
|
|
|
} |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
=item C<< execute ( $execution_context ) >> |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
=cut |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
sub execute ($); |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
=item C<< next >> |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
=cut |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
sub next; |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
=item C<< get_all >> |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
Returns all remaining rows. |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
=cut |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
sub get_all { |
106
|
12
|
|
|
12
|
1
|
28
|
my $self = shift; |
107
|
12
|
50
|
|
|
|
39
|
unless ($self->state == $self->OPEN) { |
108
|
0
|
|
|
|
|
0
|
throw RDF::Query::Error::ExecutionError -text => "get_all can't be called on an unopen plan"; |
109
|
|
|
|
|
|
|
} |
110
|
12
|
|
|
|
|
29
|
my @rows; |
111
|
12
|
|
|
|
|
75
|
while (my $row = $self->next) { |
112
|
34
|
|
|
|
|
146
|
push(@rows, $row); |
113
|
|
|
|
|
|
|
} |
114
|
12
|
|
|
|
|
62
|
return @rows; |
115
|
|
|
|
|
|
|
} |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
=item C<< close >> |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
=cut |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
sub close { |
122
|
590
|
|
|
590
|
1
|
881
|
my $self = shift; |
123
|
590
|
|
|
|
|
1243
|
$self->state( CLOSED ); |
124
|
|
|
|
|
|
|
} |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
=item C<< state ( [ $state ] ) >> |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
Returns the current state of the plan (either READY, OPEN, or CLOSED). |
129
|
|
|
|
|
|
|
If C<< $state >> is provided, updates the plan to a new state. |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
=cut |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
sub state { |
134
|
5081
|
|
|
5081
|
1
|
6781
|
my $self = shift; |
135
|
5081
|
100
|
|
|
|
10970
|
if (scalar(@_)) { |
136
|
1190
|
|
|
|
|
2095
|
$self->[0]{__state} = shift; |
137
|
|
|
|
|
|
|
} |
138
|
5081
|
|
|
|
|
26580
|
return $self->[0]{__state}; |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
=item C<< logging_keys >> |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
=cut |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
sub logging_keys { |
146
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
147
|
0
|
|
0
|
|
|
0
|
return $self->[0]{logging_keys} || {}; |
148
|
|
|
|
|
|
|
} |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
=item C<< explain >> |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
Returns a string serialization of the query plan appropriate for display |
153
|
|
|
|
|
|
|
on the command line. |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
=cut |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
sub explain { |
158
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
159
|
|
|
|
|
|
|
# warn 'Explaining query plan: ' . $self->serialize(); |
160
|
0
|
|
|
|
|
0
|
my ($s, $count) = (' ', 0); |
161
|
0
|
0
|
|
|
|
0
|
if (@_) { |
162
|
0
|
|
|
|
|
0
|
$s = shift; |
163
|
0
|
|
|
|
|
0
|
$count = shift; |
164
|
|
|
|
|
|
|
} |
165
|
0
|
|
|
|
|
0
|
my $indent = '' . ($s x $count); |
166
|
0
|
|
|
|
|
0
|
my $type = $self->plan_node_name; |
167
|
0
|
|
|
|
|
0
|
my $string = sprintf("%s%s (0x%x)\n", $indent, $type, refaddr($self)); |
168
|
0
|
|
|
|
|
0
|
foreach my $p ($self->plan_node_data) { |
169
|
0
|
0
|
|
|
|
0
|
if (blessed($p)) { |
|
|
0
|
|
|
|
|
|
170
|
0
|
0
|
|
|
|
0
|
if ($p->isa('RDF::Trine::Statement::Quad')) { |
|
|
0
|
|
|
|
|
|
171
|
0
|
0
|
|
|
|
0
|
$string .= "${indent}${s}" . join(' ', map { ($_->isa('RDF::Trine::Node::Nil')) ? "(nil)" : $_->as_sparql } $p->nodes) . "\n"; |
|
0
|
|
|
|
|
0
|
|
172
|
|
|
|
|
|
|
} elsif ($p->isa('RDF::Trine::Node::Nil')) { |
173
|
0
|
|
|
|
|
0
|
$string .= "${indent}${s}(nil)\n"; |
174
|
|
|
|
|
|
|
} else { |
175
|
0
|
|
|
|
|
0
|
$string .= $p->explain( $s, $count+1 ); |
176
|
|
|
|
|
|
|
} |
177
|
|
|
|
|
|
|
} elsif (ref($p)) { |
178
|
0
|
|
|
|
|
0
|
$string .= "${indent}${s}" . Dumper($p); |
179
|
0
|
|
|
|
|
0
|
Carp::cluck 'unexpected non-blessed ref in RDF::Query::Plan->explain: ' . Dumper($p); |
180
|
|
|
|
|
|
|
} else { |
181
|
35
|
|
|
35
|
|
230
|
no warnings 'uninitialized'; |
|
35
|
|
|
|
|
85
|
|
|
35
|
|
|
|
|
19218
|
|
182
|
0
|
|
|
|
|
0
|
$string .= "${indent}${s}$p\n"; |
183
|
|
|
|
|
|
|
} |
184
|
|
|
|
|
|
|
} |
185
|
0
|
|
|
|
|
0
|
return $string; |
186
|
|
|
|
|
|
|
} |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
=item C<< sse >> |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
=cut |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
sub sse { |
193
|
188
|
|
|
188
|
1
|
421
|
my $self = shift; |
194
|
188
|
|
100
|
|
|
717
|
my $context = shift || {}; |
195
|
188
|
|
100
|
|
|
680
|
my $indent = shift || ''; |
196
|
188
|
|
|
|
|
274
|
my $more = ' '; |
197
|
188
|
|
|
|
|
1480
|
my @proto = $self->plan_prototype; |
198
|
188
|
|
|
|
|
633
|
my @data = $self->plan_node_data; |
199
|
188
|
|
|
|
|
629
|
my $name = $self->plan_node_name; |
200
|
|
|
|
|
|
|
|
201
|
188
|
|
|
|
|
277
|
my @args; |
202
|
188
|
|
|
|
|
267
|
my $list = \@data; |
203
|
188
|
|
|
|
|
450
|
foreach my $i (0 .. $#proto) { |
204
|
583
|
|
|
|
|
6635
|
my $p = $proto[ $i ]; |
205
|
583
|
|
|
|
|
1530
|
push(@args, $self->_sse( $context, $indent, $more, $p, $list )); |
206
|
|
|
|
|
|
|
} |
207
|
188
|
100
|
|
|
|
1113
|
return "(${name} " . join(' ', map { defined($_) ? $_ : '()' } @args) . ")"; |
|
583
|
|
|
|
|
2501
|
|
208
|
|
|
|
|
|
|
} |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
sub _sse { |
211
|
687
|
|
|
687
|
|
948
|
my $self = shift; |
212
|
687
|
|
|
|
|
856
|
my $context = shift; |
213
|
687
|
|
|
|
|
881
|
my $indent = shift; |
214
|
687
|
|
|
|
|
834
|
my $more = shift; |
215
|
687
|
|
|
|
|
909
|
my $p = shift; |
216
|
687
|
|
|
|
|
836
|
my $list = shift; |
217
|
687
|
100
|
|
|
|
2175
|
if ($p =~ m/^[PQTNWEJVqibswu]$/) { |
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
218
|
630
|
|
|
|
|
907
|
my $v = shift(@$list); |
219
|
630
|
|
|
|
|
1652
|
return $self->_sse_atom($context, $indent, $more, $p, $v); |
220
|
|
|
|
|
|
|
} elsif ($p eq 'A') { |
221
|
0
|
|
|
|
|
0
|
my $v = shift(@$list); |
222
|
0
|
0
|
|
|
|
0
|
if (blessed($v)) { |
223
|
0
|
|
|
|
|
0
|
return $v->sse( $context, $indent ); |
224
|
|
|
|
|
|
|
} else { |
225
|
0
|
|
|
|
|
0
|
return '()'; |
226
|
|
|
|
|
|
|
} |
227
|
|
|
|
|
|
|
} elsif (substr($p, 0, 1) eq '\\') { |
228
|
26
|
|
|
|
|
56
|
my $rest = substr($p, 1); |
229
|
26
|
|
|
|
|
42
|
my $v = shift(@$list); |
230
|
26
|
|
|
|
|
56
|
my @args; |
231
|
26
|
|
|
|
|
95
|
while (@$v) { |
232
|
30
|
|
|
|
|
213
|
push(@args, $self->_sse( $context, $indent, $more, $rest, $v )); |
233
|
|
|
|
|
|
|
} |
234
|
26
|
|
|
|
|
580
|
return '(' . join(' ', @args) . ')'; |
235
|
|
|
|
|
|
|
} elsif (substr($p, 0, 1) eq '*') { |
236
|
31
|
|
|
|
|
64
|
my $rest = substr($p, 1); |
237
|
31
|
|
|
|
|
55
|
my @args; |
238
|
31
|
|
|
|
|
92
|
while (@$list) { |
239
|
74
|
|
|
|
|
2622
|
push(@args, $self->_sse( $context, $indent, $more, $rest, $list )); |
240
|
|
|
|
|
|
|
} |
241
|
35
|
|
|
35
|
|
208
|
no warnings 'uninitialized'; |
|
35
|
|
|
|
|
79
|
|
|
35
|
|
|
|
|
10562
|
|
242
|
31
|
|
|
|
|
1522
|
return join("\n${indent}${more}", '', @args); |
243
|
|
|
|
|
|
|
} elsif ($p =~ m/^[PQTNWEJVqibswu\\*]{2,}$/) { |
244
|
0
|
|
|
|
|
0
|
my @args; |
245
|
0
|
|
|
|
|
0
|
foreach my $p2 (split(//, $p)) { |
246
|
0
|
|
|
|
|
0
|
my $v = shift(@$list); |
247
|
0
|
|
|
|
|
0
|
push(@args, $self->_sse($context, $indent, $more, $p2, [$v])); |
248
|
|
|
|
|
|
|
} |
249
|
0
|
|
|
|
|
0
|
return '(' . join(' ', @args) . ')'; |
250
|
|
|
|
|
|
|
} else { |
251
|
0
|
|
|
|
|
0
|
Carp::confess "unrecognized plan node prototype '$p'"; |
252
|
|
|
|
|
|
|
} |
253
|
|
|
|
|
|
|
} |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
sub _sse_atom { |
256
|
630
|
|
|
630
|
|
833
|
my $self = shift; |
257
|
630
|
|
50
|
|
|
1356
|
my $context = shift || {}; |
258
|
630
|
|
|
|
|
826
|
my $indent = shift; |
259
|
630
|
|
|
|
|
799
|
my $more = shift; |
260
|
630
|
|
|
|
|
768
|
my $p = shift; |
261
|
630
|
|
|
|
|
761
|
my $v = shift; |
262
|
35
|
|
|
35
|
|
211
|
no warnings 'uninitialized'; |
|
35
|
|
|
|
|
79
|
|
|
35
|
|
|
|
|
340378
|
|
263
|
|
|
|
|
|
|
|
264
|
630
|
|
100
|
|
|
2196
|
my $ns = $context->{ namespaces } || {}; |
265
|
630
|
|
|
|
|
1371
|
my %ns = %$ns; |
266
|
|
|
|
|
|
|
|
267
|
630
|
100
|
|
|
|
3998
|
if ($p eq 's') { |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
268
|
8
|
|
|
|
|
15
|
for ($v) { |
269
|
8
|
|
|
|
|
13
|
s/\\/\\\\/g; |
270
|
8
|
|
|
|
|
14
|
s/"/\\"/g; |
271
|
8
|
|
|
|
|
33
|
s/\n/\\n/g; |
272
|
8
|
|
|
|
|
18
|
s/\t/\\t/g; |
273
|
|
|
|
|
|
|
} |
274
|
8
|
|
|
|
|
36
|
return qq["$v"]; |
275
|
|
|
|
|
|
|
} elsif ($p eq 'w') { |
276
|
0
|
|
|
|
|
0
|
return $v; |
277
|
|
|
|
|
|
|
} elsif ($p eq 'u') { |
278
|
0
|
|
|
|
|
0
|
return qq[<$v>]; |
279
|
|
|
|
|
|
|
} elsif ($p eq 'i') { |
280
|
3
|
|
|
|
|
14
|
return $v; |
281
|
|
|
|
|
|
|
} elsif ($p eq 'b') { |
282
|
0
|
|
|
|
|
0
|
return $v; |
283
|
|
|
|
|
|
|
} elsif ($p eq 'W') { |
284
|
0
|
0
|
|
|
|
0
|
if (blessed($v)) { |
285
|
0
|
|
|
|
|
0
|
return $v->sse( { namespaces => \%ns }, "${indent}${more}" ); |
286
|
|
|
|
|
|
|
} else { |
287
|
0
|
|
|
|
|
0
|
return $v; |
288
|
|
|
|
|
|
|
} |
289
|
|
|
|
|
|
|
} elsif ($p =~ m/^[PNETV]$/) { |
290
|
589
|
100
|
|
|
|
1861
|
if (blessed($v)) { |
291
|
|
|
|
|
|
|
|
292
|
580
|
50
|
|
|
|
2323
|
Carp::cluck unless ($v->can('sse')); |
293
|
580
|
|
|
|
|
2824
|
return $v->sse( { namespaces => \%ns }, "${indent}${more}" ); |
294
|
|
|
|
|
|
|
} else { |
295
|
9
|
|
|
|
|
38
|
return '()'; |
296
|
|
|
|
|
|
|
} |
297
|
|
|
|
|
|
|
} elsif ($p eq 'J') { |
298
|
30
|
100
|
|
|
|
164
|
if ($v->isa('RDF::Query::Node::Variable')) { |
299
|
2
|
|
|
|
|
6
|
return $v->name; |
300
|
|
|
|
|
|
|
} else { |
301
|
28
|
|
|
|
|
175
|
return $v->sse( { namespaces => \%ns }, "${indent}${more}" ); |
302
|
|
|
|
|
|
|
} |
303
|
|
|
|
|
|
|
} |
304
|
|
|
|
|
|
|
} |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
=item C<< serialize >> |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
Return a serialization of the query plan. |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
=cut |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
sub serialize { |
313
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
} |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
=item C<< delegate >> |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
Returns the delegate object if available. |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
=cut |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
sub delegate { |
324
|
981
|
|
|
981
|
1
|
1602
|
my $self = shift; |
325
|
981
|
|
|
|
|
4582
|
return $self->[0]{delegate}; |
326
|
|
|
|
|
|
|
} |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
=item C<< referenced_variables >> |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
Returns a list of variable names that are referenced by this plan. |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
=cut |
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
sub referenced_variables { |
335
|
366
|
|
|
366
|
1
|
567
|
my $self = shift; |
336
|
366
|
|
|
|
|
641
|
my $refs = $self->[0]{referenced_variables}; |
337
|
366
|
|
|
|
|
495
|
return @{ $refs }; |
|
366
|
|
|
|
|
1653
|
|
338
|
|
|
|
|
|
|
} |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
=item C<< as_iterator ( $context ) >> |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
Returns an RDF::Trine::Iterator object for the current (already executed) plan. |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
=cut |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
sub as_iterator { |
347
|
153
|
|
|
153
|
1
|
300
|
my $self = shift; |
348
|
153
|
|
|
|
|
258
|
my $context = shift; |
349
|
153
|
|
|
|
|
682
|
my $vars = $context->requested_variables; |
350
|
153
|
|
|
321
|
|
1977
|
my $stream = RDF::Trine::Iterator::Bindings->new( sub { $self->next }, $vars, distinct => $self->distinct, sorted_by => $self->ordered ); |
|
321
|
|
|
|
|
169173
|
|
351
|
153
|
|
|
|
|
7391
|
return $stream; |
352
|
|
|
|
|
|
|
} |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
=item C<< is_update >> |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
Returns true if the plan represents an update operation. |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
=cut |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
sub is_update { |
361
|
0
|
|
|
0
|
1
|
0
|
return 0; |
362
|
|
|
|
|
|
|
} |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
=item C<< label ( $label => $value ) >> |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
Sets the named C<< $label >> to C<< $value >> for this plan object. |
367
|
|
|
|
|
|
|
If no C<< $value >> is given, returns the current label value, or undef if none |
368
|
|
|
|
|
|
|
exists. |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
=cut |
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
sub label { |
373
|
779
|
|
|
779
|
1
|
1060
|
my $self = shift; |
374
|
779
|
|
|
|
|
1040
|
my $label = shift; |
375
|
779
|
50
|
|
|
|
1745
|
if (@_) { |
376
|
779
|
|
|
|
|
1036
|
my $value = shift; |
377
|
779
|
|
|
|
|
2241
|
$self->[0]{labels}{ $label } = $value; |
378
|
|
|
|
|
|
|
} |
379
|
779
|
|
|
|
|
1985
|
return $self->[0]{labels}{ $label }; |
380
|
|
|
|
|
|
|
} |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
=item C<< graph_labels >> |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
=cut |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
sub graph_labels { |
387
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
388
|
0
|
|
|
|
|
0
|
my @labels; |
389
|
0
|
0
|
|
|
|
0
|
foreach my $k (keys %{ $self->[0]{labels} || {} }) { |
|
0
|
|
|
|
|
0
|
|
390
|
0
|
0
|
|
|
|
0
|
next if ($k eq 'algebra'); |
391
|
0
|
|
|
|
|
0
|
my $v = $self->label( $k ); |
392
|
0
|
|
|
|
|
0
|
local($Data::Dumper::Indent) = 0; |
393
|
0
|
|
|
|
|
0
|
my $l = Data::Dumper->Dump([$v], [$k]); |
394
|
0
|
|
|
|
|
0
|
push(@labels, $l); |
395
|
|
|
|
|
|
|
} |
396
|
0
|
|
|
|
|
0
|
my $label = join(", ", @labels); |
397
|
0
|
|
|
|
|
0
|
return ' ' . $label; |
398
|
|
|
|
|
|
|
} |
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
sub DESTROY { |
401
|
546
|
|
|
546
|
|
80959
|
my $self = shift; |
402
|
546
|
100
|
|
|
|
1383
|
if ($self->state == OPEN) { |
403
|
205
|
|
|
|
|
860
|
$self->close; |
404
|
|
|
|
|
|
|
} |
405
|
|
|
|
|
|
|
} |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
################################################################################ |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
=item C<< generate_plans ( $algebra, $execution_context, %args ) >> |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
Returns a list of equivalent query plan objects for the given algebra object. |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
=cut |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
sub generate_plans { |
416
|
773
|
|
|
773
|
1
|
1261
|
my $self = shift; |
417
|
773
|
|
33
|
|
|
3065
|
my $class = ref($self) || $self; |
418
|
773
|
|
|
|
|
1068
|
my $algebra = shift; |
419
|
773
|
|
|
|
|
995
|
my $context = shift; |
420
|
773
|
|
100
|
|
|
2503
|
my $config = $context->options || {}; |
421
|
|
|
|
|
|
|
|
422
|
773
|
|
|
|
|
2001
|
my %args = @_; |
423
|
773
|
|
66
|
|
|
4306
|
my $active_graph = $args{ active_graph } || RDF::Trine::Node::Nil->new(); |
424
|
|
|
|
|
|
|
|
425
|
773
|
|
|
|
|
13222
|
my $l = Log::Log4perl->get_logger("rdf.query.plan"); |
426
|
773
|
50
|
33
|
|
|
30033
|
unless (blessed($algebra) and $algebra->isa('RDF::Query::Algebra')) { |
427
|
0
|
|
|
|
|
0
|
throw RDF::Query::Error::MethodInvocationError (-text => "Cannot generate an execution plan with a non-algebra object $algebra"); |
428
|
|
|
|
|
|
|
} |
429
|
|
|
|
|
|
|
|
430
|
773
|
|
|
|
|
4198
|
$l->trace("generating query plan for " . $algebra->sse({ indent => ' ' }, '')); |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
############################################################################ |
433
|
|
|
|
|
|
|
### Optimize simple COUNT(*) aggregates over BGPs |
434
|
773
|
100
|
|
|
|
23181
|
if ($algebra->isa('RDF::Query::Algebra::Extend')) { |
435
|
24
|
|
|
|
|
83
|
my $agg = $algebra->pattern; |
436
|
24
|
100
|
|
|
|
131
|
if ($agg->isa('RDF::Query::Algebra::Aggregate')) { |
437
|
15
|
|
|
|
|
48
|
my @group = $agg->groupby; |
438
|
15
|
100
|
|
|
|
51
|
if (scalar(@group) == 0) { |
439
|
9
|
|
|
|
|
31
|
my @ops = $agg->ops; |
440
|
9
|
50
|
66
|
|
|
75
|
if (scalar(@ops) == 1 and $ops[0][0] eq 'COUNT(*)') { |
441
|
0
|
|
|
|
|
0
|
my $ggp = $agg->pattern; |
442
|
0
|
0
|
|
|
|
0
|
if ($ggp->isa('RDF::Query::Algebra::GroupGraphPattern')) { |
443
|
0
|
|
|
|
|
0
|
my @bgp = $ggp->patterns; |
444
|
0
|
0
|
0
|
|
|
0
|
if (scalar(@bgp) == 1 and ($bgp[0]->isa('RDF::Query::Algebra::BasicGraphPattern'))) { |
445
|
0
|
|
|
|
|
0
|
my $bgp = $bgp[0]; |
446
|
0
|
|
|
|
|
0
|
my @triples = $bgp->triples; |
447
|
0
|
0
|
|
|
|
0
|
if (scalar(@triples) == 1) { |
448
|
0
|
|
|
|
|
0
|
$l->debug("Optimizing for COUNT(*) on 1-triple BGP: " . $bgp->sse({ indent => ' ' }, '')); |
449
|
0
|
|
|
|
|
0
|
my $vars = $algebra->vars; |
450
|
0
|
|
|
|
|
0
|
my $alias = $vars->[0]; |
451
|
0
|
|
|
|
|
0
|
my $name = $alias->name; |
452
|
0
|
|
|
|
|
0
|
my $done = 0; |
453
|
0
|
|
|
|
|
0
|
my $model = $context->model; |
454
|
|
|
|
|
|
|
my $code = sub { |
455
|
0
|
0
|
|
0
|
|
0
|
return if ($done); |
456
|
0
|
|
|
|
|
0
|
$done = 1; |
457
|
|
|
|
|
|
|
#warn Dumper(\@triples); # XXX |
458
|
0
|
|
|
|
|
0
|
my $count = $model->count_statements( $triples[0]->nodes ); |
459
|
0
|
|
|
|
|
0
|
my $lit = RDF::Query::Node::Literal->new($count, undef, 'http://www.w3.org/2001/XMLSchema#integer'); |
460
|
0
|
|
|
|
|
0
|
my $vb = RDF::Query::VariableBindings->new( { |
461
|
|
|
|
|
|
|
$name => $lit, |
462
|
|
|
|
|
|
|
'COUNT(*)' => $lit, # this has to be kept around in case a HAVING clause needs it without the alias $name |
463
|
|
|
|
|
|
|
} ); |
464
|
0
|
|
|
|
|
0
|
}; |
465
|
0
|
|
|
|
|
0
|
my $iter = RDF::Trine::Iterator::Bindings->new( $code, [] ); |
466
|
0
|
|
|
|
|
0
|
return RDF::Query::Plan::Iterator->new( $iter ); |
467
|
|
|
|
|
|
|
} |
468
|
|
|
|
|
|
|
} |
469
|
|
|
|
|
|
|
} |
470
|
|
|
|
|
|
|
} |
471
|
|
|
|
|
|
|
} |
472
|
|
|
|
|
|
|
} |
473
|
|
|
|
|
|
|
} |
474
|
|
|
|
|
|
|
############################################################################ |
475
|
|
|
|
|
|
|
|
476
|
773
|
|
|
|
|
1150
|
my ($project); |
477
|
773
|
|
|
|
|
1370
|
my $constant = delete $args{ constants }; |
478
|
|
|
|
|
|
|
|
479
|
773
|
100
|
100
|
|
|
6048
|
if ($algebra->isa('RDF::Query::Algebra::Sort') or not($algebra->is_solution_modifier)) { |
480
|
|
|
|
|
|
|
# projection has to happen *after* sorting, since a sort expr might reference a variable that we project away |
481
|
594
|
|
|
|
|
1048
|
$project = delete $args{ project }; |
482
|
|
|
|
|
|
|
} |
483
|
|
|
|
|
|
|
|
484
|
773
|
|
|
|
|
1107
|
my @return_plans; |
485
|
773
|
|
|
|
|
1339
|
my $aclass = ref($algebra); |
486
|
773
|
|
|
|
|
4753
|
my ($type) = ($aclass =~ m<::(\w+)$>); |
487
|
773
|
100
|
100
|
|
|
7779
|
if ($type eq 'Aggregate') { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
488
|
16
|
|
|
|
|
65
|
my @base = $self->generate_plans( $algebra->pattern, $context, %args ); |
489
|
16
|
|
|
|
|
99
|
my @groups = $algebra->groupby; |
490
|
16
|
|
|
|
|
30
|
my @ops; |
491
|
16
|
|
|
|
|
50
|
foreach my $o ($algebra->ops) { |
492
|
18
|
|
|
|
|
58
|
my ($alias, $op, $opts, @cols) = @$o; |
493
|
18
|
|
|
|
|
68
|
push(@ops, [ $alias, $op, $opts, @cols ]); |
494
|
|
|
|
|
|
|
} |
495
|
16
|
|
|
|
|
51
|
my @plans = map { RDF::Query::Plan::Aggregate->new( $_, \@groups, expressions => \@ops ) } @base; |
|
16
|
|
|
|
|
98
|
|
496
|
16
|
|
|
|
|
42
|
push(@return_plans, @plans); |
497
|
|
|
|
|
|
|
} elsif ($type eq 'Construct') { |
498
|
4
|
|
|
|
|
16
|
my $triples = $algebra->triples; |
499
|
4
|
|
|
|
|
19
|
my @base = $self->generate_plans( $algebra->pattern, $context, %args ); |
500
|
4
|
|
|
|
|
18
|
my @plans = map { RDF::Query::Plan::Construct->new( $_, $triples ) } @base; |
|
4
|
|
|
|
|
29
|
|
501
|
4
|
|
|
|
|
14
|
push(@return_plans, @plans); |
502
|
|
|
|
|
|
|
} elsif ($type eq 'Distinct') { |
503
|
10
|
|
|
|
|
40
|
my @base = $self->generate_plans( $algebra->pattern, $context, %args ); |
504
|
10
|
|
|
|
|
26
|
my @plans = map { RDF::Query::Plan::Distinct->new( $_ ) } @base; |
|
10
|
|
|
|
|
104
|
|
505
|
10
|
|
|
|
|
32
|
push(@return_plans, @plans); |
506
|
|
|
|
|
|
|
} elsif ($type eq 'Filter') { |
507
|
37
|
|
|
|
|
140
|
my @base = $self->generate_plans( $algebra->pattern, $context, %args, active_graph => $active_graph ); |
508
|
37
|
|
|
|
|
158
|
my $expr = $algebra->expr; |
509
|
37
|
|
|
|
|
86
|
my @plans = map { RDF::Query::Plan::Filter->new( $expr, $_, $active_graph ) } @base; |
|
37
|
|
|
|
|
249
|
|
510
|
37
|
|
|
|
|
93
|
push(@return_plans, @plans); |
511
|
|
|
|
|
|
|
} elsif ($type eq 'BasicGraphPattern') { |
512
|
|
|
|
|
|
|
my @triples = map { |
513
|
146
|
|
|
|
|
539
|
($args{ prevent_distinguishing_bnodes }) |
514
|
277
|
100
|
|
|
|
3439
|
? $_ |
515
|
|
|
|
|
|
|
: $_->distinguish_bnode_variables |
516
|
|
|
|
|
|
|
} $algebra->triples; |
517
|
146
|
|
|
|
|
2705
|
my @normal_triples; |
518
|
|
|
|
|
|
|
my @csg_triples; |
519
|
146
|
|
|
|
|
350
|
foreach my $t (@triples) { |
520
|
277
|
100
|
|
|
|
1091
|
if (my @csg_plans = $self->_csg_plans( $context, $t )) { |
521
|
1
|
|
|
|
|
12
|
push(@csg_triples, $t); |
522
|
|
|
|
|
|
|
} else { |
523
|
276
|
|
|
|
|
813
|
my @nodes = $t->nodes; |
524
|
276
|
|
|
|
|
2469
|
$t = RDF::Query::Algebra::Quad->new( @nodes[ 0..2 ], $active_graph ); |
525
|
|
|
|
|
|
|
# if (my $g = $args{ named_graph }) { |
526
|
|
|
|
|
|
|
# my @nodes = $t->nodes; |
527
|
|
|
|
|
|
|
# $t = RDF::Query::Algebra::Quad->new( @nodes[0..2], $g ); |
528
|
|
|
|
|
|
|
# } |
529
|
276
|
|
|
|
|
6012
|
push(@normal_triples, $t); |
530
|
|
|
|
|
|
|
} |
531
|
|
|
|
|
|
|
} |
532
|
|
|
|
|
|
|
|
533
|
146
|
|
|
|
|
284
|
my @plans; |
534
|
146
|
50
|
|
|
|
553
|
if (scalar(@normal_triples) == 0) { |
|
|
100
|
|
|
|
|
|
535
|
0
|
|
|
|
|
0
|
my $v = RDF::Query::VariableBindings->new( {} ); |
536
|
0
|
|
|
|
|
0
|
my $plan = RDF::Query::Plan::Constant->new( $v ); |
537
|
0
|
|
|
|
|
0
|
push(@plans, $plan); |
538
|
|
|
|
|
|
|
} elsif (scalar(@normal_triples) == 1) { |
539
|
70
|
|
|
|
|
981
|
push(@plans, $self->generate_plans( @normal_triples, $context, %args )); |
540
|
|
|
|
|
|
|
} else { |
541
|
76
|
|
|
|
|
647
|
my $plan = RDF::Query::Plan::BasicGraphPattern->new( @normal_triples ); |
542
|
76
|
|
|
|
|
189
|
push(@plans, $plan); |
543
|
|
|
|
|
|
|
} |
544
|
|
|
|
|
|
|
|
545
|
146
|
100
|
|
|
|
427
|
if (@csg_triples) { |
546
|
1
|
|
|
|
|
2
|
my @csg_plans; |
547
|
1
|
|
|
|
|
3
|
foreach my $t (@csg_triples) { |
548
|
1
|
|
|
|
|
5
|
push(@csg_plans, [ $self->generate_plans( $t, $context, %args ) ]); |
549
|
|
|
|
|
|
|
} |
550
|
1
|
|
|
|
|
11
|
my @join_types = RDF::Query::Plan::Join->join_classes( $config ); |
551
|
1
|
|
|
|
|
5
|
while (my $cps = shift(@csg_plans)) { |
552
|
1
|
|
|
|
|
2
|
my @temp_plans = @plans; |
553
|
1
|
|
|
|
|
2
|
@plans = (); |
554
|
1
|
|
|
|
|
2
|
foreach my $p (@temp_plans) { |
555
|
1
|
|
|
|
|
3
|
foreach my $cp (@$cps) { |
556
|
1
|
|
|
|
|
8
|
foreach my $join_type (@join_types) { |
557
|
2
|
|
|
|
|
20
|
my $plan = $join_type->new( $p, $cp, 0, {} ); |
558
|
2
|
|
|
|
|
9
|
push(@plans, $plan); |
559
|
|
|
|
|
|
|
} |
560
|
|
|
|
|
|
|
} |
561
|
|
|
|
|
|
|
} |
562
|
|
|
|
|
|
|
} |
563
|
1
|
|
|
|
|
4
|
push(@return_plans, @plans); |
564
|
|
|
|
|
|
|
} else { |
565
|
145
|
|
|
|
|
640
|
push(@return_plans, @plans); |
566
|
|
|
|
|
|
|
} |
567
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
} elsif ($type eq 'GroupGraphPattern') { |
569
|
205
|
|
|
|
|
762
|
my @input = $algebra->patterns(); |
570
|
205
|
|
|
|
|
397
|
my @patterns; |
571
|
205
|
|
|
|
|
754
|
while (my $a = shift(@input)) { |
572
|
201
|
50
|
|
|
|
1344
|
if ($a->isa('RDF::Query::Algebra::Service')) { |
573
|
0
|
0
|
0
|
|
|
0
|
if (scalar(@input) and $input[0]->isa('RDF::Query::Algebra::Service') and $a->endpoint->value eq $input[0]->endpoint->value) { |
|
|
|
0
|
|
|
|
|
574
|
0
|
|
|
|
|
0
|
my $b = shift(@input); |
575
|
0
|
0
|
|
|
|
0
|
if ($a->silent == $b->silent) { |
576
|
0
|
|
|
|
|
0
|
my $p = RDF::Query::Algebra::GroupGraphPattern->new( map { $_->pattern } ($a, $b) ); |
|
0
|
|
|
|
|
0
|
|
577
|
0
|
|
|
|
|
0
|
my $s = RDF::Query::Algebra::Service->new( $a->endpoint, $p, $a->silent ); |
578
|
0
|
|
|
|
|
0
|
push(@patterns, $s); |
579
|
0
|
|
|
|
|
0
|
next; |
580
|
|
|
|
|
|
|
} |
581
|
|
|
|
|
|
|
} |
582
|
|
|
|
|
|
|
} |
583
|
201
|
|
|
|
|
849
|
push(@patterns, $a); |
584
|
|
|
|
|
|
|
} |
585
|
|
|
|
|
|
|
|
586
|
205
|
|
|
|
|
324
|
my @plans; |
587
|
205
|
100
|
|
|
|
764
|
if (scalar(@patterns) == 0) { |
|
|
100
|
|
|
|
|
|
588
|
18
|
|
|
|
|
135
|
my $v = RDF::Query::VariableBindings->new( {} ); |
589
|
18
|
|
|
|
|
144
|
my $plan = RDF::Query::Plan::Constant->new( $v ); |
590
|
18
|
|
|
|
|
44
|
push(@plans, $plan); |
591
|
|
|
|
|
|
|
} elsif (scalar(@patterns) == 1) { |
592
|
174
|
|
|
|
|
2188
|
push(@plans, $self->generate_plans( @patterns, $context, %args )); |
593
|
|
|
|
|
|
|
} else { |
594
|
13
|
|
|
|
|
90
|
push(@plans, map { $_->[0] } $self->_join_plans( $context, \@patterns, %args, method => 'patterns' )); |
|
13
|
|
|
|
|
52
|
|
595
|
|
|
|
|
|
|
} |
596
|
|
|
|
|
|
|
|
597
|
205
|
|
|
|
|
548
|
push(@return_plans, @plans); |
598
|
|
|
|
|
|
|
} elsif ($type eq 'Limit') { |
599
|
8
|
|
|
|
|
39
|
my @base = $self->generate_plans( $algebra->pattern, $context, %args ); |
600
|
8
|
|
|
|
|
27
|
my @plans = map { RDF::Query::Plan::Limit->new( $algebra->limit, $_ ) } @base; |
|
8
|
|
|
|
|
33
|
|
601
|
8
|
|
|
|
|
24
|
push(@return_plans, @plans); |
602
|
|
|
|
|
|
|
} elsif ($type eq 'NamedGraph') { |
603
|
14
|
|
|
|
|
34
|
my @plans; |
604
|
14
|
100
|
|
|
|
56
|
if ($algebra->graph->isa('RDF::Query::Node::Resource')) { |
605
|
4
|
|
|
|
|
22
|
@plans = $self->generate_plans( $algebra->pattern, $context, %args, active_graph => $algebra->graph ); |
606
|
|
|
|
|
|
|
} else { |
607
|
10
|
|
|
|
|
40
|
@plans = map { RDF::Query::Plan::NamedGraph->new( $algebra->graph, $_ ) } $self->generate_plans( $algebra->pattern, $context, %args, active_graph => $algebra->graph ); |
|
10
|
|
|
|
|
42
|
|
608
|
|
|
|
|
|
|
} |
609
|
14
|
|
|
|
|
38
|
push(@return_plans, @plans); |
610
|
|
|
|
|
|
|
} elsif ($type eq 'Offset') { |
611
|
3
|
|
|
|
|
13
|
my @base = $self->generate_plans( $algebra->pattern, $context, %args ); |
612
|
3
|
|
|
|
|
7
|
my @plans = map { RDF::Query::Plan::Offset->new( $algebra->offset, $_ ) } @base; |
|
3
|
|
|
|
|
13
|
|
613
|
3
|
|
|
|
|
9
|
push(@return_plans, @plans); |
614
|
|
|
|
|
|
|
} elsif ($type eq 'Optional') { |
615
|
|
|
|
|
|
|
# just like a BGP or GGP, but we have to pass the optional flag to the join constructor |
616
|
11
|
|
|
|
|
43
|
my @patterns = ($algebra->pattern, $algebra->optional); |
617
|
11
|
|
|
|
|
28
|
my @base_plans = map { [ $self->generate_plans( $_, $context, %args ) ] } @patterns; |
|
22
|
|
|
|
|
153
|
|
618
|
11
|
|
|
|
|
80
|
my @join_types = RDF::Query::Plan::Join->join_classes( $config ); |
619
|
|
|
|
|
|
|
# XXX this is currently only considering left-deep trees. maybe it should produce all trees? |
620
|
11
|
|
|
|
|
26
|
my @plans; |
621
|
11
|
|
|
|
|
27
|
my $base_a = shift(@base_plans); |
622
|
11
|
|
|
|
|
24
|
my $base_b = shift(@base_plans); |
623
|
11
|
|
|
|
|
19
|
foreach my $i (0 .. $#{ $base_a }) { |
|
11
|
|
|
|
|
33
|
|
624
|
11
|
|
|
|
|
27
|
foreach my $j (0 .. $#{ $base_b }) { |
|
11
|
|
|
|
|
28
|
|
625
|
11
|
|
|
|
|
24
|
my $a = $base_a->[ $i ]; |
626
|
11
|
|
|
|
|
19
|
my $b = $base_b->[ $j ]; |
627
|
11
|
|
|
|
|
22
|
foreach my $join_type (@join_types) { |
628
|
|
|
|
|
|
|
try { |
629
|
22
|
|
|
20
|
|
689
|
my $plan = $join_type->new( $a, $b, 1, {} ); |
630
|
11
|
|
|
|
|
40
|
push( @plans, $plan ); |
631
|
|
|
|
10
|
|
|
} catch RDF::Query::Error::MethodInvocationError with { |
632
|
|
|
|
|
|
|
# my $e = shift; |
633
|
|
|
|
|
|
|
# warn "caught MethodInvocationError: " . Dumper($e); |
634
|
22
|
|
|
|
|
335
|
}; |
635
|
|
|
|
|
|
|
} |
636
|
|
|
|
|
|
|
} |
637
|
|
|
|
|
|
|
} |
638
|
11
|
|
|
|
|
277
|
push(@return_plans, @plans); |
639
|
|
|
|
|
|
|
} elsif ($type eq 'Minus') { |
640
|
0
|
|
|
|
|
0
|
my @patterns = ($algebra->pattern, $algebra->minus); |
641
|
0
|
|
|
|
|
0
|
my @base_plans = map { [ $self->generate_plans( $_, $context, %args ) ] } @patterns; |
|
0
|
|
|
|
|
0
|
|
642
|
0
|
|
|
|
|
0
|
my @plans; |
643
|
0
|
|
|
|
|
0
|
my $base_a = shift(@base_plans); |
644
|
0
|
|
|
|
|
0
|
my $base_b = shift(@base_plans); |
645
|
0
|
|
|
|
|
0
|
foreach my $i (0 .. $#{ $base_a }) { |
|
0
|
|
|
|
|
0
|
|
646
|
0
|
|
|
|
|
0
|
foreach my $j (0 .. $#{ $base_b }) { |
|
0
|
|
|
|
|
0
|
|
647
|
0
|
|
|
|
|
0
|
my $a = $base_a->[ $i ]; |
648
|
0
|
|
|
|
|
0
|
my $b = $base_b->[ $j ]; |
649
|
0
|
|
|
|
|
0
|
my $plan = RDF::Query::Plan::Minus->new( $a, $b ); |
650
|
0
|
|
|
|
|
0
|
push( @plans, $plan ); |
651
|
|
|
|
|
|
|
} |
652
|
|
|
|
|
|
|
} |
653
|
0
|
|
|
|
|
0
|
push(@return_plans, @plans); |
654
|
|
|
|
|
|
|
} elsif ($type eq 'Project') { |
655
|
134
|
|
|
|
|
476
|
my $pattern = $algebra->pattern; |
656
|
134
|
|
|
|
|
458
|
my $vars = $algebra->vars; |
657
|
134
|
|
|
|
|
1561
|
my @base = $self->generate_plans( $pattern, $context, %args ); |
658
|
|
|
|
|
|
|
|
659
|
134
|
100
|
|
|
|
408
|
if ($constant) { |
660
|
|
|
|
|
|
|
# if there's constant data to be joined, we better do it now in case |
661
|
|
|
|
|
|
|
# the project gets rid of variables needed for the join |
662
|
3
|
|
|
|
|
8
|
my @plans = splice( @base ); |
663
|
3
|
|
|
|
|
14
|
@base = $self->_add_constant_join( $context, $constant, @plans ); |
664
|
3
|
|
|
|
|
6
|
$constant = undef; |
665
|
|
|
|
|
|
|
} |
666
|
|
|
|
|
|
|
|
667
|
134
|
|
|
|
|
228
|
my @plans; |
668
|
134
|
|
|
|
|
307
|
foreach my $plan (@base) { |
669
|
138
|
|
|
|
|
1716
|
push(@return_plans, RDF::Query::Plan::Project->new( $plan, $vars )); |
670
|
|
|
|
|
|
|
} |
671
|
134
|
|
|
|
|
350
|
push(@return_plans, @plans); |
672
|
|
|
|
|
|
|
} elsif ($type eq 'Extend') { |
673
|
24
|
|
|
|
|
87
|
my $pattern = $algebra->pattern; |
674
|
24
|
|
|
|
|
76
|
my $vars = $algebra->vars; |
675
|
24
|
|
|
|
|
258
|
my @base = $self->generate_plans( $pattern, $context, %args ); |
676
|
24
|
|
|
|
|
41
|
my @plans; |
677
|
24
|
|
|
|
|
56
|
foreach my $plan (@base) { |
678
|
24
|
|
|
|
|
161
|
push(@plans, RDF::Query::Plan::Extend->new( $plan, $vars )); |
679
|
|
|
|
|
|
|
} |
680
|
24
|
|
|
|
|
60
|
push(@return_plans, @plans); |
681
|
|
|
|
|
|
|
} elsif ($type eq 'Service') { |
682
|
0
|
|
|
|
|
0
|
my $pattern = $algebra->pattern; |
683
|
0
|
|
|
|
|
0
|
my @base = $self->generate_plans( $pattern, $context, %args ); |
684
|
0
|
|
|
|
|
0
|
my @plans; |
685
|
0
|
|
|
|
|
0
|
foreach my $plan (@base) { |
686
|
|
|
|
|
|
|
my $sparqlcb = sub { |
687
|
0
|
|
|
0
|
|
0
|
my $row = shift; |
688
|
0
|
|
|
|
|
0
|
my $p = $pattern; |
689
|
0
|
0
|
|
|
|
0
|
if ($row) { |
690
|
0
|
|
|
|
|
0
|
$p = $p->bind_variables( $row ); |
691
|
|
|
|
|
|
|
} |
692
|
0
|
|
|
|
|
0
|
my $ns = $context->ns; |
693
|
0
|
|
|
|
|
0
|
my $pstr = $p->as_sparql({namespaces => $ns}, ''); |
694
|
0
|
0
|
|
|
|
0
|
unless (substr($pstr, 0, 1) eq '{') { |
695
|
0
|
|
|
|
|
0
|
$pstr = "{ $pstr }"; |
696
|
|
|
|
|
|
|
} |
697
|
|
|
|
|
|
|
my $sparql = join("\n", |
698
|
0
|
0
|
|
|
|
0
|
(map { sprintf("PREFIX %s: <%s>", ($_ eq '__DEFAULT__' ? '' : $_), $ns->{$_}) } (keys %$ns)), |
|
0
|
|
|
|
|
0
|
|
699
|
|
|
|
|
|
|
sprintf("SELECT * WHERE %s", $pstr) |
700
|
|
|
|
|
|
|
); |
701
|
0
|
|
|
|
|
0
|
return $sparql; |
702
|
0
|
|
|
|
|
0
|
}; |
703
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
# unless ($algebra->endpoint->can('uri_value')) { |
705
|
|
|
|
|
|
|
# throw RDF::Query::Error::UnimplementedError (-text => "Support for variable-endpoint SERVICE blocks is not implemented"); |
706
|
|
|
|
|
|
|
# } |
707
|
|
|
|
|
|
|
|
708
|
0
|
0
|
|
|
|
0
|
if (my $ggp = $algebra->lhs) { |
709
|
0
|
|
|
|
|
0
|
my @lhs_base = $self->generate_plans( $ggp, $context, %args ); |
710
|
0
|
|
|
|
|
0
|
foreach my $lhs_plan (@lhs_base) { |
711
|
0
|
|
|
|
|
0
|
my $splan = RDF::Query::Plan::Service->new( $algebra->endpoint, $plan, $algebra->silent, $sparqlcb, $lhs_plan ); |
712
|
0
|
|
|
|
|
0
|
push(@plans, $splan); |
713
|
|
|
|
|
|
|
} |
714
|
|
|
|
|
|
|
} else { |
715
|
0
|
|
|
|
|
0
|
push(@plans, $PLAN_CLASSES{'service'}->new( $algebra->endpoint, $plan, $algebra->silent, $sparqlcb )); |
716
|
|
|
|
|
|
|
} |
717
|
|
|
|
|
|
|
} |
718
|
0
|
|
|
|
|
0
|
push(@return_plans, @plans); |
719
|
|
|
|
|
|
|
} elsif ($type eq 'SubSelect') { |
720
|
1
|
|
|
|
|
5
|
my $query = $algebra->query; |
721
|
1
|
|
|
|
|
6
|
my $model = $context->model; |
722
|
1
|
|
|
|
|
5
|
my %pargs = %args; |
723
|
1
|
|
|
|
|
3
|
my $ag = $args{ active_graph }; |
724
|
1
|
50
|
33
|
|
|
6
|
if (blessed($ag) and $ag->isa('RDF::Query::Node::Variable')) { |
725
|
0
|
|
|
|
|
0
|
my %vars = map { $_ => 1 } $query->pattern->referenced_variables; |
|
0
|
|
|
|
|
0
|
|
726
|
0
|
0
|
|
|
|
0
|
if ($vars{ $ag->name }) { |
727
|
0
|
|
|
|
|
0
|
my $new_ag = RDF::Query::Node::Variable->new(); |
728
|
0
|
|
|
|
|
0
|
my ($pattern) = $query->pattern; |
729
|
0
|
|
|
|
|
0
|
my $new_pattern = $pattern->bind_variables( { $ag->name => $new_ag } ); |
730
|
0
|
|
|
|
|
0
|
my $apattern = RDF::Query::Algebra::Extend->new( |
731
|
|
|
|
|
|
|
$new_pattern, |
732
|
|
|
|
|
|
|
[ |
733
|
|
|
|
|
|
|
RDF::Query::Expression::Alias->new( 'alias', $ag, $new_ag ) |
734
|
|
|
|
|
|
|
] |
735
|
|
|
|
|
|
|
); |
736
|
0
|
|
|
|
|
0
|
$query->{parsed}{triples} = [$apattern]; |
737
|
|
|
|
|
|
|
} |
738
|
0
|
|
|
|
|
0
|
my ($plan) = $self->generate_plans( $query->pattern, $context, %args ); |
739
|
0
|
|
|
|
|
0
|
push(@return_plans, RDF::Query::Plan::SubSelect->new( $query, $plan )); |
740
|
|
|
|
|
|
|
} else { |
741
|
1
|
|
|
|
|
5
|
my ($plan) = $query->prepare( $context->model, planner_args => \%pargs ); |
742
|
1
|
|
|
|
|
20
|
push(@return_plans, RDF::Query::Plan::SubSelect->new( $query, $plan )); |
743
|
|
|
|
|
|
|
} |
744
|
|
|
|
|
|
|
} elsif ($type eq 'Sort') { |
745
|
12
|
|
|
|
|
53
|
my @base = $self->generate_plans( $algebra->pattern, $context, %args ); |
746
|
12
|
|
|
|
|
56
|
my @order = $algebra->orderby; |
747
|
12
|
|
|
|
|
25
|
my @neworder; |
748
|
12
|
|
|
|
|
31
|
foreach my $o (@order) { |
749
|
12
|
|
|
|
|
34
|
my ($dirname, $expr) = @$o; |
750
|
12
|
100
|
|
|
|
44
|
my $dir = ($dirname eq 'ASC') ? 0 : 1; |
751
|
12
|
|
|
|
|
40
|
push(@neworder, [$expr, $dir]); |
752
|
|
|
|
|
|
|
} |
753
|
12
|
|
|
|
|
29
|
my @plans = map { RDF::Query::Plan::Sort->new( $_, @neworder ) } @base; |
|
12
|
|
|
|
|
118
|
|
754
|
12
|
|
|
|
|
36
|
push(@return_plans, @plans); |
755
|
|
|
|
|
|
|
} elsif ($type eq 'Triple' or $type eq 'Quad') { |
756
|
133
|
|
|
|
|
206
|
my $st; |
757
|
133
|
100
|
|
|
|
334
|
if ($args{ prevent_distinguishing_bnodes }) { |
758
|
40
|
|
|
|
|
53
|
$st = $algebra; |
759
|
|
|
|
|
|
|
} else { |
760
|
93
|
|
|
|
|
422
|
$st = $algebra->distinguish_bnode_variables; |
761
|
|
|
|
|
|
|
} |
762
|
133
|
|
|
|
|
2151
|
my $pred = $st->predicate; |
763
|
133
|
|
|
|
|
1006
|
my @nodes = $st->nodes; |
764
|
|
|
|
|
|
|
|
765
|
133
|
100
|
|
|
|
940
|
if (my @csg_plans = $self->_csg_plans( $context, $st )) { |
|
|
100
|
|
|
|
|
|
766
|
1
|
|
|
|
|
5
|
push(@return_plans, @csg_plans); |
767
|
|
|
|
|
|
|
} elsif ($type eq 'Triple') { |
768
|
24
|
|
|
|
|
112
|
my $plan = RDF::Query::Plan::Quad->new( @nodes[0..2], $active_graph, { sparql => $algebra->as_sparql, bf => $algebra->bf } ); |
769
|
24
|
|
|
|
|
124
|
push(@return_plans, $plan); |
770
|
|
|
|
|
|
|
} else { |
771
|
108
|
50
|
|
|
|
572
|
my $plan = (scalar(@nodes) == 4) |
772
|
|
|
|
|
|
|
? RDF::Query::Plan::Quad->new( @nodes, { sparql => $algebra->as_sparql } ) |
773
|
|
|
|
|
|
|
: RDF::Query::Plan::Quad->new( @nodes, RDF::Trine::Node::Nil->new(), { sparql => $algebra->as_sparql, bf => $algebra->bf } ); |
774
|
108
|
|
|
|
|
545
|
push(@return_plans, $plan); |
775
|
|
|
|
|
|
|
} |
776
|
|
|
|
|
|
|
} elsif ($type eq 'Path') { |
777
|
5
|
|
|
|
|
31
|
my @plans = $self->_path_plans( $algebra, $context, %args ); |
778
|
5
|
|
|
|
|
15
|
push(@return_plans, @plans); |
779
|
|
|
|
|
|
|
} elsif ($type eq 'Union') { |
780
|
2
|
|
|
|
|
10
|
my @plans = map { [ $self->generate_plans( $_, $context, %args ) ] } $algebra->patterns; |
|
4
|
|
|
|
|
53
|
|
781
|
2
|
|
|
|
|
5
|
my $plan = RDF::Query::Plan::Union->new( map { $_->[0] } @plans ); |
|
4
|
|
|
|
|
21
|
|
782
|
2
|
|
|
|
|
7
|
push(@return_plans, $plan); |
783
|
|
|
|
|
|
|
} elsif ($type eq 'Sequence') { |
784
|
0
|
|
|
|
|
0
|
my @pat = $algebra->patterns; |
785
|
0
|
0
|
|
|
|
0
|
if (@pat) { |
786
|
0
|
|
|
|
|
0
|
my @plans = map { [ $self->generate_plans( $_, $context, %args ) ] } @pat; |
|
0
|
|
|
|
|
0
|
|
787
|
0
|
|
|
|
|
0
|
my $plan = RDF::Query::Plan::Sequence->new( map { $_->[0] } @plans ); |
|
0
|
|
|
|
|
0
|
|
788
|
0
|
|
|
|
|
0
|
push(@return_plans, $plan); |
789
|
|
|
|
|
|
|
} else { |
790
|
0
|
|
|
0
|
|
0
|
my $stream = RDF::Trine::Iterator::Bindings->new( sub {} ); |
791
|
0
|
|
|
|
|
0
|
push(@return_plans, $stream); |
792
|
|
|
|
|
|
|
} |
793
|
|
|
|
|
|
|
} elsif ($type eq 'Load') { |
794
|
0
|
|
|
|
|
0
|
push(@return_plans, RDF::Query::Plan::Load->new( $algebra->url, $algebra->graph )); |
795
|
|
|
|
|
|
|
} elsif ($type eq 'Update') { |
796
|
8
|
|
100
|
|
|
35
|
my $ds = $algebra->dataset || {}; |
797
|
8
|
|
100
|
|
|
39
|
my $default = $ds->{'default'} || []; |
798
|
8
|
|
50
|
|
|
43
|
my $named = $ds->{'named'} || {}; |
799
|
8
|
|
|
|
|
17
|
my $dcount = scalar(@$default); |
800
|
8
|
|
|
|
|
13
|
my $ncount = scalar(@{[ keys %$named ]}); |
|
8
|
|
|
|
|
25
|
|
801
|
|
|
|
|
|
|
# warn 'Update dataset: ' . Dumper($algebra->dataset); |
802
|
8
|
|
|
|
|
10
|
my @plans; |
803
|
|
|
|
|
|
|
|
804
|
8
|
|
|
|
|
19
|
my @dataset = ($ds); |
805
|
8
|
100
|
66
|
|
|
65
|
if ($dcount == 1 and $ncount == 0) { |
|
|
50
|
33
|
|
|
|
|
806
|
|
|
|
|
|
|
# if it's just a single named graph to be used as the default graph, |
807
|
|
|
|
|
|
|
# then rewrite the pattern to use the named graph (and check to make |
808
|
|
|
|
|
|
|
# sure there aren't any GRAPH blocks) |
809
|
1
|
|
|
|
|
3
|
@dataset = (); |
810
|
1
|
|
|
|
|
5
|
@plans = $self->generate_plans( $algebra->pattern, $context, %args, active_graph => $default->[0] ); |
811
|
|
|
|
|
|
|
} elsif ($dcount == 0 and $ncount == 0) { |
812
|
7
|
|
|
|
|
12
|
@dataset = (); |
813
|
7
|
|
|
|
|
26
|
@plans = $self->generate_plans( $algebra->pattern, $context, %args ); |
814
|
|
|
|
|
|
|
} else { |
815
|
0
|
|
|
|
|
0
|
@plans = $self->generate_plans( $algebra->pattern, $context, %args ); |
816
|
|
|
|
|
|
|
} |
817
|
8
|
|
|
|
|
22
|
foreach my $p (@plans) { |
818
|
8
|
|
|
|
|
34
|
push(@return_plans, RDF::Query::Plan::Update->new( $algebra->delete_template, $algebra->insert_template, $p, @dataset )); |
819
|
|
|
|
|
|
|
} |
820
|
|
|
|
|
|
|
} elsif ($type eq 'Clear') { |
821
|
0
|
|
|
|
|
0
|
push(@return_plans, RDF::Query::Plan::Clear->new( $algebra->graph )); |
822
|
|
|
|
|
|
|
} elsif ($type eq 'Create') { |
823
|
0
|
|
|
|
|
0
|
my $plan = RDF::Query::Plan::Constant->new(); |
824
|
0
|
|
|
|
|
0
|
push(@return_plans, $plan); |
825
|
|
|
|
|
|
|
} elsif ($type eq 'Copy') { |
826
|
0
|
|
|
|
|
0
|
my $plan = RDF::Query::Plan::Copy->new( $algebra->from, $algebra->to, $algebra->silent ); |
827
|
0
|
|
|
|
|
0
|
push(@return_plans, $plan); |
828
|
|
|
|
|
|
|
} elsif ($type eq 'Move') { |
829
|
0
|
|
|
|
|
0
|
my $plan = RDF::Query::Plan::Move->new( $algebra->from, $algebra->to, $algebra->silent ); |
830
|
0
|
|
|
|
|
0
|
push(@return_plans, $plan); |
831
|
|
|
|
|
|
|
} elsif ($type eq 'Table') { |
832
|
0
|
|
|
|
|
0
|
my $plan = RDF::Query::Plan::Constant->new( $algebra->rows ); |
833
|
0
|
|
|
|
|
0
|
push(@return_plans, $plan); |
834
|
|
|
|
|
|
|
} else { |
835
|
0
|
|
|
|
|
0
|
throw RDF::Query::Error::MethodInvocationError (-text => "Cannot generate an execution plan for unknown algebra class $aclass"); |
836
|
|
|
|
|
|
|
} |
837
|
|
|
|
|
|
|
|
838
|
773
|
0
|
50
|
|
|
1857
|
if ($constant and scalar(@$constant)) { |
839
|
0
|
|
|
|
|
0
|
my @plans = splice( @return_plans ); |
840
|
0
|
|
|
|
|
0
|
@return_plans = $self->_add_constant_join( $context, $constant, @plans ); |
841
|
|
|
|
|
|
|
} |
842
|
|
|
|
|
|
|
|
843
|
773
|
|
|
|
|
1377
|
foreach my $p (@return_plans) { |
844
|
779
|
50
|
|
|
|
3216
|
Carp::confess 'not a plan: ' . Dumper($p) unless ($p->isa('RDF::Query::Plan')); |
845
|
779
|
|
|
|
|
2258
|
$p->label( algebra => $algebra ); |
846
|
|
|
|
|
|
|
} |
847
|
|
|
|
|
|
|
|
848
|
773
|
50
|
|
|
|
1722
|
unless (scalar(@return_plans)) { |
849
|
0
|
|
|
|
|
0
|
throw RDF::Query::Error::CompilationError (-text => "Cannot generate an execution plan for algebra of type $type", -object => $algebra); |
850
|
|
|
|
|
|
|
} |
851
|
773
|
|
|
|
|
3003
|
return @return_plans; |
852
|
|
|
|
|
|
|
} |
853
|
|
|
|
|
|
|
|
854
|
|
|
|
|
|
|
sub _csg_plans { |
855
|
410
|
|
|
410
|
|
686
|
my $self = shift; |
856
|
410
|
|
|
|
|
590
|
my $context = shift; |
857
|
410
|
|
|
|
|
654
|
my $st = shift; |
858
|
410
|
|
|
|
|
1132
|
my $pred = $st->predicate; |
859
|
410
|
50
|
|
|
|
3123
|
return unless (blessed($context)); |
860
|
410
|
|
|
|
|
1464
|
my $query = $context->query; |
861
|
410
|
|
|
|
|
755
|
my @return_plans; |
862
|
410
|
100
|
66
|
|
|
3409
|
if (blessed($query) and $pred->isa('RDF::Trine::Node::Resource') and scalar(@{ $query->get_computed_statement_generators( $st->predicate->uri_value ) })) { |
|
406
|
|
100
|
|
|
1198
|
|
863
|
2
|
|
|
|
|
6
|
my $csg = $query->get_computed_statement_generators( $pred->uri_value ); |
864
|
2
|
|
|
|
|
7
|
my @nodes = $st->nodes; |
865
|
2
|
50
|
|
|
|
13
|
my $quad = (scalar(@nodes) == 4) ? 1 : 0; |
866
|
2
|
|
|
|
|
15
|
my $mp = RDF::Query::Plan::ComputedStatement->new( @nodes[0..3], $quad ); |
867
|
2
|
|
|
|
|
5
|
push(@return_plans, $mp); |
868
|
|
|
|
|
|
|
} |
869
|
410
|
|
|
|
|
1858
|
return @return_plans; |
870
|
|
|
|
|
|
|
} |
871
|
|
|
|
|
|
|
|
872
|
|
|
|
|
|
|
sub _join_plans { |
873
|
27
|
|
|
27
|
|
57
|
my $self = shift; |
874
|
27
|
|
|
|
|
48
|
my $context = shift; |
875
|
27
|
|
|
|
|
49
|
my $triples = shift; |
876
|
27
|
|
|
|
|
87
|
my %args = @_; |
877
|
27
|
|
50
|
|
|
105
|
my $config = $context->options || {}; |
878
|
|
|
|
|
|
|
|
879
|
27
|
|
|
|
|
99
|
my $method = $args{ method }; |
880
|
27
|
|
|
|
|
186
|
my @join_types = RDF::Query::Plan::Join->join_classes( $config ); |
881
|
|
|
|
|
|
|
|
882
|
27
|
|
|
|
|
46
|
my @plans; |
883
|
27
|
|
|
|
|
100
|
my $opt = $context->optimize; |
884
|
27
|
50
|
|
|
|
103
|
my @slice = ($opt) ? (0 .. $#{ $triples }) : (0); |
|
0
|
|
|
|
|
0
|
|
885
|
27
|
|
|
|
|
65
|
foreach my $i (@slice) { |
886
|
27
|
|
|
|
|
68
|
my @triples = @$triples; |
887
|
|
|
|
|
|
|
# pick a triple to use as the LHS |
888
|
27
|
|
|
|
|
68
|
my ($t) = splice( @triples, $i, 1 ); |
889
|
|
|
|
|
|
|
|
890
|
27
|
|
|
|
|
215
|
my @_lhs = $self->generate_plans( $t, $context, %args ); |
891
|
27
|
|
|
|
|
63
|
my @lhs_plans = map { [ $_, [$t] ] } @_lhs; |
|
27
|
|
|
|
|
112
|
|
892
|
27
|
100
|
|
|
|
77
|
if (@triples) { |
893
|
14
|
|
|
|
|
136
|
my @rhs_plans = $self->_join_plans( $context, \@triples, %args ); |
894
|
14
|
|
|
|
|
50
|
foreach my $i (0 .. $#lhs_plans) { |
895
|
14
|
|
|
|
|
34
|
foreach my $j (0 .. $#rhs_plans) { |
896
|
14
|
|
|
|
|
35
|
my $a = $lhs_plans[ $i ][0]; |
897
|
14
|
|
|
|
|
27
|
my $b = $rhs_plans[ $j ][0]; |
898
|
14
|
|
|
|
|
29
|
my $algebra_a = $lhs_plans[ $i ][1]; |
899
|
14
|
|
|
|
|
31
|
my $algebra_b = $rhs_plans[ $j ][1]; |
900
|
14
|
50
|
|
|
|
89
|
Carp::confess 'no lhs for join: ' . Dumper(\@lhs_plans) unless (blessed($a)); |
901
|
14
|
50
|
|
|
|
69
|
Carp::confess 'no rhs for join: ' . Dumper(\@triples, \@rhs_plans) unless (blessed($b)); |
902
|
14
|
|
|
|
|
37
|
foreach ($algebra_a, $algebra_b) { |
903
|
28
|
50
|
33
|
|
|
215
|
unless (ref($_) and reftype($_) eq 'ARRAY') { |
904
|
0
|
|
|
|
|
0
|
Carp::cluck Dumper($_) |
905
|
|
|
|
|
|
|
} |
906
|
|
|
|
|
|
|
} |
907
|
14
|
|
|
|
|
40
|
foreach my $join_type (@join_types) { |
908
|
28
|
50
|
66
|
|
|
451
|
next if ($join_type eq 'RDF::Query::Plan::Join::PushDownNestedLoop' and $b->subplans_of_type('RDF::Query::Plan::Service')); |
909
|
|
|
|
|
|
|
try { |
910
|
28
|
|
|
28
|
|
793
|
my @algebras; |
911
|
28
|
|
|
|
|
62
|
foreach ($algebra_a, $algebra_b) { |
912
|
56
|
50
|
|
|
|
205
|
if (reftype($_) eq 'ARRAY') { |
913
|
56
|
|
|
|
|
122
|
push(@algebras, @$_); |
914
|
|
|
|
|
|
|
} |
915
|
|
|
|
|
|
|
} |
916
|
28
|
|
|
|
|
45
|
my %logging_keys; |
917
|
28
|
50
|
|
|
|
72
|
if ($method eq 'triples') { |
918
|
0
|
|
|
|
|
0
|
my $bgp = RDF::Query::Algebra::BasicGraphPattern->new( @algebras ); |
919
|
0
|
|
|
|
|
0
|
my $sparql = $bgp->as_sparql; |
920
|
0
|
|
|
|
|
0
|
my $bf = $bgp->bf; |
921
|
0
|
|
|
|
|
0
|
$logging_keys{ bf } = $bf; |
922
|
0
|
|
|
|
|
0
|
$logging_keys{ sparql } = $sparql; |
923
|
|
|
|
|
|
|
} else { |
924
|
28
|
|
|
|
|
117
|
my $ggp = RDF::Query::Algebra::GroupGraphPattern->new( @algebras ); |
925
|
28
|
|
|
|
|
135
|
my $sparql = $ggp->as_sparql; |
926
|
28
|
|
|
|
|
108
|
$logging_keys{ sparql } = $sparql; |
927
|
|
|
|
|
|
|
} |
928
|
28
|
|
|
|
|
250
|
my $plan = $join_type->new( $b, $a, 0, \%logging_keys ); |
929
|
27
|
|
|
|
|
124
|
push( @plans, [ $plan, [ @algebras ] ] ); |
930
|
|
|
|
1
|
|
|
} catch RDF::Query::Error::MethodInvocationError with { |
931
|
|
|
|
|
|
|
# warn "caught MethodInvocationError."; |
932
|
28
|
|
|
|
|
299
|
}; |
933
|
|
|
|
|
|
|
} |
934
|
|
|
|
|
|
|
} |
935
|
|
|
|
|
|
|
} |
936
|
|
|
|
|
|
|
} else { |
937
|
13
|
|
|
|
|
50
|
@plans = @lhs_plans; |
938
|
|
|
|
|
|
|
} |
939
|
|
|
|
|
|
|
} |
940
|
|
|
|
|
|
|
|
941
|
27
|
50
|
|
|
|
346
|
if ($opt) { |
942
|
0
|
|
|
|
|
0
|
return @plans; |
943
|
|
|
|
|
|
|
} else { |
944
|
27
|
50
|
|
|
|
77
|
if (@plans) { |
945
|
27
|
|
|
|
|
129
|
return $plans[0]; # XXX need to figure out what's the 'best' plan here |
946
|
|
|
|
|
|
|
} else { |
947
|
0
|
|
|
|
|
0
|
return; |
948
|
|
|
|
|
|
|
} |
949
|
|
|
|
|
|
|
} |
950
|
|
|
|
|
|
|
} |
951
|
|
|
|
|
|
|
|
952
|
|
|
|
|
|
|
sub _add_constant_join { |
953
|
3
|
|
|
3
|
|
5
|
my $self = shift; |
954
|
3
|
|
|
|
|
6
|
my $context = shift; |
955
|
3
|
|
|
|
|
5
|
my $constant = shift; |
956
|
3
|
|
|
|
|
8
|
my @return_plans = @_; |
957
|
3
|
|
50
|
|
|
11
|
my $config = $context->options || {}; |
958
|
3
|
|
|
|
|
23
|
my @join_types = RDF::Query::Plan::Join->join_classes( $config ); |
959
|
3
|
|
|
|
|
13
|
while (my $const = shift(@$constant)) { |
960
|
3
|
|
|
|
|
7
|
my @plans = splice(@return_plans); |
961
|
3
|
|
|
|
|
8
|
foreach my $p (@plans) { |
962
|
3
|
|
|
|
|
9
|
foreach my $join_type (@join_types) { |
963
|
|
|
|
|
|
|
try { |
964
|
6
|
|
|
6
|
|
180
|
my $plan = $join_type->new( $p, $const ); |
965
|
6
|
|
|
|
|
15
|
push( @return_plans, $plan ); |
966
|
|
|
|
0
|
|
|
} catch RDF::Query::Error::MethodInvocationError with { |
967
|
|
|
|
|
|
|
# warn "caught MethodInvocationError."; |
968
|
6
|
|
|
|
|
87
|
}; |
969
|
|
|
|
|
|
|
} |
970
|
|
|
|
|
|
|
} |
971
|
|
|
|
|
|
|
} |
972
|
3
|
|
|
|
|
54
|
return @return_plans; |
973
|
|
|
|
|
|
|
} |
974
|
|
|
|
|
|
|
|
975
|
|
|
|
|
|
|
sub _path_plans { |
976
|
5
|
|
|
5
|
|
8
|
my $self = shift; |
977
|
5
|
|
|
|
|
9
|
my $algebra = shift; |
978
|
5
|
|
|
|
|
8
|
my $context = shift; |
979
|
5
|
|
|
|
|
12
|
my %args = @_; |
980
|
5
|
|
|
|
|
17
|
my $path = $algebra->path; |
981
|
5
|
|
|
|
|
16
|
my $start = $algebra->start; |
982
|
5
|
|
|
|
|
13
|
my $end = $algebra->end; |
983
|
5
|
|
|
|
|
15
|
for ($start, $end) { |
984
|
10
|
50
|
|
|
|
56
|
if ($_->isa('RDF::Query::Node::Blank')) { |
985
|
0
|
|
|
|
|
0
|
$_ = $_->make_distinguished_variable; |
986
|
|
|
|
|
|
|
} |
987
|
|
|
|
|
|
|
} |
988
|
|
|
|
|
|
|
|
989
|
5
|
|
|
|
|
20
|
my $npath = $self->_normalize_path( $path ); |
990
|
5
|
|
|
|
|
31
|
return $self->__path_plan( $start, $npath, $end, $args{ active_graph }, $context, %args ); |
991
|
|
|
|
|
|
|
} |
992
|
|
|
|
|
|
|
|
993
|
|
|
|
|
|
|
sub _normalize_path { |
994
|
17
|
|
|
17
|
|
23
|
my $self = shift; |
995
|
17
|
|
|
|
|
24
|
my $path = shift; |
996
|
17
|
100
|
|
|
|
56
|
if (blessed($path)) { |
997
|
10
|
|
|
|
|
29
|
return $path; |
998
|
|
|
|
|
|
|
} |
999
|
|
|
|
|
|
|
|
1000
|
7
|
|
|
|
|
13
|
my $op = $path->[0]; |
1001
|
7
|
|
|
|
|
15
|
my @nodes = map { $self->_normalize_path($_) } @{ $path }[ 1 .. $#{ $path } ]; |
|
12
|
|
|
|
|
36
|
|
|
7
|
|
|
|
|
15
|
|
|
7
|
|
|
|
|
15
|
|
1002
|
7
|
50
|
|
|
|
47
|
if ($op eq '0-') { |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
1003
|
0
|
|
|
|
|
0
|
$op = '*'; |
1004
|
|
|
|
|
|
|
} elsif ($op eq '1-') { |
1005
|
0
|
|
|
|
|
0
|
$op = '+'; |
1006
|
|
|
|
|
|
|
} elsif ($op eq '0-1') { |
1007
|
0
|
|
|
|
|
0
|
$op = '?'; |
1008
|
|
|
|
|
|
|
} elsif ($op =~ /^-\d+$/) { |
1009
|
0
|
|
|
|
|
0
|
$op = "0$op"; |
1010
|
|
|
|
|
|
|
} |
1011
|
|
|
|
|
|
|
|
1012
|
7
|
50
|
|
|
|
20
|
if ($op eq '!') { |
1013
|
|
|
|
|
|
|
# re-order the nodes so that forward predicates come first, followed by backwards predicates |
1014
|
|
|
|
|
|
|
# !(:fwd1|:fwd2|:fwd3|^:bkw1|^:bkw2|^:bkw3) |
1015
|
0
|
0
|
|
|
|
0
|
@nodes = sort { blessed($a) ? -1 : (($a->[0] eq '^') ? 1 : -1) } @nodes; |
|
0
|
0
|
|
|
|
0
|
|
1016
|
|
|
|
|
|
|
} |
1017
|
|
|
|
|
|
|
|
1018
|
7
|
|
|
|
|
24
|
return [$op, @nodes]; |
1019
|
|
|
|
|
|
|
} |
1020
|
|
|
|
|
|
|
|
1021
|
|
|
|
|
|
|
sub __path_plan { |
1022
|
47
|
|
|
47
|
|
67
|
my $self = shift; |
1023
|
47
|
|
|
|
|
56
|
my $start = shift; |
1024
|
47
|
|
|
|
|
56
|
my $path = shift; |
1025
|
47
|
|
|
|
|
60
|
my $end = shift; |
1026
|
47
|
|
|
|
|
66
|
my $graph = shift; |
1027
|
47
|
|
|
|
|
59
|
my $context = shift; |
1028
|
47
|
|
|
|
|
136
|
my %args = @_; |
1029
|
47
|
|
|
|
|
67
|
my $distinct = 1; #$args{distinct} ? 1 : 0; |
1030
|
47
|
|
50
|
|
|
144
|
my $config = $context->options || {}; |
1031
|
47
|
|
|
|
|
158
|
my $l = Log::Log4perl->get_logger("rdf.query.plan.path"); |
1032
|
|
|
|
|
|
|
|
1033
|
|
|
|
|
|
|
|
1034
|
|
|
|
|
|
|
# _simple_path will return an algebra object if the path can be expanded |
1035
|
|
|
|
|
|
|
# into a simple basic graph pattern (for fixed-length paths) |
1036
|
47
|
100
|
|
|
|
1397
|
if (my $a = $self->_simple_path( $start, $path, $end, $graph )) { |
1037
|
43
|
|
|
|
|
1014
|
my ($plan) = $self->generate_plans( $a, $context, %args, prevent_distinguishing_bnodes => 1 ); |
1038
|
43
|
|
|
|
|
134
|
$l->trace('expanded path to pattern: ' . $plan->sse); |
1039
|
43
|
|
|
|
|
426
|
return $plan; |
1040
|
|
|
|
|
|
|
} |
1041
|
|
|
|
|
|
|
|
1042
|
|
|
|
|
|
|
|
1043
|
4
|
50
|
|
|
|
15
|
if (blessed($path)) { |
1044
|
|
|
|
|
|
|
### X iri Y |
1045
|
|
|
|
|
|
|
# $path is a resource object: this is a triple (a path of length 1) |
1046
|
0
|
|
|
|
|
0
|
my $s = $start; |
1047
|
0
|
|
|
|
|
0
|
my $e = $end; |
1048
|
0
|
0
|
|
|
|
0
|
my $algebra = $graph |
1049
|
|
|
|
|
|
|
? RDF::Query::Algebra::Quad->new( $s, $path, $e, $graph ) |
1050
|
|
|
|
|
|
|
: RDF::Query::Algebra::Triple->new( $s, $path, $e ); |
1051
|
0
|
|
|
|
|
0
|
my ($plan) = $self->generate_plans( $algebra, $context, %args, prevent_distinguishing_bnodes => 1 ); |
1052
|
0
|
|
|
|
|
0
|
$l->trace('expanded path to pattern: ' . $plan->sse); |
1053
|
0
|
|
|
|
|
0
|
return $plan; |
1054
|
|
|
|
|
|
|
} |
1055
|
|
|
|
|
|
|
|
1056
|
4
|
|
|
|
|
12
|
my ($op, @nodes) = @$path; |
1057
|
|
|
|
|
|
|
|
1058
|
|
|
|
|
|
|
# if ($op eq 'DISTINCT') { |
1059
|
|
|
|
|
|
|
# return $self->__path_plan( $start, $nodes[0], $end, $graph, $context, %args, distinct => 1 ); |
1060
|
|
|
|
|
|
|
# } |
1061
|
4
|
50
|
|
|
|
28
|
if ($op eq '!') { |
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1062
|
0
|
|
|
|
|
0
|
my $total = scalar(@nodes); |
1063
|
0
|
0
|
|
|
|
0
|
my $neg = scalar(@{ [ grep { not(blessed($_)) and $_->[0] eq '^' } @nodes ] }); |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
1064
|
0
|
|
|
|
|
0
|
my $pos = $total - $neg; |
1065
|
0
|
0
|
|
|
|
0
|
if ($pos == $total) { |
|
|
0
|
|
|
|
|
|
1066
|
|
|
|
|
|
|
### X !(:iri1|...|:irin) Y |
1067
|
0
|
|
|
|
|
0
|
return RDF::Query::Plan::Path->new( 'NegatedPropertySet', $start, [@nodes], $end, $graph, $distinct, %args ); |
1068
|
|
|
|
|
|
|
} elsif ($neg == $total) { |
1069
|
|
|
|
|
|
|
### X !(^:iri1|...|^:irin)Y |
1070
|
0
|
|
|
|
|
0
|
my @preds = map { $_->[1] } @nodes; |
|
0
|
|
|
|
|
0
|
|
1071
|
0
|
|
|
|
|
0
|
return $self->__path_plan($start, ['^', ['!', @preds]], $end, $graph, $context, %args); |
1072
|
|
|
|
|
|
|
} else { |
1073
|
|
|
|
|
|
|
### X !(:iri1|...|:irii|^:irii+1|...|^:irim) Y |
1074
|
0
|
|
|
|
|
0
|
my @fwd = grep { blessed($_) } @nodes; |
|
0
|
|
|
|
|
0
|
|
1075
|
0
|
|
|
|
|
0
|
my @bwd = grep { not(blessed($_)) } @nodes; |
|
0
|
|
|
|
|
0
|
|
1076
|
0
|
|
|
|
|
0
|
my $fplan = $self->__path_plan($start, ['!', @fwd], $end, $graph, $context, %args); |
1077
|
0
|
|
|
|
|
0
|
my $bplan = $self->__path_plan($start, ['!', @bwd], $end, $graph, $context, %args); |
1078
|
0
|
|
|
|
|
0
|
return RDF::Query::Plan::Union->new($fplan, $bplan); |
1079
|
|
|
|
|
|
|
} |
1080
|
|
|
|
|
|
|
} elsif ($op eq '^') { |
1081
|
|
|
|
|
|
|
### X ^path Y |
1082
|
0
|
|
|
|
|
0
|
return $self->__path_plan( $end, $nodes[0], $start, $graph, $context, %args); |
1083
|
|
|
|
|
|
|
} elsif ($op eq '/') { |
1084
|
2
|
|
|
|
|
5
|
my $count = scalar(@nodes); |
1085
|
2
|
50
|
|
|
|
7
|
if ($count == 1) { |
1086
|
0
|
|
|
|
|
0
|
return $self->__path_plan( $start, $nodes[0], $end, $graph, $context, %args ); |
1087
|
|
|
|
|
|
|
} else { |
1088
|
2
|
|
|
|
|
8
|
my $joinvar = RDF::Query::Node::Variable->new(); |
1089
|
2
|
|
|
|
|
32
|
my @plans = $self->__path_plan( $start, $nodes[0], $joinvar, $graph, $context, %args ); |
1090
|
2
|
|
|
|
|
6
|
foreach my $i (2 .. $count) { |
1091
|
2
|
50
|
|
|
|
8
|
my $endvar = ($i == $count) ? $end : RDF::Query::Node::Variable->new(); |
1092
|
2
|
|
|
|
|
9
|
my ($rhs) = $self->__path_plan( $joinvar, $nodes[$i-1], $endvar, $graph, $context, %args ); |
1093
|
2
|
|
|
|
|
5
|
push(@plans, $rhs); |
1094
|
2
|
|
|
|
|
6
|
$joinvar = $endvar; |
1095
|
|
|
|
|
|
|
} |
1096
|
2
|
|
|
|
|
10
|
my @join_types = RDF::Query::Plan::Join->join_classes( $config ); |
1097
|
2
|
|
|
|
|
4
|
my @jplans; |
1098
|
2
|
|
|
|
|
5
|
foreach my $jclass (@join_types) { |
1099
|
4
|
|
|
|
|
31
|
push(@jplans, $jclass->new( @plans[0,1], 0 )); |
1100
|
|
|
|
|
|
|
} |
1101
|
2
|
|
|
|
|
9
|
$l->trace("expanded /-path to: " . $jplans[0]->sse); |
1102
|
2
|
|
|
|
|
37
|
return $jplans[0]; |
1103
|
|
|
|
|
|
|
} |
1104
|
|
|
|
|
|
|
} elsif ($op eq '|') { |
1105
|
|
|
|
|
|
|
### X path1 | path2 Y |
1106
|
0
|
|
|
|
|
0
|
my @plans = map { $self->__path_plan($start, $_, $end, $graph, $context, %args) } @nodes; |
|
0
|
|
|
|
|
0
|
|
1107
|
0
|
|
|
|
|
0
|
return RDF::Query::Plan::Union->new(@plans); |
1108
|
|
|
|
|
|
|
} elsif ($op eq '?') { |
1109
|
|
|
|
|
|
|
### X path? Y |
1110
|
0
|
|
|
|
|
0
|
my $upath = $nodes[0]; |
1111
|
0
|
|
|
|
|
0
|
my $zplan = $self->__path_plan($start, ['0', $upath], $end, $graph, $context, %args ); |
1112
|
0
|
|
|
|
|
0
|
my $oplan = $self->__path_plan($start, $upath, $end, $graph, $context, %args); |
1113
|
|
|
|
|
|
|
|
1114
|
|
|
|
|
|
|
# project away any non-distinguished variables introduced by plan-to-bgp expansion |
1115
|
0
|
0
|
|
|
|
0
|
my @vars = grep { blessed($_) and $_->isa('RDF::Query::Node::Variable') } ($start, $end); |
|
0
|
|
|
|
|
0
|
|
1116
|
0
|
|
|
|
|
0
|
my $odplan = RDF::Query::Plan::Project->new( $oplan, \@vars ); |
1117
|
|
|
|
|
|
|
|
1118
|
0
|
|
|
|
|
0
|
my $pplan = RDF::Query::Plan::Union->new($zplan, $odplan); |
1119
|
|
|
|
|
|
|
|
1120
|
|
|
|
|
|
|
# distinct the results |
1121
|
0
|
|
|
|
|
0
|
my $plan = RDF::Query::Plan::Distinct->new( $pplan ); |
1122
|
0
|
|
|
|
|
0
|
return $plan; |
1123
|
|
|
|
|
|
|
} elsif ($op eq '*') { |
1124
|
|
|
|
|
|
|
### X path* Y |
1125
|
1
|
|
|
|
|
9
|
return RDF::Query::Plan::Path->new( 'ZeroOrMorePath', $start, $nodes[0], $end, $graph, $distinct, %args ); |
1126
|
|
|
|
|
|
|
} elsif ($op eq '+') { |
1127
|
|
|
|
|
|
|
### X path+ Y |
1128
|
1
|
|
|
|
|
14
|
return RDF::Query::Plan::Path->new( 'OneOrMorePath', $start, $nodes[0], $end, $graph, $distinct, %args ); |
1129
|
|
|
|
|
|
|
} elsif ($op eq '0') { |
1130
|
|
|
|
|
|
|
### X path{0} Y |
1131
|
0
|
|
|
|
|
0
|
return RDF::Query::Plan::Path->new( 'ZeroLengthPath', $start, $nodes[0], $end, $graph, $distinct, %args ); |
1132
|
|
|
|
|
|
|
} elsif ($op =~ /^\d+$/) { |
1133
|
|
|
|
|
|
|
### X path{n} Y where n > 0 |
1134
|
0
|
|
|
|
|
0
|
my $count = $op; |
1135
|
0
|
0
|
|
|
|
0
|
if ($count == 1) { |
1136
|
0
|
|
|
|
|
0
|
return $self->__path_plan( $start, $nodes[0], $end, $graph, $context, %args ); |
1137
|
|
|
|
|
|
|
} else { |
1138
|
0
|
|
|
|
|
0
|
my $joinvar = RDF::Query::Node::Variable->new(); |
1139
|
0
|
|
|
|
|
0
|
my @plans = $self->__path_plan( $start, $nodes[0], $joinvar, $graph, $context, %args ); |
1140
|
0
|
|
|
|
|
0
|
foreach my $i (2 .. $count) { |
1141
|
0
|
0
|
|
|
|
0
|
my $endvar = ($i == $count) ? $end : RDF::Query::Node::Variable->new(); |
1142
|
0
|
|
|
|
|
0
|
my ($rhs) = $self->__path_plan( $joinvar, $nodes[0], $endvar, $graph, $context, %args ); |
1143
|
0
|
|
|
|
|
0
|
push(@plans, $rhs); |
1144
|
0
|
|
|
|
|
0
|
$joinvar = $endvar; |
1145
|
|
|
|
|
|
|
} |
1146
|
0
|
|
|
|
|
0
|
my @join_types = RDF::Query::Plan::Join->join_classes( $config ); |
1147
|
0
|
|
|
|
|
0
|
my @jplans; |
1148
|
|
|
|
|
|
|
|
1149
|
0
|
|
|
|
|
0
|
my @plan = shift(@plans); |
1150
|
0
|
|
|
|
|
0
|
while (@plans) { |
1151
|
0
|
|
|
|
|
0
|
my $q = shift(@plans); |
1152
|
0
|
|
|
|
|
0
|
my @p; |
1153
|
0
|
|
|
|
|
0
|
foreach my $p (@plan) { |
1154
|
0
|
|
|
|
|
0
|
foreach my $jclass (@join_types) { |
1155
|
0
|
|
|
|
|
0
|
push(@p, $jclass->new( $p, $q, 0 )); |
1156
|
|
|
|
|
|
|
} |
1157
|
|
|
|
|
|
|
} |
1158
|
0
|
|
|
|
|
0
|
@plan = @p; |
1159
|
|
|
|
|
|
|
} |
1160
|
0
|
|
|
|
|
0
|
return $plan[0]; |
1161
|
|
|
|
|
|
|
} |
1162
|
|
|
|
|
|
|
} elsif ($op =~ /^(\d+)-(\d+)$/) { |
1163
|
|
|
|
|
|
|
### X path{n,m} Y |
1164
|
0
|
|
|
|
|
0
|
my ($n,$m) = split('-', $op, 2); |
1165
|
|
|
|
|
|
|
# warn "$1- to $2-length path"; |
1166
|
0
|
|
|
|
|
0
|
my @range = sort { $a <=> $b } ($n, $m); |
|
0
|
|
|
|
|
0
|
|
1167
|
0
|
|
|
|
|
0
|
my $from = $range[0]; |
1168
|
0
|
|
|
|
|
0
|
my $to = $range[1]; |
1169
|
0
|
|
|
|
|
0
|
my @plans; |
1170
|
0
|
|
|
|
|
0
|
foreach my $i ($from .. $to) { |
1171
|
0
|
0
|
|
|
|
0
|
if ($i == 0) { |
1172
|
0
|
|
|
|
|
0
|
push(@plans, $self->__path_plan($start, ['0', []], $end, $graph, $context, %args )); |
1173
|
|
|
|
|
|
|
} else { |
1174
|
0
|
|
|
|
|
0
|
push(@plans, $self->__path_plan( $start, [$i, $nodes[0]], $end, $graph, $context, %args )); |
1175
|
|
|
|
|
|
|
} |
1176
|
|
|
|
|
|
|
} |
1177
|
0
|
|
|
|
|
0
|
while (scalar(@plans) > 1) { |
1178
|
0
|
|
|
|
|
0
|
my $lhs = shift(@plans); |
1179
|
0
|
|
|
|
|
0
|
my $rhs = shift(@plans); |
1180
|
0
|
|
|
|
|
0
|
unshift(@plans, RDF::Query::Plan::Union->new( $lhs, $rhs )); |
1181
|
|
|
|
|
|
|
} |
1182
|
0
|
|
|
|
|
0
|
return $plans[0]; |
1183
|
|
|
|
|
|
|
} elsif ($op =~ /^(\d+)-$/) { |
1184
|
|
|
|
|
|
|
### X path{n,} Y where n > 0 |
1185
|
0
|
|
|
|
|
0
|
my ($min) = split('-', $op); |
1186
|
|
|
|
|
|
|
# expand :p{n,} into :p{n}/:p* |
1187
|
0
|
|
|
|
|
0
|
my $path = [ '/', [ $1, @nodes ], [ '*', @nodes ] ]; |
1188
|
0
|
|
|
|
|
0
|
my $plan = $self->__path_plan( $start, $path, $end, $graph, $context, %args ); |
1189
|
0
|
|
|
|
|
0
|
return $plan; |
1190
|
|
|
|
|
|
|
} else { |
1191
|
0
|
|
|
|
|
0
|
throw RDF::Query::Error -text => "Cannot generate plan for unknown path type $op"; |
1192
|
|
|
|
|
|
|
} |
1193
|
|
|
|
|
|
|
} |
1194
|
|
|
|
|
|
|
|
1195
|
|
|
|
|
|
|
sub _simple_path { |
1196
|
55
|
|
|
55
|
|
78
|
my $self = shift; |
1197
|
55
|
|
|
|
|
71
|
my $start = shift; |
1198
|
55
|
|
|
|
|
66
|
my $path = shift; |
1199
|
55
|
|
|
|
|
63
|
my $end = shift; |
1200
|
55
|
|
|
|
|
75
|
my $graph = shift; |
1201
|
55
|
100
|
|
|
|
201
|
if (blessed($path)) { |
1202
|
46
|
100
|
|
|
|
160
|
return ($graph) |
1203
|
|
|
|
|
|
|
? RDF::Query::Algebra::Quad->new( $start, $path, $end, $graph ) |
1204
|
|
|
|
|
|
|
: RDF::Query::Algebra::Triple->new( $start, $path, $end ); |
1205
|
|
|
|
|
|
|
} |
1206
|
9
|
50
|
|
|
|
34
|
return unless (reftype($path) eq 'ARRAY'); |
1207
|
9
|
|
|
|
|
20
|
my $op = $path->[0]; |
1208
|
9
|
100
|
33
|
|
|
46
|
if ($op eq '/') { |
|
|
50
|
33
|
|
|
|
|
|
|
50
|
33
|
|
|
|
|
1209
|
5
|
|
|
|
|
9
|
my @patterns; |
1210
|
5
|
|
|
|
|
10
|
my @jvars = map { RDF::Query::Node::Variable->new() } (2 .. $#{ $path }); |
|
5
|
|
|
|
|
19
|
|
|
5
|
|
|
|
|
14
|
|
1211
|
5
|
|
|
|
|
41
|
foreach my $i (1 .. $#{ $path }) { |
|
5
|
|
|
|
|
15
|
|
1212
|
8
|
100
|
|
|
|
21
|
my $s = ($i == 1) ? $start : $jvars[ $i-2 ]; |
1213
|
8
|
100
|
|
|
|
10
|
my $e = ($i == $#{ $path }) ? $end : $jvars[ $i-1 ]; |
|
8
|
|
|
|
|
28
|
|
1214
|
8
|
|
|
|
|
29
|
my $triple = $self->_simple_path( $s, $path->[ $i ], $e, $graph ); |
1215
|
8
|
100
|
|
|
|
146
|
return unless ($triple); |
1216
|
6
|
|
|
|
|
14
|
push(@patterns, $triple); |
1217
|
|
|
|
|
|
|
} |
1218
|
3
|
50
|
|
|
|
6
|
my @triples = map { $_->isa('RDF::Query::Algebra::BasicGraphPattern') ? $_->triples : $_ } @patterns; |
|
6
|
|
|
|
|
38
|
|
1219
|
3
|
|
|
|
|
14
|
return RDF::Query::Algebra::BasicGraphPattern->new( @triples ); |
1220
|
|
|
|
|
|
|
} elsif ($op eq '^' and scalar(@$path) == 2 and blessed($path->[1])) { |
1221
|
0
|
0
|
|
|
|
0
|
return ($graph) |
1222
|
|
|
|
|
|
|
? RDF::Query::Algebra::Quad->new( $end, $path->[1], $start, $graph ) |
1223
|
|
|
|
|
|
|
: RDF::Query::Algebra::Triple->new( $end, $path->[1], $start ); |
1224
|
|
|
|
|
|
|
} elsif ($op =~ /^\d+$/ and $op == 1) { |
1225
|
0
|
|
|
|
|
0
|
return $self->_simple_path( $start, $path->[1], $end, $graph ); |
1226
|
|
|
|
|
|
|
} |
1227
|
|
|
|
|
|
|
|
1228
|
4
|
|
|
|
|
10
|
return; |
1229
|
|
|
|
|
|
|
} |
1230
|
|
|
|
|
|
|
|
1231
|
|
|
|
|
|
|
=item C<< plan_node_name >> |
1232
|
|
|
|
|
|
|
|
1233
|
|
|
|
|
|
|
Returns the string name of this plan node, suitable for use in serialization. |
1234
|
|
|
|
|
|
|
|
1235
|
|
|
|
|
|
|
=cut |
1236
|
|
|
|
|
|
|
|
1237
|
|
|
|
|
|
|
sub plan_node_name; |
1238
|
|
|
|
|
|
|
|
1239
|
|
|
|
|
|
|
=item C<< plan_prototype >> |
1240
|
|
|
|
|
|
|
|
1241
|
|
|
|
|
|
|
Returns a list of scalar identifiers for the type of the content (children) |
1242
|
|
|
|
|
|
|
nodes of this plan node. These identifiers are recognized: |
1243
|
|
|
|
|
|
|
|
1244
|
|
|
|
|
|
|
* 'A' - An RDF::Query::Algebra object |
1245
|
|
|
|
|
|
|
* 'b' - A boolean integer value (0 or 1) |
1246
|
|
|
|
|
|
|
* 'E' - An expression (either an RDF::Query::Expression object or an RDF node) |
1247
|
|
|
|
|
|
|
* 'i' - An integer |
1248
|
|
|
|
|
|
|
* 'J' - A valid Project node (an RDF::Query::Expression object or an Variable node) |
1249
|
|
|
|
|
|
|
* 'N' - An RDF node |
1250
|
|
|
|
|
|
|
* 'P' - A RDF::Query::Plan object |
1251
|
|
|
|
|
|
|
* 'q' - A RDF::Query object |
1252
|
|
|
|
|
|
|
* 'Q' - An RDF::Trine::Statement::Quad object |
1253
|
|
|
|
|
|
|
* 's' - A string |
1254
|
|
|
|
|
|
|
* 'T' - An RDF::Trine::Statement object |
1255
|
|
|
|
|
|
|
* 'u' - A valid URI string |
1256
|
|
|
|
|
|
|
* 'V' - A variable binding set (an object of type RDF::Query::VariableBindings) |
1257
|
|
|
|
|
|
|
* 'w' - A bareword string |
1258
|
|
|
|
|
|
|
* 'W' - An RDF node or wildcard ('*') |
1259
|
|
|
|
|
|
|
* '*X' - A list of X nodes (where X is another identifier scalar) |
1260
|
|
|
|
|
|
|
* '\X' - An array reference of X nodes (where X is another identifier scalar) |
1261
|
|
|
|
|
|
|
|
1262
|
|
|
|
|
|
|
=cut |
1263
|
|
|
|
|
|
|
|
1264
|
|
|
|
|
|
|
sub plan_prototype; |
1265
|
|
|
|
|
|
|
|
1266
|
|
|
|
|
|
|
=item C<< plan_node_data >> |
1267
|
|
|
|
|
|
|
|
1268
|
|
|
|
|
|
|
Returns the data for this plan node that corresponds to the values described by |
1269
|
|
|
|
|
|
|
the signature returned by C<< plan_prototype >>. |
1270
|
|
|
|
|
|
|
|
1271
|
|
|
|
|
|
|
=cut |
1272
|
|
|
|
|
|
|
|
1273
|
|
|
|
|
|
|
sub plan_node_data; |
1274
|
|
|
|
|
|
|
|
1275
|
|
|
|
|
|
|
|
1276
|
|
|
|
|
|
|
=item C<< subplans_of_type ( $type [, $block] ) >> |
1277
|
|
|
|
|
|
|
|
1278
|
|
|
|
|
|
|
Returns a list of Plan objects matching C<< $type >> (tested with C<< isa >>). |
1279
|
|
|
|
|
|
|
If C<< $block >> is given, then matching stops descending a subtree if the current |
1280
|
|
|
|
|
|
|
node is of type C<< $block >>, continuing matching on other subtrees. |
1281
|
|
|
|
|
|
|
This list includes the current plan object if it matches C<< $type >>, and is |
1282
|
|
|
|
|
|
|
generated in infix order. |
1283
|
|
|
|
|
|
|
|
1284
|
|
|
|
|
|
|
=cut |
1285
|
|
|
|
|
|
|
|
1286
|
|
|
|
|
|
|
sub subplans_of_type { |
1287
|
55
|
|
|
55
|
1
|
99
|
my $self = shift; |
1288
|
55
|
|
|
|
|
97
|
my $type = shift; |
1289
|
55
|
|
|
|
|
86
|
my $block = shift; |
1290
|
|
|
|
|
|
|
|
1291
|
55
|
50
|
33
|
|
|
178
|
return if ($block and $self->isa($block)); |
1292
|
|
|
|
|
|
|
|
1293
|
55
|
|
|
|
|
78
|
my @patterns; |
1294
|
55
|
50
|
|
|
|
361
|
push(@patterns, $self) if ($self->isa($type)); |
1295
|
|
|
|
|
|
|
|
1296
|
|
|
|
|
|
|
|
1297
|
55
|
|
|
|
|
246
|
foreach my $p ($self->plan_node_data) { |
1298
|
192
|
100
|
100
|
|
|
1497
|
if (blessed($p) and $p->isa('RDF::Query::Plan')) { |
1299
|
11
|
|
|
|
|
52
|
push(@patterns, $p->subplans_of_type($type, $block)); |
1300
|
|
|
|
|
|
|
} |
1301
|
|
|
|
|
|
|
} |
1302
|
55
|
|
|
|
|
218
|
return @patterns; |
1303
|
|
|
|
|
|
|
} |
1304
|
|
|
|
|
|
|
|
1305
|
|
|
|
|
|
|
1; |
1306
|
|
|
|
|
|
|
|
1307
|
|
|
|
|
|
|
__END__ |
1308
|
|
|
|
|
|
|
|
1309
|
|
|
|
|
|
|
=back |
1310
|
|
|
|
|
|
|
|
1311
|
|
|
|
|
|
|
=head1 AUTHOR |
1312
|
|
|
|
|
|
|
|
1313
|
|
|
|
|
|
|
Gregory Todd Williams <gwilliams@cpan.org> |
1314
|
|
|
|
|
|
|
|
1315
|
|
|
|
|
|
|
=cut |