1:- module(terminus_store, [
    2              open_memory_store/1,
    3              open_directory_store/2,
    4
    5              create_named_graph/3,
    6              open_named_graph/3,
    7              delete_named_graph/2,
    8
    9              head/2,
   10              head/3,
   11              nb_set_head/2,
   12              nb_force_set_head/2,
   13              nb_force_set_head/3,
   14
   15              open_write/2,
   16
   17              nb_add_triple/4,
   18              nb_remove_triple/4,
   19              nb_commit/2,
   20              builder_committed/1,
   21              nb_apply_delta/2,
   22              nb_apply_diff/2,
   23
   24              node_and_value_count/2,
   25              predicate_count/2,
   26              subject_id/3,
   27              predicate_id/3,
   28              object_id/3,
   29
   30              id_triple/4,
   31              triple/4,
   32
   33              id_triple_addition/4,
   34              triple_addition/4,
   35
   36              id_triple_removal/4,
   37              triple_removal/4,
   38
   39              sp_card/4,
   40
   41              parent/2,
   42              squash/2,
   43
   44              layer_addition_count/2,
   45              layer_removal_count/2,
   46              layer_total_addition_count/2,
   47              layer_total_removal_count/2,
   48              layer_total_triple_count/2,
   49
   50              layer_to_id/2,
   51              store_id_layer/3,
   52
   53              pack_export/3,
   54              pack_layerids_and_parents/2,
   55              pack_import/3,
   56
   57              csv_builder/3,
   58              csv_builder/4,
   59              csv_builder/5,
   60              csv_iri/3,
   61
   62              count_layer_stack_size/2,
   63
   64              rollup/1,
   65              rollup_upto/2,
   66              imprecise_rollup_upto/2,
   67
   68              layer_stack_names/2,
   69              layer_equals/2
   70            ]).   71
   72% There is two ways that this library is used.
   73% 1. Standalone - in this case we need to load the internal foreign library.
   74% 2. As part of TerminusDB - in this case we expect all the foreign
   75%    predicates to have been preloaded into the module '$terminus_store'.
   76%
   77% The reason for this is that TerminusDB builds its own rust module
   78% which bundles the internal foreign library. This is necessary cause
   79% otherwise TerminusDB is unable to make use of the types defined in
   80% this library, as these are not exposed through ordinarly shared
   81% objects. TerminusDB needs these types in order to build
   82% TerminusDB-specific native logic that works with store, graph,
   83% layer, and builder blobs.
   84%
   85% In order to switch between the two kinds of behavior, TerminusDB
   86% defines a special prolog flag, 'terminusdb_monolithic_module'. This
   87% suppresses loading of the internal library, and instead imports
   88% foreign predicates which are expected to have been preloaded into
   89% '$terminus_store'.
   90:- if(current_prolog_flag(terminusdb_monolithic_module, true)).   91:- add_import_module('terminus_store', '$terminus_store', start).   92:- else.   93:- use_foreign_library(foreign(libterminus_store)).   94:- endif.   95
   96:- use_module(library(lists)).   97:- use_module(library(option)).   98:- use_module(library(plunit)).   99:- use_module(library(random)).  100
  101%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  102%%% pldocs for the foreign predicates %%%
  103%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 open_memory_store(-Store:store) is det
Opens an in-memory store and unifies it with Store.
Arguments:
Store- the returned in-memory store.
 open_directory_store(+Path:text, -Store:store) is det
Opens a store backed by a directory, and unifies it with Store.

This predicate does not check if the directory actually exists, but other store-related predicates will error when used with a store backed by a non-existent directory.

Arguments:
Path- a file system path to the store directory. This can be either absolute and relative.
Store- the returned directory store.
 create_named_graph(+Store:store, +Name:text, -Graph:named_graph) is det
Create a new named graph with the given name, and unifies it with Graph.
Arguments:
Store- the store to create the graph in.
Name- the name which the new graph should have.
Graph- the returned named graph.
throws
- if a graph with the given name already exists.
 open_named_graph(+Store:store, +Name:text, -Graph:named_graph) is semidet
Opens an existing named graph with the given name.

Fails if no graph with that name exists.

Arguments:
Store- the store to create the graph in.
Name- the name of the graph to be opened.
Graph- the returned named graph.
 delete_named_graph(+Store:store, +Name:text) is semidet
Deletes an existing named graph with the given name.

Fails if no graph with that name exists.

Arguments:
Store- the store to create the graph in.
Name- the name of the graph to be opened.
 head(+Graph:named_graph, -Layer:layer) is semidet
Retrieve the layer that a named graph points at. This is the equivalent of opening a read transaction with snapshot isolation on a named graph.

Fails if the given graph has no head yet.

Arguments:
Graph- the named graph to retrieve the head layer from.
Layer- the returned head layer.
 head(+Graph:named_graph, -Layer:layer, -Version:version) is semidet
Retrieve the layer that a named graph points at and retrieve the version. This is the equivalent of opening a read transaction with snapshot isolation on a named graph.

Fails if the given graph has no head yet.

Arguments:
Graph- the named graph to retrieve the head layer from.
Layer- the returned head layer.
Version- the version of the label.
 nb_set_head(+Graph:named_graph, +Layer:layer) is semidet
Set the given layer as the new head of the given graph.

Fails if the new layer is not a proper child of the current head.

This predicate does not support backtracking.

Arguments:
Graph- the named graph to set the head layer of.
Layer- the layer to make the new head of the graph.
 nb_set_head(+Graph:named_graph, +Layer:layer, +Version:version) is semidet
Set the given layer as the new head of the given graph and checks if version matches.

Fails if the new layer is not a proper child of the current head.

This predicate does not support backtracking.

Arguments:
Graph- the named graph to set the head layer of.
Layer- the layer to make the new head of the graph.
Version- the version of the label.
 open_write(+Store_Or_Layer:term, -Builder:layer_builder) is det
Creates a layer builder from either a parent layer, or a store.

When Store_Or_Layer is a store, the resulting builder will create a base layer.

When Store_Or_Layer is a layer, the resulting builder will create a child layer whose parent is the given layer.

Arguments:
Store_Or_layer- a store when creating a new base layer, or the parent layer when creating a child layer.
Builder- a layer builder to create the new layer.
 nb_add_id_triple(+Builder:layer_builder, +Subject_Id:integer, +Predicate_Id:integer, +Object_Id:integer) is semidet
Add the given subject, predicate and object as a triple to the builder object.

This fails if any of the Ids is out of range, or if the triple already exists, either in this builder or in a parent layer.

Arguments:
Builder- the builder object to add this triple to.
Subject_Id- the id of the triple subject.
Predicate_Id- the id of the triple predicate.
Object_Id- the id of the triple object.
 nb_add_string_node_triple(+Builder:layer_builder, +Subject:text, +Predicate:text, +Object:text) is semidet
Add the given subject, predicate, and object as a triple to the builder object. The object is interpreted as pointing at a node, rather than being a literal value.

This fails if the triple already exists in this builder object or a parent layer.

Arguments:
Builder- the builder object to add this triple to.
Subject- the triple subject.
Predicate- the triple predicate.
Object- the triple object, which is interpreted as a node.
 nb_add_string_value_triple(+Builder:layer_builder, +Subject:text, +Predicate:text, +Object:text) is semidet
Add the given subject, predicate, and object as a triple to the builder object. The object is interpreted as a value, rather than a node.

This fails if the triple already exists in this builder object or a parent layer.

Arguments:
Builder- the builder object to add this triple to.
Subject- the triple subject.
Predicate- the triple predicate.
Object- the triple object, which is interpreted as a value.
 nb_remove_id_triple(+Builder:layer_builder, +Subject_Id:integer, +Predicate_Id:integer, +Object_Id:integer) is semidet
Add the given subject, predicate and object as a triple removal to the builder object.

This fails if any of the Ids is out of range, or if the triple does not exist in a parent layer, or if the removal has already been registered in this builder.

Arguments:
Builder- the builder object to add this triple removal to.
Subject_Id- the id of the triple subject.
Predicate_Id- the id of the triple predicate.
Object_Id- the id of the triple object.
 nb_remove_string_node_triple(+Builder:layer_builder, +Subject:text, +Predicate:text, +Object:text) is semidet
Add the given subject, predicate, and object as a triple removal to the builder object. The object is interpreted as pointing at a node, rather than being a literal value.

This fails if the triple does not exist in a parent layer, or if the removal has already been registered in this builder.

Arguments:
Builder- the builder object to add this triple removal to.
Subject- the triple subject.
Predicate- the triple predicate.
Object- the triple object, which is interpreted as a node.
 nb_remove_string_value_triple(+Builder:layer_builder, +Subject:text, +Predicate:text, +Object:text) is semidet
Add the given subject, predicate, and object as a triple removal to the builder object. The object is interpreted as a value, rather than a node.

This fails if the triple does not exist in a parent layer, or if the removal has already been registered in this builder.

Arguments:
Builder- the builder object to add this triple removal to.
Subject- the triple subject.
Predicate- the triple predicate.
Object- the triple object, which is interpreted as a node.
 nb_apply_delta(+Builder:layer_builder, +Layer:layer) is det
Add and remove all additions and removals from Layer into Builder
Arguments:
Builder- the layer builder to make changes to.
Layer- the layer that will apply changes from.
throws
- if the builder has already been committed.
 nb_apply_diff(+Builder:layer_builder, +Layer:layer) is det
Make whatever changes are necessary to Builder, to bring it in line with Layer. Our final visabile state should be as layer, so we are calculating the diff which should go into builder to do so.
Arguments:
Builder- the layer builder to make changes to.
Layer- the layer that we will view as a prototype.
throws
- if the builder has already been committed.
 nb_commit(+Builder:layer_builder, -Layer:layer) is det
Commit the layer builder, turning it into a layer.
Arguments:
Builder- the layer builder to commit.
Layer- the layer that will be returned.
throws
- if the builder has already been committed.
 node_and_value_count(+Layer:layer, -Count:integer) is det
Unify Count with the amount of nodes and values known to this layer, including all parent layers.
Arguments:
Layer- the layer for which to get a count.
Count- the returned count.
 predicate_count(+Layer:layer, -Count:integer) is det
Unify Count with the amount of predicates known to this layer, including all parent layers.
Arguments:
Layer- the layer for which to get a count.
Count- the returned count.
 subject_to_id(+Layer:layer, +Subject:text, -Id:integer) is semidet
Convert the given subject to its id representation in the given layer. Fails if this subject is not known in the given layer.
Arguments:
Layer- the layer to use for the conversion.
Subject- an atom or string containing the subject.
Id- the id of the subject in the given layer.
 id_to_subject(Layer:layer, +Id:integer, -Subject:string) is semidet
Convert the given id to a subject using the given layer. Fails if the id is out of range for subjects.
Arguments:
Layer- the layer to use for the conversion.
Id- the id to convert into a subject.
Subject- the subject which the id refers to.
 predicate_to_id(+Layer:layer, +Predicate:text, -Id:integer) is semidet
Convert the given predicate to its id representation in the given layer. Fails if this predicate is not known in the given layer.
Arguments:
Layer- the layer to use for the conversion.
Predicate- an atom or string containing the predicate.
Id- the id of the predicate in the given layer.
 id_to_predicate(Layer:layer, +Id:integer, -Predicate:string) is semidet
Convert the given id to a predicate using the given layer. Fails if the id is out of range for predicates.
Arguments:
Layer- the layer to use for the conversion.
Id- the id to convert into a predicate.
Predicate- the predicate which the id refers to.
 object_to_id(+Layer:layer, +Object:text, -Id:integer) is semidet
Convert the given node object to its id representation in the given layer. Fails if this subject is not known in the given layer.
Arguments:
Layer- the layer to use for the conversion.
Object- an atom or string containing the object. The object is assumed to refer to a node.
Id- the id of the object in the given layer.
 id_to_object(Layer:layer, +Id:integer, -Object:string, -Object_Type:atom) is semidet
Convert the given id to a object using the given layer. Fails if the id is out of range for objects.
Arguments:
Layer- the layer to use for the conversion.
Id- the id to convert into a object.
Object- the object which the id refers to.
Object_Type- the type of the object, either 'node' or 'value'.
 parent(+Layer:layer, +Parent:layer) is semidet
Unifies Parent with the parent layer of Layer. Fails if that layer has no parent.
Arguments:
Layer- the layer for which to do the parent lookup.
Parent- the retrieved parent layer.
 squash(+Layer:layer, +Squash:layer) is semidet
Squashes a layer-stack to create a new fresh layer
Arguments:
Layer- the layer for which to do the parent lookup.
Parent- the retrieved parent layer.
 rollup(+Layer:layer) is semidet
Produces a rollup of the current layer
Arguments:
Layer- the layer for which to do the parent lookup.
 rollup_upto(+Layer:layer, +Upto:layer) is semidet
Produces a rollup of the current layer upto (but not including) the specified layer
Arguments:
Layer- the layer for which to do the parent lookup.
Upto- the layer at which to stop the rollup.
 csv_builder(+Name:string, +Csv:path, +Builder:builder, +Options:options) is det
Creates a layer with the contents of a csv as triples
Arguments:
Name- Name of the CSV object
Csv- The path to the csv to be loaded
Builder- The builder into which to place the CSV
Layer- The returned Layer
Options- A list containing any of the following:
  • data_prefix(Prefix) (default is "csv:///data#")
  • predicate_prefix(Prefix) (default is "csv:///schema#")
  • header(Bool) (Boolean to read a header, default true)
  • skip_header(Bool) (Skip the header regardless of presence,
    default false)
 csv_builder(+Name:string, +Csv:path, +Builder:builder) is det
Creates a layer with the contents of a csv as triples. Options are defaults.
Arguments:
Name- Name of the CSV object
Csv- The path to the csv to be loaded
Builder- The builder into which to place the CSV
Layer- The returned Layer
 csv_iri(Name, Prefix, IRI) is det
Creates a CSV IRI from a name and prefix
 layer_stack_names(+Layer:layer, -Stack:list) is det
Creates a layer-id stack from a layer which contains all ancestor layers.
Arguments:
Layer- The layer from which to obtain the stack.
Stack- A list of the layer-ids of all ancestors.
  450%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  451%%% End of foreign predicate pldocs   %%%
  452%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 nb_add_triple(+Builder, +Subject, +Predicate, +Object) is semidet
Add a triple to the builder.
  457nb_add_triple(Builder, Subject, Predicate, Object) :-
  458    integer(Subject),
  459    integer(Predicate),
  460    integer(Object),
  461    !,
  462    nb_add_id_triple(Builder, Subject, Predicate, Object).
  463
  464nb_add_triple(Builder, Subject, Predicate, Object) :-
  465    !,
  466    nb_add_string_triple(Builder, Subject, Predicate, Object).
  467
  468/*
  469 * nb_add_triple(+Builder, +Subject, +Predicate, +Object) is semidet
  470 *
  471 * Remove a trible from the builder
  472 */
  473nb_remove_triple(Builder, Subject, Predicate, Object) :-
  474    integer(Subject),
  475    integer(Predicate),
  476    integer(Object),
  477    !,
  478    nb_remove_id_triple(Builder, Subject, Predicate, Object).
  479
  480nb_remove_triple(Builder, Subject, Predicate, Object) :-
  481    !,
  482    nb_remove_string_triple(Builder, Subject, Predicate, Object).
  483
  484/*
  485 * subject_id(+Layer, +Subject, -Id) is semidet
  486 *
  487 * Get the ID from a subject
  488 */
  489subject_id(Layer, Subject, Id) :-
  490    ground(Id),
  491    !,
  492    id_to_subject(Layer, Id, Subject).
  493
  494subject_id(Layer, Subject, Id) :-
  495    ground(Subject),
  496    !,
  497    subject_to_id(Layer, Subject, Id).
  498
  499subject_id(Layer, Subject, Id) :-
  500    node_and_value_count(Layer, Count),
  501    between(1, Count, Id),
  502    id_to_subject(Layer, Id, Subject).
  503
  504
  505/*
  506 * predicate_id(+Layer, +Predicate, -Id) is semidet
  507 *
  508 * Get the ID from a predicate
  509 */
  510predicate_id(Layer, Predicate, Id) :-
  511    ground(Id),
  512    !,
  513    id_to_predicate(Layer, Id, Predicate).
  514
  515predicate_id(Layer, Predicate, Id) :-
  516    ground(Predicate),
  517    !,
  518    predicate_to_id(Layer, Predicate, Id).
  519
  520predicate_id(Layer, Predicate, Id) :-
  521    node_and_value_count(Layer, Count),
  522    between(1, Count, Id),
  523    id_to_predicate(Layer, Id, Predicate).
  524
  525
  526/*
  527 * object_id(+Layer, +Predicate, -Id) is semidet
  528 *
  529 * Get the ID from an object
  530 */
  531object_id(Layer, Object, Id) :-
  532    ground(Id),
  533    !,
  534    id_to_object(Layer, Id, Object).
  535
  536object_id(Layer, node(Object), Id) :-
  537    ground(Object),
  538    !,
  539    object_to_id(Layer, node(Object), Id).
  540
  541object_id(Layer, value(Object), Id) :-
  542    ground(Object),
  543    !,
  544    object_to_id(Layer, value(Object), Id).
  545
  546object_id(Layer, Object, Id) :-
  547    node_and_value_count(Layer, Count),
  548    between(1, Count, Id),
  549    id_to_object(Layer, Id, Object).
  550
  551triple(Layer, Subject, Predicate, Object) :-
  552    (   ground(Subject)
  553    ->  subject_id(Layer, Subject, S_Id)
  554    ;   true),
  555
  556    (   ground(Predicate)
  557    ->  predicate_id(Layer, Predicate, P_Id)
  558    ;   true),
  559
  560    (   ground(Object)
  561    ->  object_id(Layer, Object, O_Id)
  562    ;   true),
  563
  564    id_triple(Layer, S_Id, P_Id, O_Id),
  565
  566    (   ground(Subject)
  567    ->  true
  568    ;   subject_id(Layer, Subject, S_Id)),
  569
  570
  571    (   ground(Predicate)
  572    ->  true
  573    ;   predicate_id(Layer, Predicate, P_Id)),
  574
  575
  576    (   ground(Object)
  577    ->  true
  578    ;   object_id(Layer,Object, O_Id)).
  579
  580csv_builder(Name, Csv, Builder) :-
  581    csv_builder(Name, Csv,Builder,[]).
  582
  583csv_builder(Name, Csv, Builder, Options) :-
  584    option(data_prefix(Data), Options, 'csv:///data/'),
  585    option(schema_prefix(Schema), Options, 'csv:///schema#'),
  586    option(header(Header), Options, true),
  587    option(skip_header(Skip), Options, false),
  588    csv_builder(Name, Csv, Builder, Data, Schema, Header, Skip).
  589
  590csv_builder(Name, Csv, Builder, Schema_Builder, Options) :-
  591    option(data_prefix(Data), Options, 'csv:///data/'),
  592    option(schema_prefix(Schema), Options, 'csv:///schema#'),
  593    option(header(Header), Options, true),
  594    option(skip_header(Skip), Options, false),
  595    csv_builder(Name, Csv, Builder, Schema_Builder, Data, Schema, Header, Skip).
  596
  597triple_addition(Layer, Subject, Predicate, Object) :-
  598    (   ground(Subject)
  599    ->  subject_id(Layer, Subject, S_Id)
  600    ;   true),
  601
  602    (   ground(Predicate)
  603    ->  predicate_id(Layer, Predicate, P_Id)
  604    ;   true),
  605
  606    (   ground(Object)
  607    ->  object_id(Layer, Object, O_Id)
  608    ;   true),
  609
  610    id_triple_addition(Layer, S_Id, P_Id, O_Id),
  611
  612    (   ground(Subject)
  613    ->  true
  614    ;   subject_id(Layer, Subject, S_Id)),
  615
  616
  617    (   ground(Predicate)
  618    ->  true
  619    ;   predicate_id(Layer, Predicate, P_Id)),
  620
  621
  622    (   ground(Object)
  623    ->  true
  624    ;   object_id(Layer,Object, O_Id)).
  625
  626triple_removal(Layer, Subject, Predicate, Object) :-
  627    (   ground(Subject)
  628    ->  subject_id(Layer, Subject, S_Id)
  629    ;   true),
  630
  631    (   ground(Predicate)
  632    ->  predicate_id(Layer, Predicate, P_Id)
  633    ;   true),
  634
  635    (   ground(Object)
  636    ->  object_id(Layer, Object, O_Id)
  637    ;   true),
  638
  639    id_triple_removal(Layer, S_Id, P_Id, O_Id),
  640
  641    (   ground(Subject)
  642    ->  true
  643    ;   subject_id(Layer, Subject, S_Id)),
  644
  645
  646    (   ground(Predicate)
  647    ->  true
  648    ;   predicate_id(Layer, Predicate, P_Id)),
  649
  650
  651    (   ground(Object)
  652    ->  true
  653    ;   object_id(Layer,Object, O_Id)).
  654
  655count_layer_stack_size(Layer, Acc, Count) :-
  656    parent(Layer, Parent),
  657    !,
  658    NextAcc is Acc + 1,
  659    count_layer_stack_size(Parent, NextAcc, Count).
  660count_layer_stack_size(_Layer, Acc, Count) :-
  661    Count is Acc + 1.
  662
  663count_layer_stack_size(Layer, Count) :-
  664    count_layer_stack_size(Layer, 0, Count).
  665
  666
  667layer_stack_names(Layer, Stack) :-
  668    ground(Layer),
  669    !,
  670    retrieve_layer_stack_names(Layer, Stack).
  671layer_stack_names(_Layer, _Stack) :-
  672    throw(error(domain_error('Layer not bound in layer_stack_names/2'),_)).
  673
  674:- begin_tests(terminus_store).  675
  676:- use_module(library(filesex)).  677
  678		 /*******************************
  679		 *     Developer Utilities      *
  680		 *******************************/
 random_string(String) is det
 /
  685random_string(String) :-
  686    Size is 2 ** (20 * 8),
  687    random(0, Size, Num),
  688    format(string(String), '~36r', [Num]).
  689
  690clean(TestDir) :-
  691    delete_directory_and_contents(TestDir).
  692
  693createng(TestDir) :-
  694    random_string(RandomString),
  695    atomic_list_concat(["testdir", RandomString], TestDir),
  696    make_directory(TestDir),
  697    open_directory_store(TestDir, X),
  698    create_named_graph(X, "sometestdb", _).
  699
  700create_memory_ng(DB) :-
  701    open_memory_store(X),
  702    create_named_graph(X, "sometestdb", DB).
  703
  704test(open_memory_store) :-
  705    open_memory_store(_).
  706
  707test(open_directory_store_atom) :-
  708    open_directory_store(this_is_an_atom, _),
  709    open_directory_store("this is a string", _).
  710
  711test(open_directory_store_atom_exception, [
  712         throws(error(type_error(text,234), _))
  713     ]) :-
  714    open_directory_store(234, _).
  715
  716test(create_db, [cleanup(clean(TestDir))]) :-
  717    make_directory("testdir"),
  718    TestDir = 'testdir',
  719    open_directory_store("testdir", X),
  720    create_named_graph(X, "sometestdb", _).
  721
  722
  723test(create_db_on_memory) :-
  724    open_memory_store(X),
  725    create_named_graph(X, "sometestdb", _).
  726
  727test(open_named_graph, [cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
  728    open_directory_store(TestDir, X),
  729    open_named_graph(X, "sometestdb", _).
  730
  731test(open_named_graph_memory) :-
  732    open_memory_store(X),
  733    create_named_graph(X, "sometestdb", _),
  734    open_named_graph(X, "sometestdb", _).
  735
  736test(delete_named_graph_memory) :-
  737    open_memory_store(X),
  738    create_named_graph(X, "sometestdb", _),
  739    delete_named_graph(X, "sometestdb"),
  740    \+ open_named_graph(X, "sometestdb", _).
  741
  742test(delete_named_graph_directory, [cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
  743    open_directory_store(TestDir, X),
  744    \+ delete_named_graph(X, "unknowndb").
  745
  746test(head_from_empty_db, [fail, cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
  747    open_directory_store(TestDir, X),
  748    open_named_graph(X, "sometestdb", DB),
  749    head(DB, _). % should be false because we have no HEAD yet
  750
  751test(head_from_empty_db_memory, [fail, setup(create_memory_ng(DB))]) :-
  752     head(DB, _).
  753
  754test(open_write_from_db_without_head, [
  755    cleanup(clean(TestDir)),
  756    setup(createng(TestDir)),
  757    throws(
  758        error(cannot_open_named_graph_without_base_layer, _)
  759    )]) :-
  760    open_directory_store(TestDir, X),
  761    open_named_graph(X, "sometestdb", DB),
  762    open_write(DB, _).
  763
  764test(open_write_from_db_with_head, [
  765         cleanup(clean(TestDir)),
  766         setup(createng(TestDir))
  767     ]) :-
  768    open_directory_store(TestDir, Store),
  769    open_write(Store, Builder),
  770    nb_commit(Builder, Layer),
  771    open_named_graph(Store, "sometestdb", DB),
  772    nb_set_head(DB, Layer),
  773    open_write(DB, _).
  774
  775
  776test(open_write_from_memory_ng_without_head, [
  777    setup(create_memory_ng(DB)),
  778    throws(
  779        error(cannot_open_named_graph_without_base_layer, _)
  780    )]) :-
  781    open_write(DB, _).
  782
  783test(create_base_layer, [cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
  784    open_directory_store(TestDir, Store),
  785    open_write(Store, _).
  786
  787
  788test(create_base_layer_memory) :-
  789    open_memory_store(Store),
  790    open_write(Store, _).
  791
  792test(write_value_triple, [cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
  793    open_directory_store(TestDir, Store),
  794    open_write(Store, Builder),
  795    nb_add_string_triple(Builder, "Subject", "Predicate", value("Object")).
  796
  797test(write_value_triple_memory) :-
  798    open_memory_store(Store),
  799    open_write(Store, Builder),
  800    nb_add_string_triple(Builder, "Subject", "Predicate", value("Object")).
  801
  802test(commit_and_set_header, [cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
  803    open_directory_store(TestDir, Store),
  804    open_write(Store, Builder),
  805    open_named_graph(Store, "sometestdb", DB),
  806    nb_add_triple(Builder, "Subject", "Predicate", value("Object")),
  807    nb_commit(Builder, Layer),
  808    nb_set_head(DB, Layer).
  809
  810
  811test(commit_and_set_header_version_first, [cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
  812    open_directory_store(TestDir, Store),
  813    open_write(Store, Builder),
  814    open_named_graph(Store, "sometestdb", DB),
  815    nb_add_triple(Builder, "Subject", "Predicate", value("Object")),
  816    nb_commit(Builder, Layer),
  817    nb_force_set_head(DB, Layer, 0).
  818
  819
  820test(commit_and_set_header_version_first_wrong_version, [cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
  821    open_directory_store(TestDir, Store),
  822    open_write(Store, Builder),
  823    open_named_graph(Store, "sometestdb", DB),
  824    nb_add_triple(Builder, "Subject", "Predicate", value("Object")),
  825    nb_commit(Builder, Layer),
  826    \+ nb_force_set_head(DB, Layer, 1).
  827
  828test(commit_and_set_header_version_multiple_commits, [cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
  829    open_directory_store(TestDir, Store),
  830    open_write(Store, Builder),
  831    open_named_graph(Store, "sometestdb", DB),
  832    nb_add_triple(Builder, "Subject", "Predicate", value("Object")),
  833    nb_commit(Builder, Layer),
  834    nb_force_set_head(DB, Layer, 0),
  835
  836    head(DB, _, 1),
  837
  838    open_write(Store, Builder2),
  839    nb_add_triple(Builder2, "Subject2", "Predicate2", value("Object2")),
  840    nb_commit(Builder2, Layer2),
  841    nb_force_set_head(DB, Layer2, 1),
  842
  843    head(DB, _, 2),
  844    \+ head(DB, _, 3).
  845
  846
  847test(commit_and_set_header_version_incorrect, [cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
  848    open_directory_store(TestDir, Store),
  849    open_write(Store, Builder),
  850    open_named_graph(Store, "sometestdb", DB),
  851    nb_add_triple(Builder, "Subject", "Predicate", value("Object")),
  852    nb_commit(Builder, Layer),
  853    \+ nb_force_set_head(DB, Layer, 1).
  854
  855
  856test(commit_and_set_header_version_multiples_incorrect, [cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
  857    open_directory_store(TestDir, Store),
  858    open_write(Store, Builder),
  859    open_named_graph(Store, "sometestdb", DB),
  860    nb_add_triple(Builder, "Subject", "Predicate", value("Object")),
  861    nb_commit(Builder, Layer),
  862    nb_force_set_head(DB, Layer, 0),
  863
  864    head(DB, _, 1),
  865
  866    open_write(Store, Builder2),
  867    nb_add_triple(Builder2, "Subject2", "Predicate2", value("Object2")),
  868    nb_commit(Builder2, Layer2),
  869    \+ nb_force_set_head(DB, Layer2, 0).
  870
  871
  872test(commit_and_set_header_memory) :-
  873    open_memory_store(Store),
  874    open_write(Store, Builder),
  875    create_named_graph(Store, "sometestdb", DB),
  876    nb_add_triple(Builder, "Subject", "Predicate", value("Object")),
  877    nb_commit(Builder, Layer),
  878    nb_set_head(DB, Layer).
  879
  880test(head_after_first_commit, [cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
  881    open_directory_store(TestDir, Store),
  882    open_named_graph(Store, "sometestdb", DB),
  883    open_write(Store, Builder),
  884    nb_add_triple(Builder, "Subject", "Predicate", value("Object")),
  885    nb_commit(Builder, Layer),
  886    nb_set_head(DB, Layer),
  887    head(DB, _).
  888
  889test(predicate_count, [cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
  890    open_directory_store(TestDir, Store),
  891    open_named_graph(Store, "sometestdb", DB),
  892    open_write(Store, Builder),
  893    nb_add_triple(Builder, "Subject", "Predicate", value("Object")),
  894    nb_commit(Builder, Layer),
  895    nb_set_head(DB, Layer),
  896    head(DB, LayerHead),
  897    predicate_count(LayerHead, Count),
  898    Count == 1.
  899
  900test(node_and_value_count, [cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
  901    open_directory_store(TestDir, Store),
  902    open_write(Store, Builder),
  903    nb_add_triple(Builder, "Subject", "Predicate", value("Object")),
  904    nb_commit(Builder, Layer),
  905    node_and_value_count(Layer, Count),
  906    Count == 2.
  907
  908test(predicate_count_2, [cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
  909    open_directory_store(TestDir, Store),
  910    open_named_graph(Store, "sometestdb", DB),
  911    open_write(Store, Builder),
  912    nb_add_triple(Builder, "Subject", "Predicate", value("Object")),
  913    nb_add_triple(Builder, "Subject2", "Predicate2", value("Object2")),
  914    nb_commit(Builder, Layer),
  915    nb_set_head(DB, Layer),
  916    predicate_count(Layer, Count),
  917    Count == 2.
  918
  919test(remove_triple, [cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
  920    open_directory_store(TestDir, Store),
  921    open_write(Store, Builder),
  922    nb_add_triple(Builder, "Subject", "Predicate", value("Object")),
  923    nb_commit(Builder, Layer),
  924    open_write(Layer, LayerBuilder),
  925    nb_remove_triple(LayerBuilder, "Subject", "Predicate", value("Object")).
  926
  927test(triple_search_test, [cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
  928    open_directory_store(TestDir, Store),
  929    open_write(Store, Builder),
  930    nb_add_triple(Builder, "Subject", "Predicate", value("Object")),
  931    nb_commit(Builder, Layer),
  932    setof(X, triple(Layer, "Subject", "Predicate", value(X)), Bag),
  933    Bag == ["Object"].
  934
  935
  936test(triple_search_test, [cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
  937    open_directory_store(TestDir, Store),
  938    open_write(Store, Builder),
  939    nb_add_triple(Builder, "Subject", "Predicate", value("Object")),
  940    nb_commit(Builder, Layer),
  941    setof(Y-X, triple(Layer, "Subject", Y, value(X)), Bag),
  942    Bag == ["Predicate"-"Object"].
  943
  944
  945test(triple_search_test, [cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
  946    open_directory_store(TestDir, Store),
  947    open_write(Store, Builder),
  948    nb_add_triple(Builder, "Subject", "Predicate", value("Object")),
  949    nb_commit(Builder, Layer),
  950    setof(X-Y-Z, triple(Layer, X, Y, value(Z)), Bag),
  951    Bag == ["Subject"-"Predicate"-"Object"].
  952
  953test(backtracking_test, [cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
  954    open_directory_store(TestDir, Store),
  955    open_write(Store, Builder),
  956    create_named_graph(Store, "testdb", DB),
  957    nb_add_triple(Builder, "A", "B", node("C")),
  958    nb_add_triple(Builder, "A", "D", node("C")),
  959    nb_add_triple(Builder, "A", "E", node("C")),
  960    nb_add_triple(Builder, "A", "E", node("O")),
  961    nb_add_triple(Builder, "A", "D", node("O")),
  962    nb_commit(Builder, Layer),
  963    nb_set_head(DB, Layer),
  964
  965    findall(P, triple(Layer, "A", P, node("O")), Ps),
  966    Ps = ["D", "E"].
  967
  968test(query_builder_for_committed, [cleanup(clean(TestDir)),setup(createng(TestDir))]) :-
  969    open_directory_store(TestDir, Store),
  970    open_write(Store, Builder),
  971
  972    \+ builder_committed(Builder),
  973
  974    nb_commit(Builder, _Layer),
  975
  976    builder_committed(Builder).
  977
  978test(squash_a_tower,[cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
  979    open_directory_store(TestDir, Store),
  980    open_write(Store, Builder),
  981    create_named_graph(Store, "testdb", DB),
  982    nb_add_triple(Builder, "joe", "eats", node("urchin")),
  983    nb_commit(Builder, Layer),
  984
  985    open_write(Layer,Builder2),
  986    nb_add_triple(Builder2, "jill", "eats", node("caviar")),
  987    nb_commit(Builder2, Layer2),
  988
  989    squash(Layer2,Squashed_Layer),
  990
  991    nb_set_head(DB, Squashed_Layer),
  992
  993    open_named_graph(Store, "testdb", DB2),
  994    head(DB2,Squash),
  995
  996    findall(X-P-Y, triple(Squash, X, P, Y), Triples),
  997
  998    Triples = ["jill"-"eats"-node("caviar"),
  999               "joe"-"eats"-node("urchin")
 1000              ],
 1001    \+ parent(Squash,_).
 1002
 1003
 1004test(force_set_head,[cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
 1005    open_directory_store(TestDir, Store),
 1006    open_write(Store, Builder1),
 1007    create_named_graph(Store, "testdb", DB1),
 1008    nb_add_triple(Builder1, "joe", "eats", node("urchin")),
 1009    nb_commit(Builder1, _Layer1),
 1010
 1011    open_write(Store, Builder2),
 1012    nb_add_triple(Builder2, "jill", "eats", node("caviar")),
 1013    nb_commit(Builder2, Layer2),
 1014
 1015    nb_force_set_head(DB1,Layer2),
 1016
 1017    head(DB1,Layer3),
 1018    findall(X-P-Y, triple(Layer3, X, P, Y), Triples),
 1019
 1020    Triples = ["jill"-"eats"-node("caviar")],
 1021
 1022    \+ parent(Layer3,_).
 1023
 1024test(apply_a_delta,[cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
 1025    open_directory_store(TestDir, Store),
 1026    open_write(Store, Builder),
 1027    nb_add_triple(Builder, "joe", "eats", node("urchin")),
 1028    nb_commit(Builder, Layer),
 1029
 1030    open_write(Layer,Builder2),
 1031    nb_add_triple(Builder2, "jill", "eats", node("caviar")),
 1032    nb_commit(Builder2, Delta),
 1033
 1034    open_write(Store, Builder_Base),
 1035    nb_add_triple(Builder_Base, "cathie", "eats", node("seaweed")),
 1036    nb_commit(Builder_Base, Base),
 1037
 1038    open_write(Base, Builder_New),
 1039    nb_apply_delta(Builder_New,Delta),
 1040    nb_commit(Builder_New,Final_Layer),
 1041
 1042    findall(X-P-Y, triple(Final_Layer, X, P, Y), Triples),
 1043
 1044    Triples = ["cathie"-"eats"-node("seaweed"),
 1045               "jill"-"eats"-node("caviar")
 1046              ].
 1047
 1048test(apply_a_diff,[cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
 1049    open_directory_store(TestDir, Store),
 1050    open_write(Store, Builder),
 1051    nb_add_triple(Builder, "joe", "eats", node("urchin")),
 1052    nb_add_triple(Builder, "jill", "eats", node("caviar")),
 1053    nb_add_triple(Builder, "cathie", "eats", node("seaweed")),
 1054    nb_commit(Builder, Layer),
 1055
 1056    open_write(Store, Builder2),
 1057    nb_add_triple(Builder2, "joe", "eats", node("seals")),
 1058    nb_add_triple(Builder2, "jill", "eats", node("caviar")),
 1059    nb_commit(Builder2, Prototype),
 1060
 1061    open_write(Layer, Diff_Builder),
 1062    nb_apply_diff(Diff_Builder,Prototype),
 1063    nb_commit(Diff_Builder,Final_Layer),
 1064
 1065    findall(X-P-Y, triple(Final_Layer, X, P, Y), Triples),
 1066    Triples = [
 1067        "jill"-"eats"-node("caviar"),
 1068        "joe"-"eats"-node("seals")
 1069    ],
 1070
 1071    findall(X-P-Y, triple_addition(Final_Layer, X, P, Y), Triple_Additions),
 1072    Triple_Additions = [
 1073        "joe"-"eats"-node("seals")
 1074    ],
 1075
 1076    findall(X-P-Y, triple_removal(Final_Layer, X, P, Y), Triple_Removals),
 1077    Triple_Removals = [
 1078        "cathie"-"eats"-node("seaweed"),
 1079        "joe"-"eats"-node("urchin")
 1080    ].
 1081
 1082test(apply_empty_diff,[cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
 1083    open_directory_store(TestDir, Store),
 1084    open_write(Store, Builder),
 1085    nb_add_triple(Builder, "joe", "eats", node("urchin")),
 1086    nb_add_triple(Builder, "jill", "eats", node("caviar")),
 1087    nb_add_triple(Builder, "cathie", "eats", node("seaweed")),
 1088    nb_commit(Builder, Prototype),
 1089
 1090    open_write(Store, Diff_Builder),
 1091    nb_apply_diff(Diff_Builder,Prototype),
 1092    nb_commit(Diff_Builder,Final_Layer),
 1093
 1094    findall(X-P-Y, triple(Final_Layer, X, P, Y), Triples),
 1095
 1096    Triples = [
 1097        "cathie"-"eats"-node("seaweed"),
 1098        "jill"-"eats"-node("caviar"),
 1099        "joe"-"eats"-node("urchin")
 1100    ],
 1101
 1102    findall(X-P-Y, triple_addition(Final_Layer, X, P, Y), Triple_Additions),
 1103    Triple_Additions = Triples,
 1104    findall(X-P-Y, triple_removal(Final_Layer, X, P, Y), Triple_Removals),
 1105    Triple_Removals = [].
 1106
 1107test(add_csv,[cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
 1108    open_directory_store(TestDir, Store),
 1109    open_write(Store, Builder),
 1110    tmp_file_stream(Filename, Stream, [encoding(utf8)]),
 1111    format(Stream, "some,header~n", []),
 1112    format(Stream, "1,2~n", []),
 1113    format(Stream, "3,4~n", []),
 1114    close(Stream),
 1115    csv_builder("csv",Filename, Builder, []),
 1116    nb_commit(Builder, Layer),
 1117    findall(X-P-Y, triple(Layer, X, P, Y), Triples),
 1118
 1119    Triples = [
 1120        X-"csv:///schema#csv_column_header"-value("\"2\"^^'http://www.w3.org/2001/XMLSchema#string'"),
 1121        X-"csv:///schema#csv_column_some"-value("\"1\"^^'http://www.w3.org/2001/XMLSchema#string'"),
 1122        X-"http://www.w3.org/1999/02/22-rdf-syntax-ns#type"-node(Row1),
 1123        Y-"csv:///schema#csv_column_header"-value("\"4\"^^'http://www.w3.org/2001/XMLSchema#string'"),
 1124        Y-"csv:///schema#csv_column_some"-value("\"3\"^^'http://www.w3.org/2001/XMLSchema#string'"),
 1125        Y-"http://www.w3.org/1999/02/22-rdf-syntax-ns#type"-node(Row1),
 1126        "csv:///data/CSV_csv"-"csv:///schema#csv_column"-node("csv:///data/ColumnObject_csv_header"),
 1127        "csv:///data/CSV_csv"-"csv:///schema#csv_column"-node("csv:///data/ColumnObject_csv_some"),
 1128        "csv:///data/CSV_csv"-"csv:///schema#csv_row"-node(X),
 1129        "csv:///data/CSV_csv"-"csv:///schema#csv_row"-node(Y),
 1130        "csv:///data/CSV_csv"-"http://www.w3.org/1999/02/22-rdf-syntax-ns#type"-node("csv:///schema#CSV"),
 1131        "csv:///data/CSV_csv"-"http://www.w3.org/2000/01/rdf-schema#label"-value("\"csv\"@en"),
 1132        "csv:///data/ColumnObject_csv_header"-"csv:///schema#csv_column_index"-value("1^^'http://www.w3.org/2001/XMLSchema#integer'"),
 1133        "csv:///data/ColumnObject_csv_header"-"csv:///schema#csv_column_name"-value("\"header\"^^'http://www.w3.org/2001/XMLSchema#string'"),
 1134        "csv:///data/ColumnObject_csv_header"-"http://www.w3.org/1999/02/22-rdf-syntax-ns#type"-node("csv:///schema#Column"),
 1135        "csv:///data/ColumnObject_csv_some"-"csv:///schema#csv_column_index"-value("0^^'http://www.w3.org/2001/XMLSchema#integer'"),
 1136        "csv:///data/ColumnObject_csv_some"-"csv:///schema#csv_column_name"-value("\"some\"^^'http://www.w3.org/2001/XMLSchema#string'"),
 1137        "csv:///data/ColumnObject_csv_some"-"http://www.w3.org/1999/02/22-rdf-syntax-ns#type"-node("csv:///schema#Column")
 1138    ].
 1139
 1140test(add_csv_skip_header,[cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
 1141    open_directory_store(TestDir, Store),
 1142    open_write(Store, Builder),
 1143    tmp_file_stream(Filename, Stream, [encoding(utf8)]),
 1144    format(Stream, "1,2~n", []),
 1145    format(Stream, "3,4~n", []),
 1146    close(Stream),
 1147    csv_builder("csv",Filename, Builder, [skip_header(true)]),
 1148    nb_commit(Builder, Layer),
 1149    findall(X-P-Y, triple(Layer, X, P, Y), Triples),
 1150    Triples = [
 1151        Row1-"csv:///schema#csv_column_0"-value("\"1\"^^'http://www.w3.org/2001/XMLSchema#string'"),
 1152        Row1-"csv:///schema#csv_column_1"-value("\"2\"^^'http://www.w3.org/2001/XMLSchema#string'"),
 1153        Row1-"http://www.w3.org/1999/02/22-rdf-syntax-ns#type"-node(Row_Type),
 1154        Row2-"csv:///schema#csv_column_0"-value("\"3\"^^'http://www.w3.org/2001/XMLSchema#string'"),
 1155        Row2-"csv:///schema#csv_column_1"-value("\"4\"^^'http://www.w3.org/2001/XMLSchema#string'"),
 1156        Row2-"http://www.w3.org/1999/02/22-rdf-syntax-ns#type"-node(Row_Type),
 1157        "csv:///data/CSV_csv"-"csv:///schema#csv_column"-node("csv:///data/ColumnObject_csv_0"),
 1158        "csv:///data/CSV_csv"-"csv:///schema#csv_column"-node("csv:///data/ColumnObject_csv_1"),
 1159        "csv:///data/CSV_csv"-"csv:///schema#csv_row"-node(Row1),
 1160        "csv:///data/CSV_csv"-"csv:///schema#csv_row"-node(Row2),
 1161        "csv:///data/CSV_csv"-"http://www.w3.org/1999/02/22-rdf-syntax-ns#type"-node("csv:///schema#CSV"),
 1162        "csv:///data/CSV_csv"-"http://www.w3.org/2000/01/rdf-schema#label"-value("\"csv\"@en"),
 1163        "csv:///data/ColumnObject_csv_0"-"csv:///schema#csv_column_index"-value("0^^'http://www.w3.org/2001/XMLSchema#integer'"),
 1164        "csv:///data/ColumnObject_csv_0"-"csv:///schema#csv_column_name"-value("\"0\"^^'http://www.w3.org/2001/XMLSchema#string'"),
 1165        "csv:///data/ColumnObject_csv_0"-"http://www.w3.org/1999/02/22-rdf-syntax-ns#type"-node("csv:///schema#Column"),
 1166        "csv:///data/ColumnObject_csv_1"-"csv:///schema#csv_column_index"-value("1^^'http://www.w3.org/2001/XMLSchema#integer'"),
 1167        "csv:///data/ColumnObject_csv_1"-"csv:///schema#csv_column_name"-value("\"1\"^^'http://www.w3.org/2001/XMLSchema#string'"),
 1168        "csv:///data/ColumnObject_csv_1"-"http://www.w3.org/1999/02/22-rdf-syntax-ns#type"-node("csv:///schema#Column")
 1169    ].
 1170
 1171test(csv_prefixes,[cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
 1172    open_directory_store(TestDir, Store),
 1173    open_write(Store, Builder),
 1174
 1175    tmp_file_stream(Filename, Stream, [encoding(utf8)]),
 1176    format(Stream, "some,header~n", []),
 1177    format(Stream, "1,2~n", []),
 1178    close(Stream),
 1179
 1180    csv_builder("csv",Filename, Builder, [data_prefix('that/'),
 1181                                          schema_prefix('this#')]),
 1182    nb_commit(Builder, Layer),
 1183    findall(X-P-Y, triple(Layer, X, P, Y), Triples),
 1184
 1185    Triples = [
 1186        Row1-"http://www.w3.org/1999/02/22-rdf-syntax-ns#type"-node(_),
 1187        Row1-"this#csv_column_header"-value("\"2\"^^'http://www.w3.org/2001/XMLSchema#string'"),
 1188        Row1-"this#csv_column_some"-value("\"1\"^^'http://www.w3.org/2001/XMLSchema#string'"),
 1189        "that/CSV_csv"-"http://www.w3.org/1999/02/22-rdf-syntax-ns#type"-node("this#CSV"),
 1190        "that/CSV_csv"-"http://www.w3.org/2000/01/rdf-schema#label"-value("\"csv\"@en"),
 1191        "that/CSV_csv"-"this#csv_column"-node("that/ColumnObject_csv_header"),
 1192        "that/CSV_csv"-"this#csv_column"-node("that/ColumnObject_csv_some"),
 1193        "that/CSV_csv"-"this#csv_row"-node(Row1),
 1194        "that/ColumnObject_csv_header"-"http://www.w3.org/1999/02/22-rdf-syntax-ns#type"-node("this#Column"),
 1195        "that/ColumnObject_csv_header"-"this#csv_column_index"-value("1^^'http://www.w3.org/2001/XMLSchema#integer'"),
 1196        "that/ColumnObject_csv_header"-"this#csv_column_name"-value("\"header\"^^'http://www.w3.org/2001/XMLSchema#string'"),
 1197        "that/ColumnObject_csv_some"-"http://www.w3.org/1999/02/22-rdf-syntax-ns#type"-node("this#Column"),
 1198        "that/ColumnObject_csv_some"-"this#csv_column_index"-value("0^^'http://www.w3.org/2001/XMLSchema#integer'"),
 1199        "that/ColumnObject_csv_some"-"this#csv_column_name"-value("\"some\"^^'http://www.w3.org/2001/XMLSchema#string'")
 1200    ].
 1201
 1202test(csv_with_schema,[cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
 1203    open_directory_store(TestDir, Store),
 1204    open_write(Store, Builder),
 1205    open_write(Store, Schema_Builder),
 1206    tmp_file_stream(Filename, Stream, [encoding(utf8)]),
 1207    format(Stream, "some,header~n", []),
 1208    format(Stream, "1,2~n", []),
 1209    format(Stream, "3,4~n", []),
 1210    close(Stream),
 1211    csv_builder("csv",Filename, Builder, Schema_Builder,
 1212                [data_prefix('data/'),
 1213                 schema_prefix('')]),
 1214    nb_commit(Schema_Builder, Schema_Layer),
 1215    findall(X-P-Y, triple(Schema_Layer, X, P, Y), Schema_Triples),
 1216
 1217    Schema_Expected = [
 1218        "CSV"-"http://www.w3.org/1999/02/22-rdf-syntax-ns#type"-node("http://www.w3.org/2002/07/owl#Class"),
 1219        "CSV"-"http://www.w3.org/2000/01/rdf-schema#comment"-value("\"CSV object\"@en"),
 1220        "CSV"-"http://www.w3.org/2000/01/rdf-schema#label"-value("\"CSV\"@en"),
 1221        "CSV"-"http://www.w3.org/2000/01/rdf-schema#subClassOf"-node("http://terminusdb.com/schema/system#Document"),
 1222        "CSVRow"-"http://www.w3.org/1999/02/22-rdf-syntax-ns#type"-node("http://www.w3.org/2002/07/owl#Class"),
 1223        "CSVRow"-"http://www.w3.org/2000/01/rdf-schema#comment"-value("\"Generic Row of a CSV file\"@en"),
 1224        "CSVRow"-"http://www.w3.org/2000/01/rdf-schema#label"-value("\"CSV Row\"@en"),
 1225        Row_Type-"http://terminusdb.com/schema/system#csv_name"-value("\"csv\"@en"),
 1226        Row_Type-"http://www.w3.org/1999/02/22-rdf-syntax-ns#type"-node("http://www.w3.org/2002/07/owl#Class"),
 1227        Row_Type-"http://www.w3.org/2000/01/rdf-schema#comment"-value("\"CSV Row object for columns [\\\"header\\\", \\\"some\\\"]\"@en"),
 1228        Row_Type-"http://www.w3.org/2000/01/rdf-schema#label"-value("\"CSV Row from csv\"@en"),
 1229        Row_Type-"http://www.w3.org/2000/01/rdf-schema#subClassOf"-node("CSVRow"),
 1230        "Column"-"http://www.w3.org/1999/02/22-rdf-syntax-ns#type"-node("http://www.w3.org/2002/07/owl#Class"),
 1231        "Column"-"http://www.w3.org/2000/01/rdf-schema#comment"-value("\"Column information object for a CSV\"@en"),
 1232        "Column"-"http://www.w3.org/2000/01/rdf-schema#label"-value("\"Column\"@en"),
 1233        "csv_column_header"-"http://www.w3.org/1999/02/22-rdf-syntax-ns#type"-node("http://www.w3.org/2002/07/owl#DatatypeProperty"),
 1234        "csv_column_header"-"http://www.w3.org/2000/01/rdf-schema#comment"-value("\"CSV Column for header name header\"@en"),
 1235        "csv_column_header"-"http://www.w3.org/2000/01/rdf-schema#domain"-node(Row_Type),
 1236        "csv_column_header"-"http://www.w3.org/2000/01/rdf-schema#label"-value("\"Column header\"@en"),
 1237        "csv_column_header"-"http://www.w3.org/2000/01/rdf-schema#range"-node("http://www.w3.org/2001/XMLSchema#string"),
 1238        "csv_column_some"-"http://www.w3.org/1999/02/22-rdf-syntax-ns#type"-node("http://www.w3.org/2002/07/owl#DatatypeProperty"),
 1239        "csv_column_some"-"http://www.w3.org/2000/01/rdf-schema#comment"-value("\"CSV Column for header name some\"@en"),
 1240        "csv_column_some"-"http://www.w3.org/2000/01/rdf-schema#domain"-node(Row_Type),
 1241        "csv_column_some"-"http://www.w3.org/2000/01/rdf-schema#label"-value("\"Column some\"@en"),
 1242        "csv_column_some"-"http://www.w3.org/2000/01/rdf-schema#range"-node("http://www.w3.org/2001/XMLSchema#string"),
 1243        "csv_column"-"http://www.w3.org/1999/02/22-rdf-syntax-ns#type"-node("http://www.w3.org/2002/07/owl#ObjectProperty"),
 1244        "csv_column"-"http://www.w3.org/2000/01/rdf-schema#comment"-value("\"Associates a CSV with a column object\"@en"),
 1245        "csv_column"-"http://www.w3.org/2000/01/rdf-schema#domain"-node("CSV"),
 1246        "csv_column"-"http://www.w3.org/2000/01/rdf-schema#label"-value("\"csv column\"@en"),
 1247        "csv_column"-"http://www.w3.org/2000/01/rdf-schema#range"-node("Column"),
 1248        "csv_column_index"-"http://www.w3.org/1999/02/22-rdf-syntax-ns#type"-node("http://www.w3.org/2002/07/owl#DatatypeProperty"),
 1249        "csv_column_index"-"http://www.w3.org/2000/01/rdf-schema#comment"-value("\"The ordering index for a column in a csv\"@en"),"csv_column_index"-"http://www.w3.org/2000/01/rdf-schema#domain"-node("Column"),
 1250        "csv_column_index"-"http://www.w3.org/2000/01/rdf-schema#label"-value("\"csv column index\"@en"),"csv_column_index"-"http://www.w3.org/2000/01/rdf-schema#range"-node("http://www.w3.org/2001/XMLSchema#integer"),
 1251        "csv_column_name"-"http://www.w3.org/1999/02/22-rdf-syntax-ns#type"-node("http://www.w3.org/2002/07/owl#DatatypeProperty"),
 1252        "csv_column_name"-"http://www.w3.org/2000/01/rdf-schema#comment"-value("\"The name of the column as it was verbatim in the CSV\"@en"),
 1253        "csv_column_name"-"http://www.w3.org/2000/01/rdf-schema#domain"-node("Column"),
 1254        "csv_column_name"-"http://www.w3.org/2000/01/rdf-schema#label"-value("\"csv column name\"@en"),
 1255        "csv_column_name"-"http://www.w3.org/2000/01/rdf-schema#range"-node("http://www.w3.org/2001/XMLSchema#string"),
 1256        "csv_row"-"http://www.w3.org/1999/02/22-rdf-syntax-ns#type"-node("http://www.w3.org/2002/07/owl#ObjectProperty"),
 1257        "csv_row"-"http://www.w3.org/2000/01/rdf-schema#comment"-value("\"Connects a CSV to its rows\"@en"),
 1258        "csv_row"-"http://www.w3.org/2000/01/rdf-schema#domain"-node("CSV"),
 1259        "csv_row"-"http://www.w3.org/2000/01/rdf-schema#label"-value("\"csv row\"@en"),
 1260        "csv_row"-"http://www.w3.org/2000/01/rdf-schema#range"-node("CSVRow")
 1261    ],
 1262
 1263    forall(member(Triple,Schema_Triples),
 1264           (   member(Triple,Schema_Expected))).
 1265
 1266test(so_mode,[cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
 1267    open_directory_store(TestDir, Store),
 1268    open_write(Store, Builder),
 1269    nb_add_triple(Builder, "A", "B", node("C")),
 1270    nb_add_triple(Builder, "A", "B", node("D")),
 1271    nb_commit(Builder, Layer),
 1272    findall(X-C, triple(Layer, X, "B", C), Ps),
 1273    Ps = ["A"-node("C"),
 1274          "A"-node("D")].
 1275
 1276test(sp_mode,[cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
 1277    open_directory_store(TestDir, Store),
 1278    open_write(Store, Builder),
 1279    nb_add_triple(Builder, "A", "B", node("D")),
 1280    nb_add_triple(Builder, "C", "B", node("D")),
 1281    nb_commit(Builder, Layer),
 1282    findall(X-C, triple(Layer, X, C, node("D")), Ps),
 1283    Ps = ["A"-"B",
 1284          "C"-"B"].
 1285
 1286test(op_mode,[cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
 1287    open_directory_store(TestDir, Store),
 1288    open_write(Store, Builder),
 1289    nb_add_triple(Builder, "A", "B", node("D")),
 1290    nb_add_triple(Builder, "C", "B", node("D")),
 1291    nb_commit(Builder, Layer),
 1292    findall(X, triple(Layer, X, "B", node("D")), Ps),
 1293    Ps = ["A","C"].
 1294
 1295test(p_mode,[cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
 1296    open_directory_store(TestDir, Store),
 1297    open_write(Store, Builder),
 1298    nb_add_triple(Builder, "A", "B", node("D")),
 1299    nb_add_triple(Builder, "C", "B", node("D")),
 1300    nb_commit(Builder, Layer),
 1301    findall(X, triple(Layer, "A", X, node("D")), Ps),
 1302    Ps = ["B"].
 1303
 1304test(rollup,[cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
 1305    open_directory_store(TestDir, Store),
 1306    open_write(Store, Builder),
 1307    nb_add_triple(Builder, "A", "B", node("D")),
 1308    nb_add_triple(Builder, "C", "B", node("D")),
 1309    nb_commit(Builder, Layer),
 1310
 1311    open_write(Layer, New_Builder),
 1312    nb_add_triple(New_Builder, "E", "F", node("G")),
 1313    nb_remove_triple(New_Builder, "C", "B", node("D")),
 1314    nb_commit(New_Builder, New_Layer),
 1315    rollup(New_Layer),
 1316    layer_to_id(New_Layer, Id),
 1317    store_id_layer(Store, Id, Rollup),
 1318    findall(X-P-Y, triple(Rollup, X, P, node(Y)), Triples),
 1319
 1320    Triples = ["A"-"B"-"D","E"-"F"-"G"].
 1321
 1322test(rollup_upto,[cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
 1323    open_directory_store(TestDir, Store),
 1324    open_write(Store, Builder),
 1325    nb_add_triple(Builder, "A", "B", node("D")),
 1326    nb_add_triple(Builder, "C", "B", node("D")),
 1327    nb_commit(Builder, Layer),
 1328
 1329    open_write(Layer, New_Builder),
 1330    nb_add_triple(New_Builder, "E", "F", node("G")),
 1331    nb_remove_triple(New_Builder, "C", "B", node("D")),
 1332    nb_commit(New_Builder, New_Layer),
 1333
 1334    open_write(New_Layer, New_Builder_2),
 1335    nb_add_triple(New_Builder_2, "G", "H", node("I")),
 1336    nb_remove_triple(New_Builder_2, "A", "B", node("D")),
 1337    nb_commit(New_Builder_2, New_Layer_2),
 1338
 1339    rollup_upto(New_Layer_2, Layer),
 1340    layer_to_id(New_Layer_2, Id),
 1341
 1342    store_id_layer(Store, Id, Rollup),
 1343    findall(X-P-Y, triple(Rollup, X, P, node(Y)), Triples),
 1344
 1345    Triples = ["E"-"F"-"G","G"-"H"-"I"].
 1346
 1347test(layer_stack_names,[cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
 1348    open_directory_store(TestDir, Store),
 1349    open_write(Store, Builder),
 1350    nb_add_triple(Builder, "A", "B", node("D")),
 1351    nb_add_triple(Builder, "C", "B", node("D")),
 1352    nb_commit(Builder, Layer),
 1353    layer_to_id(Layer, Layer_Id),
 1354
 1355    open_write(Layer, New_Builder),
 1356    nb_add_triple(New_Builder, "E", "F", node("G")),
 1357    nb_remove_triple(New_Builder, "C", "B", node("D")),
 1358    nb_commit(New_Builder, New_Layer),
 1359    layer_to_id(New_Layer, New_Layer_Id),
 1360
 1361    open_write(New_Layer, New_Builder_2),
 1362    nb_add_triple(New_Builder_2, "G", "H", node("I")),
 1363    nb_remove_triple(New_Builder_2, "A", "B", node("D")),
 1364    nb_commit(New_Builder_2, New_Layer_2),
 1365    layer_to_id(New_Layer_2, New_Layer_2_Id),
 1366
 1367    layer_stack_names(New_Layer_2, Layers),
 1368
 1369    Expected = [Layer_Id,New_Layer_Id,New_Layer_2_Id],
 1370
 1371    Expected = Layers.
 1372
 1373test(precise_rollup_rolls_up_precisely,[cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
 1374    open_directory_store(TestDir, Store),
 1375    open_write(Store, Builder),
 1376    nb_add_triple(Builder, "a", "a", value("a")),
 1377    nb_add_triple(Builder, "a", "b", value("a")),
 1378    nb_add_triple(Builder, "c", "c", node("a")),
 1379    nb_commit(Builder, Layer),
 1380
 1381    open_write(Layer, Builder2),
 1382    nb_remove_triple(Builder2, "a", "a", value("a")),
 1383    nb_add_triple(Builder2, "c", "b", node("d")),
 1384    nb_commit(Builder2, Layer2),
 1385
 1386    open_write(Layer2, Builder3),
 1387    nb_add_triple(Builder3, "c", "c", node("c")),
 1388    nb_remove_triple(Builder3, "c", "b", node("d")),
 1389    nb_commit(Builder3, Layer3),
 1390
 1391    open_write(Layer3, Builder4),
 1392    nb_add_triple(Builder4, "a", "a", value("a")),
 1393    nb_add_triple(Builder4, "x", "y", node("z")),
 1394    nb_commit(Builder4, Layer4),
 1395
 1396    rollup_upto(Layer3, Layer),
 1397
 1398    % lets reload the top layer
 1399    layer_to_id(Layer4, Layer4_Id),
 1400    store_id_layer(Store, Layer4_Id, Layer4_Reloaded),
 1401
 1402    % and rollup again
 1403    rollup_upto(Layer4_Reloaded, Layer2),
 1404
 1405    % and reload again!
 1406    store_id_layer(Store, Layer4_Id, Layer4_Reloaded_Again),
 1407    
 1408    findall(t(S,P,O), triple(Layer4_Reloaded_Again, S, P, O), Triples),
 1409
 1410    Expected = [
 1411        t("a", "a", value("a")),
 1412        t("a", "b", value("a")),
 1413        t("c", "c", node("a")),
 1414        t("c", "c", node("c")),
 1415        t("x", "y", node("z"))
 1416    ],
 1417
 1418    Triples = Expected.
 1419
 1420test(imprecise_rollup_rolls_up_imprecisely,[cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
 1421    open_directory_store(TestDir, Store),
 1422    open_write(Store, Builder),
 1423    nb_add_triple(Builder, "a", "a", value("a")),
 1424    nb_add_triple(Builder, "a", "b", value("a")),
 1425    nb_add_triple(Builder, "c", "c", node("a")),
 1426    nb_commit(Builder, Layer),
 1427
 1428    open_write(Layer, Builder2),
 1429    nb_remove_triple(Builder2, "a", "a", value("a")),
 1430    nb_add_triple(Builder2, "c", "b", node("d")),
 1431    nb_commit(Builder2, Layer2),
 1432
 1433    open_write(Layer2, Builder3),
 1434    nb_add_triple(Builder3, "c", "c", node("c")),
 1435    nb_remove_triple(Builder3, "c", "b", node("d")),
 1436    nb_commit(Builder3, Layer3),
 1437
 1438    open_write(Layer3, Builder4),
 1439    nb_add_triple(Builder4, "a", "a", value("a")),
 1440    nb_add_triple(Builder4, "x", "y", node("z")),
 1441    nb_commit(Builder4, Layer4),
 1442
 1443    rollup_upto(Layer3, Layer),
 1444
 1445    % lets reload the top layer
 1446    layer_to_id(Layer4, Layer4_Id),
 1447    store_id_layer(Store, Layer4_Id, Layer4_Reloaded),
 1448
 1449    % and rollup again
 1450    imprecise_rollup_upto(Layer4_Reloaded, Layer2),
 1451
 1452    % and reload again!
 1453    store_id_layer(Store, Layer4_Id, Layer4_Reloaded_Again),
 1454    
 1455    findall(t(S,P,O), triple(Layer4_Reloaded_Again, S, P, O), Triples),
 1456
 1457    Expected = [
 1458        t("a", "a", value("a")),
 1459        t("a", "b", value("a")),
 1460        t("c", "c", node("a")),
 1461        t("c", "c", node("c")),
 1462        t("x", "y", node("z"))
 1463    ],
 1464
 1465    Triples = Expected.
 1466
 1467test(sp_card,[cleanup(clean(TestDir)), setup(createng(TestDir))]) :-
 1468    open_directory_store(TestDir, Store),
 1469    open_write(Store, Builder),
 1470    nb_add_triple(Builder, "A", "B", node("C")),
 1471    nb_add_triple(Builder, "A", "B", node("D")),
 1472    nb_commit(Builder, Layer),
 1473    subject_id(Layer, "A", A_Id),
 1474    predicate_id(Layer, "B", B_Id),
 1475    sp_card(Layer, A_Id, B_Id, Count),
 1476    Count = 2.
 1477
 1478:- end_tests(terminus_store).