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

  536                 /*******************************
  537                 *     TRIPLE MODIFICATIONS     *
  538                 *******************************/
 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.
 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
Replaces one of the three 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.
 rdf_update(+Subject, +Predicate, +Object, +Graph, +Action) is det
As rdf_update/4 but allows for specifying the graph.
  590                 /*******************************
  591                 *          COLLECTIONS         *
  592                 *******************************/
 rdf_member_property(?Prop, ?Index)
Deal with the rdf:_1, ... properties.
  598term_expansion(member_prefix(x),
  599               member_prefix(Prefix)) :-
  600    rdf_db:ns(rdf, NS),
  601    atom_concat(NS, '_', Prefix).
  602member_prefix(x).
  603
  604rdf_member_property(P, N) :-
  605    integer(N),
  606    !,
  607    member_prefix(Prefix),
  608    atom_concat(Prefix, N, P).
  609rdf_member_property(P, N) :-
  610    member_prefix(Prefix),
  611    atom_concat(Prefix, Sub, P),
  612    atom_number(Sub, N).
  613
  614
  615                 /*******************************
  616                 *      ANONYMOUS SUBJECTS      *
  617                 *******************************/
 rdf_node(-Id)
Generate a unique blank node identifier for a subject.
deprecated
- New code should use rdf_bnode/1.
  625rdf_node(Resource) :-
  626    rdf_bnode(Resource).
 rdf_bnode(-Id)
Generate a unique anonymous identifier for a subject.
  632rdf_bnode(Value) :-
  633    repeat,
  634    gensym('_:genid', Value),
  635    \+ rdf(Value, _, _),
  636    \+ rdf(_, _, Value),
  637    \+ rdf(_, Value, _),
  638    !.
  639
  640
  641
  642                 /*******************************
  643                 *             TYPES            *
  644                 *******************************/
 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
  663rdf_is_resource(Term) :-
  664    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.
  671rdf_is_literal(literal(Value)) :-
  672    ground(Value).
  673
  674                 /*******************************
  675                 *             LITERALS         *
  676                 *******************************/
 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 (-,+)
  701:- rdf_meta
  702    rdf_literal_value(o, -),
  703    typed_value(r, +, -),
  704    numeric_value(r, +, -).  705
  706rdf_literal_value(literal(String), Value) :-
  707    atom(String),
  708    !,
  709    Value = String.
  710rdf_literal_value(literal(lang(_Lang, String)), String).
  711rdf_literal_value(literal(type(Type, String)), Value) :-
  712    typed_value(Type, String, Value).
  713
  714typed_value(Numeric, String, Value) :-
  715    xsdp_numeric_uri(Numeric, NumType),
  716    !,
  717    numeric_value(NumType, String, Value).
  718typed_value(xsd:string, String, String).
  719typed_value(rdf:'XMLLiteral', Value, DOM) :-
  720    (   atom(Value)
  721    ->  setup_call_cleanup(
  722            ( atom_to_memory_file(Value, MF),
  723              open_memory_file(MF, read, In, [free_on_close(true)])
  724            ),
  725            load_structure(stream(In), DOM, [dialect(xml)]),
  726            close(In))
  727    ;   DOM = Value
  728    ).
  729
  730numeric_value(xsd:integer, String, Value) :-
  731    atom_number(String, Value),
  732    integer(Value).
  733numeric_value(xsd:float, String, Value) :-
  734    atom_number(String, Number),
  735    Value is float(Number).
  736numeric_value(xsd:double, String, Value) :-
  737    atom_number(String, Number),
  738    Value is float(Number).
  739numeric_value(xsd:decimal, String, Value) :-
  740    atom_number(String, Value).
  741
  742
  743                 /*******************************
  744                 *            SOURCE            *
  745                 *******************************/
 rdf_source_location(+Subject, -Location) is nondet
True when triples for Subject are loaded from Location.
Arguments:
Location- is a term File:Line.
  753rdf_source_location(Subject, Source) :-
  754    findall(Source, rdf(Subject, _, _, Source), Sources),
  755    sort(Sources, Unique),
  756    member(Source, Unique).
  757
  758
  759                 /*******************************
  760                 *       GARBAGE COLLECT        *
  761                 *******************************/
 rdf_create_gc_thread
Create the garbage collection thread.
  767:- public
  768    rdf_create_gc_thread/0.  769
  770rdf_create_gc_thread :-
  771    thread_create(rdf_gc_loop, _,
  772                  [ alias('__rdf_GC')
  773                  ]).
 rdf_gc_loop
Take care of running the RDF garbage collection. This predicate is called from a thread started by creating the RDF DB.
  780rdf_gc_loop :-
  781    catch(rdf_gc_loop(0), E, recover_gc(E)).
  782
  783recover_gc('$aborted') :-
  784    !,
  785    thread_self(Me),
  786    thread_detach(Me).
  787recover_gc(Error) :-
  788    print_message(error, Error),
  789    rdf_gc_loop.
  790
  791rdf_gc_loop(CPU) :-
  792    repeat,
  793    (   consider_gc(CPU)
  794    ->  rdf_gc(CPU1),
  795        sleep(CPU1)
  796    ;   sleep(0.1)
  797    ),
  798    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.
  806rdf_gc(CPU) :-
  807    statistics(cputime, CPU0),
  808    (   rdf_gc_
  809    ->  statistics(cputime, CPU1),
  810        CPU is CPU1-CPU0,
  811        rdf_add_gc_time(CPU)
  812    ;   CPU = 0.0
  813    ).
 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 seperate 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.

  825rdf_gc :-
  826    has_garbage,
  827    !,
  828    rdf_gc(_),
  829    rdf_gc.
  830rdf_gc.
 has_garbage is semidet
True if there is something to gain using GC.
  836has_garbage :-
  837    rdf_gc_info_(Info),
  838    has_garbage(Info),
  839    !.
  840
  841has_garbage(Info) :- arg(2, Info, Garbage),     Garbage > 0.
  842has_garbage(Info) :- arg(3, Info, Reindexed),   Reindexed > 0.
  843has_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.
  850consider_gc(_CPU) :-
  851    (   rdf_gc_info_(gc_info(Triples,       % Total #triples in DB
  852                             Garbage,       % Garbage triples in DB
  853                             Reindexed,     % Reindexed & not reclaimed
  854                             Optimizable,   % Non-optimized tables
  855                             _KeepGen,      % Oldest active generation
  856                             _LastGCGen,    % Oldest active gen at last GC
  857                             _ReindexGen,
  858                             _LastGCReindexGen))
  859    ->  (   (Garbage+Reindexed) * 5 > Triples
  860        ;   Optimizable > 4
  861        )
  862    ;   print_message(error, rdf(invalid_gc_info)),
  863        sleep(10)
  864    ),
  865    !.
  866
  867
  868                 /*******************************
  869                 *           STATISTICS         *
  870                 *******************************/
 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.
  916rdf_statistics(graphs(Count)) :-
  917    rdf_statistics_(graphs(Count)).
  918rdf_statistics(triples(Count)) :-
  919    rdf_statistics_(triples(Count)).
  920rdf_statistics(duplicates(Count)) :-
  921    rdf_statistics_(duplicates(Count)).
  922rdf_statistics(lingering(Count)) :-
  923    rdf_statistics_(lingering(Count)).
  924rdf_statistics(resources(Count)) :-
  925    rdf_statistics_(resources(Count)).
  926rdf_statistics(properties(Count)) :-
  927    rdf_statistics_(predicates(Count)).
  928rdf_statistics(literals(Count)) :-
  929    rdf_statistics_(literals(Count)).
  930rdf_statistics(gc(Count, Reclaimed, Reindexed, Time)) :-
  931    rdf_statistics_(gc(Count, Reclaimed, Reindexed, Time)).
  932rdf_statistics(searched_nodes(Count)) :-
  933    rdf_statistics_(searched_nodes(Count)).
  934rdf_statistics(lookup(Index, Count)) :-
  935    functor(Indexed, indexed, 16),
  936    rdf_statistics_(Indexed),
  937    index(Index, I),
  938    Arg is I + 1,
  939    arg(Arg, Indexed, Count),
  940    Count \== 0.
  941rdf_statistics(hash_quality(Index, Size, Quality,Optimize)) :-
  942    rdf_statistics_(hash_quality(List)),
  943    member(hash(Place,Size,Quality,Optimize), List),
  944    index(Index, Place).
  945rdf_statistics(triples_by_graph(Graph, Count)) :-
  946    rdf_graph_(Graph, Count).
  947
  948index(rdf(-,-,-,-), 0).
  949index(rdf(+,-,-,-), 1).
  950index(rdf(-,+,-,-), 2).
  951index(rdf(+,+,-,-), 3).
  952index(rdf(-,-,+,-), 4).
  953index(rdf(+,-,+,-), 5).
  954index(rdf(-,+,+,-), 6).
  955index(rdf(+,+,+,-), 7).
  956
  957index(rdf(-,-,-,+), 8).
  958index(rdf(+,-,-,+), 9).
  959index(rdf(-,+,-,+), 10).
  960index(rdf(+,+,-,+), 11).
  961index(rdf(-,-,+,+), 12).
  962index(rdf(+,-,+,+), 13).
  963index(rdf(-,+,+,+), 14).
  964index(rdf(+,+,+,+), 15).
  965
  966
  967                 /*******************************
  968                 *           PREDICATES         *
  969                 *******************************/
 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.
  985rdf_current_predicate(P, DB) :-
  986    rdf_current_predicate(P),
  987    (   rdf(_,P,_,DB)
  988    ->  true
  989    ).
 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.
 1042rdf_predicate_property(P, Prop) :-
 1043    var(P),
 1044    !,
 1045    rdf_current_predicate(P),
 1046    rdf_predicate_property_(P, Prop).
 1047rdf_predicate_property(P, Prop) :-
 1048    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.
 1073                 /*******************************
 1074                 *            SNAPSHOTS         *
 1075                 *******************************/
 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.
 1098rdf_current_snapshot(Term) :-
 1099    current_blob(Term, rdf_snapshot).
 1100
 1101
 1102                 /*******************************
 1103                 *          TRANSACTION         *
 1104                 *******************************/
 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.
 1140rdf_transaction(Goal) :-
 1141    rdf_transaction(Goal, user, []).
 1142rdf_transaction(Goal, Id) :-
 1143    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.
 1154rdf_active_transaction(Id) :-
 1155    rdf_active_transactions_(List),
 1156    member(Id, List).
 rdf_monitor(:Goal, +Options)
Call Goal if specified actions occur on the database.
 1162rdf_monitor(Goal, Options) :-
 1163    monitor_mask(Options, 0xffff, Mask),
 1164    rdf_monitor_(Goal, Mask).
 1165
 1166monitor_mask([], Mask, Mask).
 1167monitor_mask([H|T], Mask0, Mask) :-
 1168    update_mask(H, Mask0, Mask1),
 1169    monitor_mask(T, Mask1, Mask).
 1170
 1171update_mask(-X, Mask0, Mask) :-
 1172    !,
 1173    monitor_mask(X, M),
 1174    Mask is Mask0 /\ \M.
 1175update_mask(+X, Mask0, Mask) :-
 1176    !,
 1177    monitor_mask(X, M),
 1178    Mask is Mask0 \/ M.
 1179update_mask(X, Mask0, Mask) :-
 1180    monitor_mask(X, M),
 1181    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
 1188                                        % C-defined broadcasts
 1189monitor_mask(assert,       0x0001).
 1190monitor_mask(assert(load), 0x0002).
 1191monitor_mask(retract,      0x0004).
 1192monitor_mask(update,       0x0008).
 1193monitor_mask(new_literal,  0x0010).
 1194monitor_mask(old_literal,  0x0020).
 1195monitor_mask(transaction,  0x0040).
 1196monitor_mask(load,         0x0080).
 1197monitor_mask(create_graph, 0x0100).
 1198monitor_mask(reset,        0x0200).
 1199                                        % prolog defined broadcasts
 1200monitor_mask(parse,        0x1000).
 1201monitor_mask(unload,       0x1000).     % FIXME: Duplicate
 1202                                        % mask for all
 1203monitor_mask(all,          0xffff).
 1204
 1205%rdf_broadcast(Term, MaskName) :-
 1206%%      monitor_mask(MaskName, Mask),
 1207%%      rdf_broadcast_(Term, Mask).
 1208
 1209
 1210                 /*******************************
 1211                 *            WARM              *
 1212                 *******************************/
 rdf_warm_indexes
Warm all indexes. See rdf_warm_indexes/1.
 1218rdf_warm_indexes :-
 1219    findall(Index, rdf_index(Index), Indexes),
 1220    rdf_warm_indexes(Indexes).
 1221
 1222rdf_index(s).
 1223rdf_index(p).
 1224rdf_index(o).
 1225rdf_index(sp).
 1226rdf_index(o).
 1227rdf_index(po).
 1228rdf_index(spo).
 1229rdf_index(g).
 1230rdf_index(sg).
 1231rdf_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.
 1242                 /*******************************
 1243                 *          DUPLICATES          *
 1244                 *******************************/
 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.

 1259:- public
 1260    rdf_update_duplicates_thread/0.
 rdf_update_duplicates_thread
Start a thread to initialize the duplicate administration.
 1266rdf_update_duplicates_thread :-
 1267    thread_create(rdf_update_duplicates, _,
 1268                  [ detached(true),
 1269                    alias('__rdf_duplicate_detecter')
 1270                  ]).
 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.

 1284                 /*******************************
 1285                 *    QUICK BINARY LOAD/SAVE    *
 1286                 *******************************/
 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.
 1296:- create_prolog_flag(rdf_triple_format, 3, [type(integer)]). 1297
 1298rdf_save_db(File) :-
 1299    current_prolog_flag(rdf_triple_format, Version),
 1300    setup_call_cleanup(
 1301        open(File, write, Out, [type(binary)]),
 1302        ( set_stream(Out, record_position(false)),
 1303          rdf_save_db_(Out, _, Version)
 1304        ),
 1305        close(Out)).
 1306
 1307
 1308rdf_save_db(File, Graph) :-
 1309    current_prolog_flag(rdf_triple_format, Version),
 1310    setup_call_cleanup(
 1311        open(File, write, Out, [type(binary)]),
 1312        ( set_stream(Out, record_position(false)),
 1313          rdf_save_db_(Out, Graph, Version)
 1314        ),
 1315        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.
 1324rdf_load_db_no_admin(File, Id, Graphs) :-
 1325    open(File, read, In, [type(binary)]),
 1326    set_stream(In, record_position(false)),
 1327    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?
 1338check_loaded_cache(DB, [DB], _Modified) :- !.
 1339check_loaded_cache(DB, Graphs, _) :-
 1340    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.
 1347rdf_load_db(File) :-
 1348    uri_file_name(URL, File),
 1349    rdf_load_db_no_admin(File, URL, _Graphs).
 1350
 1351
 1352                 /*******************************
 1353                 *          LOADING RDF         *
 1354                 *******************************/
 1355
 1356:- multifile
 1357    rdf_open_hook/8,
 1358    rdf_open_decode/4,              % +Encoding, +File, -Stream, -Cleanup
 1359    rdf_load_stream/3,              % +Format, +Stream, +Options
 1360    rdf_file_type/2,                % ?Extension, ?Format
 1361    rdf_storage_encoding/2,         % ?Extension, ?Encoding
 1362    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. 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)
 1454:- dynamic
 1455    rdf_loading/3.                          % Graph, Queue, Thread
 1456
 1457rdf_load(Spec) :-
 1458    rdf_load(Spec, []).
 1459
 1460:- if(\+current_predicate(concurrent/3)). 1461concurrent(_, Goals, _) :-
 1462    forall(member(G, Goals), call(G)).
 1463:- endif. 1464
 1465% Note that we kill atom garbage collection.  This improves performance
 1466% with about 15% loading the LUBM Univ_50 benchmark.
 1467
 1468rdf_load(Spec, M:Options) :-
 1469    must_be(list, Options),
 1470    current_prolog_flag(agc_margin, Old),
 1471    setup_call_cleanup(
 1472        set_prolog_flag(agc_margin, 0),
 1473        rdf_load_noagc(Spec, M, Options),
 1474        set_prolog_flag(agc_margin, Old)).
 1475
 1476rdf_load_noagc(List, M, Options) :-
 1477    is_list(List),
 1478    !,
 1479    flatten(List, Inputs),          % Compatibility: allow nested lists
 1480    maplist(must_be(ground), Inputs),
 1481    length(Inputs, Count),
 1482    load_jobs(Count, Jobs, Options),
 1483    (   Jobs =:= 1
 1484    ->  forall(member(Spec, Inputs),
 1485               rdf_load_one(Spec, M, Options))
 1486    ;   maplist(load_goal(Options, M), Inputs, Goals),
 1487        concurrent(Jobs, Goals, [])
 1488    ).
 1489rdf_load_noagc(One, M, Options) :-
 1490    must_be(ground, One),
 1491    rdf_load_one(One, M, Options).
 1492
 1493load_goal(Options, M, Spec, rdf_load_one(Spec, M, Options)).
 1494
 1495load_jobs(_, Jobs, Options) :-
 1496    option(concurrent(Jobs), Options),
 1497    !,
 1498    must_be(positive_integer, Jobs).
 1499load_jobs(Count, Jobs, _) :-
 1500    current_prolog_flag(cpu_count, CPUs),
 1501    CPUs > 0,
 1502    !,
 1503    Jobs is max(1, min(CPUs, Count)).
 1504load_jobs(_, 1, _).
 1505
 1506
 1507rdf_load_one(Spec, M, Options) :-
 1508    source_url(Spec, Protocol, SourceURL),
 1509    load_graph(SourceURL, Graph, Options),
 1510    setup_call_cleanup(
 1511        with_mutex(rdf_load_file,
 1512                   rdf_start_load(SourceURL, Loading)),
 1513        rdf_load_file(Loading, Spec, SourceURL, Protocol,
 1514                      Graph, M, Options),
 1515        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?
 1532rdf_start_load(SourceURL, queue(Queue)) :-
 1533    rdf_loading(SourceURL, Queue, LoadThread),
 1534    \+ thread_self(LoadThread),
 1535    !,
 1536    debug(rdf(load), '~p is being loaded by thread ~w; waiting ...',
 1537          [ SourceURL, LoadThread]).
 1538rdf_start_load(SourceURL, Ref) :-
 1539    thread_self(Me),
 1540    message_queue_create(Queue),
 1541    assertz(rdf_loading(SourceURL, Queue, Me), Ref).
 1542
 1543rdf_end_load(queue(_)) :- !.
 1544rdf_end_load(Ref) :-
 1545    clause(rdf_loading(_, Queue, _), _, Ref),
 1546    erase(Ref),
 1547    thread_send_message(Queue, done),
 1548    message_queue_destroy(Queue).
 1549
 1550rdf_load_file(queue(Queue), _Spec, _SourceURL, _Protocol, _Graph, _M, _Options) :-
 1551    !,
 1552    catch(thread_get_message(Queue, _), _, true).
 1553rdf_load_file(_Ref, _Spec, SourceURL, Protocol, Graph, M, Options) :-
 1554    debug(rdf(load), 'RDF: Loading ~q into ~q', [SourceURL, Graph]),
 1555    statistics(cputime, T0),
 1556    rdf_open_input(SourceURL, Protocol, Graph,
 1557                   In, Cleanup, Modified, Format, Options),
 1558    supported_format(Format, Cleanup),
 1559    return_modified(Modified, Options),
 1560    (   Modified == not_modified
 1561    ->  Action = none
 1562    ;   Modified = cached(CacheFile)
 1563    ->  do_unload(Graph),
 1564        catch(rdf_load_db_no_admin(CacheFile, cache(Graph), Graphs), _, fail),
 1565        check_loaded_cache(Graph, Graphs, Modified),
 1566        Action = load
 1567    ;   option(base_uri(BaseURI), Options, Graph),
 1568        (   var(BaseURI)
 1569        ->  BaseURI = SourceURL
 1570        ;   true
 1571        ),
 1572        once(phrase(derived_options(Options, NSList), Extra)),
 1573        merge_options([ base_uri(BaseURI),
 1574                        graph(Graph),
 1575                        format(Format)
 1576                      | Extra
 1577                      ], Options, RDFOptions),
 1578        (   option(multifile(true), Options)
 1579        ->  true
 1580        ;   do_unload(Graph)
 1581        ),
 1582        graph_modified(Modified, ModifiedStamp),
 1583        rdf_set_graph_source(Graph, SourceURL, ModifiedStamp),
 1584        call_cleanup(rdf_load_stream(Format, In, M:RDFOptions),
 1585                     Cleanup),
 1586        save_cache(Graph, SourceURL, Options),
 1587        register_file_prefixes(NSList),
 1588        format_action(Format, Action)
 1589    ),
 1590    rdf_statistics_(triples(Graph, Triples)),
 1591    report_loaded(Action, SourceURL, Graph, Triples, T0, Options).
 1592
 1593supported_format(Format, _Cleanup) :-
 1594    rdf_file_type(_, Format),
 1595    !.
 1596supported_format(Format, Cleanup) :-
 1597    call(Cleanup),
 1598    existence_error(rdf_format_plugin, Format).
 1599
 1600format_action(triples, load) :- !.
 1601format_action(_, parsed).
 1602
 1603save_cache(Graph, SourceURL, Options) :-
 1604    option(cache(true), Options, true),
 1605    rdf_cache_file(SourceURL, write, CacheFile),
 1606    !,
 1607    catch(save_cache(Graph, CacheFile), E,
 1608          print_message(warning, E)).
 1609save_cache(_, _, _).
 1610
 1611derived_options([], _) -->
 1612    [].
 1613derived_options([H|T], NSList) -->
 1614    (   {   H == register_namespaces(true)
 1615        ;   H == (register_namespaces = true)
 1616        }
 1617    ->  [ namespaces(NSList) ]
 1618    ;   []
 1619    ),
 1620    derived_options(T, NSList).
 1621
 1622graph_modified(last_modified(Stamp), Stamp).
 1623graph_modified(unknown, Stamp) :-
 1624    get_time(Stamp).
 1625
 1626return_modified(Modified, Options) :-
 1627    option(modified(M0), Options),
 1628    !,
 1629    M0 = Modified.
 1630return_modified(_, _).
 1631
 1632
 1633                 /*******************************
 1634                 *        INPUT HANDLING        *
 1635                 *******************************/
 1636
 1637/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 1638This section deals with pluggable input sources.  The task of the input
 1639layer is
 1640
 1641    * Decide on the graph-name
 1642    * Decide on the source-location
 1643    * Decide whether loading is needed (if-modified)
 1644    * Decide on the serialization in the input
 1645
 1646The protocol must ensure minimal  overhead,   in  particular for network
 1647protocols. E.g. for HTTP we want to make a single call on the server and
 1648use If-modified-since to verify that we need not reloading this file.
 1649- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 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
 1667rdf_open_input(SourceURL, Protocol, Graph,
 1668               Stream, Cleanup, Modified, Format, Options) :-
 1669    (   option(multifile(true), Options)
 1670    ->  true
 1671    ;   option(if(If), Options, changed),
 1672        (   If == true
 1673        ->  true
 1674        ;   rdf_graph_source_(Graph, SourceURL, HaveModified)
 1675        ->  true
 1676        ;   option(cache(true), Options, true),
 1677            rdf_cache_file(SourceURL, read, CacheFile)
 1678        ->  time_file(CacheFile, HaveModified)
 1679        ;   true
 1680        )
 1681    ),
 1682    option(format(Format), Options, _),
 1683    open_input_if_modified(Protocol, SourceURL, HaveModified,
 1684                           Stream, Cleanup, Modified0, Format, Options),
 1685    (   Modified0 == not_modified
 1686    ->  (   nonvar(CacheFile)
 1687        ->  Modified = cached(CacheFile)
 1688        ;   Modified = not_modified
 1689        )
 1690    ;   Modified = Modified0
 1691    ).
 source_url(+Spec, -Class, -SourceURL) is det
Determine class and url of the source. Class is one of
 1702source_url(stream(In), stream(In), SourceURL) :-
 1703    !,
 1704    (   stream_property(In, file_name(File))
 1705    ->  to_url(File, SourceURL)
 1706    ;   gensym('stream://', SourceURL)
 1707    ).
 1708source_url(Stream, Class, SourceURL) :-
 1709    is_stream(Stream),
 1710    !,
 1711    source_url(stream(Stream), Class, SourceURL).
 1712source_url(Spec, Protocol, SourceURL) :-
 1713    compound(Spec),
 1714    !,
 1715    source_file(Spec, Protocol, SourceURL).
 1716source_url(FileURL, Protocol, SourceURL) :-             % or return FileURL?
 1717    uri_file_name(FileURL, File),
 1718    !,
 1719    source_file(File, Protocol, SourceURL).
 1720source_url(SourceURL0, Protocol, SourceURL) :-
 1721    is_url(SourceURL0, Protocol, SourceURL),
 1722    !.
 1723source_url(File, Protocol, SourceURL) :-
 1724    source_file(File, Protocol, SourceURL).
 1725
 1726source_file(Spec, file(SExt), SourceURL) :-
 1727    findall(Ext, valid_extension(Ext), Exts),
 1728    absolute_file_name(Spec, File, [access(read), extensions([''|Exts])]),
 1729    storage_extension(_Plain, SExt, File),
 1730    uri_file_name(SourceURL, File).
 1731
 1732to_url(URL, URL) :-
 1733    uri_is_global(URL),
 1734    !.
 1735to_url(File, URL) :-
 1736    absolute_file_name(File, Path),
 1737    uri_file_name(URL, Path).
 1738
 1739storage_extension(Plain, SExt, File) :-
 1740    file_name_extension(Plain, SExt, File),
 1741    SExt \== '',
 1742    rdf_storage_encoding(SExt, _),
 1743    !.
 1744storage_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
 1756load_graph(_Source, Graph, Options) :-
 1757    option(multifile(true), Options),
 1758    !,
 1759    (   (   option(graph(Graph), Options)
 1760        ->  true
 1761        ;   option(db(Graph), Options)
 1762        ),
 1763        ground(Graph)
 1764    ->  true
 1765    ;   throw(error(existence_error(option, graph),
 1766                    context(_, "rdf_load/2: using multifile requires graph")))
 1767    ).
 1768load_graph(Source, Graph, Options) :-
 1769    (   option(graph(Graph), Options)
 1770    ;   option(db(Graph), Options)
 1771    ),
 1772    !,
 1773    load_graph2(Source, Graph, Options).
 1774load_graph(Source, Graph, Options) :-
 1775    load_graph2(Source, Graph, Options).
 1776
 1777load_graph2(_, Graph, _) :-
 1778    ground(Graph),
 1779    !.
 1780load_graph2(_Source, Graph, Options) :-
 1781    option(base_uri(Graph), Options),
 1782    Graph \== [],
 1783    ground(Graph),
 1784    !.
 1785load_graph2(Source, Graph, _) :-
 1786    load_graph(Source, Graph).
 1787
 1788load_graph(SourceURL, BaseURI) :-
 1789    file_name_extension(BaseURI, Ext, SourceURL),
 1790    rdf_storage_encoding(Ext, _),
 1791    !.
 1792load_graph(SourceURL, SourceURL).
 1793
 1794
 1795open_input_if_modified(stream(In), SourceURL, _, In, true,
 1796                       unknown, Format, _) :-
 1797    !,
 1798    (   var(Format)
 1799    ->  guess_format(SourceURL, Format)
 1800    ;   true
 1801    ).
 1802open_input_if_modified(file(SExt), SourceURL, HaveModified, Stream, Cleanup,
 1803                       Modified, Format, _) :-
 1804    !,
 1805    uri_file_name(SourceURL, File),
 1806    (   SExt == '' -> Plain = File; file_name_extension(Plain, SExt, File)),
 1807    time_file(File, LastModified),
 1808    (   nonvar(HaveModified),
 1809        HaveModified >= LastModified
 1810    ->  Modified = not_modified,
 1811        Cleanup = true
 1812    ;   storage_open(SExt, File, Stream, Cleanup),
 1813        Modified = last_modified(LastModified),
 1814        (   var(Format)
 1815        ->  guess_format(Plain, Format)
 1816        ;   true
 1817        )
 1818    ).
 1819open_input_if_modified(file, SourceURL, HaveModified, Stream, Cleanup,
 1820                       Modified, Format, Options) :-
 1821    !,
 1822    open_input_if_modified(file(''), SourceURL, HaveModified,
 1823                           Stream, Cleanup,
 1824                           Modified, Format, Options).
 1825open_input_if_modified(Protocol, SourceURL, HaveModified, Stream, Cleanup,
 1826                       Modified, Format, Options) :-
 1827    rdf_open_hook(Protocol, SourceURL, HaveModified, Stream, Cleanup,
 1828                  Modified, Format, Options).
 1829
 1830guess_format(File, Format) :-
 1831    file_name_extension(_, Ext, File),
 1832    (   rdf_file_type(Ext, Format)
 1833    ->  true
 1834    ;   Format = xml,
 1835        print_message(warning, rdf(guess_format(Ext)))
 1836    ).
 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.
 1844storage_open('', File, Stream, close(Stream)) :-
 1845    !,
 1846    open(File, read, Stream, [type(binary)]).
 1847storage_open(Ext, File, Stream, Cleanup) :-
 1848    rdf_storage_encoding(Ext, Encoding),
 1849    rdf_open_decode(Encoding, File, Stream, Cleanup).
 1850
 1851valid_extension(Ext) :-
 1852    rdf_file_type(Ext, _).
 1853valid_extension(Ext) :-
 1854    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.
 1864is_url(URL, Scheme, FetchURL) :-
 1865    atom(URL),
 1866    uri_is_global(URL),
 1867    uri_normalized(URL, URL1),              % case normalization
 1868    uri_components(URL1, Components),
 1869    uri_data(scheme, Components, Scheme0),
 1870    url_protocol(Scheme0),
 1871    !,
 1872    Scheme = Scheme0,
 1873    uri_data(fragment, Components, _, Components1),
 1874    uri_components(FetchURL, Components1).
 1875
 1876url_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.
 1884rdf_file_type(xml,   xml).
 1885rdf_file_type(rdf,   xml).
 1886rdf_file_type(rdfs,  xml).
 1887rdf_file_type(owl,   xml).
 1888rdf_file_type(htm,   xhtml).
 1889rdf_file_type(html,  xhtml).
 1890rdf_file_type(xhtml, xhtml).
 1891rdf_file_type(trp,   triples).
 rdf_file_encoding(+Extension, -Format) is semidet
True if Format describes the storage encoding of file.
 1898rdf_storage_encoding('', plain).
 rdf_load_stream(+Format, +Stream, :Options)
Load RDF data from Stream.
To be done
- Handle mime-types?
 1907rdf_load_stream(xml, Stream, Options) :-
 1908    !,
 1909    graph(Options, Graph),
 1910    rdf_transaction(load_stream(Stream, Options),
 1911                    parse(Graph)).
 1912rdf_load_stream(xhtml, Stream, M:Options) :-
 1913    !,
 1914    graph(Options, Graph),
 1915    rdf_transaction(load_stream(Stream, M:[embedded(true)|Options]),
 1916                    parse(Graph)).
 1917rdf_load_stream(triples, Stream, Options) :-
 1918    !,
 1919    graph(Options, Graph),
 1920    rdf_load_db_(Stream, Graph, _Graphs).
 1921
 1922load_stream(Stream, M:Options) :-
 1923    process_rdf(Stream, assert_triples, M:Options),
 1924    option(graph(Graph), Options),
 1925    rdf_graph_clear_modified_(Graph).
 report_loaded(+Action, +Source, +DB, +Triples, +StartCPU, +Options)
 1930report_loaded(none, _, _, _, _, _) :- !.
 1931report_loaded(Action, Source, DB, Triples, T0, Options) :-
 1932    statistics(cputime, T1),
 1933    Time is T1 - T0,
 1934    (   option(silent(true), Options)
 1935    ->  Level = silent
 1936    ;   Level = informational
 1937    ),
 1938    print_message(Level,
 1939                  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.
 1952rdf_unload(Spec) :-
 1953    source_url(Spec, _Protocol, SourceURL),
 1954    rdf_graph_source_(Graph, SourceURL, _),
 1955    !,
 1956    rdf_unload_graph(Graph).
 1957rdf_unload(Graph) :-
 1958    atom(Graph),
 1959    rdf_graph(Graph),
 1960    !,
 1961    warn_deprecated_unload(Graph),
 1962    rdf_unload_graph(Graph).
 1963rdf_unload(_).
 1964
 1965:- dynamic
 1966    warned/0. 1967
 1968warn_deprecated_unload(_) :-
 1969    warned,
 1970    !.
 1971warn_deprecated_unload(Graph) :-
 1972    assertz(warned),
 1973    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.
 1981rdf_unload_graph(Graph) :-
 1982    must_be(atom, Graph),
 1983    (   rdf_graph(Graph)
 1984    ->  rdf_transaction(do_unload(Graph), unload(Graph))
 1985    ;   true
 1986    ).
 1987
 1988do_unload(Graph) :-
 1989    (   rdf_graph_(Graph, Triples),
 1990        Triples > 0
 1991    ->  rdf_retractall(_,_,_,Graph)
 1992    ;   true
 1993    ),
 1994    rdf_destroy_graph(Graph).
 1995
 1996                 /*******************************
 1997                 *         GRAPH QUERIES        *
 1998                 *******************************/
 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.
 2010rdf_graph(Graph) :-
 2011    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)).
 2019rdf_source(Graph, SourceURL) :-
 2020    rdf_graph(Graph),
 2021    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.
 2029rdf_source(SourceURL) :-
 2030    rdf_source(_Graph, SourceURL).
 rdf_make
Reload all loaded files that have been modified since the last time they were loaded.
 2037rdf_make :-
 2038    findall(Source-Graph, modified_graph(Source, Graph), Modified),
 2039    forall(member(Source-Graph, Modified),
 2040           catch(rdf_load(Source, [graph(Graph), if(changed)]), E,
 2041                 print_message(error, E))).
 2042
 2043modified_graph(SourceURL, Graph) :-
 2044    rdf_graph(Graph),
 2045    rdf_graph_source_(Graph, SourceURL, Modified),
 2046    \+ sub_atom(SourceURL, 0, _, _, 'stream://'),
 2047    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:

 2075rdf_graph_property(Graph, Property) :-
 2076    rdf_graph(Graph),
 2077    property_of_graph(Property, Graph).
 2078
 2079:- multifile
 2080    property_of_graph/2. 2081
 2082property_of_graph(hash(Hash), Graph) :-
 2083    rdf_md5(Graph, Hash).
 2084property_of_graph(modified(Boolean), Graph) :-
 2085    rdf_graph_modified_(Graph, Boolean, _).
 2086property_of_graph(source(URL), Graph) :-
 2087    rdf_graph_source_(Graph, URL, _).
 2088property_of_graph(source_last_modified(Time), Graph) :-
 2089    rdf_graph_source_(Graph, _, Time),
 2090    Time > 0.0.
 2091property_of_graph(triples(Count), Graph) :-
 2092    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.
 2101rdf_set_graph(Graph, modified(Modified)) :-
 2102    must_be(oneof([false]), Modified),
 2103    rdf_graph_clear_modified_(Graph).
 save_cache(+DB, +Cache) is det
Save triples belonging to DB in the file Cache.
 2110save_cache(DB, Cache) :-
 2111    current_prolog_flag(rdf_triple_format, Version),
 2112    setup_call_cleanup(
 2113        catch(open(Cache, write, CacheStream, [type(binary)]), _, fail),
 2114        rdf_save_db_(CacheStream, DB, Version),
 2115        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.
 2123assert_triples([], _).
 2124assert_triples([rdf(S,P,O)|T], DB) :-
 2125    !,
 2126    rdf_assert(S, P, O, DB),
 2127    assert_triples(T, DB).
 2128assert_triples([H|_], _) :-
 2129    throw(error(type_error(rdf_triple, H), _)).
 2130
 2131
 2132                 /*******************************
 2133                 *             RESET            *
 2134                 *******************************/
 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.
 2147rdf_reset_db :-
 2148    reset_gensym('_:genid'),
 2149    rdf_reset_db_.
 2150
 2151
 2152                 /*******************************
 2153                 *           SAVE RDF           *
 2154                 *******************************/
 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
 2216:- thread_local
 2217    named_anon/2,                   % +Resource, -Id
 2218    inlined/1.                      % +Resource
 2219
 2220rdf_save(File) :-
 2221    rdf_save2(File, []).
 2222
 2223rdf_save(Spec, M:Options0) :-
 2224    is_list(Options0),
 2225    !,
 2226    meta_options(save_meta_option, M:Options0, Options),
 2227    to_file(Spec, File),
 2228    rdf_save2(File, Options).
 2229rdf_save(Spec, _:DB) :-
 2230    atom(DB),                      % backward compatibility
 2231    !,
 2232    to_file(Spec, File),
 2233    rdf_save2(File, [graph(DB)]).
 2234
 2235save_meta_option(convert_typed_literal).
 2236
 2237to_file(URL, File) :-
 2238    atom(URL),
 2239    uri_file_name(URL, File),
 2240    !.
 2241to_file(File, File).
 2242
 2243rdf_save2(File, Options) :-
 2244    option(encoding(Encoding), Options, utf8),
 2245    valid_encoding(Encoding),
 2246    open_output(File, Encoding, Out, Close),
 2247    flag(rdf_db_saved_subjects, OSavedSubjects, 0),
 2248    flag(rdf_db_saved_triples, OSavedTriples, 0),
 2249    call_cleanup(rdf_do_save(Out, Options),
 2250                 Reason,
 2251                 cleanup_save(Reason,
 2252                              File,
 2253                              OSavedSubjects,
 2254                              OSavedTriples,
 2255                              Close)).
 2256
 2257open_output(stream(Out), Encoding, Out, Cleanup) :-
 2258    !,
 2259    stream_property(Out, encoding(Old)),
 2260    (   (   Old == Encoding
 2261        ;   Old == wchar_t          % Internal encoding
 2262        )
 2263    ->  Cleanup = true
 2264    ;   set_stream(Out, encoding(Encoding)),
 2265        Cleanup = set_stream(Out, encoding(Old))
 2266    ).
 2267open_output(File, Encoding, Out,
 2268            close(Out)) :-
 2269    open(File, write, Out, [encoding(Encoding)]).
 2270
 2271valid_encoding(Enc) :-
 2272    (   xml_encoding_name(Enc, _)
 2273    ->  true
 2274    ;   throw(error(domain_error(encoding, Enc), _))
 2275    ).
 2276
 2277
 2278cleanup_save(Reason,
 2279             File,
 2280             OSavedSubjects,
 2281             OSavedTriples,
 2282             Close) :-
 2283    call(Close),
 2284    flag(rdf_db_saved_subjects, SavedSubjects, OSavedSubjects),
 2285    flag(rdf_db_saved_triples, SavedTriples, OSavedTriples),
 2286    retractall(named_anon(_, _)),
 2287    retractall(inlined(_)),
 2288    (   Reason == exit
 2289    ->  print_message(informational,
 2290                      rdf(saved(File, SavedSubjects, SavedTriples)))
 2291    ;   format(user_error, 'Reason = ~w~n', [Reason])
 2292    ).
 2293
 2294rdf_do_save(Out, Options0) :-
 2295    rdf_save_header(Out, Options0, Options),
 2296    graph(Options, DB),
 2297    (   option(sorted(true), Options, false)
 2298    ->  (   var(DB)
 2299        ->  setof(Subject, rdf_subject(Subject), Subjects)
 2300        ;   findall(Subject, rdf(Subject, _, _, DB:_), SubjectList),
 2301            sort(SubjectList, Subjects)
 2302        ),
 2303        forall(member(Subject, Subjects),
 2304               rdf_save_non_anon_subject(Out, Subject, Options))
 2305    ;   forall(rdf_subject_in_graph(Subject, DB),
 2306               rdf_save_non_anon_subject(Out, Subject, Options))
 2307    ),
 2308    rdf_save_footer(Out),
 2309    !.                                  % dubious cut; without the
 2310                                        % 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.
 2321rdf_subject_in_graph(Subject, DB) :-
 2322    var(DB),
 2323    !,
 2324    rdf_subject(Subject).
 2325rdf_subject_in_graph(Subject, DB) :-
 2326    rdf_statistics(triples(AllTriples)),
 2327    rdf_graph_property(DB, triples(DBTriples)),
 2328    DBTriples > AllTriples // 10,
 2329    !,
 2330    rdf_resource(Subject),
 2331    (   rdf(Subject, _, _, DB:_)
 2332    ->  true
 2333    ).
 2334rdf_subject_in_graph(Subject, DB) :-
 2335    findall(Subject, rdf(Subject, _, _, DB:_), SubjectList),
 2336    list_to_set(SubjectList, Subjects),
 2337    member(Subject, Subjects).
 2338
 2339
 2340graph(Options0, DB) :-
 2341    strip_module(Options0, _, Options),
 2342    (   memberchk(graph(DB0), Options)
 2343    ->  DB = DB0
 2344    ;   memberchk(db(DB0), Options)
 2345    ->  DB = DB0
 2346    ;   true                            % leave unbound
 2347    ).
 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.
 2372rdf_save_header(Out, Options) :-
 2373    rdf_save_header(Out, Options, _).
 2374
 2375rdf_save_header(Out, Options, OptionsOut) :-
 2376    is_list(Options),
 2377    !,
 2378    option(encoding(Enc), Options, utf8),
 2379    xml_encoding(Enc, Encoding),
 2380    format(Out, '<?xml version=\'1.0\' encoding=\'~w\'?>~n', [Encoding]),
 2381    format(Out, '<!DOCTYPE rdf:RDF [', []),
 2382    header_namespaces(Options, NSIdList),
 2383    nsmap(NSIdList, NsMap),
 2384    append(Options, [nsmap(NsMap)], OptionsOut),
 2385    forall(member(Id=URI, NsMap),
 2386           (   xml_quote_attribute(URI, NSText0, Enc),
 2387               xml_escape_parameter_entity(NSText0, NSText),
 2388               format(Out, '~N    <!ENTITY ~w \'~w\'>', [Id, NSText])
 2389           )),
 2390    format(Out, '~N]>~n~n', []),
 2391    format(Out, '<rdf:RDF', []),
 2392    (   member(Id, NSIdList),
 2393        format(Out, '~N    xmlns:~w="&~w;"~n', [Id, Id]),
 2394        fail
 2395    ;   true
 2396    ),
 2397    (   option(base_uri(Base), Options),
 2398        option(write_xml_base(true), Options, true)
 2399    ->  xml_quote_attribute(Base, BaseText, Enc),
 2400        format(Out, '~N    xml:base="~w"~n', [BaseText])
 2401    ;   true
 2402    ),
 2403    (   memberchk(document_language(Lang), Options)
 2404    ->  format(Out, '~N    xml:lang="~w"', [Lang])
 2405    ;   true
 2406    ),
 2407    format(Out, '>~n', []).
 2408rdf_save_header(Out, FileRef, OptionsOut) :-    % compatibility
 2409    atom(FileRef),
 2410    rdf_save_header(Out, [graph(FileRef)], OptionsOut).
 2411
 2412xml_encoding(Enc, Encoding) :-
 2413    (   xml_encoding_name(Enc, Encoding)
 2414    ->  true
 2415    ;   throw(error(domain_error(rdf_encoding, Enc), _))
 2416    ).
 2417
 2418xml_encoding_name(ascii,       'US-ASCII').
 2419xml_encoding_name(iso_latin_1, 'ISO-8859-1').
 2420xml_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
 2427nsmap([], []).
 2428nsmap([Id|T0], [Id=URI|T]) :-
 2429    ns(Id, URI),
 2430    nsmap(T0, T).
 xml_escape_parameter_entity(+In, -Out) is det
Escape % as &#37; for entity declarations.
 2436xml_escape_parameter_entity(In, Out) :-
 2437    sub_atom(In, _, _, _, '%'),
 2438    !,
 2439    atom_codes(In, Codes),
 2440    phrase(escape_parent(Codes), OutCodes),
 2441    atom_codes(Out, OutCodes).
 2442xml_escape_parameter_entity(In, In).
 2443
 2444escape_parent([]) --> [].
 2445escape_parent([H|T]) -->
 2446    (   { H == 37 }
 2447    ->  "&#37;"
 2448    ;   [H]
 2449    ),
 2450    escape_parent(T).
 header_namespaces(Options, -List)
Get namespaces we will define as entities
 2457header_namespaces(Options, List) :-
 2458    memberchk(namespaces(NSL0), Options),
 2459    !,
 2460    sort([rdf,rdfs|NSL0], List).
 2461header_namespaces(Options, List) :-
 2462    graph(Options, DB),
 2463    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.
 2501:- thread_local
 2502    graph_prefix/3. 2503:- meta_predicate
 2504    rdf_graph_prefixes(?, -, :). 2505
 2506rdf_graph_prefixes(Graph, List) :-
 2507    rdf_graph_prefixes(Graph, List, []).
 2508
 2509rdf_graph_prefixes(Graph, List, M:QOptions) :-
 2510    is_list(QOptions),
 2511    !,
 2512    meta_options(is_meta, M:QOptions, Options),
 2513    option(filter(Filter), Options, true),
 2514    option(expand(Expand), Options, rdf_db),
 2515    option(min_count(MinCount), Options, 1),
 2516    option(get_prefix(GetPrefix), Options, iri_xml_namespace),
 2517    call_cleanup(prefixes(Expand, Graph, Prefixes, Filter, MinCount, GetPrefix),
 2518                 retractall(graph_prefix(_,_,_))),
 2519    sort(Prefixes, List).
 2520rdf_graph_prefixes(Graph, List, M:Filter) :-
 2521    rdf_graph_prefixes(Graph, List, M:[filter(Filter)]).
 2522
 2523is_meta(filter).
 2524is_meta(expand).
 2525is_meta(get_prefix).
 2526
 2527
 2528prefixes(Expand, Graph, Prefixes, Filter, MinCount, GetPrefix) :-
 2529    (   call(Expand, S, P, O, Graph),
 2530        add_ns(subject, GetPrefix, Filter, S, MinCount, s(S)),
 2531        add_ns(predicate, GetPrefix, Filter, P, MinCount, sp(S,P)),
 2532        add_ns_obj(GetPrefix, Filter, O, MinCount, spo(S,P,O)),
 2533        fail
 2534    ;   true
 2535    ),
 2536    findall(Prefix, graph_prefix(Prefix, MinCount, _), Prefixes).
 2537
 2538add_ns(Where, GetPrefix, Filter, S, MinCount, Context) :-
 2539    \+ rdf_is_bnode(S),
 2540    call(GetPrefix, S, Full),
 2541    Full \== '',
 2542    !,
 2543    (   graph_prefix(Full, MinCount, _)
 2544    ->  true
 2545    ;   Filter == true
 2546    ->  add_ns(Full, Context)
 2547    ;   call(Filter, Where, Full, S)
 2548    ->  add_ns(Full, Context)
 2549    ;   true
 2550    ).
 2551add_ns(_, _, _, _, _, _).
 2552
 2553add_ns(Full, Context) :-
 2554    graph_prefix(Full, _, Contexts),
 2555    memberchk(Context, Contexts),
 2556    !.
 2557add_ns(Full, Context) :-
 2558    retract(graph_prefix(Full, C0, Contexts)),
 2559    !,
 2560    C1 is C0+1,
 2561    asserta(graph_prefix(Full, C1, [Context|Contexts])).
 2562add_ns(Full, _) :-
 2563    ns(_, Full),
 2564    !,
 2565    asserta(graph_prefix(Full, _, _)).
 2566add_ns(Full, Context) :-
 2567    asserta(graph_prefix(Full, 1, [Context])).
 2568
 2569
 2570add_ns_obj(GetPrefix, Filter, O, MinCount, Context) :-
 2571    atom(O),
 2572    !,
 2573    add_ns(object, GetPrefix, Filter, O, MinCount, Context).
 2574add_ns_obj(GetPrefix, Filter, literal(type(Type, _)), MinCount, _) :-
 2575    atom(Type),
 2576    !,
 2577    add_ns(type, GetPrefix, Filter, Type, MinCount, t(Type)).
 2578add_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.
 2588used_namespace_entities(List, Graph) :-
 2589    decl_used_predicate_ns(Graph),
 2590    used_namespaces(List, Graph).
 2591
 2592used_namespaces(List, DB) :-
 2593    rdf_graph_prefixes(DB, FullList),
 2594    ns_abbreviations(FullList, List0),
 2595    sort([rdf|List0], List).
 2596
 2597ns_abbreviations([], []).
 2598ns_abbreviations([H0|T0], [H|T]) :-
 2599    ns(H, H0),
 2600    !,
 2601    ns_abbreviations(T0, T).
 2602ns_abbreviations([_|T0], T) :-
 2603    ns_abbreviations(T0, T).
 2604
 2605
 2606/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 2607For every URL used as a predicate  we   *MUST*  define a namespace as we
 2608cannot use names holding /, :, etc. as XML identifiers.
 2609- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 2610
 2611:- thread_local
 2612    predicate_ns/2. 2613
 2614decl_used_predicate_ns(DB) :-
 2615    retractall(predicate_ns(_,_)),
 2616    (   rdf_current_predicate(P, DB),
 2617        decl_predicate_ns(P),
 2618        fail
 2619    ;   true
 2620    ).
 2621
 2622decl_predicate_ns(Pred) :-
 2623    predicate_ns(Pred, _),
 2624    !.
 2625decl_predicate_ns(Pred) :-
 2626    rdf_global_id(NS:Local, Pred),
 2627    xml_name(Local),
 2628    !,
 2629    assert(predicate_ns(Pred, NS)).
 2630decl_predicate_ns(Pred) :-
 2631    atom_codes(Pred, Codes),
 2632    append(NSCodes, LocalCodes, Codes),
 2633    xml_codes(LocalCodes),
 2634    !,
 2635    (   NSCodes \== []
 2636    ->  atom_codes(NS, NSCodes),
 2637        (   ns(Id, NS)
 2638        ->  assert(predicate_ns(Pred, Id))
 2639        ;   between(1, infinite, N),
 2640            atom_concat(ns, N, Id),
 2641            \+ ns(Id, _)
 2642        ->  rdf_register_ns(Id, NS),
 2643            print_message(informational,
 2644                          rdf(using_namespace(Id, NS)))
 2645        ),
 2646        assert(predicate_ns(Pred, Id))
 2647    ;   assert(predicate_ns(Pred, -)) % no namespace used
 2648    ).
 2649
 2650xml_codes([]).
 2651xml_codes([H|T]) :-
 2652    xml_code(H),
 2653    xml_codes(T).
 2654
 2655xml_code(X) :-
 2656    code_type(X, csym),
 2657    !.
 2658xml_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.
 2667rdf_save_footer(Out) :-
 2668    retractall(named_anon(_, _)),
 2669    retractall(inlined(_)),
 2670    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.
 2677rdf_save_non_anon_subject(_Out, Subject, Options) :-
 2678    rdf_is_bnode(Subject),
 2679    (   memberchk(anon(false), Options)
 2680    ;   graph(Options, DB),
 2681        rdf_db(_, _, Subject, DB)
 2682    ),
 2683    !.
 2684rdf_save_non_anon_subject(Out, Subject, Options) :-
 2685    rdf_save_subject(Out, Subject, Options),
 2686    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.
 2701rdf_save_subject(Out, Subject, Options) :-
 2702    is_list(Options),
 2703    !,
 2704    option(base_uri(BaseURI), Options, '-'),
 2705    (   rdf_save_subject(Out, Subject, BaseURI, 0, Options)
 2706    ->  format(Out, '~n', [])
 2707    ;   throw(error(rdf_save_failed(Subject), 'Internal error'))
 2708    ).
 2709rdf_save_subject(Out, Subject, DB) :-
 2710    (   var(DB)
 2711    ->  rdf_save_subject(Out, Subject, [])
 2712    ;   rdf_save_subject(Out, Subject, [graph(DB)])
 2713    ).
 rdf_save_subject(+Out:stream, +Subject:resource, +BaseURI, +Indent:int, +Options) is det
Save properties of Subject.
Arguments:
Indent- Current indentation
 2723rdf_save_subject(_, Subject, _, _, _) :-
 2724    inlined(Subject),
 2725    !.
 2726rdf_save_subject(Out, Subject, BaseURI, Indent, Options) :-
 2727    do_save_subject(Out, Subject, BaseURI, Indent, Options).
 2728
 2729do_save_subject(Out, Subject, BaseURI, Indent, Options) :-
 2730    graph(Options, DB),
 2731    findall(Pred=Object, rdf_db(Subject, Pred, Object, DB), Atts0),
 2732    sort(Atts0, Atts),              % remove duplicates
 2733    length(Atts, L),
 2734    (   length(Atts0, L0),
 2735        Del is L0-L,
 2736        Del > 0
 2737    ->  print_message(informational,
 2738                      rdf(save_removed_duplicates(Del, Subject)))
 2739    ;   true
 2740    ),
 2741    rdf_save_subject(Out, Subject, BaseURI, Atts, Indent, Options),
 2742    flag(rdf_db_saved_triples, X, X+L).
 2743
 2744rdf_db(Subject, Pred, Object, DB) :-
 2745    var(DB),
 2746    !,
 2747    rdf(Subject, Pred, Object).
 2748rdf_db(Subject, Pred, Object, DB) :-
 2749    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.
 2756rdf_save_subject(Out, Subject, BaseURI, Atts, Indent, Options) :-
 2757    rdf_equal(rdf:type, RdfType),
 2758    select(RdfType=Type, Atts, Atts1),
 2759    \+ rdf_is_bnode(Type),
 2760    rdf_id(Type, BaseURI, TypeId),
 2761    xml_is_name(TypeId),
 2762    !,
 2763    format(Out, '~*|<', [Indent]),
 2764    rdf_write_id(Out, TypeId),
 2765    save_about(Out, BaseURI, Subject, Options),
 2766    save_attributes(Atts1, BaseURI, Out, TypeId, Indent, Options).
 2767rdf_save_subject(Out, Subject, BaseURI, Atts, Indent, Options) :-
 2768    format(Out, '~*|<rdf:Description', [Indent]),
 2769    save_about(Out, BaseURI, Subject, Options),
 2770    save_attributes(Atts, BaseURI, Out, rdf:'Description', Indent, Options).
 2771
 2772xml_is_name(_NS:Atom) :-
 2773    !,
 2774    xml_name(Atom).
 2775xml_is_name(Atom) :-
 2776    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.
 2783save_about(Out, _BaseURI, Subject, _Options) :-
 2784    rdf_is_bnode(Subject),
 2785    !,
 2786    (   named_anon(Subject, NodeID)
 2787    ->  format(Out, ' rdf:nodeID="~w"', [NodeID])
 2788    ;   true
 2789    ).
 2790save_about(Out, BaseURI, Subject, Options) :-
 2791    option(encoding(Encoding), Options, utf8),
 2792    rdf_value(Subject, BaseURI, QSubject, Encoding),
 2793    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.
 2801save_attributes(Atts, BaseURI, Out, Element, Indent, Options) :-
 2802    split_attributes(Atts, InTag, InBody, Options),
 2803    SubIndent is Indent + 2,
 2804    save_attributes2(InTag, BaseURI, tag, Out, SubIndent, Options),
 2805    (   InBody == []
 2806    ->  format(Out, '/>~n', [])
 2807    ;   format(Out, '>~n', []),
 2808        save_attributes2(InBody, BaseURI, body, Out, SubIndent, Options),
 2809        format(Out, '~N~*|</', [Indent]),
 2810        rdf_write_id(Out, Element),
 2811        format(Out, '>~n', [])
 2812    ).
 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.
 2820split_attributes(Atts, [], Atts, Options) :-
 2821    option(xml_attributes(false), Options),
 2822    !.
 2823split_attributes(Atts, HeadAttr, BodyAttr, _) :-
 2824    duplicate_attributes(Atts, Dupls, Singles),
 2825    simple_literal_attributes(Singles, HeadAttr, Rest),
 2826    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.
 2833duplicate_attributes([], [], []).
 2834duplicate_attributes([H|T], Dupls, Singles) :-
 2835    H = (Name=_),
 2836    named_attributes(Name, T, D, R),
 2837    D \== [],
 2838    append([H|D], Dupls2, Dupls),
 2839    !,
 2840    duplicate_attributes(R, Dupls2, Singles).
 2841duplicate_attributes([H|T], Dupls2, [H|Singles]) :-
 2842    duplicate_attributes(T, Dupls2, Singles).
 2843
 2844named_attributes(_, [], [], []) :- !.
 2845named_attributes(Name, [H|T], D, R) :-
 2846    (   H = (Name=_)
 2847    ->  D = [H|DT],
 2848        named_attributes(Name, T, DT, R)
 2849    ;   R = [H|RT],
 2850        named_attributes(Name, T, D, RT)
 2851    ).
 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.
 2858simple_literal_attributes([], [], []).
 2859simple_literal_attributes([H|TA], [H|TI], B) :-
 2860    in_tag_attribute(H),
 2861    !,
 2862    simple_literal_attributes(TA, TI, B).
 2863simple_literal_attributes([H|TA], I, [H|TB]) :-
 2864    simple_literal_attributes(TA, I, TB).
 2865
 2866in_tag_attribute(_=literal(Text)) :-
 2867    atom(Text),                     % may not have lang qualifier
 2868    atom_length(Text, Len),
 2869    Len < 60.
 save_attributes2(+List, +BaseURI, +TagOrBody, +Stream, +Indent, +Options)
Save a list of attributes.
 2875save_attributes2([], _, _, _, _, _).
 2876save_attributes2([H|T], BaseURI, Where, Out, Indent, Options) :-
 2877    save_attribute(Where, H, BaseURI, Out, Indent, Options),
 2878    save_attributes2(T, BaseURI, Where, Out, Indent, Options).
 2879
 2880save_attribute(tag, Name=literal(Value), BaseURI, Out, Indent, Options) :-
 2881    AttIndent is Indent + 2,
 2882    rdf_id(Name, BaseURI, NameText),
 2883    option(encoding(Encoding), Options, utf8),
 2884    xml_quote_attribute(Value, QVal, Encoding),
 2885    format(Out, '~N~*|', [AttIndent]),
 2886    rdf_write_id(Out, NameText),
 2887    format(Out, '="~w"', [QVal]).
 2888save_attribute(body, Name=literal(Literal0), BaseURI, Out, Indent, Options) :-
 2889    !,
 2890    rdf_id(Name, BaseURI, NameText),
 2891    (   memberchk(convert_typed_literal(Converter), Options),
 2892        call(Converter, Type, Content, Literal0)
 2893    ->  Literal = type(Type, Content)
 2894    ;   Literal = Literal0
 2895    ),
 2896    save_body_literal(Literal, NameText, BaseURI, Out, Indent, Options).
 2897save_attribute(body, Name=Value, BaseURI, Out, Indent, Options) :-
 2898    rdf_is_bnode(Value),
 2899    !,
 2900    rdf_id(Name, BaseURI, NameText),
 2901    format(Out, '~N~*|<', [Indent]),
 2902    rdf_write_id(Out, NameText),
 2903    (   named_anon(Value, NodeID)
 2904    ->  format(Out, ' rdf:nodeID="~w"/>', [NodeID])
 2905    ;   (   rdf(S1, Name, Value),
 2906            rdf(S2, P2, Value),
 2907            (S1 \== S2 ; Name \== P2)
 2908        ->  predicate_property(named_anon(_,_), number_of_clauses(N)),
 2909            atom_concat('bn', N, NodeID),
 2910            assertz(named_anon(Value, NodeID))
 2911        ;   true
 2912        ),
 2913        SubIndent is Indent + 2,
 2914        (   rdf_collection(Value)
 2915        ->  save_about(Out, BaseURI, Value, Options),
 2916            format(Out, ' rdf:parseType="Collection">~n', []),
 2917            rdf_save_list(Out, Value, BaseURI, SubIndent, Options)
 2918        ;   format(Out, '>~n', []),
 2919            rdf_save_subject(Out, Value, BaseURI, SubIndent, Options)
 2920        ),
 2921        format(Out, '~N~*|</', [Indent]),
 2922        rdf_write_id(Out, NameText),
 2923        format(Out, '>~n', [])
 2924    ).
 2925save_attribute(body, Name=Value, BaseURI, Out, Indent, Options) :-
 2926    option(inline(true), Options),
 2927    has_attributes(Value, Options),
 2928    \+ inlined(Value),
 2929    !,
 2930    assertz(inlined(Value)),
 2931    rdf_id(Name, BaseURI, NameText),
 2932    format(Out, '~N~*|<', [Indent]),
 2933    rdf_write_id(Out, NameText),
 2934    SubIndent is Indent + 2,
 2935    (   rdf_collection(Value)
 2936    ->  save_about(Out, BaseURI, Value, Options),
 2937        format(Out, ' rdf:parseType="Collection">~n', []),
 2938        rdf_save_list(Out, Value, BaseURI, SubIndent, Options)
 2939    ;   format(Out, '>~n', []),
 2940        do_save_subject(Out, Value, BaseURI, SubIndent, Options)
 2941    ),
 2942    format(Out, '~N~*|</', [Indent]),
 2943    rdf_write_id(Out, NameText),
 2944    format(Out, '>~n', []).
 2945save_attribute(body, Name=Value, BaseURI, Out, Indent, Options) :-
 2946    option(encoding(Encoding), Options, utf8),
 2947    rdf_value(Value, BaseURI, QVal, Encoding),
 2948    rdf_id(Name, BaseURI, NameText),
 2949    format(Out, '~N~*|<', [Indent]),
 2950    rdf_write_id(Out, NameText),
 2951    format(Out, ' rdf:resource="~w"/>', [QVal]).
 2952
 2953has_attributes(URI, Options) :-
 2954    graph(Options, DB),
 2955    rdf_db(URI, _, _, DB),
 2956    !.
 save_body_literal(+Literal, +NameText, +BaseURI, +Out, +Indent, +Options)
 2961save_body_literal(lang(Lang, Value),
 2962                  NameText, BaseURI, Out, Indent, Options) :-
 2963    !,
 2964    format(Out, '~N~*|<', [Indent]),
 2965    rdf_write_id(Out, NameText),
 2966    (   memberchk(document_language(Lang), Options)
 2967    ->  write(Out, '>')
 2968    ;   rdf_id(Lang, BaseURI, LangText),
 2969        format(Out, ' xml:lang="~w">', [LangText])
 2970    ),
 2971    save_attribute_value(Value, Out, Options),
 2972    write(Out, '</'), rdf_write_id(Out, NameText), write(Out, '>').
 2973save_body_literal(type(Type, DOM),
 2974                  NameText, _BaseURI, Out, Indent, Options) :-
 2975    rdf_equal(Type, rdf:'XMLLiteral'),
 2976    !,
 2977    (   atom(DOM)
 2978    ->  format(Out, '~N~*|<', [Indent]),
 2979        rdf_write_id(Out, NameText),
 2980        format(Out, ' rdf:parseType="Literal">~w</', [DOM]),
 2981        rdf_write_id(Out, NameText), write(Out, '>')
 2982    ;   save_xml_literal(DOM, NameText, Out, Indent, Options)
 2983    ).
 2984save_body_literal(type(Type, Value),
 2985                  NameText, BaseURI, Out, Indent, Options) :-
 2986    !,
 2987    format(Out, '~N~*|<', [Indent]),
 2988    rdf_write_id(Out, NameText),
 2989    option(encoding(Encoding), Options, utf8),
 2990    rdf_value(Type, BaseURI, QVal, Encoding),
 2991    format(Out, ' rdf:datatype="~w">', [QVal]),
 2992    save_attribute_value(Value, Out, Options),
 2993    write(Out, '</'), rdf_write_id(Out, NameText), write(Out, '>').
 2994save_body_literal(Literal,
 2995                  NameText, _, Out, Indent, Options) :-
 2996    atomic(Literal),
 2997    !,
 2998    format(Out, '~N~*|<', [Indent]),
 2999    rdf_write_id(Out, NameText),
 3000    write(Out, '>'),
 3001    save_attribute_value(Literal, Out, Options),
 3002    write(Out, '</'), rdf_write_id(Out, NameText), write(Out, '>').
 3003save_body_literal(DOM,
 3004                  NameText, BaseURI, Out, Indent, Options) :-
 3005    rdf_equal(Type, rdf:'XMLLiteral'),
 3006    save_body_literal(type(Type, DOM),
 3007                      NameText, BaseURI, Out, Indent, Options).
 3008
 3009save_attribute_value(Value, Out, Options) :-  % strings
 3010    (	atom(Value)
 3011    ;	string(Value)
 3012    ),
 3013    !,
 3014    option(encoding(Encoding), Options, utf8),
 3015    xml_quote_cdata(Value, QVal, Encoding),
 3016    write(Out, QVal).
 3017save_attribute_value(Value, Out, _Options) :-  % numbers
 3018    number(Value),
 3019    !,
 3020    writeq(Out, Value).             % quoted: preserve floats
 3021save_attribute_value(Value, _Out, _Options) :-
 3022    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.

 3036save_xml_literal(DOM, Attr, Out, Indent, Options) :-
 3037    xml_is_dom(DOM),
 3038    !,
 3039    memberchk(nsmap(NsMap), Options),
 3040    id_to_atom(Attr, Atom),
 3041    xml_write(Out,
 3042              element(Atom, ['rdf:parseType'='Literal'], DOM),
 3043              [ header(false),
 3044                indent(Indent),
 3045                nsmap(NsMap)
 3046              ]).
 3047save_xml_literal(NoDOM, _, _, _, _) :-
 3048    must_be(xml_dom, NoDOM).
 3049
 3050id_to_atom(NS:Local, Atom) :-
 3051    !,
 3052    atomic_list_concat([NS,Local], :, Atom).
 3053id_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.
 3063:- rdf_meta
 3064    rdf_collection(r),
 3065    collection_p(r,r). 3066
 3067rdf_collection(rdf:nil) :- !.
 3068rdf_collection(Cell) :-
 3069    rdf_is_bnode(Cell),
 3070    findall(F, rdf(Cell, rdf:first, F), [_]),
 3071    findall(F, rdf(Cell, rdf:rest, F), [Rest]),
 3072    forall(rdf(Cell, P, V),
 3073           collection_p(P, V)),
 3074    rdf_collection(Rest).
 3075
 3076collection_p(rdf:first, V) :- atom(V).
 3077collection_p(rdf:rest, _).
 3078collection_p(rdf:type, rdf:'List').
 rdf_save_list(+Out, +List, +BaseURI, +Indent, +Options)
 3083rdf_save_list(_, List, _, _, _) :-
 3084    rdf_equal(List, rdf:nil),
 3085    !.
 3086rdf_save_list(Out, List, BaseURI, Indent, Options) :-
 3087    rdf_has(List, rdf:first, First),
 3088    (   rdf_is_bnode(First)
 3089    ->  nl(Out),
 3090        rdf_save_subject(Out, First, BaseURI, Indent, Options)
 3091    ;   option(encoding(Encoding), Options, utf8),
 3092        rdf_value(First, BaseURI, QVal, Encoding),
 3093        format(Out, '~N~*|<rdf:Description rdf:about="~w"/>',
 3094               [Indent, QVal])
 3095    ),
 3096    flag(rdf_db_saved_triples, X, X+3),
 3097    (   rdf_has(List, rdf:rest, List2),
 3098        \+ rdf_equal(List2, rdf:nil)
 3099    ->  rdf_save_list(Out, List2, BaseURI, Indent, Options)
 3100    ;   true
 3101    ).
 rdf_id(+Resource, +BaseURI, -NSLocal)
Generate a NS:Local name for Resource given the indicated default namespace. This call is used for elements.
 3109rdf_id(Id, BaseURI, Local) :-
 3110    assertion(atom(BaseURI)),
 3111    atom_concat(BaseURI, Local, Id),
 3112    sub_atom(Local, 0, 1, _, #),
 3113    !.
 3114rdf_id(Id, _, NS:Local) :-
 3115    iri_xml_namespace(Id, Full, Local),
 3116    ns(NS, Full),
 3117    !.
 3118rdf_id(Id, _, NS:Local) :-
 3119    ns(NS, Full),
 3120    Full \== '',
 3121    atom_concat(Full, Local, Id),
 3122    !.
 3123rdf_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.
 3131rdf_write_id(Out, NS:Local) :-
 3132    !,
 3133    format(Out, '~w:~w', [NS, Local]).
 3134rdf_write_id(Out, Atom) :-
 3135    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.
 3144rdf_value(Base, Base, '', _) :- !.
 3145rdf_value(V, Base, Text, Encoding) :-
 3146    atom_concat(Base, Local, V),
 3147    sub_atom(Local, 0, _, _, #),
 3148    !,
 3149    xml_quote_attribute(Local, Text, Encoding).
 3150rdf_value(V, _, Text, Encoding) :-
 3151    ns(NS, Full),
 3152    atom_concat(Full, Local, V),
 3153    xml_is_name(Local),
 3154    !,
 3155    xml_quote_attribute(Local, QLocal, Encoding),
 3156    atomic_list_concat(['&', NS, (';'), QLocal], Text).
 3157rdf_value(V, _, Q, Encoding) :-
 3158    xml_quote_attribute(V, Q, Encoding).
 3159
 3160
 3161                 /*******************************
 3162                 *       MATCH AND COMPARE      *
 3163                 *******************************/
 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.
 3186                 /*******************************
 3187                 *      DEPRECATED MATERIAL     *
 3188                 *******************************/
 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).
 3198rdf_split_url(Prefix, Local, URL) :-
 3199    atomic(URL),
 3200    !,
 3201    iri_xml_namespace(URL, Prefix, Local).
 3202rdf_split_url(Prefix, Local, URL) :-
 3203    atom_concat(Prefix, Local, URL).
 rdf_url_namespace(+URL, -Namespace)
Namespace is the namespace of URL.
deprecated
- Use iri_xml_namespace/2
 3211rdf_url_namespace(URL, Prefix) :-
 3212    iri_xml_namespace(URL, Prefix).
 3213
 3214
 3215                 /*******************************
 3216                 *            LITERALS          *
 3217                 *******************************/
 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.
 3305                 /*******************************
 3306                 *             MISC             *
 3307                 *******************************/
 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.
 3404                 /*******************************
 3405                 *             MESSAGES         *
 3406                 *******************************/
 3407
 3408:- multifile
 3409    prolog:message//1. 3410
 3411prolog:message(rdf(Term)) -->
 3412    message(Term).
 3413
 3414message(loaded(How, What, BaseURI, Triples, Time)) -->
 3415    how(How),
 3416    source(What),
 3417    into(What, BaseURI),
 3418    in_time(Triples, Time).
 3419message(save_removed_duplicates(N, Subject)) -->
 3420    [ 'Removed ~d duplicate triples about "~p"'-[N,Subject] ].
 3421message(saved(File, SavedSubjects, SavedTriples)) -->
 3422    [ 'Saved ~D triples about ~D subjects into ~p'-
 3423      [SavedTriples, SavedSubjects, File]
 3424    ].
 3425message(using_namespace(Id, NS)) -->
 3426    [ 'Using namespace id ~w for ~w'-[Id, NS] ].
 3427message(inconsistent_cache(DB, Graphs)) -->
 3428    [ 'RDF cache file for ~w contains the following graphs'-[DB], nl,
 3429      '~t~8|~p'-[Graphs]
 3430    ].
 3431message(guess_format(Ext)) -->
 3432    [ 'Unknown file-extension: ~w.  Assuming RDF/XML'-[Ext] ].
 3433message(meta(not_expanded(G))) -->
 3434    [ 'rdf_meta/1: ~p is not expanded'-[G] ].
 3435message(deprecated(rdf_unload(Graph))) -->
 3436    [ 'rdf_unload/1: Use ~q'-[rdf_unload_graph(Graph)] ].
 3437
 3438
 3439how(load)   --> [ 'Loaded' ].
 3440how(parsed) --> [ 'Parsed' ].
 3441
 3442source(SourceURL) -->
 3443    { uri_file_name(SourceURL, File),
 3444      !,
 3445      file_base_name(File, Base)    % TBD: relative file?
 3446    },
 3447    [ ' "~w"'-[Base] ].
 3448source(SourceURL) -->
 3449    [ ' "~w"'-[SourceURL] ].
 3450
 3451into(_, _) --> [].                      % TBD
 3452
 3453in_time(Triples, ParseTime) -->
 3454    [ ' in ~2f sec; ~D triples'-[ParseTime, Triples]
 3455    ]