36
37:- module(sparql_client,
38 [ sparql_query/3, 39 sparql_set_server/1, 40 sparql_read_xml_result/2, 41 sparql_read_json_result/2 42 ]). 43:- autoload(library(apply), [maplist/3, maplist/4, partition/4]). 44:- autoload(library(gensym), [gensym/2]). 45:- autoload(library(lists), [member/2]). 46:- autoload(library(option), [select_option/3, select_option/4, merge_options/3]). 47:- autoload(library(rdf), [load_rdf/2]). 48:- autoload(library(readutil), [read_stream_to_codes/2]). 49:- autoload(library(sgml), [load_structure/3]). 50:- autoload(library(uri),
51 [ uri_components/2,
52 uri_data/3,
53 uri_authority_components/2,
54 uri_authority_data/3
55 ]). 56:- autoload(library(http/http_open), [http_open/3]). 57:- autoload(library(http/json), [json_read/2]). 58:- autoload(library(semweb/turtle), [rdf_read_turtle/3]). 59
91
92
146
147sparql_query(Query, Row, Options) :-
148 ( select_option(endpoint(URL), Options, Options5)
149 -> uri_components(URL, Components),
150 uri_data(scheme, Components, Scheme),
151 uri_data(authority, Components, Auth),
152 uri_data(path, Components, Path),
153 uri_data(search, Components, Extra),
154 ignore(Extra = []),
155 uri_authority_components(Auth, AComp),
156 uri_authority_data(host, AComp, Host),
157 uri_authority_data(port, AComp, Port),
158 ( var(Port)
159 -> sparql_port(Scheme, Port, _, _)
160 ; true
161 )
162 ; sparql_param(scheme(Scheme), Options, Options1),
163 sparql_port(Scheme, Port, Options1, Options2),
164 sparql_param(host(Host), Options2, Options3),
165 sparql_param(path(Path), Options3, Options4),
166 select_option(search(Extra), Options4, Options5, [])
167 ),
168 select_option(variable_names(VarNames), Options5, Options6, _),
169 partition(is_url_option, Options6, UrlOptions, HTTPOptions),
170 sparql_extra_headers(HTTPOptions0),
171 merge_options(HTTPOptions, HTTPOptions0, HTTPOptions1),
172 http_open([ scheme(Scheme),
173 host(Host),
174 port(Port),
175 path(Path),
176 search([ query = Query
177 | Extra
178 ])
179 | UrlOptions
180 ], In,
181 [ header(content_type, ContentType),
182 status_code(Status)
183 | HTTPOptions1
184 ]),
185 plain_content_type(ContentType, CleanType),
186 read_reply(Status, CleanType, In, VarNames, Row).
187
188url_option(scheme).
189url_option(user).
190url_option(password).
191url_option(host).
192url_option(port).
193url_option(path).
194url_option(query_string).
195url_option(search).
196
197is_url_option(Name = _Value) :-
198 url_option(Name),
199 !.
200is_url_option(Opt) :-
201 compound(Opt),
202 functor(Opt, Name, 1),
203 url_option(Name).
204
211
(
213 [ request_header('Accept' = 'application/sparql-results+xml, \c
214 application/n-triples, \c
215 application/x-turtle; q=0.9, \c
216 application/turtle; q=0.9, \c
217 text/turtle, \c
218 application/sparql-results+json, \c
219 application/rdf+xml, \c
220 text/rdf+xml; q=0.8, \c
221 */*; q=0.1')
222 ]).
223
225
226read_reply(200, ContentType, In, Close, Row) :-
227 !,
228 read_reply(ContentType, In, Close, Row).
229read_reply(Status, _ContentType, In, _Close, _Row) :-
230 call_cleanup(read_string(In, _, Reply),
231 close(In, [force(true)])),
232 throw(error(sparql_error(Status, Reply), _)).
233
234read_reply('application/rdf+xml', In, _, Row) :-
235 !,
236 call_cleanup(load_rdf(stream(In), RDF), close(In)),
237 member(Row, RDF).
238read_reply(MIME, In, _, Row) :-
239 turtle_media_type(MIME),
240 !,
241 call_cleanup(rdf_read_turtle(stream(In), RDF, []), close(In)),
242 member(Row, RDF).
243read_reply(MIME, In, VarNames, Row) :-
244 sparql_result_mime(MIME),
245 !,
246 call_cleanup(sparql_read_xml_result(stream(In), Result),
247 close(In)),
248 varnames(Result, VarNames),
249 xml_result(Result, Row).
250read_reply(MIME, In, VarNames, Row) :-
251 json_result_mime(MIME),
252 !,
253 call_cleanup(sparql_read_json_result(stream(In), Result),
254 close(In)),
255 ( Result = select(VarNames, Rows)
256 -> member(Row, Rows)
257 ; Result = ask(True)
258 -> Row = True,
259 VarNames = []
260 ).
261read_reply(Type, In, _, _) :-
262 read_stream_to_codes(In, Codes),
263 string_codes(Reply, Codes),
264 close(In),
265 throw(error(domain_error(sparql_result_document, Type),
266 context(_, Reply))).
267
268turtle_media_type('application/x-turtle').
269turtle_media_type('application/turtle').
270turtle_media_type('application/n-triples').
271turtle_media_type('text/rdf+n3').
272turtle_media_type('text/turtle').
273
274sparql_result_mime('application/sparql-results+xml'). 275sparql_result_mime('application/sparql-result+xml').
276
277json_result_mime('application/sparql-results+json').
278
279
280plain_content_type(Type, Plain) :-
281 sub_atom(Type, B, _, _, (;)),
282 !,
283 sub_string(Type, 0, B, _, Main),
284 normalize_space(atom(Plain), Main).
285plain_content_type(Type, Type).
286
287xml_result(ask(Bool), Result) :-
288 !,
289 Result = Bool.
290xml_result(select(_VarNames, Rows), Result) :-
291 member(Result, Rows).
292
293varnames(ask(_), _).
294varnames(select(VarTerm, _Rows), VarNames) :-
295 VarTerm =.. [_|VarNames].
296
297
298 301
302:- dynamic
303 sparql_setting/1. 304
305sparql_setting(scheme(http)).
306sparql_setting(path('/sparql/')).
307
308sparql_param(Param, Options0, Options) :-
309 select_option(Param, Options0, Options),
310 !.
311sparql_param(Param, Options, Options) :-
312 sparql_setting(Param),
313 !.
314sparql_param(Param, Options, Options) :-
315 functor(Param, Name, _),
316 throw(error(existence_error(option, Name), _)).
317
318sparql_port(_Scheme, Port, Options0, Options) :-
319 select_option(port(Port), Options0, Options),
320 !.
321sparql_port(_Scheme, Port, Options, Options) :-
322 sparql_setting(port(Port)),
323 !.
324sparql_port(http, 80, Options, Options) :-
325 !.
326sparql_port(https, 443, Options, Options) :-
327 !.
328
329
343
344sparql_set_server([]) :- !.
345sparql_set_server([H|T]) :-
346 !,
347 sparql_set_server(H),
348 sparql_set_server(T).
349sparql_set_server(Term) :-
350 functor(Term, Name, Arity),
351 functor(Unbound, Name, Arity),
352 retractall(sparql_setting(Unbound)),
353 assert(sparql_setting(Term)).
354
355
356 359
360ns(sparql, 'http://www.w3.org/2005/sparql-results#').
361
366
367 370
373
374term_subst(V, _, _, V) :-
375 var(V),
376 !.
377term_subst(F, F, T, T) :- !.
378term_subst(C, F, T, C2) :-
379 compound(C),
380 !,
381 functor(C, Name, Arity),
382 functor(C2, Name, Arity),
383 term_subst(0, Arity, C, F, T, C2).
384term_subst(T, _, _, T).
385
386term_subst(A, A, _, _, _, _) :- !.
387term_subst(I0, Arity, C0, F, T, C) :-
388 I is I0 + 1,
389 arg(I, C0, A0),
390 term_subst(A0, F, T, A),
391 arg(I, C, A),
392 term_subst(I, Arity, C0, F, T, C).
393
394term_expansion(T0, T) :-
395 ns(sparql, NS),
396 term_subst(T0, sparql, NS, T).
397
398
399 402
415
416:- thread_local
417 bnode_map/2. 418
419sparql_read_xml_result(Input, Result) :-
420 load_structure(Input, DOM,
421 [ dialect(xmlns)
422 ]),
423 call_cleanup(dom_to_result(DOM, Result),
424 retractall(bnode_map(_,_))).
425
426dom_to_result(DOM, Result) :-
427 ( sub_element(DOM, sparql:head, _HAtt, Content)
428 -> variables(Content, Vars)
429 ; Vars = []
430 ),
431 ( Vars == [],
432 sub_element(DOM, sparql:boolean, _, [TrueFalse])
433 -> Result = ask(TrueFalse)
434 ; VarTerm =.. [v|Vars],
435 Result = select(VarTerm, Rows),
436 sub_element(DOM, sparql:results, _RAtt, RContent)
437 -> rows(RContent, Vars, Rows)
438 ),
439 !. 440
446
447variables([], []).
448variables([element(sparql:variable, Att, [])|T0], [Name|T]) :-
449 !,
450 memberchk(name=Name, Att),
451 variables(T0, T).
452variables([element(sparql:link, _, _)|T0], T) :-
453 !,
454 variables(T0, T).
455variables([CDATA|T0], T) :-
456 atomic(CDATA),
457 variables(T0, T).
458
459
460rows([], _, []).
461rows([R|T0], Vars, [Row|T]) :-
462 R = element(sparql:result, _, _),
463 !,
464 row_values(Vars, R, Values),
465 Row =.. [row|Values],
466 rows(T0, Vars, T).
467rows([CDATA|T0], Vars, T) :-
468 atomic(CDATA),
469 rows(T0, Vars, T).
470
471row_values([], _, []).
472row_values([Var|VarT], DOM, [Value|ValueT]) :-
473 ( sub_element(DOM, sparql:binding, Att, Content),
474 memberchk(name=Var, Att)
475 -> value(Content, Value)
476 ; Value = '$null$'
477 ),
478 row_values(VarT, DOM, ValueT).
479
480value([element(sparql:literal, Att, Content)|Rest], literal(Lit)) :-
481 !,
482 white(Rest),
483 lit_value(Content, Value),
484 ( memberchk(datatype=Type, Att)
485 -> Lit = type(Type, Value)
486 ; memberchk(xml:lang=Lang, Att)
487 -> Lit = lang(Lang, Value)
488 ; Lit = Value
489 ).
490value([element(sparql:uri, [], [URI])|Rest], URI) :- !,
491 white(Rest).
492value([element(sparql:bnode, [], [NodeID])|Rest], URI) :-
493 !,
494 white(Rest),
495 bnode(NodeID, URI).
496value([element(sparql:unbound, [], [])|Rest], '$null$') :-
497 !,
498 white(Rest).
499value([CDATA|Rest], Value) :-
500 atomic(CDATA),
501 value(Rest, Value).
502
503
504white([]).
505white([CDATA|T]) :-
506 atomic(CDATA),
507 white(T).
508
509lit_value([], '').
510lit_value([Value], Value).
511
512
514
515sub_element(element(Name, Att, Content), Name, Att, Content).
516sub_element(element(_, _, List), Name, Att, Content) :-
517 sub_element(List, Name, Att, Content).
518sub_element([H|T], Name, Att, Content) :-
519 ( sub_element(H, Name, Att, Content)
520 ; sub_element(T, Name, Att, Content)
521 ).
522
523
524bnode(Name, URI) :-
525 bnode_map(Name, URI),
526 !.
527bnode(Name, URI) :-
528 gensym('__bnode', URI0),
529 assertz(bnode_map(Name, URI0)),
530 URI = URI0.
531
532
546
547sparql_read_json_result(Input, Result) :-
548 setup_call_cleanup(
549 open_input(Input, In, Close),
550 read_json_result(In, Result),
551 close_input(Close)).
552
553open_input(stream(In), In, Close) :-
554 !,
555 encoding(In, utf8, Close).
556open_input(In, In, Close) :-
557 is_stream(In),
558 !,
559 encoding(In, utf8, Close).
560open_input(File, In, close(In)) :-
561 open(File, read, In, [encoding(utf8)]).
562
563encoding(In, Encoding, Close) :-
564 stream_property(In, encoding(Old)),
565 ( Encoding == Old
566 -> Close = true
567 ; set_stream(In, encoding(Encoding)),
568 Close = set_stream(In, Encoding, Old)
569 ).
570
571close_input(close(In)) :-
572 !,
573 retractall(bnode_map(_,_)),
574 close(In).
575close_input(_) :-
576 retractall(bnode_map(_,_)).
577
578read_json_result(In, Result) :-
579 json_read(In, JSON),
580 json_to_result(JSON, Result).
581
582json_to_result(json([ head = json(Head),
583 results = json(Body)
584 ]),
585 select(Vars, Rows)) :-
586 memberchk(vars=VarList, Head),
587 Vars =.. [v|VarList],
588 memberchk(bindings=Bindings, Body),
589 !,
590 maplist(json_row(VarList), Bindings, Rows).
591json_to_result(json(JSon), ask(Boolean)) :-
592 memberchk(boolean = @(Boolean), JSon).
593
594
595json_row(Vars, json(Columns), Row) :-
596 maplist(json_cell, Vars, Columns, Values),
597 !,
598 Row =.. [row|Values].
599json_row(Vars, json(Columns), Row) :-
600 maplist(json_cell_or_null(Columns), Vars, Values),
601 Row =.. [row|Values].
602
603json_cell(Var, Var=json(JValue), Value) :-
604 memberchk(type=Type, JValue),
605 jvalue(Type, JValue, Value).
606
607json_cell_or_null(Columns, Var, Value) :-
608 memberchk(Var=json(JValue), Columns),
609 !,
610 memberchk(type=Type, JValue),
611 jvalue(Type, JValue, Value).
612json_cell_or_null(_, _, '$null$').
613
614jvalue(uri, JValue, URI) :-
615 memberchk(value=URI, JValue).
616jvalue(literal, JValue, literal(Literal)) :-
617 memberchk(value=Value, JValue),
618 ( memberchk('xml:lang'=Lang, JValue)
619 -> Literal = lang(Lang, Value)
620 ; memberchk('datatype'=Type, JValue)
621 -> Literal = type(Type, Value)
622 ; Literal = Value
623 ).
624jvalue('typed-literal', JValue, literal(type(Type, Value))) :-
625 memberchk(value=Value, JValue),
626 memberchk('datatype'=Type, JValue).
627jvalue(bnode, JValue, URI) :-
628 memberchk(value=NodeID, JValue),
629 bnode(NodeID, URI)