View source with raw comments or as raw
    1/*  Part of SWI-Prolog
    2
    3    Author:        Jan Wielemaker
    4    E-mail:        J.Wielemaker@vu.nl
    5    WWW:           http://www.swi-prolog.org
    6    Copyright (c)  2010-2017, University of Amsterdam
    7                              VU University Amsterdam
    8    All rights reserved.
    9
   10    Redistribution and use in source and binary forms, with or without
   11    modification, are permitted provided that the following conditions
   12    are met:
   13
   14    1. Redistributions of source code must retain the above copyright
   15       notice, this list of conditions and the following disclaimer.
   16
   17    2. Redistributions in binary form must reproduce the above copyright
   18       notice, this list of conditions and the following disclaimer in
   19       the documentation and/or other materials provided with the
   20       distribution.
   21
   22    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   23    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   24    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   25    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   26    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   27    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   28    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   29    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   30    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   31    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   32    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   33    POSSIBILITY OF SUCH DAMAGE.
   34*/
   35
   36:- module(sparql_client,
   37          [ sparql_query/3,             % +Query, -Row, +Options
   38            sparql_set_server/1,        % +Options
   39            sparql_read_xml_result/2,   % +Stream, -Result
   40            sparql_read_json_result/2   % +Input, -Result
   41          ]).   42:- autoload(library(apply),[maplist/3,maplist/4]).   43:- autoload(library(gensym),[gensym/2]).   44:- autoload(library(lists),[member/2]).   45:- autoload(library(option),[select_option/3,select_option/4]).   46:- autoload(library(rdf),[load_rdf/2]).   47:- autoload(library(readutil),[read_stream_to_codes/2]).   48:- autoload(library(sgml),[load_structure/3]).   49:- autoload(library(uri),
   50	    [ uri_components/2, uri_data/3, uri_authority_components/2,
   51	      uri_authority_data/3
   52	    ]).   53:- autoload(library(http/http_open),[http_open/3]).   54:- autoload(library(http/json),[json_read/2]).   55:- autoload(library(semweb/turtle),[rdf_read_turtle/3]).

SPARQL client library

This module provides a SPARQL client. For example:

?- sparql_query('select * where { ?x rdfs:label "Amsterdam" }', Row,
                [ host('dbpedia.org'), path('/sparql/')]).

Row = row('http://www.ontologyportal.org/WordNet#WN30-108949737') ;
false.

Or, querying a local server using an ASK query:

?- sparql_query('ask { owl:Class rdfs:label "Class" }', Row,
                [ host('localhost'), port(3020), path('/sparql/')]).
Row = true.

HTTPS servers are supported using the scheme(https) option:

?- sparql_query('select * where { ?x rdfs:label "Amsterdam"@nl }',
                Row,
                [ scheme(https),
                  host('query.wikidata.org'),
                  path('/sparql')
                ]).

*/

 sparql_query(+Query, -Result, +Options) is nondet
Execute a SPARQL query on an HTTP SPARQL endpoint. Query is an atom that denotes the query. Result is unified to a term rdf(S,P,O) for CONSTRUCT and DESCRIBE queries, row(...) for SELECT queries and true or false for ASK queries. Options are

Variables that are unbound in SPARQL (e.g., due to SPARQL optional clauses), are bound in Prolog to the atom '$null$'.

endpoint(+URL)
May be used as alternative to Scheme, Host, Port and Path to specify the endpoint in a single option.
host(+Host)
port(+Port)
path(+Path)
scheme(+Scheme)
The above four options set the location of the server.
search(+ListOfParams)
Provide additional query parameters, such as the graph.
variable_names(-ListOfNames)
Unifies ListOfNames with a list of atoms that describe the names of the variables in a SELECT query.

Remaining options are passed to http_open/3. The defaults for Host, Port and Path can be set using sparql_set_server/1. The initial default for port is 80 and path is /sparql/.

For example, the ClioPatria server understands the parameter entailment. The code below queries for all triples using _rdfs_entailment.

?- sparql_query('select * where { ?s ?p ?o }',
                Row,
                [ search([entailment=rdfs])
                ]).
  130sparql_query(Query, Row, Options) :-
  131    (   select_option(endpoint(URL), Options, Options5)
  132    ->  uri_components(URL, Components),
  133        uri_data(scheme, Components, Scheme),
  134        uri_data(authority, Components, Auth),
  135        uri_data(path, Components, Path),
  136        uri_data(search, Components, Extra),
  137        ignore(Extra = []),
  138        uri_authority_components(Auth, AComp),
  139        uri_authority_data(host, AComp, Host),
  140        uri_authority_data(port, AComp, Port),
  141        (   var(Port)
  142        ->  sparql_port(Scheme, Port, _, _)
  143        ;   true
  144        )
  145    ;   sparql_param(scheme(Scheme), Options,  Options1),
  146        sparql_port(Scheme, Port,    Options1, Options2),
  147        sparql_param(host(Host),     Options2, Options3),
  148        sparql_param(path(Path),     Options3, Options4),
  149        select_option(search(Extra), Options4, Options5, [])
  150    ),
  151    select_option(variable_names(VarNames), Options5, Options6, _),
  152    sparql_extra_headers(HTTPOptions),
  153    http_open([ scheme(Scheme),
  154                host(Host),
  155                port(Port),
  156                path(Path),
  157                search([ query = Query
  158                       | Extra
  159                       ])
  160              | Options6
  161              ], In,
  162              [ header(content_type, ContentType),
  163                status_code(Status)
  164              | HTTPOptions
  165              ]),
  166    plain_content_type(ContentType, CleanType),
  167    read_reply(Status, CleanType, In, VarNames, Row).
 sparql_extra_headers(-List)
Send extra headers with the request. Note that, although we also process RDF embedded in HTML, we do not explicitely ask for it. Doing so causes some (e.g., http://w3.org/2004/02/skos/core to reply with the HTML description rather than the RDF).
  176sparql_extra_headers(
  177        [ request_header('Accept' = 'application/sparql-results+xml, \c
  178                                     application/n-triples, \c
  179                                     application/x-turtle; q=0.9, \c
  180                                     application/turtle; q=0.9, \c
  181                                     text/turtle, \c
  182                                     application/sparql-results+json, \c
  183                                     application/rdf+xml, \c
  184                                     text/rdf+xml; q=0.8, \c
  185                                     */*; q=0.1'),
  186          cert_verify_hook(ssl_verify)
  187        ]).
  188
  189:- public ssl_verify/5.
 ssl_verify(+SSL, +ProblemCert, +AllCerts, +FirstCert, +Error)
Currently we accept all certificates.
  195ssl_verify(_SSL,
  196           _ProblemCertificate, _AllCertificates, _FirstCertificate,
  197           _Error).
 read_reply(+Status, +ContentType, +In, -Close, -Row)
  201read_reply(200, ContentType, In, Close, Row) :-
  202    !,
  203    read_reply(ContentType, In, Close, Row).
  204read_reply(Status, _ContentType, In, _Close, _Row) :-
  205    call_cleanup(read_string(In, _, Reply),
  206                 close(In, [force(true)])),
  207    throw(error(sparql_error(Status, Reply), _)).
  208
  209read_reply('application/rdf+xml', In, _, Row) :-
  210    !,
  211    call_cleanup(load_rdf(stream(In), RDF), close(In)),
  212    member(Row, RDF).
  213read_reply(MIME, In, _, Row) :-
  214    turtle_media_type(MIME),
  215    !,
  216    call_cleanup(rdf_read_turtle(stream(In), RDF, []), close(In)),
  217    member(Row, RDF).
  218read_reply(MIME, In, VarNames, Row) :-
  219    sparql_result_mime(MIME),
  220    !,
  221    call_cleanup(sparql_read_xml_result(stream(In), Result),
  222                 close(In)),
  223    varnames(Result, VarNames),
  224    xml_result(Result, Row).
  225read_reply(MIME, In, VarNames, Row) :-
  226    json_result_mime(MIME),
  227    !,
  228    call_cleanup(sparql_read_json_result(stream(In), Result),
  229                 close(In)),
  230    (   Result = select(VarNames, Rows)
  231    ->  member(Row, Rows)
  232    ;   Result = ask(True)
  233    ->  Row = True,
  234        VarNames = []
  235    ).
  236read_reply(Type, In, _, _) :-
  237    read_stream_to_codes(In, Codes),
  238    string_codes(Reply, Codes),
  239    close(In),
  240    throw(error(domain_error(sparql_result_document, Type),
  241                context(_, Reply))).
  242
  243turtle_media_type('application/x-turtle').
  244turtle_media_type('application/turtle').
  245turtle_media_type('application/n-triples').
  246turtle_media_type('text/rdf+n3').
  247turtle_media_type('text/turtle').
  248
  249sparql_result_mime('application/sparql-results+xml'). % official
  250sparql_result_mime('application/sparql-result+xml').
  251
  252json_result_mime('application/sparql-results+json').
  253
  254
  255plain_content_type(Type, Plain) :-
  256    sub_atom(Type, B, _, _, (;)),
  257    !,
  258    sub_string(Type, 0, B, _, Main),
  259    normalize_space(atom(Plain), Main).
  260plain_content_type(Type, Type).
  261
  262xml_result(ask(Bool), Result) :-
  263    !,
  264    Result = Bool.
  265xml_result(select(_VarNames, Rows), Result) :-
  266    member(Result, Rows).
  267
  268varnames(ask(_), _).
  269varnames(select(VarTerm, _Rows), VarNames) :-
  270    VarTerm =.. [_|VarNames].
  271
  272
  273                 /*******************************
  274                 *            SETTINGS          *
  275                 *******************************/
  276
  277:- dynamic
  278    sparql_setting/1.  279
  280sparql_setting(scheme(http)).
  281sparql_setting(path('/sparql/')).
  282
  283sparql_param(Param, Options0, Options) :-
  284    select_option(Param, Options0, Options),
  285    !.
  286sparql_param(Param, Options, Options) :-
  287    sparql_setting(Param),
  288    !.
  289sparql_param(Param, Options, Options) :-
  290    functor(Param, Name, _),
  291    throw(error(existence_error(option, Name), _)).
  292
  293sparql_port(_Scheme, Port, Options0, Options) :-
  294    select_option(port(Port), Options0, Options),
  295    !.
  296sparql_port(_Scheme, Port, Options, Options) :-
  297    sparql_setting(port(Port)),
  298    !.
  299sparql_port(http, 80, Options, Options) :-
  300    !.
  301sparql_port(https, 443, Options, Options) :-
  302    !.
 sparql_set_server(+OptionOrList)
Set sparql server default options. Provided defaults are: host, port and repository. For example:
    sparql_set_server([ host(localhost),
                        port(8080)
                        path(world)
                      ])

The default for port is 80 and path is /sparql/.

  319sparql_set_server([]) :- !.
  320sparql_set_server([H|T]) :-
  321    !,
  322    sparql_set_server(H),
  323    sparql_set_server(T).
  324sparql_set_server(Term) :-
  325    functor(Term, Name, Arity),
  326    functor(Unbound, Name, Arity),
  327    retractall(sparql_setting(Unbound)),
  328    assert(sparql_setting(Term)).
  329
  330
  331                 /*******************************
  332                 *             RESULT           *
  333                 *******************************/
  334
  335ns(sparql, 'http://www.w3.org/2005/sparql-results#').
  336
  337/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  338Read    the    SPARQL    XML    result     format    as    defined    in
  339http://www.w3.org/TR/rdf-sparql-XMLres/, version 6 April 2006.
  340- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  341
  342                 /*******************************
  343                 *        MACRO HANDLING        *
  344                 *******************************/
  345
  346%       substitute 'sparql' by the namespace   defined  above for better
  347%       readability of the remainder of the code.
  348
  349term_subst(V, _, _, V) :-
  350    var(V),
  351    !.
  352term_subst(F, F, T, T) :- !.
  353term_subst(C, F, T, C2) :-
  354    compound(C),
  355    !,
  356    functor(C, Name, Arity),
  357    functor(C2, Name, Arity),
  358    term_subst(0, Arity, C, F, T, C2).
  359term_subst(T, _, _, T).
  360
  361term_subst(A, A, _, _, _, _) :- !.
  362term_subst(I0, Arity, C0, F, T, C) :-
  363    I is I0 + 1,
  364    arg(I, C0, A0),
  365    term_subst(A0, F, T, A),
  366    arg(I, C, A),
  367    term_subst(I, Arity, C0, F, T, C).
  368
  369term_expansion(T0, T) :-
  370    ns(sparql, NS),
  371    term_subst(T0, sparql, NS, T).
  372
  373
  374                 /*******************************
  375                 *           READING            *
  376                 *******************************/
 sparql_read_xml_result(+Input, -Result)
Specs from http://www.w3.org/TR/rdf-sparql-XMLres/. The returned Result term is of the format:
select(VarNames, Rows)
Where VarNames is a term v(Name, ...) and Rows is a list of row(....) containing the column values in the same order as the variable names.
ask(Bool)
Where Bool is either true or false
  391:- thread_local
  392    bnode_map/2.  393
  394sparql_read_xml_result(Input, Result) :-
  395    load_structure(Input, DOM,
  396                   [ dialect(xmlns),
  397                     space(remove)
  398                   ]),
  399    call_cleanup(dom_to_result(DOM, Result),
  400                 retractall(bnode_map(_,_))).
  401
  402dom_to_result(DOM, Result) :-
  403    (   sub_element(DOM, sparql:head, _HAtt, Content)
  404    ->  variables(Content, Vars)
  405    ;   Vars = []
  406    ),
  407    (   Vars == [],
  408        sub_element(DOM, sparql:boolean, _, [TrueFalse])
  409    ->  Result = ask(TrueFalse)
  410    ;   VarTerm =.. [v|Vars],
  411        Result = select(VarTerm, Rows),
  412        sub_element(DOM, sparql:results, _RAtt, RContent)
  413    ->  rows(RContent, Vars, Rows)
  414    ),
  415    !.                                   % Guarantee finalization
 variables(+DOM, -Varnames)
Deals with <variable name=Name>. Head also may contain <link href="..."/>. This points to additional meta-data. Not really clear what we can do with that.
  423variables([], []).
  424variables([element(sparql:variable, Att, [])|T0], [Name|T]) :-
  425    !,
  426    memberchk(name=Name, Att),
  427    variables(T0, T).
  428variables([element(sparql:link, _, _)|T0], T) :-
  429    variables(T0, T).
  430
  431
  432rows([], _, []).
  433rows([R|T0], Vars, [Row|T]) :-
  434    row_values(Vars, R, Values),
  435    Row =.. [row|Values],
  436    rows(T0, Vars, T).
  437
  438row_values([], _, []).
  439row_values([Var|VarT], DOM, [Value|ValueT]) :-
  440    (   sub_element(DOM, sparql:binding, Att, Content),
  441        memberchk(name=Var, Att)
  442    ->  value(Content, Value)
  443    ;   Value = '$null$'
  444    ),
  445    row_values(VarT, DOM, ValueT).
  446
  447value([element(sparql:literal, Att, Content)], literal(Lit)) :-
  448    !,
  449    lit_value(Content, Value),
  450    (   memberchk(datatype=Type, Att)
  451    ->  Lit = type(Type, Value)
  452    ;   memberchk(xml:lang=Lang, Att)
  453    ->  Lit = lang(Lang, Value)
  454    ;   Lit = Value
  455    ).
  456value([element(sparql:uri, [], [URI])], URI) :- !.
  457value([element(sparql:bnode, [], [NodeID])], URI) :-
  458    !,
  459    bnode(NodeID, URI).
  460value([element(sparql:unbound, [], [])], '$null$').
  461
  462
  463lit_value([], '').
  464lit_value([Value], Value).
 sub_element(+DOM, +Name, -Atttribs, -Content)
  469sub_element(element(Name, Att, Content), Name, Att, Content).
  470sub_element(element(_, _, List), Name, Att, Content) :-
  471    sub_element(List, Name, Att, Content).
  472sub_element([H|T], Name, Att, Content) :-
  473    (   sub_element(H, Name, Att, Content)
  474    ;   sub_element(T, Name, Att, Content)
  475    ).
  476
  477
  478bnode(Name, URI) :-
  479    bnode_map(Name, URI),
  480    !.
  481bnode(Name, URI) :-
  482    gensym('__bnode', URI0),
  483    assertz(bnode_map(Name, URI0)),
  484    URI = URI0.
 sparql_read_json_result(+Input, -Result) is det
The returned Result term is of the format:
select(VarNames, Rows)
Where VarNames is a term v(Name, ...) and Rows is a list of row(....) containing the column values in the same order as the variable names.
ask(Bool)
Where Bool is either true or false
See also
- http://www.w3.org/TR/rdf-sparql-json-res/
  501sparql_read_json_result(Input, Result) :-
  502    setup_call_cleanup(
  503        open_input(Input, In, Close),
  504        read_json_result(In, Result),
  505        close_input(Close)).
  506
  507open_input(stream(In), In, Close) :-
  508    !,
  509    encoding(In, utf8, Close).
  510open_input(In, In, Close) :-
  511    is_stream(In),
  512    !,
  513    encoding(In, utf8, Close).
  514open_input(File, In, close(In)) :-
  515    open(File, read, In, [encoding(utf8)]).
  516
  517encoding(In, Encoding, Close) :-
  518    stream_property(In, encoding(Old)),
  519    (   Encoding == Old
  520    ->  Close = true
  521    ;   set_stream(In, encoding(Encoding)),
  522        Close = set_stream(In, Encoding, Old)
  523    ).
  524
  525close_input(close(In)) :-
  526    !,
  527    retractall(bnode_map(_,_)),
  528    close(In).
  529close_input(_) :-
  530    retractall(bnode_map(_,_)).
  531
  532read_json_result(In, Result) :-
  533    json_read(In, JSON),
  534    json_to_result(JSON, Result).
  535
  536json_to_result(json([ head    = json(Head),
  537                      results = json(Body)
  538                    ]),
  539               select(Vars, Rows)) :-
  540    memberchk(vars=VarList, Head),
  541    Vars =.. [v|VarList],
  542    memberchk(bindings=Bindings, Body),
  543    !,
  544    maplist(json_row(VarList), Bindings, Rows).
  545json_to_result(json(JSon), ask(Boolean)) :-
  546    memberchk(boolean = @(Boolean), JSon).
  547
  548
  549json_row(Vars, json(Columns), Row) :-
  550    maplist(json_cell, Vars, Columns, Values),
  551    !,
  552    Row =.. [row|Values].
  553json_row(Vars, json(Columns), Row) :-
  554    maplist(json_cell_or_null(Columns), Vars, Values),
  555    Row =.. [row|Values].
  556
  557json_cell(Var, Var=json(JValue), Value) :-
  558    memberchk(type=Type, JValue),
  559    jvalue(Type, JValue, Value).
  560
  561json_cell_or_null(Columns, Var, Value) :-
  562    memberchk(Var=json(JValue), Columns),
  563    !,
  564    memberchk(type=Type, JValue),
  565    jvalue(Type, JValue, Value).
  566json_cell_or_null(_, _, '$null$').
  567
  568jvalue(uri, JValue, URI) :-
  569    memberchk(value=URI, JValue).
  570jvalue(literal, JValue, literal(Literal)) :-
  571    memberchk(value=Value, JValue),
  572    (   memberchk('xml:lang'=Lang, JValue)
  573    ->  Literal = lang(Lang, Value)
  574    ;   memberchk('datatype'=Type, JValue)
  575    ->  Literal = type(Type, Value)
  576    ;   Literal = Value
  577    ).
  578jvalue('typed-literal', JValue, literal(type(Type, Value))) :-
  579    memberchk(value=Value, JValue),
  580    memberchk('datatype'=Type, JValue).
  581jvalue(bnode, JValue, URI) :-
  582    memberchk(value=NodeID, JValue),
  583    bnode(NodeID, URI)