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