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)  2003-2023, University of Amsterdam
    7                              VU University Amsterdam
    8                              CWI, Amsterdam
    9                              SWI-Prolog Solutions b.v.
   10    All rights reserved.
   11
   12    Redistribution and use in source and binary forms, with or without
   13    modification, are permitted provided that the following conditions
   14    are met:
   15
   16    1. Redistributions of source code must retain the above copyright
   17       notice, this list of conditions and the following disclaimer.
   18
   19    2. Redistributions in binary form must reproduce the above copyright
   20       notice, this list of conditions and the following disclaimer in
   21       the documentation and/or other materials provided with the
   22       distribution.
   23
   24    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   25    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   26    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   27    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   28    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   29    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   30    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   31    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   32    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   33    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   34    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   35    POSSIBILITY OF SUCH DAMAGE.
   36*/
   37
   38:- module(rdf_db,
   39          [ rdf_version/1,              % -Version
   40
   41            rdf/3,                      % ?Subject, ?Predicate, ?Object
   42            rdf/4,                      % ?Subject, ?Predicate, ?Object, ?DB
   43            rdf_has/3,                  % ?Subject, +Pred, ?Obj
   44            rdf_has/4,                  % ?Subject, +Pred, ?Obj, -RealPred
   45            rdf_reachable/3,            % ?Subject, +Pred, ?Object
   46            rdf_reachable/5,            % ?Subject, +Pred, ?Object, +MaxD, ?D
   47            rdf_resource/1,             % ?Resource
   48            rdf_subject/1,              % ?Subject
   49
   50            rdf_member_property/2,      % ?Property, ?Index
   51
   52            rdf_assert/3,               % +Subject, +Predicate, +Object
   53            rdf_assert/4,               % +Subject, +Predicate, +Object, +DB
   54            rdf_retractall/3,           % ?Subject, ?Predicate, ?Object
   55            rdf_retractall/4,           % ?Subject, ?Predicate, ?Object, +DB
   56            rdf_update/4,               % +Subject, +Predicate, +Object, +Act
   57            rdf_update/5,               % +Subject, +Predicate, +Object, +Src, +Act
   58            rdf_set_predicate/2,        % +Predicate, +Property
   59            rdf_predicate_property/2,   % +Predicate, ?Property
   60            rdf_current_predicate/1,    % -Predicate
   61            rdf_current_literal/1,      % -Literal
   62            rdf_transaction/1,          % :Goal
   63            rdf_transaction/2,          % :Goal, +Id
   64            rdf_transaction/3,          % :Goal, +Id, +Options
   65            rdf_active_transaction/1,   % ?Id
   66
   67            rdf_monitor/2,              % :Goal, +Options
   68
   69            rdf_save_db/1,              % +File
   70            rdf_save_db/2,              % +File, +DB
   71            rdf_load_db/1,              % +File
   72            rdf_reset_db/0,
   73
   74            rdf_node/1,                 % -Id
   75            rdf_bnode/1,                % -Id
   76            rdf_is_bnode/1,             % +Id
   77
   78            rdf_is_resource/1,          % +Term
   79            rdf_is_literal/1,           % +Term
   80            rdf_literal_value/2,        % +Term, -Value
   81
   82            rdf_load/1,                 % +File
   83            rdf_load/2,                 % +File, +Options
   84            rdf_save/1,                 % +File
   85            rdf_save/2,                 % +File, +Options
   86            rdf_unload/1,               % +File
   87            rdf_unload_graph/1,         % +Graph
   88
   89            rdf_md5/2,                  % +DB, -MD5
   90            rdf_atom_md5/3,             % +Text, +Times, -MD5
   91
   92            rdf_create_graph/1,         % ?Graph
   93            rdf_graph_property/2,       % ?Graph, ?Property
   94            rdf_set_graph/2,            % +Graph, +Property
   95            rdf_graph/1,                % ?Graph
   96            rdf_source/1,               % ?File
   97            rdf_source/2,               % ?DB, ?SourceURL
   98            rdf_make/0,                 % Reload modified databases
   99            rdf_gc/0,                   % Garbage collection
  100
  101            rdf_source_location/2,      % +Subject, -Source
  102            rdf_statistics/1,           % -Key
  103            rdf_set/1,                  % +Term
  104            rdf_generation/1,           % -Generation
  105            rdf_snapshot/1,             % -Snapshot
  106            rdf_delete_snapshot/1,      % +Snapshot
  107            rdf_current_snapshot/1,     % +Snapshot
  108            rdf_estimate_complexity/4,  % +S,+P,+O,-Count
  109
  110            rdf_save_subject/3,         % +Stream, +Subject, +DB
  111            rdf_save_header/2,          % +Out, +Options
  112            rdf_save_footer/1,          % +Out
  113
  114            rdf_equal/2,                % ?Resource, ?Resource
  115            lang_equal/2,               % +Lang1, +Lang2
  116            lang_matches/2,             % +Lang, +Pattern
  117
  118            rdf_prefix/2,               % :Alias, +URI
  119            rdf_current_prefix/2,       % :Alias, ?URI
  120            rdf_register_prefix/2,      % +Alias, +URI
  121            rdf_register_prefix/3,      % +Alias, +URI, +Options
  122            rdf_unregister_prefix/1,    % +Alias
  123            rdf_current_ns/2,           % :Alias, ?URI
  124            rdf_register_ns/2,          % +Alias, +URI
  125            rdf_register_ns/3,          % +Alias, +URI, +Options
  126            rdf_global_id/2,            % ?NS:Name, :Global
  127            rdf_global_object/2,        % +Object, :NSExpandedObject
  128            rdf_global_term/2,          % +Term, :WithExpandedNS
  129
  130            rdf_compare/3,              % -Dif, +Object1, +Object2
  131            rdf_match_label/3,          % +How, +String, +Label
  132            rdf_split_url/3,            % ?Base, ?Local, ?URL
  133            rdf_url_namespace/2,        % +URL, ?Base
  134
  135            rdf_warm_indexes/0,
  136            rdf_warm_indexes/1,         % +Indexed
  137            rdf_update_duplicates/0,
  138
  139            rdf_debug/1,                % Set verbosity
  140
  141            rdf_new_literal_map/1,      % -Handle
  142            rdf_destroy_literal_map/1,  % +Handle
  143            rdf_reset_literal_map/1,    % +Handle
  144            rdf_insert_literal_map/3,   % +Handle, +Key, +Literal
  145            rdf_insert_literal_map/4,   % +Handle, +Key, +Literal, -NewKeys
  146            rdf_delete_literal_map/3,   % +Handle, +Key, +Literal
  147            rdf_delete_literal_map/2,   % +Handle, +Key
  148            rdf_find_literal_map/3,     % +Handle, +KeyList, -Literals
  149            rdf_keys_in_literal_map/3,  % +Handle, +Spec, -Keys
  150            rdf_statistics_literal_map/2, % +Handle, +Name(-Arg...)
  151
  152            rdf_graph_prefixes/2,       % ?Graph, -Prefixes
  153            rdf_graph_prefixes/3,       % ?Graph, -Prefixes, :Filter
  154
  155            (rdf_meta)/1,               % +Heads
  156            op(1150, fx, (rdf_meta))
  157          ]).  158:- use_module(library(semweb/rdf_prefixes),
  159              [ (rdf_meta)/1,
  160                register_file_prefixes/1,
  161                rdf_global_id/2,
  162                rdf_register_ns/2,
  163                                        % re-exported predicates
  164                rdf_global_object/2,
  165                rdf_current_ns/2,
  166                rdf_prefix/2,
  167                rdf_global_term/2,
  168                rdf_register_ns/3,
  169                rdf_register_prefix/3,
  170                rdf_register_prefix/2,
  171                rdf_current_prefix/2,
  172                rdf_unregister_prefix/1
  173              ]).  174
  175:- autoload(library(apply),[maplist/2,maplist/3]).  176:- use_module(library(debug),[debug/3,assertion/1]).  177:- autoload(library(error),[must_be/2,existence_error/2]).  178:- autoload(library(gensym),[gensym/2,reset_gensym/1]).  179:- autoload(library(lists),
  180	    [member/2,flatten/2,list_to_set/2,append/3,select/3]).  181:- autoload(library(memfile),
  182	    [atom_to_memory_file/2,open_memory_file/4]).  183:- autoload(library(option),
  184	    [option/2,option/3,merge_options/3,meta_options/3]).  185:- autoload(library(rdf),[process_rdf/3]).  186:- autoload(library(sgml),
  187	    [ load_structure/3,
  188	      xml_quote_attribute/3,
  189	      xml_name/1,
  190	      xml_quote_cdata/3,
  191	      xml_is_dom/1,
  192	      iri_xml_namespace/3,
  193	      iri_xml_namespace/2
  194	    ]).  195:- autoload(library(sgml_write),[xml_write/3]).  196:- autoload(library(uri),
  197	    [ uri_file_name/2,
  198	      uri_is_global/1,
  199	      uri_normalized/2,
  200	      uri_components/2,
  201	      uri_data/3,
  202	      uri_data/4
  203	    ]).  204:- autoload(library(xsdp_types),[xsdp_numeric_uri/2]).  205:- autoload(library(semweb/rdf_cache),[rdf_cache_file/3]).  206
  207:- if(exists_source(library(thread))).  208:- autoload(library(thread), [concurrent/3]).  209:- endif.  210
  211:- use_foreign_library(foreign(rdf_db)).  212:- public rdf_print_predicate_cloud/2.  % print matrix of reachable predicates
  213
  214:- meta_predicate
  215    rdf_transaction(0),
  216    rdf_transaction(0, +),
  217    rdf_transaction(0, +, +),
  218    rdf_monitor(1, +),
  219    rdf_save(+, :),
  220    rdf_load(+, :).  221
  222:- predicate_options(rdf_graph_prefixes/3, 3,
  223                     [ expand(callable+4),
  224                       filter(callable+3),
  225                       get_prefix(callable+2),
  226                       min_count(nonneg)
  227                     ]).  228:- predicate_options(rdf_load/2, 2,
  229                     [ base_uri(atom),
  230                       blank_nodes(oneof([share,noshare])),
  231                       cache(boolean),
  232                       concurrent(positive_integer),
  233                       db(atom),
  234                       format(oneof([xml,triples,turtle,trig,nquads,ntriples])),
  235                       graph(atom),
  236                       multifile(boolean),
  237                       if(oneof([true,changed,not_loaded])),
  238                       modified(-float),
  239                       prefixes(-list),
  240                       silent(boolean),
  241                       register_namespaces(boolean)
  242                     ]).  243:- predicate_options(rdf_save/2, 2,
  244                     [ graph(atom),
  245                       db(atom),
  246                       anon(boolean),
  247                       base_uri(atom),
  248                       write_xml_base(boolean),
  249                       convert_typed_literal(callable),
  250                       encoding(encoding),
  251                       document_language(atom),
  252                       namespaces(list(atom)),
  253                       xml_attributes(boolean),
  254                       inline(boolean)
  255                     ]).  256:- predicate_options(rdf_save_header/2, 2,
  257                     [ graph(atom),
  258                       db(atom),
  259                       namespaces(list(atom))
  260                     ]).  261:- predicate_options(rdf_save_subject/3, 3,
  262                     [ graph(atom),
  263                       base_uri(atom),
  264                       convert_typed_literal(callable),
  265                       document_language(atom)
  266                     ]).  267:- predicate_options(rdf_transaction/3, 3,
  268                     [ snapshot(any)
  269                     ]).  270
  271:- discontiguous
  272    term_expansion/2.

Core RDF database

The file library(semweb/rdf_db) provides the core of the SWI-Prolog RDF store.

deprecated
-
New applications should use library(semweb/rdf11), which provides a much more intuitive API to the RDF store, notably for handling literals. The library(semweb/rdf11) runs currently on top of this library and both can run side-by-side in the same application. Terms retrieved from the database however have a different shape and can not be exchanged without precautions. */
  288		 /*******************************
  289		 *            PREFIXES		*
  290		 *******************************/
  291
  292% the ns/2 predicate is historically defined  in this module. We'll keep
  293% that for compatibility reasons.
  294
  295:- multifile ns/2.  296:- dynamic   ns/2.                      % ID, URL
  297
  298:- multifile
  299    rdf_prefixes:rdf_empty_prefix_cache/2.  300
  301rdf_prefixes:rdf_empty_prefix_cache(_Prefix, _IRI) :-
  302    rdf_empty_prefix_cache.
  303
  304:- rdf_meta
  305    rdf(r,r,o),
  306    rdf_has(r,r,o,r),
  307    rdf_has(r,r,o),
  308    rdf_assert(r,r,o),
  309    rdf_retractall(r,r,o),
  310    rdf(r,r,o,?),
  311    rdf_assert(r,r,o,+),
  312    rdf_retractall(r,r,o,?),
  313    rdf_reachable(r,r,o),
  314    rdf_reachable(r,r,o,+,?),
  315    rdf_update(r,r,o,t),
  316    rdf_update(r,r,o,+,t),
  317    rdf_equal(o,o),
  318    rdf_source_location(r,-),
  319    rdf_resource(r),
  320    rdf_subject(r),
  321    rdf_create_graph(r),
  322    rdf_graph(r),
  323    rdf_graph_property(r,?),
  324    rdf_set_graph(r,+),
  325    rdf_unload_graph(r),
  326    rdf_set_predicate(r, t),
  327    rdf_predicate_property(r, -),
  328    rdf_estimate_complexity(r,r,r,-),
  329    rdf_print_predicate_cloud(r,+).
 rdf_equal(?Resource1, ?Resource2)
Simple equality test to exploit goal-expansion.
  335rdf_equal(Resource, Resource).
 lang_equal(+Lang1, +Lang2) is semidet
True if two RFC language specifiers denote the same language
See also
- lang_matches/2.
  343lang_equal(Lang, Lang) :- !.
  344lang_equal(Lang1, Lang2) :-
  345    downcase_atom(Lang1, LangCannon),
  346    downcase_atom(Lang2, LangCannon).
 lang_matches(+Lang, +Pattern) is semidet
True if Lang matches Pattern. This implements XML language matching conform RFC 4647. Both Lang and Pattern are dash-separated strings of identifiers or (for Pattern) the wildcard *. Identifiers are matched case-insensitive and a * matches any number of identifiers. A short pattern is the same as *.
  358                 /*******************************
  359                 *     BASIC TRIPLE QUERIES     *
  360                 *******************************/
 rdf(?Subject, ?Predicate, ?Object) is nondet
Elementary query for triples. Subject and Predicate are atoms representing the fully qualified URL of the resource. Object is either an atom representing a resource or literal(Value) if the object is a literal value. If a value of the form NameSpaceID:LocalName is provided it is expanded to a ground atom using expand_goal/2. This implies you can use this construct in compiled code without paying a performance penalty. Literal values take one of the following forms:
Atom
If the value is a simple atom it is the textual representation of a string literal without explicit type or language qualifier.
lang(LangID, Atom)
Atom represents the text of a string literal qualified with the given language.
type(TypeID, Value)
Used for attributes qualified using the rdf:datatype TypeID. The Value is either the textual representation or a natural Prolog representation. See the option convert_typed_literal(:Convertor) of the parser. The storage layer provides efficient handling of atoms, integers (64-bit) and floats (native C-doubles). All other data is represented as a Prolog record.

For literal querying purposes, Object can be of the form literal(+Query, -Value), where Query is one of the terms below. If the Query takes a literal argument and the value has a numeric type numerical comparison is performed.

plain(+Text)
Perform exact match and demand the language or type qualifiers to match. This query is fully indexed.
icase(+Text)
Perform a full but case-insensitive match. This query is fully indexed.
exact(+Text)
Same as icase(Text). Backward compatibility.
substring(+Text)
Match any literal that contains Text as a case-insensitive substring. The query is not indexed on Object.
word(+Text)
Match any literal that contains Text delimited by a non alpha-numeric character, the start or end of the string. The query is not indexed on Object.
prefix(+Text)
Match any literal that starts with Text. This call is intended for completion. The query is indexed using the skip list of literals.
ge(+Literal)
Match any literal that is equal or larger than Literal in the ordered set of literals.
gt(+Literal)
Match any literal that is larger than Literal in the ordered set of literals.
eq(+Literal)
Match any literal that is equal to Literal in the ordered set of literals.
le(+Literal)
Match any literal that is equal or smaller than Literal in the ordered set of literals.
lt(+Literal)
Match any literal that is smaller than Literal in the ordered set of literals.
between(+Literal1, +Literal2)
Match any literal that is between Literal1 and Literal2 in the ordered set of literals. This may include both Literal1 and Literal2.
like(+Pattern)
Match any literal that matches Pattern case insensitively, where the `*' character in Pattern matches zero or more characters.

Backtracking never returns duplicate triples. Duplicates can be retrieved using rdf/4. The predicate rdf/3 raises a type-error if called with improper arguments. If rdf/3 is called with a term literal(_) as Subject or Predicate object it fails silently. This allows for graph matching goals like rdf(S,P,O),rdf(O,P2,O2) to proceed without errors.

 rdf(?Subject, ?Predicate, ?Object, ?Source) is nondet
As rdf/3 but in addition query the graph to which the triple belongs. Unlike rdf/3, this predicate does not remove duplicates from the result set.
Arguments:
Source- is a term Graph:Line. If Source is instatiated, passing an atom is the same as passing Atom:_.
 rdf_has(?Subject, +Predicate, ?Object) is nondet
Succeeds if the triple rdf(Subject, Predicate, Object) is true exploiting the rdfs:subPropertyOf predicate as well as inverse predicates declared using rdf_set_predicate/2 with the inverse_of property.
 rdf_has(?Subject, +Predicate, ?Object, -RealPredicate) is nondet
Same as rdf_has/3, but RealPredicate is unified to the actual predicate that makes this relation true. RealPredicate must be Predicate or an rdfs:subPropertyOf Predicate. If an inverse match is found, RealPredicate is the term inverse_of(Pred).
 rdf_reachable(?Subject, +Predicate, ?Object) is nondet
Is true if Object can be reached from Subject following the transitive predicate Predicate or a sub-property thereof, while repecting the symetric(true) or inverse_of(P2) properties.

If used with either Subject or Object unbound, it first returns the origin, followed by the reachable nodes in breadth-first search-order. The implementation internally looks one solution ahead and succeeds deterministically on the last solution. This predicate never generates the same node twice and is robust against cycles in the transitive relation.

With all arguments instantiated, it succeeds deterministically if a path can be found from Subject to Object. Searching starts at Subject, assuming the branching factor is normally lower. A call with both Subject and Object unbound raises an instantiation error. The following example generates all subclasses of rdfs:Resource:

?- rdf_reachable(X, rdfs:subClassOf, rdfs:'Resource').
X = 'http://www.w3.org/2000/01/rdf-schema#Resource' ;
X = 'http://www.w3.org/2000/01/rdf-schema#Class' ;
X = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#Property' ;
...
 rdf_reachable(?Subject, +Predicate, ?Object, +MaxD, -D) is nondet
Same as rdf_reachable/3, but in addition, MaxD limits the number of edges expanded and D is unified with the `distance' between Subject and Object. Distance 0 means Subject and Object are the same resource. MaxD can be the constant infinite to impose no distance-limit.
 rdf_subject(?Resource) is nondet
True if Resource appears as a subject. This query respects the visibility rules implied by the logical update view.
See also
- rdf_resource/1.
  526rdf_subject(Resource) :-
  527    rdf_resource(Resource),
  528    ( rdf(Resource, _, _) -> true ).
 rdf_resource(?Resource) is nondet
True when Resource is a resource used as a subject or object in a triple.

This predicate is primarily intended as a way to process all resources without processing resources twice. The user must be aware that some of the returned resources may not appear in any visible triple.

  541                 /*******************************
  542                 *     TRIPLE MODIFICATIONS     *
  543                 *******************************/
 rdf_assert(+Subject, +Predicate, +Object) is det
Assert a new triple into the database. This is equivalent to rdf_assert/4 using Graph user. Subject and Predicate are resources. Object is either a resource or a term literal(Value). See rdf/3 for an explanation of Value for typed and language qualified literals. All arguments are subject to name-space expansion. Complete duplicates (including the same graph and `line' and with a compatible `lifespan') are not added to the database.
 rdf_assert(+Subject, +Predicate, +Object, +Graph) is det
As rdf_assert/3, adding the predicate to the indicated named graph.
Arguments:
Graph- is either the name of a graph (an atom) or a term Graph:Line, where Line is an integer that denotes a line number.
 rdf_retractall(?Subject, ?Predicate, ?Object) is det
Remove all matching triples from the database. As rdf_retractall/4 using an unbound graph. See also rdf_retractall/4 and rdf_unload/1.
 rdf_retractall(?Subject, ?Predicate, ?Object, ?Graph) is det
As rdf_retractall/3, also matching Graph. This is particulary useful to remove all triples coming from a loaded file. See also rdf_unload/1.
 rdf_update(+Subject, +Predicate, +Object, ++Action) is det
 rdf_update(+Subject, +Predicate, +Object, +Graph, ++Action) is det
Replaces one of the three (four) fields on the matching triples depending on Action:
subject(Resource)
Changes the first field of the triple.
predicate(Resource)
Changes the second field of the triple.
object(Object)
Changes the last field of the triple to the given resource or literal(Value).
graph(Graph)
Moves the triple from its current named graph to Graph. This only works with rdf_update/5 and throws an error when used with rdf_update/4.
  595                 /*******************************
  596                 *          COLLECTIONS         *
  597                 *******************************/
 rdf_member_property(?Prop, ?Index)
Deal with the rdf:_1, ... properties.
  603term_expansion(member_prefix(x),
  604               member_prefix(Prefix)) :-
  605    rdf_db:ns(rdf, NS),
  606    atom_concat(NS, '_', Prefix).
  607member_prefix(x).
  608
  609rdf_member_property(P, N) :-
  610    integer(N),
  611    !,
  612    member_prefix(Prefix),
  613    atom_concat(Prefix, N, P).
  614rdf_member_property(P, N) :-
  615    member_prefix(Prefix),
  616    atom_concat(Prefix, Sub, P),
  617    atom_number(Sub, N).
  618
  619
  620                 /*******************************
  621                 *      ANONYMOUS SUBJECTS      *
  622                 *******************************/
 rdf_node(-Id)
Generate a unique blank node identifier for a subject.
deprecated
- New code should use rdf_bnode/1.
  630rdf_node(Resource) :-
  631    rdf_bnode(Resource).
 rdf_bnode(-Id)
Generate a unique anonymous identifier for a subject.
  637rdf_bnode(Value) :-
  638    repeat,
  639    gensym('_:genid', Value),
  640    \+ rdf(Value, _, _),
  641    \+ rdf(_, _, Value),
  642    \+ rdf(_, Value, _),
  643    !.
  644
  645
  646
  647                 /*******************************
  648                 *             TYPES            *
  649                 *******************************/
 rdf_is_bnode(+Id)
Tests if a resource is a blank node (i.e. is an anonymous resource). A blank node is represented as an atom that starts with _:. For backward compatibility reason, __ is also considered to be a blank node.
See also
- rdf_bnode/1.
 rdf_is_resource(@Term) is semidet
True if Term is an RDF resource. Note that this is merely a type-test; it does not mean this resource is involved in any triple. Blank nodes are also considered resources.
See also
- rdf_is_bnode/1
  668rdf_is_resource(Term) :-
  669    atom(Term).
 rdf_is_literal(@Term) is semidet
True if Term is an RDF literal object. Currently only checks for groundness and the literal functor.
  676rdf_is_literal(literal(Value)) :-
  677    ground(Value).
  678
  679                 /*******************************
  680                 *             LITERALS         *
  681                 *******************************/
 rdf_current_literal(-Literal) is nondet
True when Literal is a currently known literal. Enumerates each unique literal exactly once. Note that it is possible that the literal only appears in already deleted triples. Deleted triples may be locked due to active queries, transactions or snapshots or may not yet be reclaimed by the garbage collector.
 rdf_literal_value(+Literal, -Value) is semidet
True when value is the appropriate Prolog representation of Literal in the RDF value space. Current mapping:
Plain literalsAtom
Language tagged literalAtom holding plain text
xsd:stringAtom
rdf:XMLLiteralXML DOM Tree
Numeric XSD typeNumber
To be done
- Well, this is the long-term idea.
- Add mode (-,+)
  706:- rdf_meta
  707    rdf_literal_value(o, -),
  708    typed_value(r, +, -),
  709    numeric_value(r, +, -).  710
  711rdf_literal_value(literal(String), Value) :-
  712    atom(String),
  713    !,
  714    Value = String.
  715rdf_literal_value(literal(lang(_Lang, String)), String).
  716rdf_literal_value(literal(type(Type, String)), Value) :-
  717    typed_value(Type, String, Value).
  718
  719typed_value(Numeric, String, Value) :-
  720    xsdp_numeric_uri(Numeric, NumType),
  721    !,
  722    numeric_value(NumType, String, Value).
  723typed_value(xsd:string, String, String).
  724typed_value(rdf:'XMLLiteral', Value, DOM) :-
  725    (   atom(Value)
  726    ->  setup_call_cleanup(
  727            ( atom_to_memory_file(Value, MF),
  728              open_memory_file(MF, read, In, [free_on_close(true)])
  729            ),
  730            load_structure(stream(In), DOM, [dialect(xml)]),
  731            close(In))
  732    ;   DOM = Value
  733    ).
  734
  735numeric_value(xsd:integer, String, Value) :-
  736    atom_number(String, Value),
  737    integer(Value).
  738numeric_value(xsd:float, String, Value) :-
  739    atom_number(String, Number),
  740    Value is float(Number).
  741numeric_value(xsd:double, String, Value) :-
  742    atom_number(String, Number),
  743    Value is float(Number).
  744numeric_value(xsd:decimal, String, Value) :-
  745    atom_number(String, Value).
  746
  747
  748                 /*******************************
  749                 *            SOURCE            *
  750                 *******************************/
 rdf_source_location(+Subject, -Location) is nondet
True when triples for Subject are loaded from Location.
Arguments:
Location- is a term File:Line.
  758rdf_source_location(Subject, Source) :-
  759    findall(Source, rdf(Subject, _, _, Source), Sources),
  760    sort(Sources, Unique),
  761    member(Source, Unique).
  762
  763
  764                 /*******************************
  765                 *       GARBAGE COLLECT        *
  766                 *******************************/
 rdf_create_gc_thread
Create the garbage collection thread.
  772:- public
  773    rdf_create_gc_thread/0.  774
  775rdf_create_gc_thread :-
  776    thread_create(rdf_gc_loop, _,
  777                  [ alias('__rdf_GC')
  778                  ]).
 rdf_gc_loop
Take care of running the RDF garbage collection. This predicate is called from a thread started by creating the RDF DB.
  785rdf_gc_loop :-
  786    catch(rdf_gc_loop(0), E, recover_gc(E)).
  787
  788recover_gc('$aborted') :-
  789    !,
  790    thread_self(Me),
  791    thread_detach(Me).
  792recover_gc(Error) :-
  793    print_message(error, Error),
  794    rdf_gc_loop.
  795
  796rdf_gc_loop(CPU) :-
  797    repeat,
  798    (   consider_gc(CPU)
  799    ->  rdf_gc(CPU1),
  800        sleep(CPU1)
  801    ;   sleep(0.1)
  802    ),
  803    fail.
 rdf_gc(-CPU) is det
Run RDF GC one time. CPU is the amount of CPU time spent. We update this in Prolog because portable access to thread specific CPU is really hard in C.
  811rdf_gc(CPU) :-
  812    statistics(cputime, CPU0),
  813    (   rdf_gc_
  814    ->  statistics(cputime, CPU1),
  815        CPU is CPU1-CPU0,
  816        rdf_add_gc_time(CPU)
  817    ;   CPU = 0.0
  818    ).
 rdf_gc is det
Run the RDF-DB garbage collector until no garbage is left and all tables are fully optimized. Under normal operation a separate thread with identifier __rdf_GC performs garbage collection as long as it is considered `useful'.

Using rdf_gc/0 should only be needed to ensure a fully clean database for analysis purposes such as leak detection.

  830rdf_gc :-
  831    has_garbage,
  832    !,
  833    rdf_gc(_),
  834    rdf_gc.
  835rdf_gc.
 has_garbage is semidet
True if there is something to gain using GC.
  841has_garbage :-
  842    rdf_gc_info_(Info),
  843    has_garbage(Info),
  844    !.
  845
  846has_garbage(Info) :- arg(2, Info, Garbage),     Garbage > 0.
  847has_garbage(Info) :- arg(3, Info, Reindexed),   Reindexed > 0.
  848has_garbage(Info) :- arg(4, Info, Optimizable), Optimizable > 0.
 consider_gc(+CPU) is semidet
Arguments:
CPU- is the amount of CPU time spent in the most recent GC.
  855consider_gc(_CPU) :-
  856    (   rdf_gc_info_(gc_info(Triples,       % Total #triples in DB
  857                             Garbage,       % Garbage triples in DB
  858                             Reindexed,     % Reindexed & not reclaimed
  859                             Optimizable,   % Non-optimized tables
  860                             _KeepGen,      % Oldest active generation
  861                             _LastGCGen,    % Oldest active gen at last GC
  862                             _ReindexGen,
  863                             _LastGCReindexGen))
  864    ->  (   (Garbage+Reindexed) * 5 > Triples
  865        ;   Optimizable > 4
  866        )
  867    ;   print_message(error, rdf(invalid_gc_info)),
  868        sleep(10)
  869    ),
  870    !.
  871
  872
  873                 /*******************************
  874                 *           STATISTICS         *
  875                 *******************************/
 rdf_statistics(?KeyValue) is nondet
Obtain statistics on the RDF database. Defined statistics are:
graphs(-Count)
Number of named graphs.
triples(-Count)
Total number of triples in the database. This is the number of asserted triples minus the number of retracted ones. The number of visible triples in a particular context may be different due to visibility rules defined by the logical update view and transaction isolation.
resources(-Count)
Number of resources that appear as subject or object in a triple. See rdf_resource/1.
properties(-Count)
Number of current predicates. See rdf_current_predicate/1.
literals(-Count)
Number of current literals. See rdf_current_literal/1.
gc(GCCount, ReclaimedTriples, ReindexedTriples, Time)
Information about the garbage collector.
searched_nodes(-Count)
Number of nodes expanded by rdf_reachable/3 and rdf_reachable/5.
lookup(rdf(S,P,O,G),Count)
Number of queries that have been performed for this particular instantiation pattern. Each of S,P,O,G is either + or -. Fails in case the number of performed queries is zero.
hash_quality(rdf(S,P,O,G),Buckets,Quality,PendingResize)
Statistics on the index for this pattern. Indices are created lazily on the first relevant query.
triples_by_graph(Graph, Count)
This statistics is produced for each named graph. See triples for the interpretation of this value.
  921rdf_statistics(graphs(Count)) :-
  922    rdf_statistics_(graphs(Count)).
  923rdf_statistics(triples(Count)) :-
  924    rdf_statistics_(triples(Count)).
  925rdf_statistics(duplicates(Count)) :-
  926    rdf_statistics_(duplicates(Count)).
  927rdf_statistics(lingering(Count)) :-
  928    rdf_statistics_(lingering(Count)).
  929rdf_statistics(resources(Count)) :-
  930    rdf_statistics_(resources(Count)).
  931rdf_statistics(properties(Count)) :-
  932    rdf_statistics_(predicates(Count)).
  933rdf_statistics(literals(Count)) :-
  934    rdf_statistics_(literals(Count)).
  935rdf_statistics(gc(Count, Reclaimed, Reindexed, Time)) :-
  936    rdf_statistics_(gc(Count, Reclaimed, Reindexed, Time)).
  937rdf_statistics(searched_nodes(Count)) :-
  938    rdf_statistics_(searched_nodes(Count)).
  939rdf_statistics(lookup(Index, Count)) :-
  940    functor(Indexed, indexed, 16),
  941    rdf_statistics_(Indexed),
  942    index(Index, I),
  943    Arg is I + 1,
  944    arg(Arg, Indexed, Count),
  945    Count \== 0.
  946rdf_statistics(hash_quality(Index, Size, Quality,Optimize)) :-
  947    rdf_statistics_(hash_quality(List)),
  948    member(hash(Place,Size,Quality,Optimize), List),
  949    index(Index, Place).
  950rdf_statistics(triples_by_graph(Graph, Count)) :-
  951    rdf_graph_(Graph, Count).
  952
  953index(rdf(-,-,-,-), 0).
  954index(rdf(+,-,-,-), 1).
  955index(rdf(-,+,-,-), 2).
  956index(rdf(+,+,-,-), 3).
  957index(rdf(-,-,+,-), 4).
  958index(rdf(+,-,+,-), 5).
  959index(rdf(-,+,+,-), 6).
  960index(rdf(+,+,+,-), 7).
  961
  962index(rdf(-,-,-,+), 8).
  963index(rdf(+,-,-,+), 9).
  964index(rdf(-,+,-,+), 10).
  965index(rdf(+,+,-,+), 11).
  966index(rdf(-,-,+,+), 12).
  967index(rdf(+,-,+,+), 13).
  968index(rdf(-,+,+,+), 14).
  969index(rdf(+,+,+,+), 15).
  970
  971
  972                 /*******************************
  973                 *           PREDICATES         *
  974                 *******************************/
 rdf_current_predicate(?Predicate) is nondet
True when Predicate is a currently known predicate. Predicates are created if a triples is created that uses this predicate or a property of the predicate is set using rdf_set_predicate/2. The predicate may (no longer) have triples associated with it.

Note that resources that have rdf:type rdf:Property are not automatically included in the result-set of this predicate, while all resources that appear as the second argument of a triple are included.

See also
- rdf_predicate_property/2.
  990rdf_current_predicate(P, DB) :-
  991    rdf_current_predicate(P),
  992    (   rdf(_,P,_,DB)
  993    ->  true
  994    ).
 rdf_predicate_property(?Predicate, ?Property)
Query properties of a defined predicate. Currently defined properties are given below.
symmetric(Bool)
True if the predicate is defined to be symetric. I.e., {A} P {B} implies {B} P {A}. Setting symmetric is equivalent to inverse_of(Self).
inverse_of(Inverse)
True if this predicate is the inverse of Inverse. This property is used by rdf_has/3, rdf_has/4, rdf_reachable/3 and rdf_reachable/5.
transitive(Bool)
True if this predicate is transitive. This predicate is currently not used. It might be used to make rdf_has/3 imply rdf_reachable/3 for transitive predicates.
triples(Triples)
Unify Triples with the number of existing triples using this predicate as second argument. Reporting the number of triples is intended to support query optimization.
rdf_subject_branch_factor(-Float)
Unify Float with the average number of triples associated with each unique value for the subject-side of this relation. If there are no triples the value 0.0 is returned. This value is cached with the predicate and recomputed only after substantial changes to the triple set associated to this relation. This property is intended for path optimalisation when solving conjunctions of rdf/3 goals.
rdf_object_branch_factor(-Float)
Unify Float with the average number of triples associated with each unique value for the object-side of this relation. In addition to the comments with the rdf_subject_branch_factor property, uniqueness of the object value is computed from the hash key rather than the actual values.
rdfs_subject_branch_factor(-Float)
Same as rdf_subject_branch_factor, but also considering triples of `subPropertyOf' this relation. See also rdf_has/3.
rdfs_object_branch_factor(-Float)
Same as rdf_object_branch_factor, but also considering triples of `subPropertyOf' this relation. See also rdf_has/3.
See also
- rdf_set_predicate/2.
 1047rdf_predicate_property(P, Prop) :-
 1048    var(P),
 1049    !,
 1050    rdf_current_predicate(P),
 1051    rdf_predicate_property_(P, Prop).
 1052rdf_predicate_property(P, Prop) :-
 1053    rdf_predicate_property_(P, Prop).
 rdf_set_predicate(+Predicate, +Property) is det
Define a property of the predicate. This predicate currently supports the following properties:
symmetric(+Boolean)
Set/unset the predicate as being symmetric. Using symmetric(true) is the same as inverse_of(Predicate), i.e., creating a predicate that is the inverse of itself.
transitive(+Boolean)
Sets the transitive property.
inverse_of(+Predicate2)
Define Predicate as the inverse of Predicate2. An inverse relation is deleted using inverse_of([]).

The transitive property is currently not used. The symmetric and inverse_of properties are considered by rdf_has/3,4 and rdf_reachable/3.

To be done
- Maintain these properties based on OWL triples.
 1078                 /*******************************
 1079                 *            SNAPSHOTS         *
 1080                 *******************************/
 rdf_snapshot(-Snapshot) is det
Take a snapshot of the current state of the RDF store. Later, goals may be executed in the context of the database at this moment using rdf_transaction/3 with the snapshot option. A snapshot created outside a transaction exists until it is deleted. Snapshots taken inside a transaction can only be used inside this transaction.
 rdf_delete_snapshot(+Snapshot) is det
Delete a snapshot as obtained from rdf_snapshot/1. After this call, resources used for maintaining the snapshot become subject to garbage collection.
 rdf_current_snapshot(?Term) is nondet
True when Term is a currently known snapshot.
bug
- Enumeration of snapshots is slow.
 1103rdf_current_snapshot(Term) :-
 1104    current_blob(Term, rdf_snapshot).
 1105
 1106
 1107                 /*******************************
 1108                 *          TRANSACTION         *
 1109                 *******************************/
 rdf_transaction(:Goal) is semidet
Same as rdf_transaction(Goal, user, []). See rdf_transaction/3.
 rdf_transaction(:Goal, +Id) is semidet
Same as rdf_transaction(Goal, Id, []). See rdf_transaction/3.
 rdf_transaction(:Goal, +Id, +Options) is semidet
Run Goal in an RDF transaction. Compared to the ACID model, RDF transactions have the following properties:
  1. Modifications inside the transactions become all atomically visible to the outside world if Goal succeeds or remain invisible if Goal fails or throws an exception. I.e., the atomicy property is fully supported.
  2. Consistency is not guaranteed. Later versions may implement consistency constraints that will be checked serialized just before the actual commit of a transaction.
  3. Concurrently executing transactions do not infuence each other. I.e., the isolation property is fully supported.
  4. Durability can be activated by loading library(semweb/rdf_persistency).

Processed options are:

snapshot(+Snapshot)
Execute Goal using the state of the RDF store as stored in Snapshot. See rdf_snapshot/1. Snapshot can also be the atom true, which implies that an anonymous snapshot is created at the current state of the store. Modifications due to executing Goal are only visible to Goal.
 1145rdf_transaction(Goal) :-
 1146    rdf_transaction(Goal, user, []).
 1147rdf_transaction(Goal, Id) :-
 1148    rdf_transaction(Goal, Id, []).
 rdf_active_transaction(?Id) is nondet
True if Id is the identifier of a transaction in the context of which this call is executed. If Id is not instantiated, backtracking yields transaction identifiers starting with the innermost nested transaction. Transaction identifier terms are not copied, need not be ground and can be instantiated during the transaction.
 1159rdf_active_transaction(Id) :-
 1160    rdf_active_transactions_(List),
 1161    member(Id, List).
 rdf_monitor(:Goal, +Options)
Call Goal if specified actions occur on the database.
 1167rdf_monitor(Goal, Options) :-
 1168    monitor_mask(Options, 0xffff, Mask),
 1169    rdf_monitor_(Goal, Mask).
 1170
 1171monitor_mask([], Mask, Mask).
 1172monitor_mask([H|T], Mask0, Mask) :-
 1173    update_mask(H, Mask0, Mask1),
 1174    monitor_mask(T, Mask1, Mask).
 1175
 1176update_mask(-X, Mask0, Mask) :-
 1177    !,
 1178    monitor_mask(X, M),
 1179    Mask is Mask0 /\ \M.
 1180update_mask(+X, Mask0, Mask) :-
 1181    !,
 1182    monitor_mask(X, M),
 1183    Mask is Mask0 \/ M.
 1184update_mask(X, Mask0, Mask) :-
 1185    monitor_mask(X, M),
 1186    Mask is Mask0 \/ M.
 monitor_mask(Name, Mask)
Mask bit for the monitor events. Note that this must be kept consistent with the enum broadcast_id defined in rdf_db.c
 1193                                        % C-defined broadcasts
 1194monitor_mask(assert,       0x0001).
 1195monitor_mask(assert(load), 0x0002).
 1196monitor_mask(retract,      0x0004).
 1197monitor_mask(update,       0x0008).
 1198monitor_mask(new_literal,  0x0010).
 1199monitor_mask(old_literal,  0x0020).
 1200monitor_mask(transaction,  0x0040).
 1201monitor_mask(load,         0x0080).
 1202monitor_mask(create_graph, 0x0100).
 1203monitor_mask(reset,        0x0200).
 1204                                        % prolog defined broadcasts
 1205monitor_mask(parse,        0x1000).
 1206monitor_mask(unload,       0x1000).     % FIXME: Duplicate
 1207                                        % mask for all
 1208monitor_mask(all,          0xffff).
 1209
 1210%rdf_broadcast(Term, MaskName) :-
 1211%%      monitor_mask(MaskName, Mask),
 1212%%      rdf_broadcast_(Term, Mask).
 1213
 1214
 1215                 /*******************************
 1216                 *            WARM              *
 1217                 *******************************/
 rdf_warm_indexes
Warm all indexes. See rdf_warm_indexes/1.
 1223rdf_warm_indexes :-
 1224    findall(Index, rdf_index(Index), Indexes),
 1225    rdf_warm_indexes(Indexes).
 1226
 1227rdf_index(s).
 1228rdf_index(p).
 1229rdf_index(o).
 1230rdf_index(sp).
 1231rdf_index(o).
 1232rdf_index(po).
 1233rdf_index(spo).
 1234rdf_index(g).
 1235rdf_index(sg).
 1236rdf_index(pg).
 rdf_warm_indexes(+Indexes) is det
Create the named indexes. Normally, the RDF database creates indexes on lazily the first time they are needed. This predicate serves two purposes: it provides an explicit way to make sure that the required indexes are present and creating multiple indexes at the same time is more efficient.
 1247                 /*******************************
 1248                 *          DUPLICATES          *
 1249                 *******************************/
 rdf_update_duplicates is det
Update the duplicate administration of the RDF store. This marks every triple that is potentionally a duplicate of another as duplicate. Being potentially a duplicate means that subject, predicate and object are equivalent and the life-times of the two triples overlap.

The duplicates marks are used to reduce the administrative load of avoiding duplicate answers. Normally, the duplicates are marked using a background thread that is started on the first query that produces a substantial amount of duplicates.

 1264:- public
 1265    rdf_update_duplicates_thread/0.
 rdf_update_duplicates_thread
Start a thread to initialize the duplicate administration.
 1271rdf_update_duplicates_thread :-
 1272    thread_create(rdf_update_duplicates, _,
 1273                  [ detached(true),
 1274                    alias('__rdf_duplicate_detecter')
 1275                  ]).
 rdf_update_duplicates is det
Update the duplicate administration. If this adminstration is up-to-date, each triples that may have a duplicate is flagged. The predicate rdf/3 uses this administration to speedup checking for duplicate answers.

This predicate is normally executed from a background thread named =__rdf_duplicate_detecter= which is created when a query discovers that checking for duplicates becomes too expensive.

 1289                 /*******************************
 1290                 *    QUICK BINARY LOAD/SAVE    *
 1291                 *******************************/
 rdf_save_db(+File) is det
 rdf_save_db(+File, +Graph) is det
Save triples into File in a quick-to-load binary format. If Graph is supplied only triples flagged to originate from that database are added. Files created this way can be loaded using rdf_load_db/1.
 1301:- create_prolog_flag(rdf_triple_format, 3, [type(integer)]). 1302
 1303rdf_save_db(File) :-
 1304    current_prolog_flag(rdf_triple_format, Version),
 1305    setup_call_cleanup(
 1306        open(File, write, Out, [type(binary)]),
 1307        ( set_stream(Out, record_position(false)),
 1308          rdf_save_db_(Out, _, Version)
 1309        ),
 1310        close(Out)).
 1311
 1312
 1313rdf_save_db(File, Graph) :-
 1314    current_prolog_flag(rdf_triple_format, Version),
 1315    setup_call_cleanup(
 1316        open(File, write, Out, [type(binary)]),
 1317        ( set_stream(Out, record_position(false)),
 1318          rdf_save_db_(Out, Graph, Version)
 1319        ),
 1320        close(Out)).
 rdf_load_db_no_admin(+File, +Id, -Graphs) is det
Load triples from a .trp file without updating the source administration. Id is handled to monitor action. Graphs is a list of graph-names encountered in File.
 1329rdf_load_db_no_admin(File, Id, Graphs) :-
 1330    open(File, read, In, [type(binary)]),
 1331    set_stream(In, record_position(false)),
 1332    call_cleanup(rdf_load_db_(In, Id, Graphs), close(In)).
 check_loaded_cache(+Graph, +Graphs, +Modified) is det
Verify the loaded cache file and optionally fix the modification time (new versions save this along with the snapshot).
To be done
- What to do if there is a cache mismatch? Delete the loaded graphs and fail?
 1343check_loaded_cache(DB, [DB], _Modified) :- !.
 1344check_loaded_cache(DB, Graphs, _) :-
 1345    print_message(warning, rdf(inconsistent_cache(DB, Graphs))).
 rdf_load_db(+File) is det
Load triples from a file created using rdf_save_db/2.
 1352rdf_load_db(File) :-
 1353    uri_file_name(URL, File),
 1354    rdf_load_db_no_admin(File, URL, _Graphs).
 1355
 1356
 1357                 /*******************************
 1358                 *          LOADING RDF         *
 1359                 *******************************/
 1360
 1361:- multifile
 1362    rdf_open_hook/8,
 1363    rdf_open_decode/4,              % +Encoding, +File, -Stream, -Cleanup
 1364    rdf_load_stream/3,              % +Format, +Stream, +Options
 1365    rdf_file_type/2,                % ?Extension, ?Format
 1366    rdf_storage_encoding/2,         % ?Extension, ?Encoding
 1367    url_protocol/1.                 % ?Protocol
 rdf_load(+FileOrList) is det
Same as rdf_load(FileOrList, []). See rdf_load/2.
 rdf_load(+FileOrList, :Options) is det
Load RDF data. If this predicate is called a second time for the same file, it is by default treated as a no-op. See option =if(changed)=.

Options provides additional processing options. Defined options are:

blank_nodes(+ShareMode)
How to handle equivalent blank nodes. If share (default), equivalent blank nodes are shared in the same resource.
base_uri(+URI)
URI that is used for rdf:about="" and other RDF constructs that are relative to the base uri. Default is the source URL.
concurrent(+Jobs)
If FileOrList is a list of files, process the input files using Jobs threads concurrently. Default is the mininum of the number of cores and the number of inputs. Higher values can be useful when loading inputs from (slow) network connections. Using 1 (one) does not use separate worker threads.
format(+Format)
Specify the source format explicitly. Normally this is deduced from the filename extension or the mime-type. The core library understands the formats xml (RDF/XML) and triples (internal quick load and cache format). Plugins, such as library(semweb/turtle) extend the set of recognised extensions.
graph(?Graph)
Named graph in which to load the data. It is not allowed to load two sources into the same named graph. If Graph is unbound, it is unified to the graph into which the data is loaded. The default graph is a file:// URL when loading a file or, if the specification is a URL, its normalized version without the optional #fragment.
if(Condition)
When to load the file. One of true, changed (default) or not_loaded.
modified(-Modified)
Unify Modified with one of not_modified, cached(File), last_modified(Stamp) or unknown.
cache(Bool)
If false, do not use or create a cache file.
register_namespaces(Bool)
If true (default false), register xmlns namespace declarations or Turtle @prefix prefixes using rdf_register_prefix/3 if there is no conflict.
silent(+Bool)
If true, the message reporting completion is printed using level silent. Otherwise the level is informational. See also print_message/2.
prefixes(-Prefixes)
Returns the prefixes defined in the source data file as a list of pairs.
multifile +Boolean
Indicate that the addressed graph may be populated with triples from multiple sources. This disables caching and avoids that an rdf_load/2 call affecting the specified graph cleans the graph.

Other options are forwarded to process_rdf/3. By default, rdf_load/2 only loads RDF/XML from files. It can be extended to load data from other formats and locations using plugins. The full set of plugins relevant to support different formats and locations is below:

:- use_module(library(semweb/turtle)).        % Turtle and TriG
:- use_module(library(semweb/rdf_ntriples)).
:- use_module(library(semweb/rdf_zlib_plugin)).
:- use_module(library(semweb/rdf_http_plugin)).
:- use_module(library(http/http_ssl_plugin)).
See also
- rdf_open_hook/3, library(semweb/rdf_persistency) and library(semweb/rdf_cache)
 1463:- dynamic
 1464    rdf_loading/3.                          % Graph, Queue, Thread
 1465
 1466rdf_load(Spec) :-
 1467    rdf_load(Spec, []).
 1468
 1469:- if(\+current_predicate(concurrent/3)). 1470concurrent(_, Goals, _) :-
 1471    forall(member(G, Goals), call(G)).
 1472:- endif. 1473
 1474% Note that we kill atom garbage collection.  This improves performance
 1475% with about 15% loading the LUBM Univ_50 benchmark.
 1476
 1477rdf_load(Spec, M:Options) :-
 1478    must_be(list, Options),
 1479    current_prolog_flag(agc_margin, Old),
 1480    setup_call_cleanup(
 1481        set_prolog_flag(agc_margin, 0),
 1482        rdf_load_noagc(Spec, M, Options),
 1483        set_prolog_flag(agc_margin, Old)).
 1484
 1485rdf_load_noagc(List, M, Options) :-
 1486    is_list(List),
 1487    !,
 1488    flatten(List, Inputs),          % Compatibility: allow nested lists
 1489    maplist(must_be(ground), Inputs),
 1490    length(Inputs, Count),
 1491    load_jobs(Count, Jobs, Options),
 1492    (   Jobs =:= 1
 1493    ->  forall(member(Spec, Inputs),
 1494               rdf_load_one(Spec, M, Options))
 1495    ;   maplist(load_goal(Options, M), Inputs, Goals),
 1496        concurrent(Jobs, Goals, [])
 1497    ).
 1498rdf_load_noagc(One, M, Options) :-
 1499    must_be(ground, One),
 1500    rdf_load_one(One, M, Options).
 1501
 1502load_goal(Options, M, Spec, rdf_load_one(Spec, M, Options)).
 1503
 1504load_jobs(_, Jobs, Options) :-
 1505    option(concurrent(Jobs), Options),
 1506    !,
 1507    must_be(positive_integer, Jobs).
 1508load_jobs(Count, Jobs, _) :-
 1509    current_prolog_flag(cpu_count, CPUs),
 1510    CPUs > 0,
 1511    !,
 1512    Jobs is max(1, min(CPUs, Count)).
 1513load_jobs(_, 1, _).
 1514
 1515
 1516rdf_load_one(Spec, M, Options) :-
 1517    source_url(Spec, Protocol, SourceURL),
 1518    load_graph(SourceURL, Graph, Options),
 1519    setup_call_cleanup(
 1520        with_mutex(rdf_load_file,
 1521                   rdf_start_load(SourceURL, Loading)),
 1522        rdf_load_file(Loading, Spec, SourceURL, Protocol,
 1523                      Graph, M, Options),
 1524        rdf_end_load(Loading)).
 rdf_start_load(+SourceURL, -WhatToDo) is det
 rdf_end_load(+WhatToDo) is det
 rdf_load_file(+WhatToDo, +Spec, +SourceURL, +Protocol, +Graph, +Module, +Options) is det
Of these three predicates, rdf_load_file/7 does the real work. The others deal with the possibility that the graph is being loaded by another thread. In that case, we wait for the other thread to complete the work.
See also
- Code is modelled closely after how concurrent loading is handled in SWI-Prolog's boot/init.pl
To be done
- What if both threads disagree on what is loaded into the graph?
 1541rdf_start_load(SourceURL, queue(Queue)) :-
 1542    rdf_loading(SourceURL, Queue, LoadThread),
 1543    \+ thread_self(LoadThread),
 1544    !,
 1545    debug(rdf(load), '~p is being loaded by thread ~w; waiting ...',
 1546          [ SourceURL, LoadThread]).
 1547rdf_start_load(SourceURL, Ref) :-
 1548    thread_self(Me),
 1549    message_queue_create(Queue),
 1550    assertz(rdf_loading(SourceURL, Queue, Me), Ref).
 1551
 1552rdf_end_load(queue(_)) :- !.
 1553rdf_end_load(Ref) :-
 1554    clause(rdf_loading(_, Queue, _), _, Ref),
 1555    erase(Ref),
 1556    thread_send_message(Queue, done),
 1557    message_queue_destroy(Queue).
 1558
 1559rdf_load_file(queue(Queue), _Spec, _SourceURL, _Protocol, _Graph, _M, _Options) :-
 1560    !,
 1561    catch(thread_get_message(Queue, _), _, true).
 1562rdf_load_file(_Ref, _Spec, SourceURL, Protocol, Graph, M, Options) :-
 1563    debug(rdf(load), 'RDF: Loading ~q into ~q', [SourceURL, Graph]),
 1564    statistics(cputime, T0),
 1565    rdf_open_input(SourceURL, Protocol, Graph,
 1566                   In, Cleanup, Modified, Format, Options),
 1567    supported_format(Format, Cleanup),
 1568    return_modified(Modified, Options),
 1569    (   Modified == not_modified
 1570    ->  Action = none
 1571    ;   Modified = cached(CacheFile)
 1572    ->  do_unload(Graph),
 1573        catch(rdf_load_db_no_admin(CacheFile, cache(Graph), Graphs), _, fail),
 1574        check_loaded_cache(Graph, Graphs, Modified),
 1575        Action = load
 1576    ;   option(base_uri(BaseURI), Options, Graph),
 1577        (   var(BaseURI)
 1578        ->  BaseURI = SourceURL
 1579        ;   true
 1580        ),
 1581        once(phrase(derived_options(Options, NSList), Extra)),
 1582        merge_options([ base_uri(BaseURI),
 1583                        graph(Graph),
 1584                        format(Format)
 1585                      | Extra
 1586                      ], Options, RDFOptions),
 1587        (   option(multifile(true), Options)
 1588        ->  true
 1589        ;   do_unload(Graph)
 1590        ),
 1591        graph_modified(Modified, ModifiedStamp),
 1592        rdf_set_graph_source(Graph, SourceURL, ModifiedStamp),
 1593        call_cleanup(rdf_load_stream(Format, In, M:RDFOptions),
 1594                     Cleanup),
 1595        save_cache(Graph, SourceURL, Options),
 1596        register_file_prefixes(NSList),
 1597        format_action(Format, Action)
 1598    ),
 1599    rdf_statistics_(triples(Graph, Triples)),
 1600    report_loaded(Action, SourceURL, Graph, Triples, T0, Options).
 1601
 1602supported_format(Format, _Cleanup) :-
 1603    rdf_file_type(_, Format),
 1604    !.
 1605supported_format(Format, Cleanup) :-
 1606    call(Cleanup),
 1607    existence_error(rdf_format_plugin, Format).
 1608
 1609format_action(triples, load) :- !.
 1610format_action(_, parsed).
 1611
 1612save_cache(Graph, SourceURL, Options) :-
 1613    option(cache(true), Options, true),
 1614    rdf_cache_file(SourceURL, write, CacheFile),
 1615    !,
 1616    catch(save_cache(Graph, CacheFile), E,
 1617          print_message(warning, E)).
 1618save_cache(_, _, _).
 1619
 1620derived_options([], _) -->
 1621    [].
 1622derived_options([H|T], NSList) -->
 1623    (   {   H == register_namespaces(true)
 1624        ;   H == (register_namespaces = true)
 1625        }
 1626    ->  [ namespaces(NSList) ]
 1627    ;   []
 1628    ),
 1629    derived_options(T, NSList).
 1630
 1631graph_modified(last_modified(Stamp), Stamp).
 1632graph_modified(unknown, Stamp) :-
 1633    get_time(Stamp).
 1634
 1635return_modified(Modified, Options) :-
 1636    option(modified(M0), Options),
 1637    !,
 1638    M0 = Modified.
 1639return_modified(_, _).
 1640
 1641
 1642                 /*******************************
 1643                 *        INPUT HANDLING        *
 1644                 *******************************/
 1645
 1646/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 1647This section deals with pluggable input sources.  The task of the input
 1648layer is
 1649
 1650    * Decide on the graph-name
 1651    * Decide on the source-location
 1652    * Decide whether loading is needed (if-modified)
 1653    * Decide on the serialization in the input
 1654
 1655The protocol must ensure minimal  overhead,   in  particular for network
 1656protocols. E.g. for HTTP we want to make a single call on the server and
 1657use If-modified-since to verify that we need not reloading this file.
 1658- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 rdf_open_input(+SourceURL, +Protocol, +Graph, -Stream, -Cleanup, -Modified, -Format, +Options)
Open an input source.

Options processed:

Arguments:
Modified- is one of not_modified, last_modified(Time), cached(CacheFile) or unknown
 1676rdf_open_input(SourceURL, Protocol, Graph,
 1677               Stream, Cleanup, Modified, Format, Options) :-
 1678    (   option(multifile(true), Options)
 1679    ->  true
 1680    ;   option(if(If), Options, changed),
 1681        (   If == true
 1682        ->  true
 1683        ;   rdf_graph_source_(Graph, SourceURL, HaveModified)
 1684        ->  true
 1685        ;   option(cache(true), Options, true),
 1686            rdf_cache_file(SourceURL, read, CacheFile)
 1687        ->  time_file(CacheFile, HaveModified)
 1688        ;   true
 1689        )
 1690    ),
 1691    option(format(Format), Options, _),
 1692    open_input_if_modified(Protocol, SourceURL, HaveModified,
 1693                           Stream, Cleanup, Modified0, Format, Options),
 1694    (   Modified0 == not_modified
 1695    ->  (   nonvar(CacheFile)
 1696        ->  Modified = cached(CacheFile)
 1697        ;   Modified = not_modified
 1698        )
 1699    ;   Modified = Modified0
 1700    ).
 source_url(+Spec, -Class, -SourceURL) is det
Determine class and url of the source. Class is one of
 1711source_url(stream(In), stream(In), SourceURL) :-
 1712    !,
 1713    (   stream_property(In, file_name(File))
 1714    ->  to_url(File, SourceURL)
 1715    ;   gensym('stream://', SourceURL)
 1716    ).
 1717source_url(Stream, Class, SourceURL) :-
 1718    is_stream(Stream),
 1719    !,
 1720    source_url(stream(Stream), Class, SourceURL).
 1721source_url(Spec, Protocol, SourceURL) :-
 1722    compound(Spec),
 1723    !,
 1724    source_file(Spec, Protocol, SourceURL).
 1725source_url(FileURL, Protocol, SourceURL) :-             % or return FileURL?
 1726    uri_file_name(FileURL, File),
 1727    !,
 1728    source_file(File, Protocol, SourceURL).
 1729source_url(SourceURL0, Protocol, SourceURL) :-
 1730    is_url(SourceURL0, Protocol, SourceURL),
 1731    !.
 1732source_url(File, Protocol, SourceURL) :-
 1733    source_file(File, Protocol, SourceURL).
 1734
 1735source_file(Spec, file(SExt), SourceURL) :-
 1736    findall(Ext, valid_extension(Ext), Exts),
 1737    absolute_file_name(Spec, File, [access(read), extensions([''|Exts])]),
 1738    storage_extension(_Plain, SExt, File),
 1739    uri_file_name(SourceURL, File).
 1740
 1741to_url(URL, URL) :-
 1742    uri_is_global(URL),
 1743    !.
 1744to_url(File, URL) :-
 1745    absolute_file_name(File, Path),
 1746    uri_file_name(URL, Path).
 1747
 1748storage_extension(Plain, SExt, File) :-
 1749    file_name_extension(Plain, SExt, File),
 1750    SExt \== '',
 1751    rdf_storage_encoding(SExt, _),
 1752    !.
 1753storage_extension(File, '', File).
 load_graph(+SourceURL, -Graph, +Options) is det
Graph is the graph into which we load the data. Tries these options:
  1. The graph(Graph) option
  2. The db(Graph) option (backward compatibility)
  3. The base_uri(BaseURI) option
  4. The source URL
 1765load_graph(_Source, Graph, Options) :-
 1766    option(multifile(true), Options),
 1767    !,
 1768    (   (   option(graph(Graph), Options)
 1769        ->  true
 1770        ;   option(db(Graph), Options)
 1771        ),
 1772        ground(Graph)
 1773    ->  true
 1774    ;   throw(error(existence_error(option, graph),
 1775                    context(_, "rdf_load/2: using multifile requires graph")))
 1776    ).
 1777load_graph(Source, Graph, Options) :-
 1778    (   option(graph(Graph), Options)
 1779    ;   option(db(Graph), Options)
 1780    ),
 1781    !,
 1782    load_graph2(Source, Graph, Options).
 1783load_graph(Source, Graph, Options) :-
 1784    load_graph2(Source, Graph, Options).
 1785
 1786load_graph2(_, Graph, _) :-
 1787    ground(Graph),
 1788    !.
 1789load_graph2(_Source, Graph, Options) :-
 1790    option(base_uri(Graph), Options),
 1791    Graph \== [],
 1792    ground(Graph),
 1793    !.
 1794load_graph2(Source, Graph, _) :-
 1795    load_graph(Source, Graph).
 1796
 1797load_graph(SourceURL, BaseURI) :-
 1798    file_name_extension(BaseURI, Ext, SourceURL),
 1799    rdf_storage_encoding(Ext, _),
 1800    !.
 1801load_graph(SourceURL, SourceURL).
 1802
 1803
 1804open_input_if_modified(stream(In), SourceURL, _, In, true,
 1805                       unknown, Format, _) :-
 1806    !,
 1807    (   var(Format)
 1808    ->  guess_format(SourceURL, Format)
 1809    ;   true
 1810    ).
 1811open_input_if_modified(file(SExt), SourceURL, HaveModified, Stream, Cleanup,
 1812                       Modified, Format, _) :-
 1813    !,
 1814    uri_file_name(SourceURL, File),
 1815    (   SExt == '' -> Plain = File; file_name_extension(Plain, SExt, File)),
 1816    time_file(File, LastModified),
 1817    (   nonvar(HaveModified),
 1818        HaveModified >= LastModified
 1819    ->  Modified = not_modified,
 1820        Cleanup = true
 1821    ;   storage_open(SExt, File, Stream, Cleanup),
 1822        Modified = last_modified(LastModified),
 1823        (   var(Format)
 1824        ->  guess_format(Plain, Format)
 1825        ;   true
 1826        )
 1827    ).
 1828open_input_if_modified(file, SourceURL, HaveModified, Stream, Cleanup,
 1829                       Modified, Format, Options) :-
 1830    !,
 1831    open_input_if_modified(file(''), SourceURL, HaveModified,
 1832                           Stream, Cleanup,
 1833                           Modified, Format, Options).
 1834open_input_if_modified(Protocol, SourceURL, HaveModified, Stream, Cleanup,
 1835                       Modified, Format, Options) :-
 1836    rdf_open_hook(Protocol, SourceURL, HaveModified, Stream, Cleanup,
 1837                  Modified, Format, Options).
 1838
 1839guess_format(File, Format) :-
 1840    file_name_extension(_, Ext, File),
 1841    (   rdf_file_type(Ext, Format)
 1842    ->  true
 1843    ;   Format = xml,
 1844        print_message(warning, rdf(guess_format(Ext)))
 1845    ).
 storage_open(+Extension, +File, -Stream, -Cleanup)
Open the low-level storage. Note that the file is opened as binary. This is the same as for HTTP resources. The correct encoding will be set by the XML parser or the Turtle parser.
 1853storage_open('', File, Stream, close(Stream)) :-
 1854    !,
 1855    open(File, read, Stream, [type(binary)]).
 1856storage_open(Ext, File, Stream, Cleanup) :-
 1857    rdf_storage_encoding(Ext, Encoding),
 1858    rdf_open_decode(Encoding, File, Stream, Cleanup).
 1859
 1860valid_extension(Ext) :-
 1861    rdf_file_type(Ext, _).
 1862valid_extension(Ext) :-
 1863    rdf_storage_encoding(Ext, _).
 is_url(@Term, -Scheme, -URL) is semidet
True if Term is an atom denoting URL of the given Scheme. URL is normalized (see uri_normalized/2) and a possible fragment identifier (#fragment) is removed. This predicate only succeeds if the scheme is registered using the multifile hook url_protocol/1.
 1873is_url(URL, Scheme, FetchURL) :-
 1874    atom(URL),
 1875    uri_is_global(URL),
 1876    uri_normalized(URL, URL1),              % case normalization
 1877    uri_components(URL1, Components),
 1878    uri_data(scheme, Components, Scheme0),
 1879    url_protocol(Scheme0),
 1880    !,
 1881    Scheme = Scheme0,
 1882    uri_data(fragment, Components, _, Components1),
 1883    uri_components(FetchURL, Components1).
 1884
 1885url_protocol(file).                     % built-in
 rdf_file_type(+Extension, -Format) is semidet
True if Format is the format belonging to the given file extension. This predicate is multifile and can thus be extended by plugins.
 1893rdf_file_type(xml,   xml).
 1894rdf_file_type(rdf,   xml).
 1895rdf_file_type(rdfs,  xml).
 1896rdf_file_type(owl,   xml).
 1897rdf_file_type(htm,   xhtml).
 1898rdf_file_type(html,  xhtml).
 1899rdf_file_type(xhtml, xhtml).
 1900rdf_file_type(trp,   triples).
 rdf_file_encoding(+Extension, -Format) is semidet
True if Format describes the storage encoding of file.
 1907rdf_storage_encoding('', plain).
 rdf_load_stream(+Format, +Stream, :Options)
Load RDF data from Stream.
To be done
- Handle mime-types?
 1916rdf_load_stream(xml, Stream, Options) :-
 1917    !,
 1918    graph(Options, Graph),
 1919    rdf_transaction(load_stream(Stream, Options),
 1920                    parse(Graph)).
 1921rdf_load_stream(xhtml, Stream, M:Options) :-
 1922    !,
 1923    graph(Options, Graph),
 1924    rdf_transaction(load_stream(Stream, M:[embedded(true)|Options]),
 1925                    parse(Graph)).
 1926rdf_load_stream(triples, Stream, Options) :-
 1927    !,
 1928    graph(Options, Graph),
 1929    rdf_load_db_(Stream, Graph, _Graphs).
 1930
 1931load_stream(Stream, M:Options) :-
 1932    process_rdf(Stream, assert_triples, M:Options),
 1933    option(graph(Graph), Options),
 1934    rdf_graph_clear_modified_(Graph).
 report_loaded(+Action, +Source, +DB, +Triples, +StartCPU, +Options)
 1939report_loaded(none, _, _, _, _, _) :- !.
 1940report_loaded(Action, Source, DB, Triples, T0, Options) :-
 1941    statistics(cputime, T1),
 1942    Time is T1 - T0,
 1943    (   option(silent(true), Options)
 1944    ->  Level = silent
 1945    ;   Level = informational
 1946    ),
 1947    print_message(Level,
 1948                  rdf(loaded(Action, Source, DB, Triples, Time))).
 rdf_unload(+Source) is det
Identify the graph loaded from Source and use rdf_unload_graph/1 to erase this graph.
deprecated
- For compatibility, this predicate also accepts a graph name instead of a source specification. Please update your code to use rdf_unload_graph/1.
 1961rdf_unload(Spec) :-
 1962    source_url(Spec, _Protocol, SourceURL),
 1963    rdf_graph_source_(Graph, SourceURL, _),
 1964    !,
 1965    rdf_unload_graph(Graph).
 1966rdf_unload(Graph) :-
 1967    atom(Graph),
 1968    rdf_graph(Graph),
 1969    !,
 1970    warn_deprecated_unload(Graph),
 1971    rdf_unload_graph(Graph).
 1972rdf_unload(_).
 1973
 1974:- dynamic
 1975    warned/0. 1976
 1977warn_deprecated_unload(_) :-
 1978    warned,
 1979    !.
 1980warn_deprecated_unload(Graph) :-
 1981    assertz(warned),
 1982    print_message(warning, rdf(deprecated(rdf_unload(Graph)))).
 rdf_unload_graph(+Graph) is det
Remove Graph from the RDF store. Succeeds silently if the named graph does not exist.
 1990rdf_unload_graph(Graph) :-
 1991    must_be(atom, Graph),
 1992    (   rdf_graph(Graph)
 1993    ->  rdf_transaction(do_unload(Graph), unload(Graph))
 1994    ;   true
 1995    ).
 1996
 1997do_unload(Graph) :-
 1998    (   rdf_graph_(Graph, Triples),
 1999        Triples > 0
 2000    ->  rdf_retractall(_,_,_,Graph)
 2001    ;   true
 2002    ),
 2003    rdf_destroy_graph(Graph).
 2004
 2005                 /*******************************
 2006                 *         GRAPH QUERIES        *
 2007                 *******************************/
 rdf_create_graph(+Graph) is det
Create an RDF graph without triples. Succeeds silently if the graph already exists.
 rdf_graph(?Graph) is nondet
True when Graph is an existing graph.
 2019rdf_graph(Graph) :-
 2020    rdf_graph_(Graph, _Triples).
 rdf_source(?Graph, ?SourceURL) is nondet
True if named Graph is loaded from SourceURL.
deprecated
- Use rdf_graph_property(Graph, source(SourceURL)).
 2028rdf_source(Graph, SourceURL) :-
 2029    rdf_graph(Graph),
 2030    rdf_graph_source_(Graph, SourceURL, _Modified).
 rdf_source(?Source)
True if Source is a loaded source.
deprecated
- Use rdf_graph/1 or rdf_source/2.
 2038rdf_source(SourceURL) :-
 2039    rdf_source(_Graph, SourceURL).
 rdf_make
Reload all loaded files that have been modified since the last time they were loaded.
 2046rdf_make :-
 2047    findall(Source-Graph, modified_graph(Source, Graph), Modified),
 2048    forall(member(Source-Graph, Modified),
 2049           catch(rdf_load(Source, [graph(Graph), if(changed)]), E,
 2050                 print_message(error, E))).
 2051
 2052modified_graph(SourceURL, Graph) :-
 2053    rdf_graph(Graph),
 2054    rdf_graph_source_(Graph, SourceURL, Modified),
 2055    \+ sub_atom(SourceURL, 0, _, _, 'stream://'),
 2056    Modified > 0.
 rdf_graph_property(?Graph, ?Property) is nondet
True when Property is a property of Graph. Defined properties are:
hash(Hash)
Hash is the (MD5-)hash for the content of Graph.
modified(Boolean)
True if the graph is modified since it was loaded or rdf_set_graph/2 was called with modified(false).
source(Source)
The graph is loaded from the Source (a URL)
source_last_modified(?Time)
Time is the last-modified timestamp of Source at the moment the graph was loaded from Source.
triples(Count)
True when Count is the number of triples in Graph.

Additional graph properties can be added by defining rules for the multifile predicate property_of_graph/2. Currently, the following extensions are defined:

 2084rdf_graph_property(Graph, Property) :-
 2085    rdf_graph(Graph),
 2086    property_of_graph(Property, Graph).
 2087
 2088:- multifile
 2089    property_of_graph/2. 2090
 2091property_of_graph(hash(Hash), Graph) :-
 2092    rdf_md5(Graph, Hash).
 2093property_of_graph(modified(Boolean), Graph) :-
 2094    rdf_graph_modified_(Graph, Boolean, _).
 2095property_of_graph(source(URL), Graph) :-
 2096    rdf_graph_source_(Graph, URL, _).
 2097property_of_graph(source_last_modified(Time), Graph) :-
 2098    rdf_graph_source_(Graph, _, Time),
 2099    Time > 0.0.
 2100property_of_graph(triples(Count), Graph) :-
 2101    rdf_graph_(Graph, Count).
 rdf_set_graph(+Graph, +Property) is det
Set properties of Graph. Defined properties are:
modified(false)
Set the modified state of Graph to false.
 2110rdf_set_graph(Graph, modified(Modified)) :-
 2111    must_be(oneof([false]), Modified),
 2112    rdf_graph_clear_modified_(Graph).
 save_cache(+DB, +Cache) is det
Save triples belonging to DB in the file Cache.
 2119save_cache(DB, Cache) :-
 2120    current_prolog_flag(rdf_triple_format, Version),
 2121    setup_call_cleanup(
 2122        catch(open(Cache, write, CacheStream, [type(binary)]), _, fail),
 2123        rdf_save_db_(CacheStream, DB, Version),
 2124        close(CacheStream)).
 assert_triples(+Triples, +Source)
Assert a list of triples into the database. Foir security reasons we check we aren't inserting anything but nice RDF triples.
 2132assert_triples([], _).
 2133assert_triples([rdf(S,P,O)|T], DB) :-
 2134    !,
 2135    rdf_assert(S, P, O, DB),
 2136    assert_triples(T, DB).
 2137assert_triples([H|_], _) :-
 2138    throw(error(type_error(rdf_triple, H), _)).
 2139
 2140
 2141                 /*******************************
 2142                 *             RESET            *
 2143                 *******************************/
 rdf_reset_db
Remove all triples from the RDF database and reset all its statistics.
bug
- This predicate checks for active queries, but this check is not properly synchronized and therefore the use of this predicate is unsafe in multi-threaded contexts. It is mainly used to run functionality tests that need to start with an empty database.
 2156rdf_reset_db :-
 2157    reset_gensym('_:genid'),
 2158    rdf_reset_db_.
 2159
 2160
 2161                 /*******************************
 2162                 *           SAVE RDF           *
 2163                 *******************************/
 rdf_save(+Out) is det
Same as rdf_save(Out, []). See rdf_save/2 for details.
 rdf_save(+Out, :Options) is det
Write RDF data as RDF/XML. Options is a list of one or more of the following options:
graph(+Graph)
Save only triples associated to the given named Graph.
anon(Bool)
If false (default true) do not save blank nodes that do not appear (indirectly) as object of a named resource.
base_uri(URI)
BaseURI used. If present, all URIs that can be represented relative to this base are written using their shorthand. See also write_xml_base option.
convert_typed_literal(:Convertor)
Call Convertor(-Type, -Content, +RDFObject), providing the opposite for the convert_typed_literal option of the RDF parser.
document_language(+Lang)
Initial xml:lang saved with rdf:RDF element.
encoding(Encoding)
Encoding for the output. Either utf8 or iso_latin_1.
inline(+Bool)
If true (default false), inline resources when encountered for the first time. Normally, only bnodes are handled this way.
namespaces(+List)
Explicitly specify saved namespace declarations. See rdf_save_header/2 option namespaces for details.
sorted(+Boolean)
If true (default false), emit subjects sorted on the full URI. Useful to make file comparison easier.
write_xml_base(Bool)
If false, do not include the xml:base declaration that is written normally when using the base_uri option.
xml_attributes(+Bool)
If false (default true), never use xml attributes to save plain literal attributes, i.e., always used an XML element as in <name>Joe</name>.
Arguments:
Out- Location to save the data. This can also be a file-url (file://path) or a stream wrapped in a term stream(Out).
See also
- rdf_save_db/1
 2225:- thread_local
 2226    named_anon/2,                   % +Resource, -Id
 2227    inlined/1.                      % +Resource
 2228
 2229rdf_save(File) :-
 2230    rdf_save2(File, []).
 2231
 2232rdf_save(Spec, M:Options0) :-
 2233    is_list(Options0),
 2234    !,
 2235    meta_options(save_meta_option, M:Options0, Options),
 2236    to_file(Spec, File),
 2237    rdf_save2(File, Options).
 2238rdf_save(Spec, _:DB) :-
 2239    atom(DB),                      % backward compatibility
 2240    !,
 2241    to_file(Spec, File),
 2242    rdf_save2(File, [graph(DB)]).
 2243
 2244save_meta_option(convert_typed_literal).
 2245
 2246to_file(URL, File) :-
 2247    atom(URL),
 2248    uri_file_name(URL, File),
 2249    !.
 2250to_file(File, File).
 2251
 2252rdf_save2(File, Options) :-
 2253    option(encoding(Encoding), Options, utf8),
 2254    valid_encoding(Encoding),
 2255    open_output(File, Encoding, Out, Close),
 2256    flag(rdf_db_saved_subjects, OSavedSubjects, 0),
 2257    flag(rdf_db_saved_triples, OSavedTriples, 0),
 2258    call_cleanup(rdf_do_save(Out, Options),
 2259                 Reason,
 2260                 cleanup_save(Reason,
 2261                              File,
 2262                              OSavedSubjects,
 2263                              OSavedTriples,
 2264                              Close)).
 2265
 2266open_output(stream(Out), Encoding, Out, Cleanup) :-
 2267    !,
 2268    stream_property(Out, encoding(Old)),
 2269    (   (   Old == Encoding
 2270        ;   Old == wchar_t          % Internal encoding
 2271        )
 2272    ->  Cleanup = true
 2273    ;   set_stream(Out, encoding(Encoding)),
 2274        Cleanup = set_stream(Out, encoding(Old))
 2275    ).
 2276open_output(File, Encoding, Out,
 2277            close(Out)) :-
 2278    open(File, write, Out, [encoding(Encoding)]).
 2279
 2280valid_encoding(Enc) :-
 2281    (   xml_encoding_name(Enc, _)
 2282    ->  true
 2283    ;   throw(error(domain_error(encoding, Enc), _))
 2284    ).
 2285
 2286
 2287cleanup_save(Reason,
 2288             File,
 2289             OSavedSubjects,
 2290             OSavedTriples,
 2291             Close) :-
 2292    call(Close),
 2293    flag(rdf_db_saved_subjects, SavedSubjects, OSavedSubjects),
 2294    flag(rdf_db_saved_triples, SavedTriples, OSavedTriples),
 2295    retractall(named_anon(_, _)),
 2296    retractall(inlined(_)),
 2297    (   Reason == exit
 2298    ->  print_message(informational,
 2299                      rdf(saved(File, SavedSubjects, SavedTriples)))
 2300    ;   format(user_error, 'Reason = ~w~n', [Reason])
 2301    ).
 2302
 2303rdf_do_save(Out, Options0) :-
 2304    rdf_save_header(Out, Options0, Options),
 2305    graph(Options, DB),
 2306    (   option(sorted(true), Options, false)
 2307    ->  (   var(DB)
 2308        ->  setof(Subject, rdf_subject(Subject), Subjects)
 2309        ;   findall(Subject, rdf(Subject, _, _, DB:_), SubjectList),
 2310            sort(SubjectList, Subjects)
 2311        ),
 2312        forall(member(Subject, Subjects),
 2313               rdf_save_non_anon_subject(Out, Subject, Options))
 2314    ;   forall(rdf_subject_in_graph(Subject, DB),
 2315               rdf_save_non_anon_subject(Out, Subject, Options))
 2316    ),
 2317    rdf_save_footer(Out),
 2318    !.                                  % dubious cut; without the
 2319                                        % cleanup handlers isn't called!?
 rdf_subject_in_graph(-Subject, ?DB) is nondet
True when Subject is a subject in the graph DB. If DB is unbound, all subjects are enumerated. Otherwise we have two options: enumerate all subjects and filter by graph or collect all triples of the graph and get the unique subjects. The first is attractive if the graph is big compared to the DB, also because it does not require memory, the second if the graph is small compared to the DB.
 2330rdf_subject_in_graph(Subject, DB) :-
 2331    var(DB),
 2332    !,
 2333    rdf_subject(Subject).
 2334rdf_subject_in_graph(Subject, DB) :-
 2335    rdf_statistics(triples(AllTriples)),
 2336    rdf_graph_property(DB, triples(DBTriples)),
 2337    DBTriples > AllTriples // 10,
 2338    !,
 2339    rdf_resource(Subject),
 2340    (   rdf(Subject, _, _, DB:_)
 2341    ->  true
 2342    ).
 2343rdf_subject_in_graph(Subject, DB) :-
 2344    findall(Subject, rdf(Subject, _, _, DB:_), SubjectList),
 2345    list_to_set(SubjectList, Subjects),
 2346    member(Subject, Subjects).
 2347
 2348
 2349graph(Options0, DB) :-
 2350    strip_module(Options0, _, Options),
 2351    (   memberchk(graph(DB0), Options)
 2352    ->  DB = DB0
 2353    ;   memberchk(db(DB0), Options)
 2354    ->  DB = DB0
 2355    ;   true                            % leave unbound
 2356    ).
 rdf_save_header(+Fd, +Options)
Save XML document header, doctype and open the RDF environment. This predicate also sets up the namespace notation.

Save an RDF header, with the XML header, DOCTYPE, ENTITY and opening the rdf:RDF element with appropriate namespace declarations. It uses the primitives from section 3.5 to generate the required namespaces and desired short-name. Options is one of:

graph(+URI)
Only search for namespaces used in triples that belong to the given named graph.
namespaces(+List)
Where List is a list of namespace abbreviations. With this option, the expensive search for all namespaces that may be used by your data is omitted. The namespaces rdf and rdfs are added to the provided List. If a namespace is not declared, the resource is emitted in non-abreviated form.
 2381rdf_save_header(Out, Options) :-
 2382    rdf_save_header(Out, Options, _).
 2383
 2384rdf_save_header(Out, Options, OptionsOut) :-
 2385    is_list(Options),
 2386    !,
 2387    option(encoding(Enc), Options, utf8),
 2388    xml_encoding(Enc, Encoding),
 2389    format(Out, '<?xml version=\'1.0\' encoding=\'~w\'?>~n', [Encoding]),
 2390    format(Out, '<!DOCTYPE rdf:RDF [', []),
 2391    header_namespaces(Options, NSIdList),
 2392    nsmap(NSIdList, NsMap),
 2393    append(Options, [nsmap(NsMap)], OptionsOut),
 2394    forall(member(Id=URI, NsMap),
 2395           (   xml_quote_attribute(URI, NSText0, Enc),
 2396               xml_escape_parameter_entity(NSText0, NSText),
 2397               format(Out, '~N    <!ENTITY ~w \'~w\'>', [Id, NSText])
 2398           )),
 2399    format(Out, '~N]>~n~n', []),
 2400    format(Out, '<rdf:RDF', []),
 2401    (   member(Id, NSIdList),
 2402        format(Out, '~N    xmlns:~w="&~w;"~n', [Id, Id]),
 2403        fail
 2404    ;   true
 2405    ),
 2406    (   option(base_uri(Base), Options),
 2407        option(write_xml_base(true), Options, true)
 2408    ->  xml_quote_attribute(Base, BaseText, Enc),
 2409        format(Out, '~N    xml:base="~w"~n', [BaseText])
 2410    ;   true
 2411    ),
 2412    (   memberchk(document_language(Lang), Options)
 2413    ->  format(Out, '~N    xml:lang="~w"', [Lang])
 2414    ;   true
 2415    ),
 2416    format(Out, '>~n', []).
 2417rdf_save_header(Out, FileRef, OptionsOut) :-    % compatibility
 2418    atom(FileRef),
 2419    rdf_save_header(Out, [graph(FileRef)], OptionsOut).
 2420
 2421xml_encoding(Enc, Encoding) :-
 2422    (   xml_encoding_name(Enc, Encoding)
 2423    ->  true
 2424    ;   throw(error(domain_error(rdf_encoding, Enc), _))
 2425    ).
 2426
 2427xml_encoding_name(ascii,       'US-ASCII').
 2428xml_encoding_name(iso_latin_1, 'ISO-8859-1').
 2429xml_encoding_name(utf8,        'UTF-8').
 nsmap(+NSIds, -Map:list(id=uri)) is det
Create a namespace-map that is compatible to xml_write/2 for dealing with XML-Literals
 2436nsmap([], []).
 2437nsmap([Id|T0], [Id=URI|T]) :-
 2438    ns(Id, URI),
 2439    nsmap(T0, T).
 xml_escape_parameter_entity(+In, -Out) is det
Escape % as &#37; for entity declarations.
 2445xml_escape_parameter_entity(In, Out) :-
 2446    sub_atom(In, _, _, _, '%'),
 2447    !,
 2448    atom_codes(In, Codes),
 2449    phrase(escape_parent(Codes), OutCodes),
 2450    atom_codes(Out, OutCodes).
 2451xml_escape_parameter_entity(In, In).
 2452
 2453escape_parent([]) --> [].
 2454escape_parent([H|T]) -->
 2455    (   { H == 37 }
 2456    ->  "&#37;"
 2457    ;   [H]
 2458    ),
 2459    escape_parent(T).
 header_namespaces(Options, -List)
Get namespaces we will define as entities
 2466header_namespaces(Options, List) :-
 2467    memberchk(namespaces(NSL0), Options),
 2468    !,
 2469    sort([rdf,rdfs|NSL0], List).
 2470header_namespaces(Options, List) :-
 2471    graph(Options, DB),
 2472    used_namespace_entities(List, DB).
 rdf_graph_prefixes(?Graph, -List:ord_set) is det
 rdf_graph_prefixes(?Graph, -List:ord_set, :Options) is det
List is a sorted list of prefixes (namepaces) in Graph. Options defined are:
filter(:Filter)
optional Filter argument is used to filter the results. It is called with 3 additional arguments:
call(Filter, Where, Prefix, URI)

The Where argument gives the location of the prefix ans is one of subject, predicate, object or type. The Prefix argument is the potentionally new prefix and URI is the full URI that is being processed.

expand(:Goal)
Hook to generate the graph. Called using
call(Goal,S,P,O,Graph)
min_count(+Count)
Only include prefixes that appear at least N times. Default is 1. Declared prefixes are always returned if found at least one time.
get_prefix(:GetPrefix)
Predicate to extract the candidate prefix from an IRI. Default is iri_xml_namespace/2.
 2510:- thread_local
 2511    graph_prefix/3. 2512:- meta_predicate
 2513    rdf_graph_prefixes(?, -, :). 2514
 2515rdf_graph_prefixes(Graph, List) :-
 2516    rdf_graph_prefixes(Graph, List, []).
 2517
 2518rdf_graph_prefixes(Graph, List, M:QOptions) :-
 2519    is_list(QOptions),
 2520    !,
 2521    meta_options(is_meta, M:QOptions, Options),
 2522    option(filter(Filter), Options, true),
 2523    option(expand(Expand), Options, rdf_db),
 2524    option(min_count(MinCount), Options, 1),
 2525    option(get_prefix(GetPrefix), Options, iri_xml_namespace),
 2526    call_cleanup(prefixes(Expand, Graph, Prefixes, Filter, MinCount, GetPrefix),
 2527                 retractall(graph_prefix(_,_,_))),
 2528    sort(Prefixes, List).
 2529rdf_graph_prefixes(Graph, List, M:Filter) :-
 2530    rdf_graph_prefixes(Graph, List, M:[filter(Filter)]).
 2531
 2532is_meta(filter).
 2533is_meta(expand).
 2534is_meta(get_prefix).
 2535
 2536
 2537prefixes(Expand, Graph, Prefixes, Filter, MinCount, GetPrefix) :-
 2538    (   call(Expand, S, P, O, Graph),
 2539        add_ns(subject, GetPrefix, Filter, S, MinCount, s(S)),
 2540        add_ns(predicate, GetPrefix, Filter, P, MinCount, sp(S,P)),
 2541        add_ns_obj(GetPrefix, Filter, O, MinCount, spo(S,P,O)),
 2542        fail
 2543    ;   true
 2544    ),
 2545    findall(Prefix, graph_prefix(Prefix, MinCount, _), Prefixes).
 2546
 2547add_ns(Where, GetPrefix, Filter, S, MinCount, Context) :-
 2548    \+ rdf_is_bnode(S),
 2549    call(GetPrefix, S, Full),
 2550    Full \== '',
 2551    !,
 2552    (   graph_prefix(Full, MinCount, _)
 2553    ->  true
 2554    ;   Filter == true
 2555    ->  add_ns(Full, Context)
 2556    ;   call(Filter, Where, Full, S)
 2557    ->  add_ns(Full, Context)
 2558    ;   true
 2559    ).
 2560add_ns(_, _, _, _, _, _).
 2561
 2562add_ns(Full, Context) :-
 2563    graph_prefix(Full, _, Contexts),
 2564    memberchk(Context, Contexts),
 2565    !.
 2566add_ns(Full, Context) :-
 2567    retract(graph_prefix(Full, C0, Contexts)),
 2568    !,
 2569    C1 is C0+1,
 2570    asserta(graph_prefix(Full, C1, [Context|Contexts])).
 2571add_ns(Full, _) :-
 2572    ns(_, Full),
 2573    !,
 2574    asserta(graph_prefix(Full, _, _)).
 2575add_ns(Full, Context) :-
 2576    asserta(graph_prefix(Full, 1, [Context])).
 2577
 2578
 2579add_ns_obj(GetPrefix, Filter, O, MinCount, Context) :-
 2580    atom(O),
 2581    !,
 2582    add_ns(object, GetPrefix, Filter, O, MinCount, Context).
 2583add_ns_obj(GetPrefix, Filter, literal(type(Type, _)), MinCount, _) :-
 2584    atom(Type),
 2585    !,
 2586    add_ns(type, GetPrefix, Filter, Type, MinCount, t(Type)).
 2587add_ns_obj(_, _, _, _, _).
 used_namespace_entities(-List, ?Graph) is det
Return the namespace aliases that are actually used in Graph. In addition, this predicate creates ns<N> aliases for namespaces used in predicates because RDF/XML cannot write predicates other than as an XML name.
 2597used_namespace_entities(List, Graph) :-
 2598    decl_used_predicate_ns(Graph),
 2599    used_namespaces(List, Graph).
 2600
 2601used_namespaces(List, DB) :-
 2602    rdf_graph_prefixes(DB, FullList),
 2603    ns_abbreviations(FullList, List0),
 2604    sort([rdf|List0], List).
 2605
 2606ns_abbreviations([], []).
 2607ns_abbreviations([H0|T0], [H|T]) :-
 2608    ns(H, H0),
 2609    !,
 2610    ns_abbreviations(T0, T).
 2611ns_abbreviations([_|T0], T) :-
 2612    ns_abbreviations(T0, T).
 2613
 2614
 2615/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 2616For every URL used as a predicate  we   *MUST*  define a namespace as we
 2617cannot use names holding /, :, etc. as XML identifiers.
 2618- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 2619
 2620:- thread_local
 2621    predicate_ns/2. 2622
 2623decl_used_predicate_ns(DB) :-
 2624    retractall(predicate_ns(_,_)),
 2625    (   rdf_current_predicate(P, DB),
 2626        decl_predicate_ns(P),
 2627        fail
 2628    ;   true
 2629    ).
 2630
 2631decl_predicate_ns(Pred) :-
 2632    predicate_ns(Pred, _),
 2633    !.
 2634decl_predicate_ns(Pred) :-
 2635    rdf_global_id(NS:Local, Pred),
 2636    xml_name(Local),
 2637    !,
 2638    assert(predicate_ns(Pred, NS)).
 2639decl_predicate_ns(Pred) :-
 2640    atom_codes(Pred, Codes),
 2641    append(NSCodes, LocalCodes, Codes),
 2642    xml_codes(LocalCodes),
 2643    !,
 2644    (   NSCodes \== []
 2645    ->  atom_codes(NS, NSCodes),
 2646        (   ns(Id, NS)
 2647        ->  assert(predicate_ns(Pred, Id))
 2648        ;   between(1, infinite, N),
 2649            atom_concat(ns, N, Id),
 2650            \+ ns(Id, _)
 2651        ->  rdf_register_ns(Id, NS),
 2652            print_message(informational,
 2653                          rdf(using_namespace(Id, NS)))
 2654        ),
 2655        assert(predicate_ns(Pred, Id))
 2656    ;   assert(predicate_ns(Pred, -)) % no namespace used
 2657    ).
 2658
 2659xml_codes([]).
 2660xml_codes([H|T]) :-
 2661    xml_code(H),
 2662    xml_codes(T).
 2663
 2664xml_code(X) :-
 2665    code_type(X, csym),
 2666    !.
 2667xml_code(0'-).                          % Match 0'-
 rdf_save_footer(Out:stream) is det
Finish XML generation and write the document footer.
See also
- rdf_save_header/2, rdf_save_subject/3.
 2676rdf_save_footer(Out) :-
 2677    retractall(named_anon(_, _)),
 2678    retractall(inlined(_)),
 2679    format(Out, '</rdf:RDF>~n', []).
 rdf_save_non_anon_subject(+Out, +Subject, +Options)
Save an object. Anonymous objects not saved if anon(false) is present in the Options list.
 2686rdf_save_non_anon_subject(_Out, Subject, Options) :-
 2687    rdf_is_bnode(Subject),
 2688    (   memberchk(anon(false), Options)
 2689    ;   graph(Options, DB),
 2690        rdf_db(_, _, Subject, DB)
 2691    ),
 2692    !.
 2693rdf_save_non_anon_subject(Out, Subject, Options) :-
 2694    rdf_save_subject(Out, Subject, Options),
 2695    flag(rdf_db_saved_subjects, X, X+1).
 rdf_save_subject(+Out, +Subject:resource, +Options) is det
Save the triples associated to Subject to Out. Options:
graph(+Graph)
Only save properties from Graph.
base_uri(+URI)
convert_typed_literal(:Goal)
document_language(+XMLLang)
See also
- rdf_save/2 for a description of these options.
 2710rdf_save_subject(Out, Subject, Options) :-
 2711    is_list(Options),
 2712    !,
 2713    option(base_uri(BaseURI), Options, '-'),
 2714    (   rdf_save_subject(Out, Subject, BaseURI, 0, Options)
 2715    ->  format(Out, '~n', [])
 2716    ;   throw(error(rdf_save_failed(Subject), 'Internal error'))
 2717    ).
 2718rdf_save_subject(Out, Subject, DB) :-
 2719    (   var(DB)
 2720    ->  rdf_save_subject(Out, Subject, [])
 2721    ;   rdf_save_subject(Out, Subject, [graph(DB)])
 2722    ).
 rdf_save_subject(+Out:stream, +Subject:resource, +BaseURI, +Indent:int, +Options) is det
Save properties of Subject.
Arguments:
Indent- Current indentation
 2732rdf_save_subject(_, Subject, _, _, _) :-
 2733    inlined(Subject),
 2734    !.
 2735rdf_save_subject(Out, Subject, BaseURI, Indent, Options) :-
 2736    do_save_subject(Out, Subject, BaseURI, Indent, Options).
 2737
 2738do_save_subject(Out, Subject, BaseURI, Indent, Options) :-
 2739    graph(Options, DB),
 2740    findall(Pred=Object, rdf_db(Subject, Pred, Object, DB), Atts0),
 2741    sort(Atts0, Atts),              % remove duplicates
 2742    length(Atts, L),
 2743    (   length(Atts0, L0),
 2744        Del is L0-L,
 2745        Del > 0
 2746    ->  print_message(informational,
 2747                      rdf(save_removed_duplicates(Del, Subject)))
 2748    ;   true
 2749    ),
 2750    rdf_save_subject(Out, Subject, BaseURI, Atts, Indent, Options),
 2751    flag(rdf_db_saved_triples, X, X+L).
 2752
 2753rdf_db(Subject, Pred, Object, DB) :-
 2754    var(DB),
 2755    !,
 2756    rdf(Subject, Pred, Object).
 2757rdf_db(Subject, Pred, Object, DB) :-
 2758    rdf(Subject, Pred, Object, DB:_).
 rdf_save_subject(+Out:stream, +Subject:resource, +BaseURI, +Atts:list(Pred=Obj), +Indent:int, +Options) is det
Save triples defined by Atts on Subject.
 2765rdf_save_subject(Out, Subject, BaseURI, Atts, Indent, Options) :-
 2766    rdf_equal(rdf:type, RdfType),
 2767    select(RdfType=Type, Atts, Atts1),
 2768    \+ rdf_is_bnode(Type),
 2769    rdf_id(Type, BaseURI, TypeId),
 2770    xml_is_name(TypeId),
 2771    !,
 2772    format(Out, '~*|<', [Indent]),
 2773    rdf_write_id(Out, TypeId),
 2774    save_about(Out, BaseURI, Subject, Options),
 2775    save_attributes(Atts1, BaseURI, Out, TypeId, Indent, Options).
 2776rdf_save_subject(Out, Subject, BaseURI, Atts, Indent, Options) :-
 2777    format(Out, '~*|<rdf:Description', [Indent]),
 2778    save_about(Out, BaseURI, Subject, Options),
 2779    save_attributes(Atts, BaseURI, Out, rdf:'Description', Indent, Options).
 2780
 2781xml_is_name(_NS:Atom) :-
 2782    !,
 2783    xml_name(Atom).
 2784xml_is_name(Atom) :-
 2785    xml_name(Atom).
 save_about(+Out, +BaseURI, +Subject, +Options) is det
Save the rdf:about. If Subject is a blank node, save the nodeID if any.
 2792save_about(Out, _BaseURI, Subject, _Options) :-
 2793    rdf_is_bnode(Subject),
 2794    !,
 2795    (   named_anon(Subject, NodeID)
 2796    ->  format(Out, ' rdf:nodeID="~w"', [NodeID])
 2797    ;   true
 2798    ).
 2799save_about(Out, BaseURI, Subject, Options) :-
 2800    option(encoding(Encoding), Options, utf8),
 2801    rdf_value(Subject, BaseURI, QSubject, Encoding),
 2802    format(Out, ' rdf:about="~w"', [QSubject]).
 save_attributes(+List, +BaseURI, +Stream, +Element, +Indent, +Options)
Save the attributes. Short literal attributes are saved in the tag. Others as the content of the description element. The begin tag has already been filled.
 2810save_attributes(Atts, BaseURI, Out, Element, Indent, Options) :-
 2811    split_attributes(Atts, InTag, InBody, Options),
 2812    SubIndent is Indent + 2,
 2813    save_attributes2(InTag, BaseURI, tag, Out, SubIndent, Options),
 2814    (   InBody == []
 2815    ->  format(Out, '/>~n', [])
 2816    ;   format(Out, '>~n', []),
 2817        save_attributes2(InBody, BaseURI, body, Out, SubIndent, Options),
 2818        format(Out, '~N~*|</', [Indent]),
 2819        rdf_write_id(Out, Element),
 2820        format(Out, '>~n', [])
 2821    ).
 split_attributes(+Attributes, -HeadAttrs, -BodyAttr, Options)
Split attribute (Name=Value) list into attributes for the head and body. Attributes can only be in the head if they are literal and appear only one time in the attribute list.
 2829split_attributes(Atts, [], Atts, Options) :-
 2830    option(xml_attributes(false), Options),
 2831    !.
 2832split_attributes(Atts, HeadAttr, BodyAttr, _) :-
 2833    duplicate_attributes(Atts, Dupls, Singles),
 2834    simple_literal_attributes(Singles, HeadAttr, Rest),
 2835    append(Dupls, Rest, BodyAttr).
 duplicate_attributes(+Attrs, -Duplicates, -Singles)
Extract attributes that appear more than onces as we cannot dublicate an attribute in the head according to the XML rules.
 2842duplicate_attributes([], [], []).
 2843duplicate_attributes([H|T], Dupls, Singles) :-
 2844    H = (Name=_),
 2845    named_attributes(Name, T, D, R),
 2846    D \== [],
 2847    append([H|D], Dupls2, Dupls),
 2848    !,
 2849    duplicate_attributes(R, Dupls2, Singles).
 2850duplicate_attributes([H|T], Dupls2, [H|Singles]) :-
 2851    duplicate_attributes(T, Dupls2, Singles).
 2852
 2853named_attributes(_, [], [], []) :- !.
 2854named_attributes(Name, [H|T], D, R) :-
 2855    (   H = (Name=_)
 2856    ->  D = [H|DT],
 2857        named_attributes(Name, T, DT, R)
 2858    ;   R = [H|RT],
 2859        named_attributes(Name, T, D, RT)
 2860    ).
 simple_literal_attributes(+Attributes, -Inline, -Body)
Split attributes for (literal) attributes to be used in the begin-tag and ones that have to go into the body of the description.
 2867simple_literal_attributes([], [], []).
 2868simple_literal_attributes([H|TA], [H|TI], B) :-
 2869    in_tag_attribute(H),
 2870    !,
 2871    simple_literal_attributes(TA, TI, B).
 2872simple_literal_attributes([H|TA], I, [H|TB]) :-
 2873    simple_literal_attributes(TA, I, TB).
 2874
 2875in_tag_attribute(_=literal(Text)) :-
 2876    atom(Text),                     % may not have lang qualifier
 2877    atom_length(Text, Len),
 2878    Len < 60.
 save_attributes2(+List, +BaseURI, +TagOrBody, +Stream, +Indent, +Options)
Save a list of attributes.
 2884save_attributes2([], _, _, _, _, _).
 2885save_attributes2([H|T], BaseURI, Where, Out, Indent, Options) :-
 2886    save_attribute(Where, H, BaseURI, Out, Indent, Options),
 2887    save_attributes2(T, BaseURI, Where, Out, Indent, Options).
 2888
 2889save_attribute(tag, Name=literal(Value), BaseURI, Out, Indent, Options) :-
 2890    AttIndent is Indent + 2,
 2891    rdf_id(Name, BaseURI, NameText),
 2892    option(encoding(Encoding), Options, utf8),
 2893    xml_quote_attribute(Value, QVal, Encoding),
 2894    format(Out, '~N~*|', [AttIndent]),
 2895    rdf_write_id(Out, NameText),
 2896    format(Out, '="~w"', [QVal]).
 2897save_attribute(body, Name=literal(Literal0), BaseURI, Out, Indent, Options) :-
 2898    !,
 2899    rdf_id(Name, BaseURI, NameText),
 2900    (   memberchk(convert_typed_literal(Converter), Options),
 2901        call(Converter, Type, Content, Literal0)
 2902    ->  Literal = type(Type, Content)
 2903    ;   Literal = Literal0
 2904    ),
 2905    save_body_literal(Literal, NameText, BaseURI, Out, Indent, Options).
 2906save_attribute(body, Name=Value, BaseURI, Out, Indent, Options) :-
 2907    rdf_is_bnode(Value),
 2908    !,
 2909    rdf_id(Name, BaseURI, NameText),
 2910    format(Out, '~N~*|<', [Indent]),
 2911    rdf_write_id(Out, NameText),
 2912    (   named_anon(Value, NodeID)
 2913    ->  format(Out, ' rdf:nodeID="~w"/>', [NodeID])
 2914    ;   (   rdf(S1, Name, Value),
 2915            rdf(S2, P2, Value),
 2916            (S1 \== S2 ; Name \== P2)
 2917        ->  predicate_property(named_anon(_,_), number_of_clauses(N)),
 2918            atom_concat('bn', N, NodeID),
 2919            assertz(named_anon(Value, NodeID))
 2920        ;   true
 2921        ),
 2922        SubIndent is Indent + 2,
 2923        (   rdf_collection(Value)
 2924        ->  save_about(Out, BaseURI, Value, Options),
 2925            format(Out, ' rdf:parseType="Collection">~n', []),
 2926            rdf_save_list(Out, Value, BaseURI, SubIndent, Options)
 2927        ;   format(Out, '>~n', []),
 2928            rdf_save_subject(Out, Value, BaseURI, SubIndent, Options)
 2929        ),
 2930        format(Out, '~N~*|</', [Indent]),
 2931        rdf_write_id(Out, NameText),
 2932        format(Out, '>~n', [])
 2933    ).
 2934save_attribute(body, Name=Value, BaseURI, Out, Indent, Options) :-
 2935    option(inline(true), Options),
 2936    has_attributes(Value, Options),
 2937    \+ inlined(Value),
 2938    !,
 2939    assertz(inlined(Value)),
 2940    rdf_id(Name, BaseURI, NameText),
 2941    format(Out, '~N~*|<', [Indent]),
 2942    rdf_write_id(Out, NameText),
 2943    SubIndent is Indent + 2,
 2944    (   rdf_collection(Value)
 2945    ->  save_about(Out, BaseURI, Value, Options),
 2946        format(Out, ' rdf:parseType="Collection">~n', []),
 2947        rdf_save_list(Out, Value, BaseURI, SubIndent, Options)
 2948    ;   format(Out, '>~n', []),
 2949        do_save_subject(Out, Value, BaseURI, SubIndent, Options)
 2950    ),
 2951    format(Out, '~N~*|</', [Indent]),
 2952    rdf_write_id(Out, NameText),
 2953    format(Out, '>~n', []).
 2954save_attribute(body, Name=Value, BaseURI, Out, Indent, Options) :-
 2955    option(encoding(Encoding), Options, utf8),
 2956    rdf_value(Value, BaseURI, QVal, Encoding),
 2957    rdf_id(Name, BaseURI, NameText),
 2958    format(Out, '~N~*|<', [Indent]),
 2959    rdf_write_id(Out, NameText),
 2960    format(Out, ' rdf:resource="~w"/>', [QVal]).
 2961
 2962has_attributes(URI, Options) :-
 2963    graph(Options, DB),
 2964    rdf_db(URI, _, _, DB),
 2965    !.
 save_body_literal(+Literal, +NameText, +BaseURI, +Out, +Indent, +Options)
 2970save_body_literal(lang(Lang, Value),
 2971                  NameText, BaseURI, Out, Indent, Options) :-
 2972    !,
 2973    format(Out, '~N~*|<', [Indent]),
 2974    rdf_write_id(Out, NameText),
 2975    (   memberchk(document_language(Lang), Options)
 2976    ->  write(Out, '>')
 2977    ;   rdf_id(Lang, BaseURI, LangText),
 2978        format(Out, ' xml:lang="~w">', [LangText])
 2979    ),
 2980    save_attribute_value(Value, Out, Options),
 2981    write(Out, '</'), rdf_write_id(Out, NameText), write(Out, '>').
 2982save_body_literal(type(Type, DOM),
 2983                  NameText, _BaseURI, Out, Indent, Options) :-
 2984    rdf_equal(Type, rdf:'XMLLiteral'),
 2985    !,
 2986    (   atom(DOM)
 2987    ->  format(Out, '~N~*|<', [Indent]),
 2988        rdf_write_id(Out, NameText),
 2989        format(Out, ' rdf:parseType="Literal">~w</', [DOM]),
 2990        rdf_write_id(Out, NameText), write(Out, '>')
 2991    ;   save_xml_literal(DOM, NameText, Out, Indent, Options)
 2992    ).
 2993save_body_literal(type(Type, Value),
 2994                  NameText, BaseURI, Out, Indent, Options) :-
 2995    !,
 2996    format(Out, '~N~*|<', [Indent]),
 2997    rdf_write_id(Out, NameText),
 2998    option(encoding(Encoding), Options, utf8),
 2999    rdf_value(Type, BaseURI, QVal, Encoding),
 3000    format(Out, ' rdf:datatype="~w">', [QVal]),
 3001    save_attribute_value(Value, Out, Options),
 3002    write(Out, '</'), rdf_write_id(Out, NameText), write(Out, '>').
 3003save_body_literal(Literal,
 3004                  NameText, _, Out, Indent, Options) :-
 3005    atomic(Literal),
 3006    !,
 3007    format(Out, '~N~*|<', [Indent]),
 3008    rdf_write_id(Out, NameText),
 3009    write(Out, '>'),
 3010    save_attribute_value(Literal, Out, Options),
 3011    write(Out, '</'), rdf_write_id(Out, NameText), write(Out, '>').
 3012save_body_literal(DOM,
 3013                  NameText, BaseURI, Out, Indent, Options) :-
 3014    rdf_equal(Type, rdf:'XMLLiteral'),
 3015    save_body_literal(type(Type, DOM),
 3016                      NameText, BaseURI, Out, Indent, Options).
 3017
 3018save_attribute_value(Value, Out, Options) :-  % strings
 3019    (	atom(Value)
 3020    ;	string(Value)
 3021    ),
 3022    !,
 3023    option(encoding(Encoding), Options, utf8),
 3024    xml_quote_cdata(Value, QVal, Encoding),
 3025    write(Out, QVal).
 3026save_attribute_value(Value, Out, _Options) :-  % numbers
 3027    number(Value),
 3028    !,
 3029    writeq(Out, Value).             % quoted: preserve floats
 3030save_attribute_value(Value, _Out, _Options) :-
 3031    throw(error(save_attribute_value(Value), _)).
 save_xml_literal(+DOM, +Attr, +Out, +Indent, +Options) is det
Save an XMLLiteral value. We already emitted
<prop parseType="literal"

but not the terminating >. We need to establish the namespaces used in the DOM. The namespaces in the rdf document are in the nsmap-option of Options.

 3045save_xml_literal(DOM, Attr, Out, Indent, Options) :-
 3046    xml_is_dom(DOM),
 3047    !,
 3048    memberchk(nsmap(NsMap), Options),
 3049    id_to_atom(Attr, Atom),
 3050    xml_write(Out,
 3051              element(Atom, ['rdf:parseType'='Literal'], DOM),
 3052              [ header(false),
 3053                indent(Indent),
 3054                nsmap(NsMap)
 3055              ]).
 3056save_xml_literal(NoDOM, _, _, _, _) :-
 3057    must_be(xml_dom, NoDOM).
 3058
 3059id_to_atom(NS:Local, Atom) :-
 3060    !,
 3061    atomic_list_concat([NS,Local], :, Atom).
 3062id_to_atom(ID, ID).
 rdf_collection(+URI) is semidet
True if URI represents an RDF list that fits the RDF parseType=collection syntax. This means it is a linked list of bnode-cells with a rdf:first that is a resource, optionally a rdf:type that is an rdf:list and the list ends in an rdf:nil.
 3072:- rdf_meta
 3073    rdf_collection(r),
 3074    collection_p(r,r). 3075
 3076rdf_collection(rdf:nil) :- !.
 3077rdf_collection(Cell) :-
 3078    rdf_is_bnode(Cell),
 3079    findall(F, rdf(Cell, rdf:first, F), [_]),
 3080    findall(F, rdf(Cell, rdf:rest, F), [Rest]),
 3081    forall(rdf(Cell, P, V),
 3082           collection_p(P, V)),
 3083    rdf_collection(Rest).
 3084
 3085collection_p(rdf:first, V) :- atom(V).
 3086collection_p(rdf:rest, _).
 3087collection_p(rdf:type, rdf:'List').
 rdf_save_list(+Out, +List, +BaseURI, +Indent, +Options)
 3092rdf_save_list(_, List, _, _, _) :-
 3093    rdf_equal(List, rdf:nil),
 3094    !.
 3095rdf_save_list(Out, List, BaseURI, Indent, Options) :-
 3096    rdf_has(List, rdf:first, First),
 3097    (   rdf_is_bnode(First)
 3098    ->  nl(Out),
 3099        rdf_save_subject(Out, First, BaseURI, Indent, Options)
 3100    ;   option(encoding(Encoding), Options, utf8),
 3101        rdf_value(First, BaseURI, QVal, Encoding),
 3102        format(Out, '~N~*|<rdf:Description rdf:about="~w"/>',
 3103               [Indent, QVal])
 3104    ),
 3105    flag(rdf_db_saved_triples, X, X+3),
 3106    (   rdf_has(List, rdf:rest, List2),
 3107        \+ rdf_equal(List2, rdf:nil)
 3108    ->  rdf_save_list(Out, List2, BaseURI, Indent, Options)
 3109    ;   true
 3110    ).
 rdf_id(+Resource, +BaseURI, -NSLocal)
Generate a NS:Local name for Resource given the indicated default namespace. This call is used for elements.
 3118rdf_id(Id, BaseURI, Local) :-
 3119    assertion(atom(BaseURI)),
 3120    atom_concat(BaseURI, Local, Id),
 3121    sub_atom(Local, 0, 1, _, #),
 3122    !.
 3123rdf_id(Id, _, NS:Local) :-
 3124    iri_xml_namespace(Id, Full, Local),
 3125    ns(NS, Full),
 3126    !.
 3127rdf_id(Id, _, NS:Local) :-
 3128    ns(NS, Full),
 3129    Full \== '',
 3130    atom_concat(Full, Local, Id),
 3131    !.
 3132rdf_id(Id, _, Id).
 rdf_write_id(+Out, +NSLocal) is det
Write an identifier. We cannot use native write on it as both NS and Local can be operators.
 3140rdf_write_id(Out, NS:Local) :-
 3141    !,
 3142    format(Out, '~w:~w', [NS, Local]).
 3143rdf_write_id(Out, Atom) :-
 3144    write(Out, Atom).
 rdf_value(+Resource, +BaseURI, -Text, +Encoding)
According to "6.4 RDF URI References" of the RDF Syntax specification, a URI reference is UNICODE string not containing control sequences, represented as UTF-8 and then as escaped US-ASCII.
 3153rdf_value(Base, Base, '', _) :- !.
 3154rdf_value(V, Base, Text, Encoding) :-
 3155    atom_concat(Base, Local, V),
 3156    sub_atom(Local, 0, _, _, #),
 3157    !,
 3158    xml_quote_attribute(Local, Text, Encoding).
 3159rdf_value(V, _, Text, Encoding) :-
 3160    ns(NS, Full),
 3161    atom_concat(Full, Local, V),
 3162    xml_is_name(Local),
 3163    !,
 3164    xml_quote_attribute(Local, QLocal, Encoding),
 3165    atomic_list_concat(['&', NS, (';'), QLocal], Text).
 3166rdf_value(V, _, Q, Encoding) :-
 3167    xml_quote_attribute(V, Q, Encoding).
 3168
 3169
 3170                 /*******************************
 3171                 *       MATCH AND COMPARE      *
 3172                 *******************************/
 rdf_compare(-Dif, +Object1, +Object2) is det
Compare two object terms. Where SPARQL defines a partial ordering, we define a complete ordering of terms. The ordering is defines as:
 rdf_match_label(+How, +Pattern, +Label) is semidet
True if Label matches Pattern according to How. How is one of icase, substring, word, prefix or like. For backward compatibility, exact is a synonym for icase.
 3195                 /*******************************
 3196                 *      DEPRECATED MATERIAL     *
 3197                 *******************************/
 rdf_split_url(+Prefix, +Local, -URL) is det
rdf_split_url(-Prefix, -Local, +URL) is det
Split/join a URL. This functionality is moved to library(sgml).
deprecated
- Use iri_xml_namespace/3. Note that the argument order is iri_xml_namespace(+IRI, -Namespace, -Localname).
 3207rdf_split_url(Prefix, Local, URL) :-
 3208    atomic(URL),
 3209    !,
 3210    iri_xml_namespace(URL, Prefix, Local).
 3211rdf_split_url(Prefix, Local, URL) :-
 3212    atom_concat(Prefix, Local, URL).
 rdf_url_namespace(+URL, -Namespace)
Namespace is the namespace of URL.
deprecated
- Use iri_xml_namespace/2
 3220rdf_url_namespace(URL, Prefix) :-
 3221    iri_xml_namespace(URL, Prefix).
 3222
 3223
 3224                 /*******************************
 3225                 *            LITERALS          *
 3226                 *******************************/
 rdf_new_literal_map(-Map) is det
Create a new literal map, returning an opaque handle.
 rdf_destroy_literal_map(+Map) is det
Destroy a literal map. After this call, further use of the Map handle is illegal. Additional synchronisation is needed if maps that are shared between threads are destroyed to guarantee the handle is no longer used. In some scenarios rdf_reset_literal_map/1 provides a safe alternative.
 rdf_reset_literal_map(+Map) is det
Delete all content from the literal map.
 rdf_insert_literal_map(+Map, +Key, +Value) is det
Add a relation between Key and Value to the map. If this relation already exists no action is performed.
 rdf_insert_literal_map(+Map, +Key, +Value, -KeyCount) is det
As rdf_insert_literal_map/3. In addition, if Key is a new key in Map, unify KeyCount with the number of keys in Map. This serves two purposes. Derived maps, such as the stem and metaphone maps need to know about new keys and it avoids additional foreign calls for doing the progress in rdf_litindex.pl.
 rdf_delete_literal_map(+Map, +Key) is det
Delete Key and all associated values from the map.
 rdf_delete_literal_map(+Map, +Key, +Value) is det
Delete the association between Key and Value from the map.
 rdf_find_literal_map(+Map, +KeyList, -ValueList) is det
Unify ValueList with an ordered set of values associated to all keys from KeyList. Each key in KeyList is either an atom, an integer or a term not(Key). If not-terms are provided, there must be at least one positive keywords. The negations are tested after establishing the positive matches.
 rdf_keys_in_literal_map(+Map, +Spec, -Answer) is det
Realises various queries on the key-set:
 rdf_statistics_literal_map(+Map, -KeyValue)
Query some statistics of the map. Provides KeyValue are:
size(-Keys, -Relations)
Unify Keys with the total key-count of the index and Relation with the total Key-Value count.
 3314                 /*******************************
 3315                 *             MISC             *
 3316                 *******************************/
 rdf_version(-Version) is det
True when Version is the numerical version-id of this library. The version is computed as
Major*10000 + Minor*100 + Patch.
 rdf_set(+Term) is det
Set properties of the RDF store. Currently defines:
hash(+Hash, +Parameter, +Value)
Set properties for a triple index. Hash is one of s, p, sp, o, po, spo, g, sg or pg. Parameter is one of:
size
Value defines the number of entries in the hash-table. Value is rounded down to a power of 2. After setting the size explicitly, auto-sizing for this table is disabled. Setting the size smaller than the current size results in a permission_error exception.
average_chain_len
Set maximum average collision number for the hash.
optimize_threshold
Related to resizing hash-tables. If 0, all triples are moved to the new size by the garbage collector. If more then zero, those of the last Value resize steps remain at their current location. Leaving cells at their current location reduces memory fragmentation and slows down access.
 rdf_md5(+Graph, -MD5) is det
True when MD5 is the MD5 hash for all triples in graph. The MD5 digest itself is represented as an atom holding a 32-character hexadecimal string. The library maintains the digest incrementally on rdf_load/[1,2], rdf_load_db/1, rdf_assert/[3,4] and rdf_retractall/[3,4]. Checking whether the digest has changed since the last rdf_load/[1,2] call provides a practical means for checking whether the file needs to be saved.
deprecated
- New code should use rdf_graph_property(Graph, hash(Hash)).
 rdf_generation(-Generation) is det
True when Generation is the current generation of the database. Each modification to the database increments the generation. It can be used to check the validity of cached results deduced from the database. Committing a non-empty transaction increments the generation by one.

When inside a transaction, Generation is unified to a term TransactionStartGen + InsideTransactionGen. E.g., 4+3 means that the transaction was started at generation 4 of the global database and we have created 3 new generations inside the transaction. Note that this choice of representation allows for comparing generations using Prolog arithmetic. Comparing a generation in one transaction with a generation in another transaction is meaningless.

 rdf_estimate_complexity(?Subject, ?Predicate, ?Object, -Complexity)
Return the number of alternatives as indicated by the database internal hashed indexing. This is a rough measure for the number of alternatives we can expect for an rdf_has/3 call using the given three arguments. When called with three variables, the total number of triples is returned. This estimate is used in query optimisation. See also rdf_predicate_property/2 and rdf_statistics/1 for additional information to help optimizers.
 rdf_debug(+Level) is det
Set debugging to Level. Level is an integer 0..9. Default is 0 no debugging.
 rdf_atom_md5(+Text, +Times, -MD5) is det
Computes the MD5 hash from Text, which is an atom, string or list of character codes. Times is an integer >= 1. When > 0, the MD5 algorithm is repeated Times times on the generated hash. This can be used for password encryption algorithms to make generate-and-test loops slow.
deprecated
- Obviously, password hash primitives do not belong in this library. The library(crypto) from the \const{ssl} package provides extensive support for hashes. The \const{clib} package provides library(crypt) to access the OS (Unix) password hash implementation as well as lightweight implementations of several popular hashes.
 3413                 /*******************************
 3414                 *             MESSAGES         *
 3415                 *******************************/
 3416
 3417:- multifile
 3418    prolog:message//1. 3419
 3420prolog:message(rdf(Term)) -->
 3421    message(Term).
 3422
 3423message(loaded(How, What, BaseURI, Triples, Time)) -->
 3424    how(How),
 3425    source(What),
 3426    into(What, BaseURI),
 3427    in_time(Triples, Time).
 3428message(save_removed_duplicates(N, Subject)) -->
 3429    [ 'Removed ~d duplicate triples about "~p"'-[N,Subject] ].
 3430message(saved(File, SavedSubjects, SavedTriples)) -->
 3431    [ 'Saved ~D triples about ~D subjects into ~p'-
 3432      [SavedTriples, SavedSubjects, File]
 3433    ].
 3434message(using_namespace(Id, NS)) -->
 3435    [ 'Using namespace id ~w for ~w'-[Id, NS] ].
 3436message(inconsistent_cache(DB, Graphs)) -->
 3437    [ 'RDF cache file for ~w contains the following graphs'-[DB], nl,
 3438      '~t~8|~p'-[Graphs]
 3439    ].
 3440message(guess_format(Ext)) -->
 3441    [ 'Unknown file-extension: ~w.  Assuming RDF/XML'-[Ext] ].
 3442message(meta(not_expanded(G))) -->
 3443    [ 'rdf_meta/1: ~p is not expanded'-[G] ].
 3444message(deprecated(rdf_unload(Graph))) -->
 3445    [ 'rdf_unload/1: Use ~q'-[rdf_unload_graph(Graph)] ].
 3446
 3447
 3448how(load)   --> [ 'Loaded' ].
 3449how(parsed) --> [ 'Parsed' ].
 3450
 3451source(SourceURL) -->
 3452    { uri_file_name(SourceURL, File),
 3453      !,
 3454      file_base_name(File, Base)    % TBD: relative file?
 3455    },
 3456    [ ' "~w"'-[Base] ].
 3457source(SourceURL) -->
 3458    [ ' "~w"'-[SourceURL] ].
 3459
 3460into(_, _) --> [].                      % TBD
 3461
 3462in_time(Triples, ParseTime) -->
 3463    [ ' in ~2f sec; ~D triples'-[ParseTime, Triples]
 3464    ]