1# RDF::Query 2# ----------------------------------------------------------------------------- 3 4=head1 NAME 5 6RDF::Query - A complete SPARQL 1.1 Query and Update implementation for use with RDF::Trine. 7 8=head1 VERSION 9 10This document describes RDF::Query version 2.918. 11 12=head1 SYNOPSIS 13 14 # SPARQL SELECT Query 15 my $query = RDF::Query->new( 'SELECT * WHERE ...' ); 16 my $iterator = $query->execute( $model ); 17 while (my $row = $iterator->next) { 18 # $row is a HASHref containing variable name -> RDF Term bindings 19 print $row->{ 'var' }->as_string; 20 } 21 22 # SPARQL CONSTRUCT/DESCRIBE Query 23 my $query = RDF::Query->new( 'CONSTRUCT { ... } WHERE ...' ); 24 my $iterator = $query->execute( $model ); 25 while (my $st = $iterator->next) { 26 # $st is a RDF::Trine::Statement object representing an RDF triple 27 print $st->as_string; 28 } 29 30 # SPARQL ASK Query 31 my $query = RDF::Query->new( 'ASK WHERE ...' ); 32 my $iterator = $query->execute( $model ); 33 my $bool = $iterator->get_boolean; 34 if ($bool) { 35 print "Yes!\n"; 36 } 37 38 # RDQL Query 39 my $query = new RDF::Query ( $rdql, { lang => 'rdql' } ); 40 my @rows = $query->execute( $model ); # in list context, returns all results 41 42=head1 DESCRIPTION 43 44RDF::Query allows SPARQL and RDQL queries to be run against an RDF model, 45returning rows of matching results. 46 47See L<http://www.w3.org/TR/rdf-sparql-query/> for more information on SPARQL. 48 49See L<http://www.w3.org/Submission/2004/SUBM-RDQL-20040109/> for more 50information on RDQL. 51 52=head1 CHANGES IN VERSION 2.900 53 54The 2.9xx versions of RDF::Query introduce some significant changes that will 55lead to a stable 3.000 release supporting SPARQL 1.1. Version 2.902 introduces 56the SPARQL 1.1 features up to date with the SPARQL 1.1 working drafts as of its 57release date. Version 2.902 also is the first version to require use of 58RDF::Trine for the underlying RDF store. This change means that RDF::Core is 59no longer supported, and while Redland is still supported, its handling of 60"contexts" (named graphs) means that existing RDF triples stored in Redland 61without associated contexts will not be accessible from RDF::Query. 62See L<RDF::Trine::Store> for more information on supported backend stores. 63 64=head1 CHANGES IN VERSION 2.000 65 66There are many changes in the code between the 1.x and 2.x releases. Most of 67these changes will only affect queries that should have raised errors in the 68first place (SPARQL parsing, queries that use undefined namespaces, etc.). 69Beyond these changes, however, there are some significant API changes that will 70affect all users: 71 72=over 4 73 74=item Use of RDF::Trine objects 75 76All nodes and statements returned by RDF::Query are now RDF::Trine objects 77(more specifically, RDF::Trine::Node and RDF::Trine::Statement objects). This 78differes from RDF::Query 1.x where nodes and statements were of the same type 79as the underlying model (Redland nodes from a Redland model and RDF::Core nodes 80from an RDF::Core model). 81 82In the past, it was possible to execute a query and not know what type of nodes 83were going to be returned, leading to overly verbose code that required 84examining all nodes and statements with the bridge object. This new API brings 85consistency to both the execution model and client code, greatly simplifying 86interaction with query results. 87 88=item Binding Result Values 89 90Binding result values returned by calling C<< $iterator->next >> are now HASH 91references (instead of ARRAY references), keyed by variable name. Where prior 92code might use this code (modulo model definition and namespace declarations): 93 94 my $sparql = 'SELECT ?name ?homepage WHERE { [ foaf:name ?name ; foaf:homepage ?homepage ] }'; 95 my $query = RDF::Query->new( $sparql ); 96 my $iterator = $query->execute( $model ); 97 while (my $row = $iterator->()) { 98 my ($name, $homepage) = @$row; 99 # ... 100 } 101 102New code using RDF::Query 2.000 and later should instead use: 103 104 my $sparql = 'SELECT ?name ?homepage WHERE { [ foaf:name ?name ; foaf:homepage ?homepage ] }'; 105 my $query = RDF::Query->new( $sparql ); 106 my $iterator = $query->execute( $model ); 107 while (my $row = $iterator->next) { 108 my $name = $row->{ name }; 109 my $homepage = $row->{ homepage }; 110 # ... 111 } 112 113(Also notice the new method calling syntax for retrieving rows.) 114 115=back 116 117=cut 118 119package RDF::Query; 120 121use strict; 122use warnings; 123no warnings 'redefine'; 124use Carp qw(carp croak confess); 125 126use Data::Dumper; 127use LWP::UserAgent; 128use I18N::LangTags; 129use List::Util qw(first); 130use Scalar::Util qw(blessed reftype looks_like_number); 131use DateTime::Format::W3CDTF; 132 133use Log::Log4perl qw(:easy); 134if (! Log::Log4perl::initialized()) { 135 Log::Log4perl->easy_init($ERROR); 136} 137 138no warnings 'numeric'; 139use RDF::Trine 1.004; 140require RDF::Query::Functions; # (needs to happen at runtime because some of the functions rely on RDF::Query being fully loaded (to call add_hook(), for example)) 141 # all the built-in functions including: 142 # datatype casting, language ops, logical ops, 143 # numeric ops, datetime ops, and node type testing 144 # also, custom functions including: 145 # jena:sha1sum, jena:now, jena:langeq, jena:listMember 146 # ldodds:Distance, kasei:warn 147use RDF::Query::Expression; 148use RDF::Query::Algebra; 149use RDF::Query::Node qw(iri); 150use RDF::Query::Parser::RDQL; 151use RDF::Query::Parser::SPARQL; 152use RDF::Query::Parser::SPARQL11; 153use RDF::Query::Compiler::SQL; 154use RDF::Query::Error qw(:try); 155use RDF::Query::Plan; 156 157###################################################################### 158 159our ($VERSION, $DEFAULT_PARSER); 160BEGIN { 161 $VERSION = '2.918'; 162 $DEFAULT_PARSER = 'sparql11'; 163} 164 165 166###################################################################### 167 168=head1 METHODS 169 170=over 4 171 172=item C<< new ( $query, \%options ) >> 173 174Returns a new RDF::Query object for the specified C<$query>. 175The query language defaults to SPARQL 1.1, but may be set specifically 176with the appropriate C<< %options >> value. Valid C<< %options >> are: 177 178* lang 179 180Specifies the query language. Acceptable values are 'sparql11', 'sparql', or 'rdql'. 181 182* base_uri 183 184Specifies the base URI used in parsing the query. 185 186* update 187 188A boolean value indicating whether update operations are allowed during query execution. 189 190* load_data 191 192A boolean value indicating whether URIs used in SPARQL FROM and FROM NAMED clauses 193should be dereferenced and the resulting RDF content used to construct the dataset 194against which the query is run. 195 196=cut 197 198sub new { 199 my $class = shift; 200 my $query = shift; 201 202 my ($base_uri, $languri, $lang, %options); 203 if (@_ and ref($_[0])) { 204 %options = %{ shift() }; 205 $lang = delete $options{ lang }; 206 $base_uri = $options{ base_uri } || $options{ base } ; 207 delete $options{ base_uri }; 208 delete $options{ base }; 209 } else { 210 ($base_uri, $languri, $lang, %options) = @_; 211 } 212 $class->clear_error; 213 214 my $l = Log::Log4perl->get_logger("rdf.query"); 215 no warnings 'uninitialized'; 216 217 my %names = ( 218 rdql => 'RDF::Query::Parser::RDQL', 219 sparql => 'RDF::Query::Parser::SPARQL', 220 sparql11 => 'RDF::Query::Parser::SPARQL11', 221 ); 222 my %uris = ( 223 'http://jena.hpl.hp.com/2003/07/query/RDQL' => 'RDF::Query::Parser::RDQL', 224 'http://www.w3.org/TR/rdf-sparql-query/' => 'RDF::Query::Parser::SPARQL', 225 'http://www.w3.org/ns/sparql-service-description#SPARQL10Query' => 'RDF::Query::Parser::SPARQL', 226 'http://www.w3.org/ns/sparql-service-description#SPARQL11Query' => 'RDF::Query::Parser::SPARQL11', 227 'http://www.w3.org/ns/sparql-service-description#SPARQL11Update' => 'RDF::Query::Parser::SPARQL11', 228 ); 229 230 if ($base_uri) { 231 $base_uri = RDF::Query::Node::Resource->new( $base_uri ); 232 } 233 234 my %pargs; 235 if ($options{canonicalize}) { 236 $pargs{canonicalize} = 1; 237 } 238 my $update = ((delete $options{update}) ? 1 : 0); 239 my $pclass = $names{ $lang } || $uris{ $languri } || $names{ $DEFAULT_PARSER }; 240 my $parser = $pclass->new( %pargs ); 241 my $parsed; 242 243 if (ref($query) and $query->isa('RDF::Query::Algebra')) { 244 my $method = 'SELECT'; 245 $method = 'ASK' if ($query->isa('RDF::Query::Algebra::Ask')); 246 $method = 'CONSTRUCT' if ($query->isa('RDF::Query::Algebra::Construct')); 247 my @vars = map { RDF::Query::Node::Variable->new($_) } _uniq($query->potentially_bound); 248 if ($method eq 'SELECT') { 249 unless ($query->isa('RDF::Query::Algebra::Project')) { 250 $query = RDF::Query::Algebra::Project->new($query, \@vars); 251 } 252 } 253 $parsed = { 254 method => $method, 255 triples => [$query], 256 sources => [], 257 base => $base_uri, 258 options => {}, 259 star => 0, 260 variables => \@vars, 261 }; 262 $query = $query->as_sparql; 263 } else { 264 $parsed = $parser->parse( $query, $base_uri, $update ); 265 } 266 267 my $self = $class->_new( 268 base_uri => $base_uri, 269 parser => $parser, 270 parsed => $parsed, 271 query_string => $query, 272 update => $update, 273 options => { %options }, 274 ); 275 if (exists $options{load_data}) { 276 $self->{load_data} = delete $options{load_data}; 277 } elsif ($pclass =~ /^RDF::Query::Parser::(RDQL|SPARQL)$/) { 278 $self->{load_data} = 1; 279 } else { 280 $self->{load_data} = 0; 281 } 282 unless ($parsed->{'triples'}) { 283 $class->set_error( $parser->error ); 284 $l->debug($parser->error); 285 return; 286 } 287 288 if (defined $options{defines}) { 289 @{ $self->{options} }{ keys %{ $options{defines} } } = values %{ delete $options{defines} }; 290 } 291 292 if ($options{logger}) { 293 $l->debug("got external logger"); 294 $self->{logger} = delete $options{logger}; 295 } 296 297 if (my $opt = delete $options{optimize}) { 298 $l->debug("got optimization flag: $opt"); 299 $self->{optimize} = $opt; 300 } else { 301 $self->{optimize} = 0; 302 } 303 304 if (my $opt = delete $options{force_no_optimization}) { 305 $l->debug("got force_no_optimization flag"); 306 $self->{force_no_optimization} = 1; 307 } 308 309 if (my $time = delete $options{optimistic_threshold_time}) { 310 $l->debug("got optimistic_threshold_time flag"); 311 $self->{optimistic_threshold_time} = $time; 312 } 313 314 # add rdf as a default namespace to RDQL queries 315 if ($pclass eq 'RDF::Query::Parser::RDQL') { 316 $self->{parsed}{namespaces}{rdf} = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'; 317 } 318 return $self; 319} 320 321sub _new { 322 my $class = shift; 323 my $self = bless( { @_ }, $class ); 324 return $self; 325} 326 327=item C<< get ( $model ) >> 328 329Executes the query using the specified model, and returns the first matching row as a LIST of values. 330 331=cut 332 333sub get { 334 my $self = shift; 335 my $stream = $self->execute( @_ ); 336 my $row = $stream->next; 337 if (ref($row)) { 338 return @{ $row }{ $self->variables }; 339 } else { 340 return undef; 341 } 342} 343 344=item C<< prepare ( $model ) >> 345 346Prepares the query, constructing a query execution plan, and returns a list 347containing ($plan, $context). To execute the plan, call 348C<< execute_plan( $plan, $context ) >>. 349 350=cut 351 352sub prepare { 353 my $self = shift; 354 my $_model = shift; 355 my %args = @_; 356 my $l = Log::Log4perl->get_logger("rdf.query"); 357 358 $self->{_query_cache} = {}; # a new scratch hash for each execution. 359 my %bound; 360 if ($args{ 'bind' }) { 361 %bound = %{ $args{ 'bind' } }; 362 } 363 364 my $delegate; 365 if (defined $args{ 'delegate' }) { 366 $delegate = delete $args{ 'delegate' }; 367 if ($delegate and not blessed($delegate)) { 368 $delegate = $delegate->new(); 369 } 370 } 371 my $errors = ($args{ 'strict_errors' }) ? 1 : 0; 372 my $parsed = $self->{parsed}; 373 my @vars = $self->variables( $parsed ); 374 375 local($self->{model}) = $self->{model}; 376 my $model = $self->{model} || $self->get_model( $_model, %args ); 377 if ($model) { 378 $self->model( $model ); 379 $l->debug("got model $model"); 380 } else { 381 throw RDF::Query::Error::ModelError ( -text => "Could not create a model object." ); 382 } 383 384 if ($self->{load_data}) { 385 $l->trace("loading data"); 386 $self->load_data(); 387 } 388 389 $model = $self->model(); # reload the model object, because load_data might have changed it. 390 391 my $dataset = ($model->isa('RDF::Trine::Model::Dataset')) ? $model : RDF::Trine::Model::Dataset->new($model); 392 393 $l->trace("constructing ExecutionContext"); 394 my $context = RDF::Query::ExecutionContext->new( 395 bound => \%bound, 396 model => $dataset, 397 query => $self, 398 base_uri => $parsed->{base_uri}, 399 ns => $parsed->{namespaces}, 400 logger => $self->logger, 401 optimize => $self->{optimize}, 402 force_no_optimization => $self->{force_no_optimization}, 403 optimistic_threshold_time => $self->{optimistic_threshold_time} || 0, 404 requested_variables => \@vars, 405 strict_errors => $errors, 406 options => $self->{options}, 407 delegate => $delegate, 408 ); 409 $self->{model} = $model; 410 411 $l->trace("getting QEP..."); 412 my %plan_args = %{ $args{ planner_args } || {} }; 413 my $plan = $self->query_plan( $context, %plan_args ); 414 $l->trace("-> done."); 415 416 unless ($plan) { 417 throw RDF::Query::Error::CompilationError -text => "Query didn't produce a valid execution plan"; 418 } 419 420 return ($plan, $context); 421} 422 423=item C<execute ( $model, %args )> 424 425Executes the query using the specified RDF C<< $model >>. If called in a list 426context, returns an array of rows, otherwise returns an L<RDF::Trine::Iterator> 427object. The iterator returned may be an instance of several subclasses of 428L<RDF::Trine::Iterator>: 429 430* A L<RDF::Trine::Iterator::Bindings> object is returned for query forms producing variable binding results (SELECT queries). 431 432* A L<RDF::Trine::Iterator::Graph> object is returned for query forms producing in an RDF graph result (DESCRIBE and CONSTRUCT queries). 433 434* A L<RDF::Trine::Iterator::Boolean> object is returned for query forms producing a true/false result (ASK queries). 435 436=cut 437 438sub execute { 439 my $self = shift; 440 my $model = shift; 441 my %args = @_; 442 my $l = Log::Log4perl->get_logger("rdf.query"); 443 $l->debug("executing query with model " . ($model or '')); 444 445 my $lang_iri = ''; 446 my $parser = $self->{parser}; 447 my $name; 448 if ($parser->isa('RDF::Query::Parser::SPARQL11')) { 449 if ($self->is_update) { 450 $name = 'SPARQL 1.1 Update'; 451 $lang_iri = 'http://www.w3.org/ns/sparql-service-description#SPARQL11Update'; 452 } else { 453 $name = 'SPARQL 1.1 Query'; 454 $lang_iri = 'http://www.w3.org/ns/sparql-service-description#SPARQL11Query'; 455 } 456 } elsif ($parser->isa('RDF::Query::Parser::SPARQL')) { 457 $name = 'SPARQL 1.0 Query'; 458 $lang_iri = 'http://www.w3.org/ns/sparql-service-description#SPARQL10Query'; 459 } 460 461 local($self->{model}) = $self->{model}; 462# warn "model: $self->{model}"; 463# warn "passthrough checking if model supports $lang_iri\n"; 464 if ($self->{options}{allow_passthrough} and $model->supports($lang_iri)) { 465 $l->info("delegating $name execution to the underlying model"); 466 return $model->get_sparql( $self->{query_string} ); 467 } else { 468 my ($plan, $context) = $self->prepare( $model, %args ); 469 if ($l->is_trace) { 470 $l->trace(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); 471 $l->trace($self->as_sparql); 472 $l->trace(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); 473 } 474 return $self->execute_plan( $plan, $context ); 475 } 476} 477 478=item C<< execute_plan ( $plan, $context ) >> 479 480Executes the query plan generated by the C<<prepare>> method using the supplied 481L<RDF::Query::ExecutionContext> object. Return value(s) are the same as for the 482C<<execute>> method. 483 484=cut 485 486sub execute_plan { 487 my $self = shift; 488 my $plan = shift; 489 my $context = shift; 490 my $model = $context->model; 491 my $parsed = $self->{parsed}; 492 my @vars = $self->variables( $parsed ); 493 494 my $l = Log::Log4perl->get_logger("rdf.query"); 495 496 my $pattern = $self->pattern; 497# $l->trace("calling fixup()"); 498# my $cpattern = $self->fixup(); 499 500 my @funcs = $pattern->referenced_functions; 501 foreach my $f (@funcs) { 502 $self->run_hook( 'http://kasei.us/code/rdf-query/hooks/function_init', $f ); 503 } 504 505 # RUN THE QUERY! 506 507 $l->debug("executing the graph pattern"); 508 509 my $options = $parsed->{options} || {}; 510 if ($self->{options}{plan}) { 511 warn $plan->sse({}, ''); 512 } 513 514 $plan->execute( $context ); 515 my $stream = $plan->as_iterator( $context ); 516 517 if ($parsed->{'method'} eq 'DESCRIBE') { 518 $stream = $self->describe( $stream, $context ); 519 } elsif ($parsed->{'method'} eq 'ASK') { 520 $stream = $self->ask( $stream, $context ); 521 } 522 523 $l->debug("going to call post-execute hook"); 524 $self->run_hook( 'http://kasei.us/code/rdf-query/hooks/post-execute', $model, $stream ); 525 526 if (wantarray) { 527 return $stream->get_all(); 528 } else { 529 return $stream; 530 } 531} 532 533=item C<< prepare_with_named_graphs ( $model, @uris ) >> 534 535=cut 536 537sub prepare_with_named_graphs { 538 my $self = shift; 539 my $_model = shift; 540 my @graphs = @_; 541 my $l = Log::Log4perl->get_logger("rdf.query"); 542# $self->{model} = $model; 543 my $model = $self->get_model( $_model ); 544 if ($model) { 545 $self->model( $model ); 546 } else { 547 throw RDF::Query::Error::ModelError ( -text => "Could not create a model object." ); 548 } 549 550 foreach my $gdata (@graphs) { 551 my $url = (blessed($gdata)) ? $gdata->uri_value : $gdata; 552 $l->debug("-> adding graph data $url"); 553 $self->parse_url( $url, 1 ); 554 } 555 556 return $self->prepare( $model ); 557} 558 559=item C<< execute_with_named_graphs ( $model, @uris ) >> 560 561Executes the query using the specified RDF C<< $model >>, loading the contents 562of the specified C<@uris> into named graphs immediately prior to matching the 563query. Otherwise, acts just like C<< execute >>. 564 565=cut 566 567sub execute_with_named_graphs { 568 my $self = shift; 569 my $_model = shift; 570 my @graphs; 571 my @options; 572 if (scalar(@_)) { 573 if (not(blessed($_[0])) and reftype($_[0]) eq 'ARRAY') { 574 @graphs = @{ shift(@_) }; 575 @options = @_; 576 } else { 577 @graphs = @_; 578 } 579 } 580 581 my ($plan, $ctx) = $self->prepare_with_named_graphs( $_model, @graphs ); 582 return $self->execute_plan( $plan, $ctx ); 583} 584 585=begin private 586 587=item C<< query_plan ( $execution_context ) >> 588 589Returns a RDF::Query::Plan object that is (hopefully) the optimal QEP for the 590current query. 591 592=end private 593 594=cut 595 596sub query_plan { 597 my $self = shift; 598 my $context = shift; 599 my %args = @_; 600 my $parsed = $self->{parsed}; 601 602 my $bound = $context->bound; 603 my @bkeys = keys %{ $bound }; 604 my $model = $context->model; 605 606 unless ($self->{update}) { 607 if (not exists $self->{options}{'rdf.query.plan.delegate'} or $self->{options}{'rdf.query.plan.delegate'}) { 608 my $delegate_key = $self->{update} 609 ? 'http://www.w3.org/ns/sparql-service-description#SPARQL11Update' 610 : "http://www.w3.org/ns/sparql-service-description#SPARQL10Query"; # TODO: need to determine if the query is only 1.0, and if so, check for 1.0 support. otherwise check for 1.1 support 611 if (scalar(@bkeys) == 0 and $model->supports($delegate_key)) { 612 my $plan = RDF::Query::Plan::Iterator->new( sub { 613 my $context = shift; 614 my $model = $context->model; 615 my $iter = $model->get_sparql( $self->{query_string} ); 616 return $iter; 617 } ); 618 return $plan; 619 } 620 } 621 } 622 623 my %constant_plan; 624 if (my $b = $self->{parsed}{bindings}) { 625 my $vars = $b->{vars}; 626 my $values = $b->{terms}; 627 my @names = map { $_->name } @{ $vars }; 628 my @constants; 629 while (my $values = shift(@{ $b->{terms} })) { 630 my %bound; 631# @bound{ @names } = @{ $values }; 632 foreach my $i (0 .. $#names) { 633 my $k = $names[$i]; 634 my $v = $values->[$i]; 635 next unless defined($v); 636 $bound{ $k } = $v; 637 } 638 my $bound = RDF::Query::VariableBindings->new( \%bound ); 639 push(@constants, $bound); 640 } 641 my $constant_plan = RDF::Query::Plan::Constant->new( @constants ); 642 %constant_plan = ( constants => [ $constant_plan ] ); 643 } 644 645 my $algebra = $self->pattern; 646 my $pclass = $self->plan_class; 647 my @plans = $pclass->generate_plans( $algebra, $context, %args, %constant_plan ); 648 649 my $l = Log::Log4perl->get_logger("rdf.query.plan"); 650 if (wantarray) { 651 return @plans; 652 } else { 653 my ($plan) = @plans; # XXX need to figure out what's the 'best' plan here 654 if ($l->is_debug) { 655 $l->debug("using query plan: " . $plan->sse({}, '')); 656 } 657 return $plan; 658 } 659} 660 661=begin private 662 663=item C<< plan_class >> 664 665Returns the class name for Plan generation. This method should be overloaded by 666RDF::Query subclasses if the implementation also provides a subclass of 667RDF::Query::Plan. 668 669=end private 670 671=cut 672 673sub plan_class { 674 return 'RDF::Query::Plan'; 675} 676 677=begin private 678 679=item C<< describe ( $iter, $context ) >> 680 681Takes a stream of matching statements and constructs a DESCRIBE graph. 682 683=end private 684 685=cut 686 687sub describe { 688 my $self = shift; 689 my $stream = shift; 690 my $context = shift; 691 my $model = $context->model; 692 my @nodes; 693 my %seen; 694 while (my $row = $stream->next) { 695 foreach my $v (@{ $self->{parsed}{variables} }) { 696 if ($v->isa('RDF::Query::Node::Variable')) { 697 my $node = $row->{ $v->name }; 698 my $string = blessed($node) ? $node->as_string : ''; 699 push(@nodes, $node) unless ($seen{ $string }++); 700 } elsif ($v->isa('RDF::Query::Node::Resource')) { 701 my $string = blessed($v) ? $v->as_string : ''; 702 push(@nodes, $v) unless ($seen{ $string }++); 703 } 704 } 705 } 706 707 my @streams; 708 $self->{'describe_nodes'} = []; 709 foreach my $node (@nodes) { 710 push(@{ $self->{'describe_nodes'} }, $node); 711 push(@streams, $model->bounded_description( $node )); 712 } 713 714 my $ret = sub { 715 while (@streams) { 716 my $val = $streams[0]->next; 717 if (defined $val) { 718 return $val; 719 } else { 720 shift(@streams); 721 return undef if (not @streams); 722 } 723 } 724 }; 725 return RDF::Trine::Iterator::Graph->new( $ret ); 726} 727 728 729=begin private 730 731=item C<ask ( $iter, $context )> 732 733Takes a stream of matching statements and returns a boolean query result stream. 734 735=end private 736 737=cut 738 739sub ask { 740 my $self = shift; 741 my $stream = shift; 742 my $context = shift; 743 my $value = $stream->next; 744 my $bool = ($value) ? 1 : 0; 745 return RDF::Trine::Iterator::Boolean->new( [ $bool ] ); 746} 747 748###################################################################### 749 750=item C<< pattern >> 751 752Returns the RDF::Query::Algebra::GroupGraphPattern algebra pattern for this query. 753 754=cut 755 756sub pattern { 757 my $self = shift; 758 my $parsed = $self->parsed; 759 my @triples = @{ $parsed->{triples} }; 760 if (scalar(@triples) == 1 and ($triples[0]->isa('RDF::Query::Algebra::GroupGraphPattern') 761 or $triples[0]->isa('RDF::Query::Algebra::Filter') 762 or $triples[0]->isa('RDF::Query::Algebra::Sort') 763 or $triples[0]->isa('RDF::Query::Algebra::Limit') 764 or $triples[0]->isa('RDF::Query::Algebra::Offset') 765 or $triples[0]->isa('RDF::Query::Algebra::Distinct') 766 or $triples[0]->isa('RDF::Query::Algebra::Project') 767 or $triples[0]->isa('RDF::Query::Algebra::Construct') 768 or $triples[0]->isa('RDF::Query::Algebra::Load') 769 or $triples[0]->isa('RDF::Query::Algebra::Clear') 770 or $triples[0]->isa('RDF::Query::Algebra::Create') 771 or $triples[0]->isa('RDF::Query::Algebra::Update') 772 )) { 773 my $ggp = $triples[0]; 774 return $ggp; 775 } else { 776 return RDF::Query::Algebra::GroupGraphPattern->new( @triples ); 777 } 778} 779 780=item C<< is_update >> 781 782=cut 783 784sub is_update { 785 my $self = shift; 786 my $pat = $self->pattern; 787 return 1 if ($pat->subpatterns_of_type('RDF::Query::Algebra::Clear')); 788 return 1 if ($pat->subpatterns_of_type('RDF::Query::Algebra::Copy')); 789 return 1 if ($pat->subpatterns_of_type('RDF::Query::Algebra::Create')); 790 return 1 if ($pat->subpatterns_of_type('RDF::Query::Algebra::Move')); 791 return 1 if ($pat->subpatterns_of_type('RDF::Query::Algebra::Update')); 792 return 0; 793} 794 795=item C<< as_sparql >> 796 797Returns the query as a string in the SPARQL syntax. 798 799=cut 800 801sub as_sparql { 802 my $self = shift; 803 my $parsed = $self->parsed || {}; 804 805 my $context = { namespaces => { %{ $parsed->{namespaces} || {} } } }; 806 my $method = $parsed->{method}; 807 808 if ($method =~ /^(DESCRIBE|ASK)$/i) { 809 $context->{force_ggp_braces} = 1; 810 } 811 812 my @vars = map { $_->as_sparql( $context, '' ) } @{ $parsed->{ variables } }; 813 my $vars = join(' ', @vars); 814 my $ggp = $self->pattern; 815 816 if ($method =~ /^(LOAD|CLEAR|CREATE|UPDATE)$/) { 817 return $ggp->as_sparql; 818 } else { 819 { 820 my $pvars = join(' ', sort $ggp->referenced_variables); 821 my $svars = join(' ', sort map { $_->isa('RDF::Query::Node::Resource') ? $_->as_string : $_->name } @{ $parsed->{ variables } }); 822 if ($pvars eq $svars) { 823 $vars = '*'; 824 } 825 } 826 827 my @ns = map { "PREFIX " . ($_ eq '__DEFAULT__' ? '' : $_) . ": <$parsed->{namespaces}{$_}>" } (sort keys %{ $parsed->{namespaces} }); 828 my @mod; 829 if (my $ob = $parsed->{options}{orderby}) { 830 push(@mod, 'ORDER BY ' . join(' ', map { 831 my ($dir,$v) = @$_; 832 ($dir eq 'ASC') 833 ? $v->as_sparql( $context, '' ) 834 : "${dir}" . $v->as_sparql( $context, '' ); 835 } @$ob)); 836 } 837 if (my $l = $parsed->{options}{limit}) { 838 push(@mod, "LIMIT $l"); 839 } 840 if (my $o = $parsed->{options}{offset}) { 841 push(@mod, "OFFSET $o"); 842 } 843 my $mod = join("\n", @mod); 844 845 my $methoddata = ''; 846 if ($method eq 'SELECT') { 847 $methoddata = $method; 848 } elsif ($method eq 'ASK') { 849 $methoddata = $method; 850 } elsif ($method eq 'DESCRIBE') { 851 $methoddata = sprintf("%s %s\nWHERE", $method, $vars); 852 } 853 854 my $ns = scalar(@ns) ? join("\n", @ns, '') : ''; 855 my $sparql; 856 if ($methoddata or $ns) { 857 $sparql = sprintf( 858 "$ns%s %s\n%s", 859 $methoddata, 860 $ggp->as_sparql( $context, '' ), 861 $mod, 862 ); 863 } else { 864 $sparql = sprintf( 865 "%s\n%s", 866 $ggp->as_sparql( $context, '' ), 867 $mod, 868 ); 869 } 870 871 chomp($sparql); 872 return $sparql; 873 } 874} 875 876=item C<< as_hash >> 877 878Returns the query as a nested set of plain data structures (no objects). 879 880=cut 881 882sub as_hash { 883 my $self = shift; 884 my $pattern = $self->pattern; 885 return $pattern->as_hash; 886} 887 888=item C<< sse >> 889 890Returns the query as a string in the SSE syntax. 891 892=cut 893 894sub sse { 895 my $self = shift; 896 my $parsed = $self->parsed; 897 898 my $ggp = $self->pattern; 899 my $ns = $parsed->{namespaces}; 900 my $nscount = scalar(@{ [ keys %$ns ] }); 901 my $base_uri = $parsed->{base}; 902 903 my $indent = ' '; 904 my $context = { namespaces => $ns, indent => $indent }; 905 my $indentcount = 0; 906 $indentcount++ if ($base_uri); 907 $indentcount++ if ($nscount); 908 my $prefix = $indent x $indentcount; 909 910 my $sse = $ggp->sse( $context, $prefix ); 911 912 if ($nscount) { 913 $sse = sprintf("(prefix (%s)\n${prefix}%s)", join("\n${indent}" . ' 'x9, map { "(${_}: <$ns->{$_}>)" } (sort keys %$ns)), $sse); 914 } 915 916 if ($base_uri) { 917 $sse = sprintf("(base <%s>\n${indent}%s)", $base_uri->uri_value, $sse); 918 } 919 920 chomp($sse); 921 return $sse; 922} 923 924=item C<< dateparser >> 925 926Returns the DateTime::Format::W3CDTF object associated with this query object. 927 928=cut 929 930sub dateparser { 931 my $self = shift; 932 my $parser = ($self->{dateparser} ||= DateTime::Format::W3CDTF->new); 933 return $parser; 934} 935 936=begin private 937 938=item C<< supports ( $model, $feature ) >> 939 940Returns a boolean value representing the support of $feature for the given model. 941 942=end private 943 944=cut 945 946sub supports { 947 my $self = shift; 948 my $obj = shift; 949 my $model = $self->get_model( $obj ); 950 return $model->supports( @_ ); 951} 952 953=item C<< specifies_update_dataset >> 954 955Returns true if the query specifies a custom update dataset via the WITH or 956USING keywords, false otherwise. 957 958=cut 959 960sub specifies_update_dataset { 961 my $self = shift; 962 no warnings 'uninitialized'; 963 return $self->{parsed}{custom_update_dataset} ? 1 : 0; 964} 965 966=begin private 967 968=item C<< get_model ( $model ) >> 969 970Returns a model object for use during execution. 971If C<< $model >> is a usable model, it is simply returned. 972Otherwise, a temporary model is constructed and returned. 973 974=end private 975 976=cut 977 978sub get_model { 979 my $self = shift; 980 my $store = shift; 981 my %args = @_; 982 983 my $parsed = ref($self) ? $self->{parsed} : undef; 984 985 my $model; 986 if (not $store) { 987 $model = RDF::Trine::Model->temporary_model; 988 } elsif (($store->isa('RDF::Trine::Model'))) { 989 $model = $store; 990 } elsif ($store->isa('RDF::Redland::Model')) { 991 my $s = RDF::Trine::Store->new_with_object( $store ); 992 $model = RDF::Trine::Model->new( $s ); 993 unless (blessed($model)) { 994 Carp::cluck "Failed to construct an RDF::Trine model from $store"; 995 return; 996 } 997 } elsif ($store->isa('RDF::Core::Model')) { 998 Carp::croak "RDF::Core is no longer supported"; 999 } else { 1000 Carp::confess "unknown store type: $store"; 1001 } 1002 1003 return $model; 1004} 1005 1006=begin private 1007 1008=item C<< load_data >> 1009 1010Loads any external data required by this query (FROM and FROM NAMED clauses). 1011 1012=end private 1013 1014=cut 1015 1016sub load_data { 1017 my $self = shift; 1018 my $parsed = $self->{parsed}; 1019 1020 ## LOAD ANY EXTERNAL RDF FILES 1021 my $sources = $parsed->{'sources'}; 1022 if (ref($sources) and reftype($sources) eq 'ARRAY' and scalar(@$sources)) { 1023 my $model = RDF::Trine::Model->temporary_model; 1024 $self->model( $model ); 1025 foreach my $source (@$sources) { 1026 my $named_source = (2 == @{$source} and $source->[1] eq 'NAMED'); 1027 my $uri = $source->[0]->uri_value; 1028 $self->parse_url( $uri, $named_source ); 1029 } 1030 $self->run_hook( 'http://kasei.us/code/rdf-query/hooks/post-create-model', $model ); 1031 } 1032} 1033 1034 1035=begin private 1036 1037=item C<< var_or_expr_value ( \%bound, $value, $context ) >> 1038 1039Returns an (non-variable) RDF::Query::Node value based on C<< $value >>. 1040If C<< $value >> is a node object, it is simply returned. If it is an 1041RDF::Query::Node::Variable object, the corresponding value in C<< \%bound >> 1042is returned. If it is an RDF::Query::Expression object, the expression 1043is evaluated using C<< \%bound >>, and the resulting value is returned. 1044 1045=end private 1046 1047=cut 1048 1049sub var_or_expr_value { 1050 my $self = shift; 1051 my $bound = shift; 1052 my $v = shift; 1053 my $ctx = shift; 1054 Carp::confess 'not an object value in var_or_expr_value: ' . Dumper($v) unless (blessed($v)); 1055 if ($v->isa('RDF::Query::Expression')) { 1056 return $v->evaluate( $self, $bound, $ctx ); 1057 } elsif ($v->isa('RDF::Trine::Node::Variable')) { 1058 return $bound->{ $v->name }; 1059 } elsif ($v->isa('RDF::Query::Node')) { 1060 return $v; 1061 } else { 1062 Carp::cluck "not an expression or node value in var_or_expr_value: " . Dumper($v, $bound); 1063 throw RDF::Query::Error -text => 'Not an expression or node value'; 1064 } 1065} 1066 1067 1068=item C<add_function ( $uri, $function )> 1069 1070Associates the custom function C<$function> (a CODE reference) with the 1071specified URI, allowing the function to be called by query FILTERs. 1072 1073=cut 1074 1075sub add_function { 1076 my $self = shift; 1077 my $uri = shift; 1078 my $code = shift; 1079 if (ref($self)) { 1080 $self->{'functions'}{$uri} = $code; 1081 } else { 1082 our %functions; 1083 $RDF::Query::functions{ $uri } = $code; 1084 } 1085} 1086 1087=item C<< supported_extensions >> 1088 1089Returns a list of URLs representing extensions to SPARQL that are supported 1090by the query engine. 1091 1092=cut 1093 1094sub supported_extensions { 1095 my $self = shift; 1096 return qw( 1097 http://kasei.us/2008/04/sparql-extension/service 1098 http://kasei.us/2008/04/sparql-extension/service/bloom_filters 1099 http://kasei.us/2008/04/sparql-extension/unsaid 1100 http://kasei.us/2008/04/sparql-extension/federate_bindings 1101 http://kasei.us/2008/04/sparql-extension/select_expression 1102 http://kasei.us/2008/04/sparql-extension/aggregate 1103 http://kasei.us/2008/04/sparql-extension/aggregate/count 1104 http://kasei.us/2008/04/sparql-extension/aggregate/count-distinct 1105 http://kasei.us/2008/04/sparql-extension/aggregate/min 1106 http://kasei.us/2008/04/sparql-extension/aggregate/max 1107 ); 1108} 1109 1110=item C<< supported_functions >> 1111 1112Returns a list URLs that may be used as functions in FILTER clauses 1113(and the SELECT clause if the SPARQL 1.1 parser is used). 1114 1115=cut 1116 1117sub supported_functions { 1118 my $self = shift; 1119 my @funcs; 1120 1121 if (blessed($self)) { 1122 push(@funcs, keys %{ $self->{'functions'} }); 1123 } 1124 1125 push(@funcs, keys %RDF::Query::functions); 1126 return grep { not(/^sparql:/) } @funcs; 1127} 1128 1129=begin private 1130 1131=item C<get_function ( $uri, %args )> 1132 1133If C<$uri> is associated with a query function, returns a CODE reference 1134to the function. Otherwise returns C<undef>. 1135 1136=end private 1137 1138=cut 1139 1140sub get_function { 1141 my $self = shift; 1142 my $uri = shift; 1143 my %args = @_; 1144 my $l = Log::Log4perl->get_logger("rdf.query"); 1145 if (blessed($uri) and $uri->isa('RDF::Query::Node::Resource')) { 1146 $uri = $uri->uri_value; 1147 } 1148 $l->debug("trying to get function from $uri"); 1149 1150 if (blessed($uri) and $uri->isa('RDF::Query::Node::Resource')) { 1151 $uri = $uri->uri_value; 1152 } 1153 1154 my $func; 1155 if (ref($self)) { 1156 $func = $self->{'functions'}{$uri} || $RDF::Query::functions{ $uri }; 1157 } else { 1158 $func = $RDF::Query::functions{ $uri }; 1159 } 1160 1161 if ($func) { 1162 return $func; 1163 } 1164 return; 1165} 1166 1167 1168=begin private 1169 1170=item C<< call_function ( $model, $bound, $uri, @args ) >> 1171 1172If C<$uri> is associated with a query function, calls the function with the supplied arguments. 1173 1174=end private 1175 1176=cut 1177 1178sub call_function { 1179 my $self = shift; 1180 my $model = shift; 1181 my $bound = shift; 1182 my $uri = shift; 1183 my $l = Log::Log4perl->get_logger("rdf.query"); 1184 $l->debug("trying to get function from $uri"); 1185 1186 my $filter = RDF::Query::Expression::Function->new( $uri, @_ ); 1187 return $filter->evaluate( $self, $bound ); 1188} 1189 1190=item C<< add_computed_statement_generator ( $predicate => \&generator ) >> 1191 1192Adds a statement generator for the given C<< $predicate >> to the query object. 1193This statement generator will be called as 1194C<< $generator->( $query, $model, \%bound, $s, $p, $o, $c ) >> 1195and is expected to return an RDF::Trine::Iterator::Graph object containing 1196statements with C<< $predicate >>. 1197 1198=cut 1199 1200sub add_computed_statement_generator { 1201 my $self = shift; 1202 if (scalar(@_) == 1) { 1203 throw RDF::Query::Error::MethodInvocationError -text => 'RDF::Query::add_computed_statement_generator must now take two arguments: ( $predicate, \&generator ).'; 1204 } 1205 my $pred = shift; 1206 my $gen = shift; 1207 if (blessed($pred)) { 1208 if ($pred->can('uri_value')) { 1209 $pred = $pred->uri_value; 1210 } else { 1211 $pred = "$pred"; 1212 } 1213 } 1214 push( @{ $self->{'computed_statement_generators'}{ $pred } }, $gen ); 1215} 1216 1217=item C<< get_computed_statement_generators ( [ $predicate ] ) >> 1218 1219Returns an ARRAY reference of computed statement generator closures. 1220 1221=cut 1222 1223sub get_computed_statement_generators { 1224 my $self = shift; 1225 if (@_) { 1226 my $pred = shift; 1227 if (blessed($pred)) { 1228 if ($pred->can('uri_value')) { 1229 $pred = $pred->uri_value; 1230 } else { 1231 $pred = "$pred"; 1232 } 1233 } 1234 return $self->{'computed_statement_generators'}{ $pred } || []; 1235 } else { 1236 return $self->{'computed_statement_generators'} || {}; 1237 } 1238} 1239 1240=item C<< add_hook_once ( $hook_uri, $function, $token ) >> 1241 1242Calls C<< add_hook >> adding the supplied C<< $function >> only once based on 1243the C<< $token >> identifier. This may be useful if the only code that is able 1244to add a hook is called many times (in an extension function, for example). 1245 1246=cut 1247 1248sub add_hook_once { 1249 my $self = shift; 1250 my $uri = shift; 1251 my $code = shift; 1252 my $token = shift; 1253 unless ($self->{'hooks_once'}{ $token }++) { 1254 $self->add_hook( $uri, $code ); 1255 } 1256} 1257 1258=item C<< add_hook ( $hook_uri, $function ) >> 1259 1260Associates the custom function C<$function> (a CODE reference) with the 1261RDF::Query code hook specified by C<$uri>. Each function that has been 1262associated with a particular hook will be called (in the order they were 1263registered as hooks) when the hook event occurs. See L</"Defined Hooks"> 1264for more information. 1265 1266=cut 1267 1268sub add_hook { 1269 my $self = shift; 1270 my $uri = shift; 1271 my $code = shift; 1272 if (ref($self)) { 1273 push(@{ $self->{'hooks'}{$uri} }, $code); 1274 } else { 1275 our %hooks; 1276 push(@{ $RDF::Query::hooks{ $uri } }, $code); 1277 } 1278} 1279 1280=begin private 1281 1282=item C<get_hooks ( $uri )> 1283 1284If C<$uri> is associated with any query callback functions ("hooks"), 1285returns an ARRAY reference to the functions. If no hooks are associated 1286with C<$uri>, returns a reference to an empty array. 1287 1288=end private 1289 1290=cut 1291 1292sub get_hooks { 1293 my $self = shift; 1294 my $uri = shift; 1295 my $func = $self->{'hooks'}{ $uri } 1296 || $RDF::Query::hooks{ $uri } 1297 || []; 1298 return $func; 1299} 1300 1301=begin private 1302 1303=item C<run_hook ( $uri, @args )> 1304 1305Calls any query callback functions associated with C<$uri>. Each callback 1306is called with the query object as the first argument, followed by any 1307caller-supplied arguments from C<@args>. 1308 1309=end private 1310 1311=cut 1312 1313sub run_hook { 1314 my $self = shift; 1315 my $uri = shift; 1316 my @args = @_; 1317 my $hooks = $self->get_hooks( $uri ); 1318 foreach my $hook (@$hooks) { 1319 $hook->( $self, @args ); 1320 } 1321} 1322 1323=begin private 1324 1325=item C<< parse_url ( $url, $named ) >> 1326 1327Retrieve a remote file by URL, and parse RDF into the RDF store. 1328If $named is TRUE, associate all parsed triples with a named graph. 1329 1330=end private 1331 1332=cut 1333sub parse_url { 1334 my $self = shift; 1335 my $url = shift; 1336 my $named = shift; 1337 my $model = $self->model; 1338 1339 if ($named) { 1340 RDF::Trine::Parser->parse_url_into_model( $url, $model, context => iri($url) ); 1341 } else { 1342 RDF::Trine::Parser->parse_url_into_model( $url, $model ); 1343 } 1344} 1345 1346=begin private 1347 1348=item C<variables ()> 1349 1350Returns a list of the ordered variables the query is selecting. 1351 1352=end private 1353 1354=cut 1355 1356sub variables { 1357 my $self = shift; 1358 my $parsed = shift || $self->parsed; 1359 my @vars = map { $_->name } 1360 grep { 1361 $_->isa('RDF::Query::Node::Variable') or $_->isa('RDF::Query::Expression::Alias') 1362 } @{ $parsed->{'variables'} }; 1363 return @vars; 1364} 1365 1366=item C<parsed ()> 1367 1368Returns the parse tree. 1369 1370=cut 1371 1372sub parsed { 1373 my $self = shift; 1374 if (@_) { 1375 $self->{parsed} = shift; 1376 } 1377 return $self->{parsed}; 1378} 1379 1380=item C<< model >> 1381 1382Returns the RDF::Trine::Model object for this query. 1383 1384=cut 1385 1386sub model { 1387 my $self = shift; 1388 if (@_) { 1389 $self->{model} = shift; 1390 } 1391 my $model = $self->{model}; 1392 unless (defined $model) { 1393 Carp::confess "query->model shouldn't be calling get_model"; 1394 $model = $self->get_model(); 1395 } 1396 1397 return $model; 1398} 1399 1400 1401=item C<< useragent >> 1402 1403Returns the LWP::UserAgent object used for retrieving web content. 1404 1405=cut 1406 1407sub useragent { 1408 my $self = shift; 1409 if (my $ua = $self->{useragent}) { 1410 return $ua; 1411 } else { 1412 my $ua = LWP::UserAgent->new( agent => "RDF::Query/${VERSION}" ); 1413 $ua->default_headers->push_header( 'Accept' => "application/sparql-results+xml;q=0.9,application/rdf+xml;q=0.5,text/turtle;q=0.7,text/xml" ); 1414 $self->{useragent} = $ua; 1415 return $ua; 1416 } 1417} 1418 1419 1420=item C<< log ( $key [, $value ] ) >> 1421 1422If no logger object is associated with this query object, does nothing. 1423Otherwise, return or set the corresponding value depending on whether a 1424C<< $value >> is specified. 1425 1426=cut 1427 1428sub log { 1429 my $self = shift; 1430 if (blessed(my $l = $self->{ logger })) { 1431 $l->log( @_ ); 1432 } 1433} 1434 1435 1436=item C<< logger >> 1437 1438Returns the logger object associated with this query object (if present). 1439 1440=cut 1441 1442sub logger { 1443 my $self = shift; 1444 return $self->{ logger }; 1445} 1446 1447=item C<error ()> 1448 1449Returns the last error the parser experienced. 1450 1451=cut 1452 1453sub error { 1454 my $self = shift; 1455 if (blessed($self)) { 1456 return $self->{error}; 1457 } else { 1458 our $_ERROR; 1459 return $_ERROR; 1460 } 1461} 1462 1463sub _uniq { 1464 my %seen; 1465 my @data; 1466 foreach (@_) { 1467 push(@data, $_) unless ($seen{ $_ }++); 1468 } 1469 return @data; 1470} 1471 1472=begin private 1473 1474=item C<set_error ( $error )> 1475 1476Sets the object's error variable. 1477 1478=end private 1479 1480=cut 1481 1482sub set_error { 1483 my $self = shift; 1484 my $error = shift; 1485 my $e = shift; 1486 if (blessed($self)) { 1487 $self->{error} = $error; 1488 $self->{exception} = $e; 1489 } 1490 our $_ERROR = $error; 1491 our $_EXCEPTION = $e; 1492} 1493 1494=begin private 1495 1496=item C<clear_error ()> 1497 1498Clears the object's error variable. 1499 1500=end private 1501 1502=cut 1503 1504sub clear_error { 1505 my $self = shift; 1506 if (blessed($self)) { 1507 $self->{error} = undef; 1508 $self->{exception} = undef; 1509 } 1510 our($_ERROR, $_EXCEPTION); 1511 undef $_ERROR; 1512 undef $_EXCEPTION; 1513} 1514 1515 1516# =begin private 1517# 1518# =item C<_debug_closure ( $code )> 1519# 1520# Debugging function to print out a deparsed (textual) version of a closure. 1521# 1522# =end private 1523# 1524# =cut 1525# 1526# sub _debug_closure { 1527# my $closure = shift; 1528# my $l = Log::Log4perl->get_logger("rdf.query"); 1529# if ($l->is_trace) { 1530# require B::Deparse; 1531# my $deparse = B::Deparse->new("-p", "-sC"); 1532# my $body = $deparse->coderef2text($closure); 1533# $l->trace("--- --- CLOSURE --- ---"); 1534# $l->logcluck($body); 1535# } 1536# } 1537 1538 15391; 1540 1541__END__ 1542 1543=back 1544 1545=head1 DEFINED HOOKS 1546 1547The following hook URIs are defined and may be used to extend the query engine 1548functionality using the C<< add_hook >> method: 1549 1550=over 4 1551 1552=item http://kasei.us/code/rdf-query/hooks/post-create-model 1553 1554Called after loading all external files to a temporary model in queries that 1555use FROM and FROM NAMED. 1556 1557Args: ( $query, $model ) 1558 1559C<$query> is the RDF::Query object. 1560C<$model> is the RDF::Trine::Model object. 1561 1562=item http://kasei.us/code/rdf-query/hooks/post-execute 1563 1564Called immediately before returning a result iterator from the execute method. 1565 1566Args: ( $query, $model, $iterator ) 1567 1568C<$query> is the RDF::Query object. 1569C<$model> is the RDF::Trine::Model object. 1570C<$iterator> is a RDF::Trine::Iterator object. 1571 1572=back 1573 1574=head1 SEE ALSO 1575 1576L<http://www.perlrdf.org/> 1577 1578=head1 AUTHOR 1579 1580 Gregory Todd Williams <gwilliams@cpan.org> 1581 1582=head1 LICENSE 1583 1584Copyright (c) 2005-2012 Gregory Todd Williams. This 1585program is free software; you can redistribute it and/or modify it under 1586the same terms as Perl itself. 1587 1588=cut 1589