File Coverage

blib/lib/App/perlrdf/Command/Query.pm
Criterion Covered Total %
statement 15 17 88.2
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 21 23 91.3


line stmt bran cond sub pod time code
1             package App::perlrdf::Command::Query;
2              
3 1     1   29512 use 5.010;
  1         4  
  1         38  
4 1     1   5 use strict;
  1         1  
  1         35  
5 1     1   3 use warnings;
  1         5  
  1         51  
6 1     1   1059 use utf8;
  1         8  
  1         4  
7              
8             BEGIN {
9 1     1   81 $App::perlrdf::Command::Query::AUTHORITY = 'cpan:TOBYINK';
10 1         26 $App::perlrdf::Command::Query::VERSION = '0.004';
11             }
12              
13 1     1   1273 use App::perlrdf -command;
  0            
  0            
14             use namespace::clean;
15              
16             use constant abstract => q (query stores, files or remote endpoints with SPARQL);
17             use constant command_names => qw( query sparql q );
18             use constant description => <<'DESCRIPTION';
19             Use SPARQL to query:
20              
21             * an RDF::Trine::Store;
22             * a remote SPARQL Protocol (1.0/1.1) endpoint; or
23             * one or more input files;
24              
25             But not a combination of the above.
26             DESCRIPTION
27              
28             use constant opt_spec => (
29             __PACKAGE__->store_opt_spec,
30             []=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>,
31             [ 'input|i=s@', 'Input filename or URL' ],
32             [ 'input-spec|I=s@', 'Input file specification' ],
33             [ 'input-format|p=s', 'Input format (mnemonic: parse)' ],
34             [ 'input-base|b=s', 'Input base URI' ],
35             [ 'graph|g=s', 'Graph URI for input' ],
36             [ 'autograph|G', 'Generate graph URI based on input URI' ],
37             []=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>,
38             [ 'endpoint=s', 'Remote SPARQL Protocol endpoint' ],
39             [ 'query_method=s', 'Query method (GET/POST/etc)' ],
40             []=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>,
41             [ 'execute|e=s', 'Query to execute' ],
42             [ 'sparql-file|f=s', 'File containing query to execute' ],
43             []=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>
44             [ 'output|o=s@', 'Output filename or URL' ],
45             [ 'output-spec|O=s@', 'Output file specification' ],
46             [ 'output-format|s=s', 'Output format (mnemonic: serialise)' ],
47             );
48              
49             sub validate_args
50             {
51             my ($self, $opt, $arg) = @_;
52            
53             my %exclusions = (
54             execute => ['sparql_file'],
55             endpoint => [
56             qw[ store dbi sqlite username password host port dbname database ],
57             qw[ input input_spec input_format input_base ],
58             ],
59             query_method => [
60             qw[ store dbi sqlite username password host port dbname database ],
61             qw[ input input_spec input_format input_base ],
62             ],
63             map { ; $_ => [
64             qw[ store dbi sqlite username password host port dbname database ],
65             qw[ endpoint ],
66             ] } qw( input input_spec input_format input_base )
67             );
68            
69             foreach my $k (keys %exclusions)
70             {
71             next unless exists $opt->{$k};
72             foreach my $e (@{ $exclusions{$k} })
73             {
74             next unless exists $opt->{$e};
75             $self->usage_error("Must not provide both '$k' and '$e' options.");
76             }
77             }
78             }
79              
80             sub _sparql
81             {
82             require App::perlrdf::FileSpec::InputFile;
83             my ($self, $opt, $arg) = @_;
84            
85             # SPARQL provided on command line
86             #
87             return $opt->{execute}
88             if $opt->{execute};
89            
90             # SPARQL from input file
91             #
92             App::perlrdf::FileSpec::InputFile::
93             -> new_from_filespec(
94             ($opt->{sparql_file} // shift @$arg),
95             'SPARQL',
96             )
97             -> content;
98             }
99              
100             sub _model
101             {
102             require App::perlrdf::FileSpec::InputRDF;
103             my ($self, $opt, $arg) = @_;
104            
105             if (grep { exists $opt->{$_} } qw[ store dbi sqlite dbname database ])
106             {
107             return RDF::Trine::Model->new( $self->get_store($opt) );
108             }
109              
110             my $model = RDF::Trine::Model->new;
111            
112             my @inputs = $self->get_filespecs(
113             'App::perlrdf::FileSpec::InputRDF',
114             input => $opt,
115             );
116            
117             push @inputs, map {
118             App::perlrdf::FileSpec::InputRDF::->new_from_filespec(
119             $_,
120             $opt->{input_format},
121             $opt->{input_base},
122             )
123             } @$arg;
124              
125             push @inputs,
126             App::perlrdf::FileSpec::InputRDF::->new_from_filespec(
127             '-',
128             $opt->{input_format},
129             $opt->{input_base},
130             )
131             unless @inputs;
132              
133             for (@inputs)
134             {
135             printf STDERR "Loading %s\n", $_->uri;
136            
137             my %params = ();
138             if ($opt->{autograph})
139             { $params{graph} = $_->uri }
140             elsif ($opt->{graph})
141             { $params{graph} = $opt->{graph} }
142            
143             eval {
144             local $@ = undef;
145             $_->parse_into_model($model, %params);
146             1;
147             } or warn "$@\n";
148             }
149              
150             return $model;
151             }
152              
153             sub _outputs
154             {
155             require App::perlrdf::FileSpec::OutputRDF;
156             require App::perlrdf::FileSpec::OutputBindings;
157            
158             my ($self, $opt, $arg, $class) = @_;
159            
160             my @outputs = $self->get_filespecs(
161             $class,
162             output => $opt,
163             );
164             push @outputs,
165             $class->new_from_filespec(
166             '-',
167             $opt->{output_format}||'text',
168             undef,
169             )
170             unless @outputs;
171            
172             return @outputs;
173             }
174              
175             sub _process_sparql
176             {
177             require RDF::Query;
178             require RDF::Query::Client;
179             my ($self, $opt, $arg, $sparql, $model) = @_;
180            
181             my $qclass = ref $model ? 'RDF::Query' : 'RDF::Query::Client';
182             my @params = ref $model ? () : ({
183             QueryMethod => ($opt->{query_method} // $ENV{PERLRDF_QUERY_METHOD} // "POST"),
184             });
185             my $query = $qclass->new($sparql) or die RDF::Query->error;
186             if ($query->can('useragent')) {
187             $query->useragent->max_redirect(5);
188             $query->useragent->agent(
189             sprintf(
190             '%s/%s (%s) %s',
191             ref($self),
192             $self->VERSION,
193             $self->AUTHORITY,
194             $query->useragent->agent,
195             ),
196             );
197             }
198             my $result = $query->execute($model, @params) or do {
199             if (($ENV{PERLRDF_QUERY_DEBUG}//'') and $query->can('http_response')) {
200             warn $query->http_response->request->as_string;
201             for my $redir ($query->http_response->redirects) {
202             warn $redir->status_line;
203             }
204             warn $query->http_response->as_string;
205             }
206             die $query->error;
207             };
208            
209             if ($result->is_graph)
210             {
211             my $m = RDF::Trine::Model->new;
212             $m->add_iterator($m);
213            
214             my (@outputs) = $self->_outputs(
215             $opt,
216             $arg,
217             'App::perlrdf::FileSpec::OutputRDF',
218             );
219            
220             foreach my $out (@outputs)
221             {
222             $out->serialize_model($m);
223             $out->handle->close;
224             }
225             }
226            
227             if ($result->is_bindings)
228             {
229             if (($ENV{PERLRDF_QUERY_DEBUG}//'') and $query->can('http_response')) {
230             warn $query->http_response->as_string;
231             }
232            
233             my $mat = $result->materialize;
234            
235             my (@outputs) = $self->_outputs(
236             $opt,
237             $arg,
238             'App::perlrdf::FileSpec::OutputBindings',
239             );
240            
241             foreach my $out (@outputs)
242             {
243             $out->serialize_iterator($mat);
244             $mat->reset;
245             }
246             }
247             }
248              
249             sub execute
250             {
251             require RDF::Trine;
252             my ($self, $opt, $arg) = @_;
253            
254             my $sparql = $self->_sparql($opt, $arg);
255              
256             if (exists $opt->{endpoint})
257             {
258             return $self->_process_sparql($opt, $arg, $sparql, $opt->{endpoint});
259             }
260              
261             my $model = $self->_model($opt, $arg);
262             $self->_process_sparql($opt, $arg, $sparql, $model);
263             }
264              
265             1;
266              
267              
268             __END__