| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | # RDF::Query::Plan::Path | 
| 2 |  |  |  |  |  |  | # ----------------------------------------------------------------------------- | 
| 3 |  |  |  |  |  |  |  | 
| 4 |  |  |  |  |  |  | =head1 NAME | 
| 5 |  |  |  |  |  |  |  | 
| 6 |  |  |  |  |  |  | RDF::Query::Plan::Path - Executable query plan for Paths. | 
| 7 |  |  |  |  |  |  |  | 
| 8 |  |  |  |  |  |  | =head1 VERSION | 
| 9 |  |  |  |  |  |  |  | 
| 10 |  |  |  |  |  |  | This document describes RDF::Query::Plan::Path 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::Path; | 
| 22 |  |  |  |  |  |  |  | 
| 23 | 35 |  |  | 35 |  | 192 | use strict; | 
|  | 35 |  |  |  |  | 65 |  | 
|  | 35 |  |  |  |  | 880 |  | 
| 24 | 35 |  |  | 35 |  | 180 | use warnings; | 
|  | 35 |  |  |  |  | 76 |  | 
|  | 35 |  |  |  |  | 925 |  | 
| 25 | 35 |  |  | 35 |  | 199 | use base qw(RDF::Query::Plan); | 
|  | 35 |  |  |  |  | 71 |  | 
|  | 35 |  |  |  |  | 2389 |  | 
| 26 |  |  |  |  |  |  |  | 
| 27 | 35 |  |  | 35 |  | 204 | use Log::Log4perl; | 
|  | 35 |  |  |  |  | 72 |  | 
|  | 35 |  |  |  |  | 256 |  | 
| 28 | 35 |  |  | 35 |  | 1720 | use Scalar::Util qw(blessed refaddr); | 
|  | 35 |  |  |  |  | 79 |  | 
|  | 35 |  |  |  |  | 2062 |  | 
| 29 | 35 |  |  | 35 |  | 187 | use Time::HiRes qw(gettimeofday tv_interval); | 
|  | 35 |  |  |  |  | 78 |  | 
|  | 35 |  |  |  |  | 243 |  | 
| 30 |  |  |  |  |  |  |  | 
| 31 | 35 |  |  | 35 |  | 3714 | use RDF::Query::ExecutionContext; | 
|  | 35 |  |  |  |  | 79 |  | 
|  | 35 |  |  |  |  | 878 |  | 
| 32 | 35 |  |  | 35 |  | 188 | use RDF::Query::VariableBindings; | 
|  | 35 |  |  |  |  | 80 |  | 
|  | 35 |  |  |  |  | 1702 |  | 
| 33 |  |  |  |  |  |  |  | 
| 34 |  |  |  |  |  |  | ###################################################################### | 
| 35 |  |  |  |  |  |  |  | 
| 36 |  |  |  |  |  |  | our ($VERSION); | 
| 37 |  |  |  |  |  |  | BEGIN { | 
| 38 | 35 |  |  | 35 |  | 124148 | $VERSION	= '2.915_01'; | 
| 39 |  |  |  |  |  |  | } | 
| 40 |  |  |  |  |  |  |  | 
| 41 |  |  |  |  |  |  | ###################################################################### | 
| 42 |  |  |  |  |  |  |  | 
| 43 |  |  |  |  |  |  | =item C<< new ( $path_operator, $path, $start, $end, $graph, $distinct, %args ) >> | 
| 44 |  |  |  |  |  |  |  | 
| 45 |  |  |  |  |  |  | =cut | 
| 46 |  |  |  |  |  |  |  | 
| 47 |  |  |  |  |  |  | sub new { | 
| 48 | 2 |  |  | 2 | 1 | 4 | my $class	= shift; | 
| 49 | 2 |  |  |  |  | 3 | my $op		= shift; | 
| 50 | 2 |  |  |  |  | 4 | my $start	= shift; | 
| 51 | 2 |  |  |  |  | 2 | my $path	= shift; | 
| 52 | 2 |  |  |  |  | 3 | my $end		= shift; | 
| 53 | 2 |  |  |  |  | 4 | my $graph	= shift; | 
| 54 | 2 |  |  |  |  | 3 | my $distinct	= shift; | 
| 55 | 2 |  |  |  |  | 6 | my %args	= @_; | 
| 56 | 2 |  |  |  |  | 13 | my $self	= $class->SUPER::new( $op, $path, $start, $end, $graph, $distinct, \%args ); | 
| 57 | 2 |  |  |  |  | 5 | my %vars; | 
| 58 | 2 |  |  |  |  | 5 | for ($start, $end) { | 
| 59 | 4 | 50 |  |  |  | 31 | $vars{ $_->name }++ if ($_->isa('RDF::Query::Node::Variable')); | 
| 60 |  |  |  |  |  |  | } | 
| 61 | 2 |  |  |  |  | 21 | $self->[0]{referenced_variables}	= [keys %vars]; | 
| 62 | 2 |  |  |  |  | 12 | return $self; | 
| 63 |  |  |  |  |  |  | } | 
| 64 |  |  |  |  |  |  |  | 
| 65 |  |  |  |  |  |  | =item C<< execute ( $execution_context ) >> | 
| 66 |  |  |  |  |  |  |  | 
| 67 |  |  |  |  |  |  | =cut | 
| 68 |  |  |  |  |  |  |  | 
| 69 |  |  |  |  |  |  | sub execute ($) { | 
| 70 | 2 |  |  | 2 | 1 | 3 | my $self	= shift; | 
| 71 | 2 |  |  |  |  | 3 | my $context	= shift; | 
| 72 | 2 |  |  |  |  | 9 | $self->[0]{delegate}	= $context->delegate; | 
| 73 | 2 | 50 |  |  |  | 19 | if ($self->state == $self->OPEN) { | 
| 74 | 0 |  |  |  |  | 0 | throw RDF::Query::Error::ExecutionError -text => "PATH plan can't be executed while already open"; | 
| 75 |  |  |  |  |  |  | } | 
| 76 |  |  |  |  |  |  |  | 
| 77 | 2 |  |  |  |  | 9 | my $l		= Log::Log4perl->get_logger("rdf.query.plan.path"); | 
| 78 | 2 |  |  |  |  | 45 | $l->trace( "executing RDF::Query::Plan::Path " . $self->sse ); | 
| 79 |  |  |  |  |  |  |  | 
| 80 | 2 |  |  |  |  | 21 | my $start	= $self->start; | 
| 81 | 2 |  |  |  |  | 6 | my $end		= $self->end; | 
| 82 | 2 |  |  |  |  | 6 | my $graph	= $self->graph; | 
| 83 | 2 |  |  |  |  | 10 | my $bound	= $context->bound; | 
| 84 | 2 | 50 |  |  |  | 8 | if (%$bound) { | 
| 85 | 0 |  |  |  |  | 0 | for ($start, $end, $graph) { | 
| 86 | 0 | 0 |  |  |  | 0 | next unless (blessed($_)); | 
| 87 | 0 | 0 |  |  |  | 0 | next unless ($_->isa('RDF::Trine::Node::Variable')); | 
| 88 | 0 | 0 |  |  |  | 0 | next unless (blessed($bound->{ $_->name })); | 
| 89 | 0 |  |  |  |  | 0 | $_	= $bound->{ $_->name }; | 
| 90 |  |  |  |  |  |  | } | 
| 91 |  |  |  |  |  |  | } | 
| 92 |  |  |  |  |  |  |  | 
| 93 | 2 |  |  |  |  | 7 | $self->[0]{results}	= []; | 
| 94 | 2 | 50 |  |  |  | 7 | my @vars		= grep { blessed($_) and $_->isa('RDF::Trine::Node::Variable') } ($self->start, $self->end); | 
|  | 4 |  |  |  |  | 30 |  | 
| 95 | 2 |  |  |  |  | 8 | my $model		= $context->model; | 
| 96 |  |  |  |  |  |  |  | 
| 97 | 2 |  |  |  |  | 7 | $self->[0]{bound}	= $bound; | 
| 98 | 2 |  |  |  |  | 5 | $self->[0]{graph}	= $graph; | 
| 99 | 2 |  |  |  |  | 5 | $self->[0]{count}	= 0; | 
| 100 | 2 |  |  |  |  | 7 | $self->[0]{context}	= $context; | 
| 101 | 2 |  |  |  |  | 10 | $self->state( $self->OPEN ); | 
| 102 |  |  |  |  |  |  |  | 
| 103 | 2 |  |  |  |  | 4 | my $op			= $self->path_operator; | 
| 104 | 2 | 50 |  |  |  | 12 | if ($op eq 'NegatedPropertySet') { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 105 | 0 |  |  |  |  | 0 | $self->_run_nps(); | 
| 106 |  |  |  |  |  |  | } elsif ($op eq 'ZeroOrMorePath') { | 
| 107 | 1 |  |  |  |  | 5 | $self->_run_zeroormore(); | 
| 108 |  |  |  |  |  |  | } elsif ($op eq 'OneOrMorePath') { | 
| 109 | 1 |  |  |  |  | 5 | $self->_run_oneormore(); | 
| 110 |  |  |  |  |  |  | } elsif ($op eq 'ZeroLengthPath') { | 
| 111 | 0 |  |  |  |  | 0 | $self->_run_zerolength(); | 
| 112 |  |  |  |  |  |  | } | 
| 113 |  |  |  |  |  |  |  | 
| 114 | 2 |  |  |  |  | 10 | $self; | 
| 115 |  |  |  |  |  |  | } | 
| 116 |  |  |  |  |  |  |  | 
| 117 |  |  |  |  |  |  |  | 
| 118 |  |  |  |  |  |  | sub _run_nps { | 
| 119 | 0 |  |  | 0 |  | 0 | my $self	= shift; | 
| 120 | 0 |  |  |  |  | 0 | my $context	= $self->[0]{context}; | 
| 121 | 0 |  |  |  |  | 0 | my $graph	= $self->[0]{graph}; | 
| 122 | 0 | 0 |  |  |  | 0 | $graph		= RDF::Trine::Node::Nil->new() unless (defined($graph)); | 
| 123 | 0 |  |  |  |  | 0 | my $model	= $context->model; | 
| 124 | 0 |  |  |  |  | 0 | my $path	= $self->path; | 
| 125 |  |  |  |  |  |  |  | 
| 126 | 0 |  |  |  |  | 0 | my $var		= RDF::Query::Node::Variable->new(); | 
| 127 | 0 |  |  |  |  | 0 | my $st		= RDF::Query::Algebra::Quad->new( $self->start, $var, $self->end, $graph ); | 
| 128 | 0 |  |  |  |  | 0 | my @nodes	= $st->nodes; | 
| 129 | 0 |  |  |  |  | 0 | my $plan    = RDF::Query::Plan::Quad->new( @nodes[0..2], $graph ); | 
| 130 | 0 |  |  |  |  | 0 | my %not; | 
| 131 | 0 |  |  |  |  | 0 | foreach my $n (@$path) { | 
| 132 | 0 |  |  |  |  | 0 | $not{ $n->uri_value }++; | 
| 133 |  |  |  |  |  |  | } | 
| 134 |  |  |  |  |  |  |  | 
| 135 | 0 |  |  |  |  | 0 | $plan->execute( $context ); | 
| 136 | 0 |  |  |  |  | 0 | while (my $row = $plan->next) { | 
| 137 | 0 | 0 |  |  |  | 0 | if (my $p = $row->{ $var->name }) { | 
| 138 | 0 | 0 |  |  |  | 0 | next if (exists $not{ $p->uri_value }); | 
| 139 |  |  |  |  |  |  | } | 
| 140 | 0 |  |  |  |  | 0 | push(@{ $self->[0]{results} }, $row); | 
|  | 0 |  |  |  |  | 0 |  | 
| 141 |  |  |  |  |  |  | } | 
| 142 |  |  |  |  |  |  | } | 
| 143 |  |  |  |  |  |  |  | 
| 144 |  |  |  |  |  |  | sub _run_zeroormore { | 
| 145 | 1 |  |  | 1 |  | 3 | my $self	= shift; | 
| 146 | 1 |  |  |  |  | 3 | my $context	= $self->[0]{context}; | 
| 147 | 1 |  |  |  |  | 3 | my $graph	= $self->[0]{graph}; | 
| 148 | 1 | 50 |  |  |  | 8 | $graph		= RDF::Trine::Node::Nil->new() unless (defined($graph)); | 
| 149 | 1 |  |  |  |  | 10 | my $model	= $context->model; | 
| 150 | 1 |  |  |  |  | 4 | my $path	= $self->path; | 
| 151 | 1 | 50 |  |  |  | 4 | my @vars		= grep { blessed($_) and $_->isa('RDF::Trine::Node::Variable') } ($self->start, $self->end); | 
|  | 2 |  |  |  |  | 17 |  | 
| 152 | 1 | 50 |  |  |  | 5 | if (scalar(@vars) == 2) { | 
|  |  | 0 |  |  |  |  |  | 
| 153 |  |  |  |  |  |  | # var path+ var | 
| 154 | 1 |  |  |  |  | 2 | my %nodes; | 
| 155 | 1 |  |  |  |  | 8 | foreach my $n ($model->subjects(undef, undef, $graph), $model->objects(undef, undef, $graph)) { | 
| 156 | 18 |  |  |  |  | 1269 | $nodes{ $n->as_string } = $n; | 
| 157 |  |  |  |  |  |  | } | 
| 158 | 1 |  |  |  |  | 10 | my $end		= $self->end; | 
| 159 | 1 |  |  |  |  | 3 | my $path	= $self->path; | 
| 160 | 1 |  |  |  |  | 3 | my @names	= map { $_->name } @vars; | 
|  | 2 |  |  |  |  | 11 |  | 
| 161 | 1 |  |  |  |  | 9 | foreach my $start (values %nodes) { | 
| 162 |  |  |  |  |  |  | # 			warn "starting var path* var path at $start"; | 
| 163 | 13 |  |  |  |  | 25 | my $r	= []; | 
| 164 | 13 |  |  |  |  | 35 | $self->_alp( $start, $path, $r, {} ); | 
| 165 | 13 |  |  |  |  | 37 | foreach my $term (@$r) { | 
| 166 | 19 |  |  |  |  | 56 | my %data	= ($names[0] => $start, $names[1] => $term); | 
| 167 | 19 |  |  |  |  | 95 | my $vb          = RDF::Query::VariableBindings->new(\%data); | 
| 168 | 19 |  |  |  |  | 25 | push(@{ $self->[0]{results} }, $vb); | 
|  | 19 |  |  |  |  | 80 |  | 
| 169 |  |  |  |  |  |  | } | 
| 170 |  |  |  |  |  |  | } | 
| 171 |  |  |  |  |  |  | } elsif (scalar(@vars) == 1) { | 
| 172 | 0 |  |  |  |  | 0 | my $start	= $self->start; | 
| 173 | 0 |  |  |  |  | 0 | my $end		= $self->end; | 
| 174 | 0 |  |  |  |  | 0 | my $path	= $self->path; | 
| 175 | 0 | 0 |  |  |  | 0 | if ($start->isa('RDF::Trine::Node::Variable')) { | 
| 176 |  |  |  |  |  |  | # var path+ term | 
| 177 | 0 |  |  |  |  | 0 | ($start, $end)	= ($end, $start); | 
| 178 | 0 |  |  |  |  | 0 | $path			= ['^', $path]; | 
| 179 |  |  |  |  |  |  | } | 
| 180 |  |  |  |  |  |  |  | 
| 181 |  |  |  |  |  |  | # term path+ var | 
| 182 | 0 |  |  |  |  | 0 | my $r	= []; | 
| 183 | 0 |  |  |  |  | 0 | $self->_alp( $start, $path, $r, {} ); | 
| 184 |  |  |  |  |  |  |  | 
| 185 | 0 |  |  |  |  | 0 | my $name	= $vars[0]->name; | 
| 186 | 0 |  |  |  |  | 0 | foreach my $term (@$r) { | 
| 187 | 0 |  |  |  |  | 0 | my $vb          = RDF::Query::VariableBindings->new({ $name => $term }); | 
| 188 | 0 |  |  |  |  | 0 | push(@{ $self->[0]{results} }, $vb); | 
|  | 0 |  |  |  |  | 0 |  | 
| 189 |  |  |  |  |  |  | } | 
| 190 |  |  |  |  |  |  | } else { | 
| 191 |  |  |  |  |  |  | # term path+ term | 
| 192 | 0 |  |  |  |  | 0 | my $var	= RDF::Trine::Node::Variable->new(); | 
| 193 | 0 |  |  |  |  | 0 | my $start	= $self->start; | 
| 194 | 0 |  |  |  |  | 0 | my $end		= $self->end; | 
| 195 | 0 |  |  |  |  | 0 | my $path	= $self->path; | 
| 196 | 0 |  |  |  |  | 0 | my $r	= []; | 
| 197 | 0 |  |  |  |  | 0 | $self->_alp( $start, $path, $r, {} ); | 
| 198 | 0 |  |  |  |  | 0 | foreach my $term (@$r) { | 
| 199 | 0 | 0 |  |  |  | 0 | if ($term->equal( $end )) { | 
| 200 | 0 |  |  |  |  | 0 | my $vb          = RDF::Query::VariableBindings->new({}); | 
| 201 | 0 |  |  |  |  | 0 | push(@{ $self->[0]{results} }, $vb); | 
|  | 0 |  |  |  |  | 0 |  | 
| 202 | 0 |  |  |  |  | 0 | return; | 
| 203 |  |  |  |  |  |  | } | 
| 204 |  |  |  |  |  |  | } | 
| 205 |  |  |  |  |  |  | } | 
| 206 |  |  |  |  |  |  | } | 
| 207 |  |  |  |  |  |  |  | 
| 208 |  |  |  |  |  |  | sub _run_oneormore { | 
| 209 | 1 |  |  | 1 |  | 2 | my $self	= shift; | 
| 210 | 1 |  |  |  |  | 4 | my $context	= $self->[0]{context}; | 
| 211 | 1 |  |  |  |  | 3 | my $graph	= $self->[0]{graph}; | 
| 212 | 1 | 50 |  |  |  | 7 | $graph		= RDF::Trine::Node::Nil->new() unless (defined($graph)); | 
| 213 | 1 |  |  |  |  | 10 | my $model	= $context->model; | 
| 214 | 1 |  |  |  |  | 7 | my $path	= $self->path; | 
| 215 | 1 | 50 |  |  |  | 9 | my @vars		= grep { blessed($_) and $_->isa('RDF::Trine::Node::Variable') } ($self->start, $self->end); | 
|  | 2 |  |  |  |  | 18 |  | 
| 216 | 1 | 50 |  |  |  | 4 | if (scalar(@vars) == 2) { | 
|  |  | 0 |  |  |  |  |  | 
| 217 |  |  |  |  |  |  | # var path+ var | 
| 218 | 1 |  |  |  |  | 2 | my %nodes; | 
| 219 | 1 |  |  |  |  | 11 | foreach my $n ($model->subjects(undef, undef, $graph), $model->objects(undef, undef, $graph)) { | 
| 220 | 18 |  |  |  |  | 1364 | $nodes{ $n->as_string } = $n; | 
| 221 |  |  |  |  |  |  | } | 
| 222 | 1 |  |  |  |  | 11 | my $end		= $self->end; | 
| 223 | 1 |  |  |  |  | 4 | my $path	= $self->path; | 
| 224 | 1 |  |  |  |  | 3 | my @names	= map { $_->name } @vars; | 
|  | 2 |  |  |  |  | 11 |  | 
| 225 | 1 |  |  |  |  | 9 | foreach my $start (values %nodes) { | 
| 226 |  |  |  |  |  |  | # 			warn "starting var path+ var path at $start"; | 
| 227 | 13 |  |  |  |  | 33 | my $x	= $self->_path_eval($start, $path); | 
| 228 | 13 |  |  |  |  | 265 | my $r	= []; | 
| 229 | 13 |  |  |  |  | 38 | while (my $n = $x->next) { | 
| 230 | 3 |  |  |  |  | 32 | $self->_alp( $n, $path, $r, {} ); | 
| 231 |  |  |  |  |  |  | } | 
| 232 | 13 |  |  |  |  | 191 | foreach my $term (@$r) { | 
| 233 | 6 |  |  |  |  | 20 | my %data	= ($names[0] => $start, $names[1] => $term); | 
| 234 | 6 |  |  |  |  | 19 | my $vb          = RDF::Query::VariableBindings->new(\%data); | 
| 235 | 6 |  |  |  |  | 11 | push(@{ $self->[0]{results} }, $vb); | 
|  | 6 |  |  |  |  | 34 |  | 
| 236 |  |  |  |  |  |  | } | 
| 237 |  |  |  |  |  |  | } | 
| 238 |  |  |  |  |  |  | } elsif (scalar(@vars) == 1) { | 
| 239 | 0 |  |  |  |  | 0 | my $start	= $self->start; | 
| 240 | 0 |  |  |  |  | 0 | my $end		= $self->end; | 
| 241 | 0 |  |  |  |  | 0 | my $path	= $self->path; | 
| 242 | 0 | 0 |  |  |  | 0 | if ($start->isa('RDF::Trine::Node::Variable')) { | 
| 243 |  |  |  |  |  |  | # var path+ term | 
| 244 | 0 |  |  |  |  | 0 | ($start, $end)	= ($end, $start); | 
| 245 | 0 |  |  |  |  | 0 | $path			= ['^', $path]; | 
| 246 |  |  |  |  |  |  | } | 
| 247 |  |  |  |  |  |  |  | 
| 248 |  |  |  |  |  |  | # term path+ var | 
| 249 | 0 |  |  |  |  | 0 | my $x	= $self->_path_eval($start, $path); | 
| 250 | 0 |  |  |  |  | 0 | my $r	= []; | 
| 251 | 0 |  |  |  |  | 0 | my $V	= {}; | 
| 252 | 0 |  |  |  |  | 0 | while (my $n = $x->next) { | 
| 253 | 0 |  |  |  |  | 0 | $self->_alp( $n, $path, $r, $V ); | 
| 254 |  |  |  |  |  |  | } | 
| 255 |  |  |  |  |  |  |  | 
| 256 | 0 |  |  |  |  | 0 | my $name	= $vars[0]->name; | 
| 257 | 0 |  |  |  |  | 0 | foreach my $term (@$r) { | 
| 258 | 0 |  |  |  |  | 0 | my $vb          = RDF::Query::VariableBindings->new({ $name => $term }); | 
| 259 | 0 |  |  |  |  | 0 | push(@{ $self->[0]{results} }, $vb); | 
|  | 0 |  |  |  |  | 0 |  | 
| 260 |  |  |  |  |  |  | } | 
| 261 |  |  |  |  |  |  | } else { | 
| 262 |  |  |  |  |  |  | # term path+ term | 
| 263 | 0 |  |  |  |  | 0 | my $var	= RDF::Trine::Node::Variable->new(); | 
| 264 | 0 |  |  |  |  | 0 | my $start	= $self->start; | 
| 265 | 0 |  |  |  |  | 0 | my $end		= $self->end; | 
| 266 | 0 |  |  |  |  | 0 | my $path	= $self->path; | 
| 267 | 0 |  |  |  |  | 0 | my $x		= $self->_path_eval($start, $path); | 
| 268 | 0 |  |  |  |  | 0 | my $V		= {}; | 
| 269 | 0 |  |  |  |  | 0 | while (my $n = $x->next) { | 
| 270 | 0 |  |  |  |  | 0 | my $r	= []; | 
| 271 | 0 |  |  |  |  | 0 | $self->_alp( $n, $path, $r, $V ); | 
| 272 | 0 |  |  |  |  | 0 | foreach my $term (@$r) { | 
| 273 | 0 | 0 |  |  |  | 0 | if ($term->equal( $end )) { | 
| 274 | 0 |  |  |  |  | 0 | my $vb          = RDF::Query::VariableBindings->new({}); | 
| 275 | 0 |  |  |  |  | 0 | push(@{ $self->[0]{results} }, $vb); | 
|  | 0 |  |  |  |  | 0 |  | 
| 276 | 0 |  |  |  |  | 0 | return; | 
| 277 |  |  |  |  |  |  | } | 
| 278 |  |  |  |  |  |  | } | 
| 279 |  |  |  |  |  |  | } | 
| 280 |  |  |  |  |  |  | } | 
| 281 |  |  |  |  |  |  | } | 
| 282 |  |  |  |  |  |  |  | 
| 283 |  |  |  |  |  |  | # returns an iterator of terms | 
| 284 |  |  |  |  |  |  | sub _path_eval { | 
| 285 | 38 |  |  | 38 |  | 53 | my $self	= shift; | 
| 286 | 38 |  |  |  |  | 46 | my $start	= shift; | 
| 287 | 38 |  |  |  |  | 60 | my $path	= shift; | 
| 288 | 38 |  |  |  |  | 57 | my $context	= $self->[0]{context}; | 
| 289 | 38 |  |  |  |  | 65 | my $graph	= $self->[0]{graph}; | 
| 290 | 38 | 50 |  |  |  | 153 | $graph		= RDF::Trine::Node::Nil->new() unless (defined($graph)); | 
| 291 | 38 |  |  |  |  | 340 | my $var		= RDF::Query::Node::Variable->new(); | 
| 292 | 38 |  |  |  |  | 284 | my $plan	= RDF::Query::Plan->__path_plan( $start, $path, $var, $graph, $context, prevent_distinguishing_bnodes => 1, distinct => $self->distinct ); | 
| 293 | 38 |  |  |  |  | 143 | $plan->execute( $context ); | 
| 294 |  |  |  |  |  |  | my $iter	= RDF::Trine::Iterator->new( sub { | 
| 295 | 50 |  |  | 50 |  | 532 | my $r	= $plan->next; | 
| 296 | 50 | 100 |  |  |  | 158 | return unless ($r); | 
| 297 | 12 |  |  |  |  | 42 | my $t	= $r->{ $var->name }; | 
| 298 | 12 |  |  |  |  | 81 | return $t; | 
| 299 | 38 |  |  |  |  | 242 | } ); | 
| 300 |  |  |  |  |  |  | } | 
| 301 |  |  |  |  |  |  |  | 
| 302 |  |  |  |  |  |  | sub _alp { | 
| 303 | 25 |  |  | 25 |  | 37 | my $self	= shift; | 
| 304 | 25 |  |  |  |  | 32 | my $term	= shift; | 
| 305 | 25 |  |  |  |  | 29 | my $path	= shift; | 
| 306 | 25 |  |  |  |  | 31 | my $r		= shift; | 
| 307 | 25 |  |  |  |  | 33 | my $v		= shift; | 
| 308 | 25 | 50 |  |  |  | 82 | return if (exists($v->{ $term->as_string })); | 
| 309 | 25 |  |  |  |  | 297 | $v->{ $term->as_string }	= $term; | 
| 310 | 25 |  |  |  |  | 220 | push(@$r, $term); | 
| 311 |  |  |  |  |  |  |  | 
| 312 | 25 |  |  |  |  | 67 | my $x	= $self->_path_eval($term, $path); | 
| 313 | 25 |  |  |  |  | 646 | while (my $n = $x->next) { | 
| 314 | 9 |  |  |  |  | 92 | $self->_alp( $n, $path, $r, $v ); | 
| 315 |  |  |  |  |  |  | } | 
| 316 |  |  |  |  |  |  |  | 
| 317 | 25 | 50 |  |  |  | 239 | unless ($self->distinct) { | 
| 318 | 0 |  |  |  |  | 0 | delete $v->{ $term->as_string }; | 
| 319 |  |  |  |  |  |  | } | 
| 320 |  |  |  |  |  |  | } | 
| 321 |  |  |  |  |  |  |  | 
| 322 |  |  |  |  |  |  | sub _run_zerolength { | 
| 323 | 0 |  |  | 0 |  | 0 | my $self	= shift; | 
| 324 | 0 |  |  |  |  | 0 | my $context	= $self->[0]{context}; | 
| 325 | 0 |  |  |  |  | 0 | my $graph	= $self->[0]{graph}; | 
| 326 | 0 | 0 |  |  |  | 0 | $graph		= RDF::Trine::Node::Nil->new() unless (defined($graph)); | 
| 327 | 0 |  |  |  |  | 0 | my $model	= $context->model; | 
| 328 | 0 |  |  |  |  | 0 | my $path	= $self->path; | 
| 329 | 0 | 0 |  |  |  | 0 | my @vars		= grep { blessed($_) and $_->isa('RDF::Trine::Node::Variable') } ($self->start, $self->end); | 
|  | 0 |  |  |  |  | 0 |  | 
| 330 | 0 | 0 |  |  |  | 0 | if (scalar(@vars) == 2) { | 
|  |  | 0 |  |  |  |  |  | 
| 331 |  |  |  |  |  |  | # -- bind VAR(s) to subjects and objects in the current active graph | 
| 332 | 0 |  |  |  |  | 0 | my @names	= map { $_->name } @vars; | 
|  | 0 |  |  |  |  | 0 |  | 
| 333 | 0 |  |  |  |  | 0 | my %nodes; | 
| 334 | 0 |  |  |  |  | 0 | foreach my $n ($model->subjects(undef, undef, $graph), $model->objects(undef, undef, $graph)) { | 
| 335 | 0 |  |  |  |  | 0 | $nodes{ $n->as_string } = $n; | 
| 336 |  |  |  |  |  |  | } | 
| 337 | 0 |  |  |  |  | 0 | foreach my $n (values %nodes) { | 
| 338 | 0 |  |  |  |  | 0 | my %data; | 
| 339 | 0 |  |  |  |  | 0 | @data{ @names }	= ($n) x scalar(@names); | 
| 340 | 0 |  |  |  |  | 0 | my $vb			= RDF::Query::VariableBindings->new(\%data); | 
| 341 | 0 |  |  |  |  | 0 | push(@{ $self->[0]{results} }, $vb); | 
|  | 0 |  |  |  |  | 0 |  | 
| 342 |  |  |  |  |  |  | } | 
| 343 |  |  |  |  |  |  | } elsif (scalar(@vars) == 1) { | 
| 344 | 0 | 0 |  |  |  | 0 | my ($term)	= grep { blessed($_) and not($_->isa('RDF::Trine::Node::Variable')) } ($self->start, $self->end); | 
|  | 0 |  |  |  |  | 0 |  | 
| 345 | 0 |  |  |  |  | 0 | my $name	= $vars[0]->name; | 
| 346 | 0 |  |  |  |  | 0 | my $vb		= RDF::Query::VariableBindings->new({ $name => $term }); | 
| 347 | 0 |  |  |  |  | 0 | push(@{ $self->[0]{results} }, $vb); | 
|  | 0 |  |  |  |  | 0 |  | 
| 348 |  |  |  |  |  |  | } else { | 
| 349 | 0 | 0 |  |  |  | 0 | if ($self->start->equal( $self->end )) { | 
| 350 | 0 |  |  |  |  | 0 | my $vb          = RDF::Query::VariableBindings->new({}); | 
| 351 | 0 |  |  |  |  | 0 | push(@{ $self->[0]{results} }, $vb); | 
|  | 0 |  |  |  |  | 0 |  | 
| 352 |  |  |  |  |  |  | } | 
| 353 |  |  |  |  |  |  | } | 
| 354 |  |  |  |  |  |  | } | 
| 355 |  |  |  |  |  |  |  | 
| 356 |  |  |  |  |  |  |  | 
| 357 |  |  |  |  |  |  |  | 
| 358 |  |  |  |  |  |  | =item C<< next >> | 
| 359 |  |  |  |  |  |  |  | 
| 360 |  |  |  |  |  |  | =cut | 
| 361 |  |  |  |  |  |  |  | 
| 362 |  |  |  |  |  |  | sub next { | 
| 363 | 27 |  |  | 27 | 1 | 31 | my $self	= shift; | 
| 364 | 27 |  |  |  |  | 78 | my $l		= Log::Log4perl->get_logger("rdf.query.plan.path"); | 
| 365 |  |  |  |  |  |  |  | 
| 366 | 27 | 50 |  |  |  | 506 | unless ($self->state == $self->OPEN) { | 
| 367 | 0 |  |  |  |  | 0 | throw RDF::Query::Error::ExecutionError -text => "next() cannot be called on an un-open PATH"; | 
| 368 |  |  |  |  |  |  | } | 
| 369 |  |  |  |  |  |  |  | 
| 370 | 27 | 100 |  |  |  | 32 | if (scalar(@{ $self->[0]{results} })) { | 
|  | 27 |  |  |  |  | 76 |  | 
| 371 | 25 |  |  |  |  | 26 | my $result	= shift(@{ $self->[0]{results} }); | 
|  | 25 |  |  |  |  | 59 |  | 
| 372 | 25 | 50 |  |  |  | 131 | $l->trace( 'returning path result: ' . $result ) if (defined($result)); | 
| 373 | 25 | 50 |  |  |  | 797 | if (my $d = $self->delegate) { | 
| 374 | 0 |  |  |  |  | 0 | $d->log_result( $self, $result ); | 
| 375 |  |  |  |  |  |  | } | 
| 376 | 25 |  |  |  |  | 85 | return $result; | 
| 377 |  |  |  |  |  |  | } | 
| 378 |  |  |  |  |  |  |  | 
| 379 | 2 |  |  |  |  | 7 | return; | 
| 380 |  |  |  |  |  |  | } | 
| 381 |  |  |  |  |  |  |  | 
| 382 |  |  |  |  |  |  | =item C<< close >> | 
| 383 |  |  |  |  |  |  |  | 
| 384 |  |  |  |  |  |  | =cut | 
| 385 |  |  |  |  |  |  |  | 
| 386 |  |  |  |  |  |  | sub close { | 
| 387 | 2 |  |  | 2 | 1 | 5 | my $self	= shift; | 
| 388 | 2 | 50 |  |  |  | 7 | unless ($self->state == $self->OPEN) { | 
| 389 | 0 |  |  |  |  | 0 | throw RDF::Query::Error::ExecutionError -text => "close() cannot be called on an un-open PATH"; | 
| 390 |  |  |  |  |  |  | } | 
| 391 | 2 |  |  |  |  | 5 | delete $self->[0]{iter}; | 
| 392 | 2 |  |  |  |  | 12 | $self->SUPER::close(); | 
| 393 |  |  |  |  |  |  | } | 
| 394 |  |  |  |  |  |  |  | 
| 395 |  |  |  |  |  |  | =item C<< path_operator >> | 
| 396 |  |  |  |  |  |  |  | 
| 397 |  |  |  |  |  |  | Returns the path operation. | 
| 398 |  |  |  |  |  |  |  | 
| 399 |  |  |  |  |  |  | =cut | 
| 400 |  |  |  |  |  |  |  | 
| 401 |  |  |  |  |  |  | sub path_operator { | 
| 402 | 10 |  |  | 10 | 1 | 15 | my $self	= shift; | 
| 403 | 10 |  |  |  |  | 29 | return $self->[1]; | 
| 404 |  |  |  |  |  |  | } | 
| 405 |  |  |  |  |  |  |  | 
| 406 |  |  |  |  |  |  | =item C<< path >> | 
| 407 |  |  |  |  |  |  |  | 
| 408 |  |  |  |  |  |  | Returns the path expression. | 
| 409 |  |  |  |  |  |  |  | 
| 410 |  |  |  |  |  |  | =cut | 
| 411 |  |  |  |  |  |  |  | 
| 412 |  |  |  |  |  |  | sub path { | 
| 413 | 14 |  |  | 14 | 1 | 17 | my $self	= shift; | 
| 414 | 14 |  |  |  |  | 23 | return $self->[2]; | 
| 415 |  |  |  |  |  |  | } | 
| 416 |  |  |  |  |  |  |  | 
| 417 |  |  |  |  |  |  | =item C<< start >> | 
| 418 |  |  |  |  |  |  |  | 
| 419 |  |  |  |  |  |  | Returns the path start node. | 
| 420 |  |  |  |  |  |  |  | 
| 421 |  |  |  |  |  |  | =cut | 
| 422 |  |  |  |  |  |  |  | 
| 423 |  |  |  |  |  |  | sub start { | 
| 424 | 16 |  |  | 16 | 1 | 103 | my $self	= shift; | 
| 425 | 16 |  |  |  |  | 38 | return $self->[3]; | 
| 426 |  |  |  |  |  |  | } | 
| 427 |  |  |  |  |  |  |  | 
| 428 |  |  |  |  |  |  | =item C<< end >> | 
| 429 |  |  |  |  |  |  |  | 
| 430 |  |  |  |  |  |  | Returns the path end node. | 
| 431 |  |  |  |  |  |  |  | 
| 432 |  |  |  |  |  |  | =cut | 
| 433 |  |  |  |  |  |  |  | 
| 434 |  |  |  |  |  |  | sub end { | 
| 435 | 18 |  |  | 18 | 1 | 21 | my $self	= shift; | 
| 436 | 18 |  |  |  |  | 40 | return $self->[4]; | 
| 437 |  |  |  |  |  |  | } | 
| 438 |  |  |  |  |  |  |  | 
| 439 |  |  |  |  |  |  | =item C<< graph >> | 
| 440 |  |  |  |  |  |  |  | 
| 441 |  |  |  |  |  |  | Returns the named graph. | 
| 442 |  |  |  |  |  |  |  | 
| 443 |  |  |  |  |  |  | =cut | 
| 444 |  |  |  |  |  |  |  | 
| 445 |  |  |  |  |  |  | sub graph { | 
| 446 | 12 |  |  | 12 | 1 | 17 | my $self	= shift; | 
| 447 | 12 |  |  |  |  | 37 | return $self->[5]; | 
| 448 |  |  |  |  |  |  | } | 
| 449 |  |  |  |  |  |  |  | 
| 450 |  |  |  |  |  |  | =item C<< distinct >> | 
| 451 |  |  |  |  |  |  |  | 
| 452 |  |  |  |  |  |  | Returns true if the pattern is guaranteed to return distinct results. | 
| 453 |  |  |  |  |  |  |  | 
| 454 |  |  |  |  |  |  | =cut | 
| 455 |  |  |  |  |  |  |  | 
| 456 |  |  |  |  |  |  | sub distinct { | 
| 457 | 63 |  |  | 63 | 1 | 78 | my $self	= shift; | 
| 458 | 63 |  |  |  |  | 328 | return $self->[6]; | 
| 459 |  |  |  |  |  |  | } | 
| 460 |  |  |  |  |  |  |  | 
| 461 |  |  |  |  |  |  | =item C<< ordered >> | 
| 462 |  |  |  |  |  |  |  | 
| 463 |  |  |  |  |  |  | Returns true if the pattern is guaranteed to return ordered results. | 
| 464 |  |  |  |  |  |  |  | 
| 465 |  |  |  |  |  |  | =cut | 
| 466 |  |  |  |  |  |  |  | 
| 467 |  |  |  |  |  |  | sub ordered { | 
| 468 | 0 |  |  | 0 | 1 | 0 | return []; | 
| 469 |  |  |  |  |  |  | } | 
| 470 |  |  |  |  |  |  |  | 
| 471 |  |  |  |  |  |  | =item C<< plan_node_name >> | 
| 472 |  |  |  |  |  |  |  | 
| 473 |  |  |  |  |  |  | Returns the string name of this plan node, suitable for use in serialization. | 
| 474 |  |  |  |  |  |  |  | 
| 475 |  |  |  |  |  |  | =cut | 
| 476 |  |  |  |  |  |  |  | 
| 477 |  |  |  |  |  |  | sub plan_node_name { | 
| 478 | 8 |  |  | 8 | 1 | 11 | my $self	= shift; | 
| 479 | 8 |  |  |  |  | 17 | return $self->path_operator; | 
| 480 |  |  |  |  |  |  | } | 
| 481 |  |  |  |  |  |  |  | 
| 482 |  |  |  |  |  |  | =item C<< plan_prototype >> | 
| 483 |  |  |  |  |  |  |  | 
| 484 |  |  |  |  |  |  | Returns a list of scalar identifiers for the type of the content (children) | 
| 485 |  |  |  |  |  |  | nodes of this plan node. See L<RDF::Query::Plan> for a list of the allowable | 
| 486 |  |  |  |  |  |  | identifiers. | 
| 487 |  |  |  |  |  |  |  | 
| 488 |  |  |  |  |  |  | =cut | 
| 489 |  |  |  |  |  |  |  | 
| 490 |  |  |  |  |  |  | sub plan_prototype { | 
| 491 | 8 |  |  | 8 | 1 | 15 | my $self	= shift; | 
| 492 | 8 |  |  |  |  | 26 | return qw(s N N N); | 
| 493 |  |  |  |  |  |  | } | 
| 494 |  |  |  |  |  |  |  | 
| 495 |  |  |  |  |  |  | =item C<< plan_node_data >> | 
| 496 |  |  |  |  |  |  |  | 
| 497 |  |  |  |  |  |  | Returns the data for this plan node that corresponds to the values described by | 
| 498 |  |  |  |  |  |  | the signature returned by C<< plan_prototype >>. | 
| 499 |  |  |  |  |  |  |  | 
| 500 |  |  |  |  |  |  | =cut | 
| 501 |  |  |  |  |  |  |  | 
| 502 |  |  |  |  |  |  | sub plan_node_data { | 
| 503 | 10 |  |  | 10 | 1 | 17 | my $self	= shift; | 
| 504 | 10 |  |  |  |  | 26 | my $path	= $self->path; | 
| 505 | 10 | 50 |  |  |  | 36 | if (blessed($path)) { | 
| 506 | 10 |  |  |  |  | 32 | return ($path->sse, $self->start, $self->end, $self->graph); | 
| 507 |  |  |  |  |  |  | } else { | 
| 508 | 0 |  |  |  |  |  | return ('(undefined path)', $self->start, $self->end, $self->graph); | 
| 509 |  |  |  |  |  |  | } | 
| 510 |  |  |  |  |  |  | } | 
| 511 |  |  |  |  |  |  |  | 
| 512 |  |  |  |  |  |  |  | 
| 513 |  |  |  |  |  |  | =item C<< explain >> | 
| 514 |  |  |  |  |  |  |  | 
| 515 |  |  |  |  |  |  | Returns a string serialization of the plan appropriate for display on the | 
| 516 |  |  |  |  |  |  | command line. | 
| 517 |  |  |  |  |  |  |  | 
| 518 |  |  |  |  |  |  | =cut | 
| 519 |  |  |  |  |  |  |  | 
| 520 |  |  |  |  |  |  | sub explain { | 
| 521 | 0 |  |  | 0 | 1 |  | my $self	= shift; | 
| 522 | 0 |  |  |  |  |  | my $s		= shift; | 
| 523 | 0 |  |  |  |  |  | my $count	= shift; | 
| 524 | 0 |  |  |  |  |  | my $indent	= $s x $count; | 
| 525 | 0 |  |  |  |  |  | my $type	= $self->plan_node_name; | 
| 526 | 0 |  |  |  |  |  | my $string	= sprintf("%s%s (0x%x)\n", $indent, $type, refaddr($self)); | 
| 527 | 0 |  |  |  |  |  | $string		.= $self->start->explain($s, $count+1); | 
| 528 | 0 |  |  |  |  |  | my $path	= $self->path; | 
| 529 | 0 | 0 |  |  |  |  | if ($type eq 'NegatedPropertySet') { | 
|  |  | 0 |  |  |  |  |  | 
| 530 | 0 |  |  |  |  |  | $string	.= "${indent}${s}(\n"; | 
| 531 | 0 |  |  |  |  |  | foreach my $iri (@$path) { | 
| 532 | 0 |  |  |  |  |  | $string		.= "${indent}${s}${s}" . $iri->as_string . "\n"; | 
| 533 |  |  |  |  |  |  | } | 
| 534 | 0 |  |  |  |  |  | $string	.= "${indent}${s})\n"; | 
| 535 |  |  |  |  |  |  | } elsif ($type =~ /^ZeroOrMorePath|OneOrMorePath|ZeroLengthPath$/) { | 
| 536 | 0 |  |  |  |  |  | $string	.= "${indent}${s}${s}" . $self->_path_as_string($path) . "\n"; | 
| 537 |  |  |  |  |  |  | } else { | 
| 538 | 0 |  |  |  |  |  | throw RDF::Query::Error; | 
| 539 |  |  |  |  |  |  | } | 
| 540 | 0 |  |  |  |  |  | $string		.= $self->end->explain($s, $count+1); | 
| 541 |  |  |  |  |  |  | # 	$string		.= $self->pattern->explain( $s, $count+1 ); | 
| 542 | 0 |  |  |  |  |  | return $string; | 
| 543 |  |  |  |  |  |  | } | 
| 544 |  |  |  |  |  |  |  | 
| 545 |  |  |  |  |  |  | sub _path_as_string { | 
| 546 | 0 |  |  | 0 |  |  | my $self	= shift; | 
| 547 | 0 |  |  |  |  |  | my $path	= shift; | 
| 548 | 0 | 0 |  |  |  |  | if (blessed($path)) { | 
| 549 | 0 |  |  |  |  |  | return $path->as_string; | 
| 550 |  |  |  |  |  |  | } | 
| 551 |  |  |  |  |  |  |  | 
| 552 | 0 |  |  |  |  |  | my ($op, @nodes)	= @$path; | 
| 553 | 0 | 0 |  |  |  |  | if ($op eq '/') { | 
|  |  | 0 |  |  |  |  |  | 
| 554 | 0 |  |  |  |  |  | return join('/', map { $self->_path_as_string($_) } @nodes); | 
|  | 0 |  |  |  |  |  |  | 
| 555 |  |  |  |  |  |  | } elsif ($op =~ /^[?+*]$/) { | 
| 556 | 0 |  |  |  |  |  | return '(' . $self->_path_as_string($nodes[0]) . ')' . $op; | 
| 557 |  |  |  |  |  |  | } else { | 
| 558 | 0 |  |  |  |  |  | throw RDF::Query::Error -text => "Can't serialize path '$op' in plan explanation"; | 
| 559 |  |  |  |  |  |  | } | 
| 560 |  |  |  |  |  |  | } | 
| 561 |  |  |  |  |  |  |  | 
| 562 |  |  |  |  |  |  | 1; | 
| 563 |  |  |  |  |  |  |  | 
| 564 |  |  |  |  |  |  | __END__ | 
| 565 |  |  |  |  |  |  |  | 
| 566 |  |  |  |  |  |  | =back | 
| 567 |  |  |  |  |  |  |  | 
| 568 |  |  |  |  |  |  | =head1 AUTHOR | 
| 569 |  |  |  |  |  |  |  | 
| 570 |  |  |  |  |  |  | Gregory Todd Williams <gwilliams@cpan.org> | 
| 571 |  |  |  |  |  |  |  | 
| 572 |  |  |  |  |  |  | =cut |