#5 Transaction control.

Merged
scossu merged 8 commits from scossu/txn into scossu/master 2 years ago
10 changed files with 309 additions and 101 deletions
  1. 2 2
      TODO.md
  2. 55 3
      include/graph.h
  3. 4 2
      include/store.h
  4. 1 1
      include/store_htable.h
  5. 137 36
      include/store_base.h
  6. 11 6
      include/store_mdb.h
  7. 3 4
      profile.c
  8. 68 26
      src/graph.c
  9. 28 21
      src/store_htable.c
  10. 0 0
      src/store_mdb.c

+ 2 - 2
TODO.md

@@ -22,8 +22,8 @@
     - *D* Subclass term types
 - *D* Namespaced IRIs
 - *D* Relative IRIs
-- D Flexible store interface
-- *P* Transaction control
+- *D* Flexible store interface
+- *D* Transaction control
 - *P* Turtle serialization / deserialization
 - *P* Full UTF-8 support
 - *P* Extended tests

+ 55 - 3
include/graph.h

@@ -49,6 +49,10 @@ LSUP_graph_new (
 /** @brief Copy triples from a source graph into a destination one.
  *
  * The destination graph is not initialized here, so the copy is cumulative.
+ *
+ * @param src[in] Source graph.
+ *
+ * @param dest[in] Destination graph.
  */
 LSUP_rc
 LSUP_graph_copy_contents (const LSUP_Graph *src, LSUP_Graph *dest);
@@ -86,6 +90,9 @@ LSUP_graph_free (LSUP_Graph *gr);
 
 /** @brief Compare two graphs.
  *
+ * Note that if any of the two graphs has an open transaction, the function
+ * is performed in the first graph's transaction.
+ *
  * @param[in] gr1 First operand.
  *
  * @param[in] gr2 Second operand.
@@ -132,7 +139,7 @@ LSUP_graph_namespace (const LSUP_Graph *gr);
 
 /** @brief Set the namespace map for an in-memory graph.
  *
- * This has no effect on MDB graphs.
+ * This has no effect on graph stores with LSUP_STORE_PERM.
  *
  * @param[in] gr Graph to set the namespace map for.
  *
@@ -160,6 +167,50 @@ bool
 LSUP_graph_contains (const LSUP_Graph *gr, const LSUP_Triple *spo);
 
 
+/** @brief Begin a transaction.
+ *
+ * If the underlying store supports it, begin a transaction for the given
+ * graph. Note that each graph can only have one open transaction at a time.
+ *
+ * The transaction must be either committed with #LSUP_graph_commit() or
+ * rolled back with #LSUP_graph_abort().
+ *
+ * @param[in] gr Graph handle.
+ *
+ * @param[in] flags Unused for now, use 0. TODO
+ *
+ * @return LSUP_OK on success; LSUP_VALUE_ERR if the graph store does not
+ *  support transactions; LSUP_TXN_ERR if the store has already an uncommitted
+ *  transaction; <0 on other errors from the underlying store.
+ */
+LSUP_rc
+LSUP_graph_begin (LSUP_Graph *gr, int flags);
+
+
+/** @brief Commit a transaction.
+ *
+ * If the underlying store supports it, commit an open transaction. In case of
+ * error, the transaction is left open and it is advisable to roll it back with
+ * #LSUP_graph_abort().
+ *
+ * @param[in] gr Graph handle.
+ *
+ * @return LSUP_OK if the transaction was committed successfully; LSUP_NOACTION
+ *  if NULL was passed; LSUP_TXN_ERR on error.
+ */
+LSUP_rc LSUP_graph_commit (LSUP_Graph *gr);
+
+
+/** @brief Abort (roll back) a transaction.
+ *
+ * If the underlying store supports it, abort an open transaction and abandon
+ * all changes.
+ *
+ * @param[in] gr Graph handle.
+ */
+void LSUP_graph_abort (LSUP_Graph *gr);
+
+
 /** @brief Initialize an iterator to add triples.
  *
  * @param[in] gr Graph to add to. It is added to the iterator state.
@@ -200,11 +251,12 @@ LSUP_graph_add_done (LSUP_GraphIterator *it);
  *
  * @param[in] strp Array of buffer triples to add. The last one must be NULL.
  *
- * @param[out] inserted This will be filled with the total number of triples
+ * @param[out] ct This will be filled with the total number of triples
  *  inserted.
  */
 LSUP_rc
-LSUP_graph_add (LSUP_Graph *gr, const LSUP_Triple trp[], size_t *inserted);
+LSUP_graph_add (
+        LSUP_Graph *gr, const LSUP_Triple trp[], size_t *ct);
 
 
 /** @brief Delete triples by a matching pattern.

+ 4 - 2
include/store.h

@@ -58,9 +58,11 @@ const LSUP_StoreInt *LSUP_store_int (LSUP_StoreType type);
  *
  * @sa #LSUP_graph_new()
  */
-typedef struct store_it {
+typedef struct store_t {
     LSUP_StoreType                  type;   ///< Store type.
-    char *                          id;     /**< Store ID. NOTE: This is
+    char *                          id;     /**< Store ID.
+                                             *
+                                             *   NOTE: This is
                                              *   NULL for volatile stores.
                                              */
     const LSUP_StoreInt *           sif;    ///< Store interface.

+ 1 - 1
include/store_htable.h

@@ -21,7 +21,7 @@
 #define _LSUP_STORE_HTABLE_H
 
 #include "buffer.h"
-#include "store_base.h"
+#include "store_interface.h"
 
 
 extern const LSUP_StoreInt htstore_int;

+ 137 - 36
include/store_base.h

@@ -1,4 +1,4 @@
-/** @file store.h
+/** @file store_interface.h
  *
  * @brief Common store back end interfaces.
  *
@@ -32,8 +32,8 @@
  */
 
 
-#ifndef _LSUP_STORE_BASE_H
-#define _LSUP_STORE_BASE_H
+#ifndef _LSUP_STORE_INTERFACE_H
+#define _LSUP_STORE_INTERFACE_H
 
 #include "environment.h"
 
@@ -102,7 +102,7 @@ typedef void (*store_free_fn_t)(void *store);
 
 /** @brief Prototype: get the store ID.
  *
- * @param store[in] Store handle.
+ * @param[in] store  Store handle.
  *
  * @return store ID string. This is a copy and should be freed after use.
  */
@@ -111,22 +111,65 @@ typedef char * (*store_id_fn_t)(const void *store);
 
 /** @brief Prototype: get store size.
  *
- * @param store[in] The store to calculate size of.
+ * @param[in] store  The store to calculate size of.
  *
  * @return Number of stored SPO triples (across all contexts if supported).
  */
 typedef size_t (*store_size_fn_t)(const void *store);
 
 
+#if 0
 /** @brief Print stats about a store.
  *
  * TODO
  *
- * @param store[in] The store to get stats for.
+ * @param[in] store The store to get stats for.
  */
-/* TODO
 typedef LSUP_rc (*store_stat_fn_t)(void *store, void *stat);
-*/
+#endif
+
+
+/** @brief Begin a transaction.
+ *
+ * Only for LSUP_STORE_TXN stores.
+ *
+ * The transaction handle is managed by the store implementation and can be any
+ * data type.
+ *
+ * @param[in] store Store handle.
+ *
+ * @param[in] flags Transaction flags. These vary with each implementation.
+ *
+ * @param[out] txn Will point to the new open transaction on success, or to
+ * undefined content on failure.
+ *
+ * @return LSUP_OK if the transaction started successfully, <0 on error.
+ */
+typedef LSUP_rc (*store_txn_begin_fn_t)(void *store, int flags, void **txn);
+
+
+/** @brief Commit a transaction.
+ *
+ * Only for LSUP_STORE_TXN stores.
+ *
+ * @param[in] store Store handle.
+ *
+ * @param[in] txn Transaction handle generated by #store_txn_begin_fn_t.
+ *
+ * @return LSUP_OK if the transaction was committed successfully, <0 on error.
+ */
+typedef LSUP_rc (*store_txn_commit_fn_t)(void *store);
+
+
+/** @brief Abort a transaction.
+ *
+ * Only for LSUP_STORE_TXN stores.
+ *
+ * @param[in] store Store handle.
+ *
+ * @param[in] txn Transaction handle generated by #store_txn_begin_fn_t.
+ */
+typedef void (*store_txn_abort_fn_t)(void *store);
 
 
 /** @brief Initialize bulk triple load.
@@ -143,9 +186,13 @@ typedef LSUP_rc (*store_stat_fn_t)(void *store, void *stat);
  *  the value of sc, triples will be added with no context. Only meaningful
  *  for stores with the LSUP_STORE_CTX feature.
  *
+ *  @param[in] udata User data. Consult individual store implementations for
+ *   how this is interpreted.
+ *
  * @return Iterator handle to be passed to the following load steps.
  */
-typedef void * (*store_add_init_fn_t)(void *store, const LSUP_Buffer * sc);
+typedef void * (*store_add_init_fn_t)(
+        void *store, const LSUP_Buffer *sc, void *udata);
 
 
 /** @brief Add one triple into the store.
@@ -154,7 +201,7 @@ typedef void * (*store_add_init_fn_t)(void *store, const LSUP_Buffer * sc);
  * yielded by that function. It may be called multiple times and must be
  * followed by #add_done_fn or #add_abort_fn (if supported).
  *
- * @param it[in] Iterator obtained by #LSUP_mdbstore_add_init.
+ * @param it[in] Iterator obtained by #store_add_init_fn_t.
  *  The following members are of interest:
  *  it->i stores the total number of records inserted.
  *
@@ -172,11 +219,30 @@ typedef LSUP_rc (*store_add_iter_fn_t)(
  * Usually called on an irrecoverable error from #add_iter_fn. None of the
  * successful inserts in the same loop is retained.
  *
- * @param it[in] Iterator obtained by #LSUP_mdbstore_add_init.
+ * @param it[in] Iterator obtained by #store_add_init_fn_t.
  */
 typedef void (*store_add_abort_fn_t)(void *it);
 
 
+/*
+ * Iterator function types.
+ */
+
+/** @brief Get iterator active transaction handle.
+ *
+ * This function is used to get an active transaction during an iteration loop
+ * in order to perform an action using the store state within that loop. Some
+ * stores (e.g. MDB) only support one R/W open transaction per thread, so this
+ * is also the only way to perform anything else than iterating or committing
+ * while a loop is open.
+ *
+ * @param[in] it Iterator handle to get the transaction from.
+ *
+ * @return Transaction handle. DO NOT close this transaction directly.
+ */
+typedef void * (*iter_txn_fn_t)(void *it);
+
+
 /** @brief Finalize an add loop and free iterator.
  *
  * This must be called after #add_iter_fn.
@@ -192,7 +258,8 @@ typedef LSUP_rc (*store_add_done_fn_t)(void *it);
  *
  * @param[in] sterm Serialized term to store.
  */
-typedef LSUP_rc (*store_add_term_fn_t)(void *store, const LSUP_Buffer *sterm);
+typedef LSUP_rc (*store_add_term_fn_t)(
+        void *store, const LSUP_Buffer *sterm, void *udata);
 
 
 /** @brief Prototype: look up triples by pattern matching.
@@ -200,17 +267,20 @@ typedef LSUP_rc (*store_add_term_fn_t)(void *store, const LSUP_Buffer *sterm);
  * This function may return a count of matches and/or an iterator of results as
  * serialized triples.
  *
+ * For stores with #LSUP_STORE_TXN, this opens a read-only transaction. The
+ * transaction handle is held in the iterator structure and is closed when the
+ * iterator is freed with #iter_free_fn_t().
+ *
  * Any and all of the terms may be NULL, which indicates an unbound query
- * term. Stores with context not set or witout context support will always
- * ignore the fourth term.
+ * term. Stores witout context support will always ignore sc.
  *
  * @param[in] store The store to be queried.
  *
- * @param[in] ss Buffer representing the serialized s term.
+ * @param[in] ss Serialized s term.
  *
- * @param[in] sp Buffer representing the serialized p term.
+ * @param[in] sp Serialized p term.
  *
- * @param[in] so Buffer representing the serialized o term.
+ * @param[in] so Serialized o term.
  *
  * @param[in] sc Serialized context to limit search to. It may be NULL, in
  * which case search is done in all contexts. Note that triples inserted
@@ -221,6 +291,9 @@ typedef LSUP_rc (*store_add_term_fn_t)(void *store, const LSUP_Buffer *sterm);
  *  much less so for 1-bound and 2-bound context lookups, in which cases it
  *  should be set only if needed.
  *
+ *  @param[in] udata User data. Consult individual store implementations for
+ *   how this is interpreted.
+ *
  * @return Iterator handle that will be populated with a result iterator. This
  * is always created even if no matches are found and must be freed with
  * #LSUP_mdbiter_free() after use. If matches are found, the iterator points to
@@ -229,7 +302,7 @@ typedef LSUP_rc (*store_add_term_fn_t)(void *store, const LSUP_Buffer *sterm);
 typedef void * (*store_lookup_fn_t)(
         void *store,
         const LSUP_Buffer *ss, const LSUP_Buffer *sp, const LSUP_Buffer *so,
-        const LSUP_Buffer *sc, size_t *ct);
+        const LSUP_Buffer *sc, void *udata, size_t *ct);
 
 
 /** @brief Prototype: check for existence of a triple (T/F).
@@ -250,13 +323,16 @@ typedef bool (*store_trp_exist_fn_t)(
 /** @brief Prototype: delete triples by pattern matching.
  *
  * The ss, sp, so, sc terms act as a matching pattern as documented in
- * #store_lookup_fn. if not NULL, ct yields the number of triples actually
+ * @sa #store_lookup_fn. if not NULL, ct yields the number of triples actually
  * deleted.
+ *
+ *  @param[in] udata User data. Consult individual store implementations for
+ *   how this is interpreted.
  */
 typedef LSUP_rc (*store_remove_fn_t)(
         void *store,
         const LSUP_Buffer *ss, const LSUP_Buffer *sp, const LSUP_Buffer *so,
-        const LSUP_Buffer *sc, size_t *ct);
+        const LSUP_Buffer *sc, void *udata, size_t *ct);
 
 
 /** @brief Put an in-memory namespace map into a permanent back end.
@@ -278,11 +354,15 @@ typedef LSUP_rc (*store_remove_fn_t)(
  *
  * @param[out] nsm Namespace map handle to store.
  *
+ * @param[in] udata User-defined data. Consult individual implementations for
+ *  details.
+ *
  * @return LSUP_OK if all terms were updated; LSUP_CONFLICT if one or more
  *  namespaces or terms were not updated because they already existed; <0 if
  *  an error occurred.
  */
-typedef LSUP_rc (*store_nsm_put_fn_t)(void *store, const LSUP_NSMap * nsm);
+typedef LSUP_rc (*store_nsm_put_fn_t)(
+        void *store, const LSUP_NSMap * nsm, void *udata);
 
 
 /** @brief Get the store's namespace prefix map.
@@ -294,10 +374,6 @@ typedef LSUP_rc (*store_nsm_put_fn_t)(void *store, const LSUP_NSMap * nsm);
 typedef LSUP_NSMap * (*store_nsm_get_fn_t)(void *store);
 
 
-/*
- * Iterator function types.
- */
-
 /** @brief Prototype: yield the matching triples and advance the iterator.
  *
  * NOTE: Iterators keep transactions open. Don't hold on to them longer than
@@ -349,6 +425,18 @@ typedef void (*iter_free_fn_t)(void * it);
  */
 
 /** @brief Store interface.
+ *
+ * New store implementations should define a static structure with the relevant
+ * members filled in. Some members are only relevant to certain types of stores
+ * and may be set to NULL.
+ *
+ * #setup_fn may be optionally defined and MUST cause an idempotent action,
+ * unless the `clear` argument is set to `true`. Callers should check if this
+ * member is NULL and if it is not, call it at the beginning of the
+ * interaction with the store.
+ *
+ * Transaction control members are only applicable to stores with the
+ * #LSUP_STORE_TXN feature.
  */
 typedef struct store_if_t {
     // Basic properties.
@@ -364,18 +452,26 @@ typedef struct store_if_t {
     store_size_fn_t     size_fn;        ///< Number of triples in the store.
     store_id_fn_t       id_fn;          ///< Get store ID.
 
+    // Transaction control.
+    store_txn_begin_fn_t txn_begin_fn;  ///< Begin transaction.
+    store_txn_commit_fn_t txn_commit_fn; ///< Commit transaction.
+    store_txn_abort_fn_t txn_abort_fn;  ///< Abort transaction.
+    iter_txn_fn_t       iter_txn_fn;    ///< Get iterator's transaction.
+
     // Addition.
     store_add_init_fn_t add_init_fn;    ///< Initialize add iteration.
     store_add_iter_fn_t add_iter_fn;    ///< Add one triple.
-    store_add_abort_fn_t add_abort_fn;  /**< Abort (roll back) the add
-                                         *  process.  Only available in
-                                         *  stores with #LSUP_STORE_TXN
-                                         *  feature. Optional.
+    store_add_abort_fn_t add_abort_fn;  /**< Abort (roll back) the add process.
+                                         *
+                                         *   Only available in
+                                         *   stores with #LSUP_STORE_TXN
+                                         *   feature. Optional.
                                          */
     store_add_done_fn_t add_done_fn;    ///< Complete the add process.
     store_add_term_fn_t add_term_fn;    /**< Add (index) a term to the store.
-                                         *  Only available in stores with
-                                         *  #LSUP_STORE_IDX feature. Optional.
+                                         *
+                                         *   Only available in stores with
+                                         *   #LSUP_STORE_IDX feature. Optional.
                                          */
 
     // Look up.
@@ -388,14 +484,14 @@ typedef struct store_if_t {
     store_remove_fn_t   remove_fn;      ///< Remove triples by pattern.
 
     // Namespace prefix mapping.
-    store_nsm_put_fn_t  nsm_put_fn;     /**< Add a namespace/prefix pair to
-                                         *  the prefix map.
+    store_nsm_put_fn_t  nsm_put_fn;     /**< Add a ns/pfx pair to the map.
+                                         *
                                          *  Only available (and mandatory)
                                          *  in stores with the
                                          *  #LSUP_STORE_IDX feature.
                                          */
-    store_nsm_get_fn_t  nsm_get_fn;     /**< Get a namespace/prefix from
-                                         *  the prefix map.
+    store_nsm_get_fn_t  nsm_get_fn;     /**< Get a namespace from the map.
+                                         *
                                          *  Only available (and mandatory)
                                          *  in stores with the
                                          *  #LSUP_STORE_IDX feature.
@@ -417,6 +513,11 @@ const LSUP_StoreInt my_store_int = {
     .free_fn        = my_free_fn,
 
     .size_fn        = my_size_fn,
+    .id_fn          = my_id_fn,
+
+    .txn_begin_fn   = my_txn_begin_fn,
+    .txn_commit_fn  = my_txn_commit_fn,
+    .txn_abort_fn   = my_txn_abort_fn,
 
     .add_init_fn    = my_init_fn,
     .add_iter_fn    = my_iter_fn,
@@ -435,4 +536,4 @@ const LSUP_StoreInt my_store_int = {
 };
 */
 
-#endif  /* _LSUP_STORE_BASE_H */
+#endif  /* _LSUP_STORE_INTERFACE_H */

+ 11 - 6
include/store_mdb.h

@@ -11,11 +11,15 @@
  * per session. Within that session multiple R/W operations can be performed
  * using transactions.
  *
- * Note that, even though the terms "graph", "context", etc. are used, no code
- * in this module checks for valid RDF data. In theory any term can be any
- * binary data. This allows using the store for non-RDF graph data.
- *
- * TODO more doc
+ * This store supports transactions. Under the hood, LMDB supports nested RW
+ * transactions, which are used here, but not exposed to the caller. Some
+ * functions have a transaction handle parameter that may be NULL. In that
+ * case, a new transaction is opened and closed within the scope of the
+ * function (or, in cases such as #mdbstore_lookup(), within the life cycle of
+ * the iterator); if not, the transaction handle may either be used as the
+ * parent for a new transaction (which is closed as in the previous case), or
+ * the function uses the same transaction (i.e. changes are only committed 
+ * after the parent transaction is committed).
  */
 
 
@@ -25,10 +29,11 @@
 #include "lmdb.h"
 
 #include "buffer.h"
-#include "store_base.h"
+#include "store_interface.h"
 
 
 // FIXME find a better cross-platform path.
+/// Default MDB store identifier and location.
 #define LSUP_MDB_STORE_URN "file://" TMPDIR "/mdb_store"
 
 /// MDB store interface.

+ 3 - 4
profile.c

@@ -31,14 +31,13 @@ int main(int argc, char *argv[])
 {
     size_t nt = (argc > 1) ? atoi (argv[1]) : NT;
     // Set env variable to test path.
-    putenv ("LSUP_MDB_STORE_PATH=" TMPDIR "/lsup_profile_mdb");
-    // Clear out database from previous test.
-    rm_r (getenv ("LSUP_MDB_STORE_PATH"));
+    putenv ("LSUP_MDB_STORE_URN=file://" TMPDIR "/lsup_profile_mdb");
 
     if (LSUP_init() != LSUP_OK) {
         log_fatal ("Failed to initialize LSUP environment.");
         exit (-1);
     }
+    LSUP_store_int (LSUP_STORE_MDB)->setup_fn (NULL, true);
 
     int rc;
     clock_t start, tc1, tc2, end;
@@ -54,7 +53,7 @@ int main(int argc, char *argv[])
 
     log_info ("Inserting triples.");
     LSUP_Graph *gr = LSUP_graph_new (
-            LSUP_iriref_new (NULL, NULL), LSUP_STORE_MDB);
+            LSUP_iriref_new (NULL, NULL), LSUP_STORE_MDB, NULL, NULL, nt);
     if (!gr) {
         log_error ("Error creating graph!");
         return -1;

+ 68 - 26
src/graph.c

@@ -7,12 +7,16 @@
 struct graph_t {
     LSUP_Term               *uri;           ///< Graph "name" (URI).
     LSUP_Store *            store;          ///< Store handle.
-    LSUP_NSMap *            nsm;            /**< Namespace map. NOTE: This is
-                                               * NULL for permanent stores. */
+    LSUP_NSMap *            nsm;            /**< Namespace map.
+                                              *
+                                              * NOTE: This is
+                                              * NULL for permanent stores.
+                                              */
+    void *                  txn;            ///< Store transaction.
 };
 
 struct graph_iter_t {
-    LSUP_Store *            store;          ///< Store tied to the iterator.
+    const LSUP_Graph *      graph;          ///< Parent graph.
     void *                  data;           ///< Iterator state.
     size_t                  ct;             ///< Total lookup matches.
 };
@@ -72,6 +76,8 @@ BACKEND_TBL
     if (gr->store->sif->features & LSUP_STORE_PERM) gr->nsm = NULL;
     else gr->nsm = nsm ? nsm : LSUP_default_nsm;
 
+    gr->txn = NULL;
+
     log_debug ("Graph created.");
     return gr;
 }
@@ -110,15 +116,16 @@ LSUP_graph_bool_op(
     LSUP_BufferTriple *sspo = BTRP_DUMMY;
     size_t ct;
 
-    add_it = res->store->sif->add_init_fn (res->store->data, res_sc);
+    add_it = res->store->sif->add_init_fn (res->store->data, res_sc, gr1->txn);
 
     if (op == LSUP_BOOL_XOR) {
         // Add triples from gr2 if not found in gr1.
         lu2_it = gr2->store->sif->lookup_fn (
-                gr2->store->data, NULL, NULL, NULL, gr2_sc, NULL);
+                gr2->store->data, NULL, NULL, NULL, gr2_sc, NULL, gr1->txn);
         while (gr2->store->sif->lu_next_fn (lu2_it, sspo, NULL) == LSUP_OK) {
             lu1_it = gr1->store->sif->lookup_fn (
-                    gr1->store->data, sspo->s, sspo->p, sspo->o, gr1_sc, &ct);
+                    gr1->store->data, sspo->s, sspo->p, sspo->o, gr1_sc,
+                    gr1->txn, &ct);
             if (ct > 0)
                 res->store->sif->add_iter_fn (add_it, sspo);
             gr1->store->sif->lu_free_fn (lu1_it);
@@ -127,10 +134,11 @@ LSUP_graph_bool_op(
     }
 
     lu1_it = gr1->store->sif->lookup_fn (
-            gr1->store->data, NULL, NULL, NULL, gr1_sc, NULL);
+            gr1->store->data, NULL, NULL, NULL, gr1_sc, gr1->txn, NULL);
     while (gr1->store->sif->lu_next_fn (lu1_it, sspo, NULL) == LSUP_OK) {
         lu2_it = gr2->store->sif->lookup_fn (
-                gr2->store->data, sspo->s, sspo->p, sspo->o, gr2_sc, &ct);
+                gr2->store->data, sspo->s, sspo->p, sspo->o, gr2_sc,
+                gr1->txn, &ct);
         // For XOR and subtraction, add if not found.
         // For intersection, add if found.
         if ((ct == 0) ^ (op == LSUP_BOOL_INTERSECTION))
@@ -229,10 +237,10 @@ LSUP_graph_add_init (LSUP_Graph *gr)
 
     LSUP_Buffer *sc = LSUP_term_serialize (gr->uri);
 
-    it->data = gr->store->sif->add_init_fn (gr->store->data, sc);
+    it->data = gr->store->sif->add_init_fn (gr->store->data, sc, gr->txn);
     LSUP_buffer_free (sc);
 
-    it->store = gr->store;
+    it->graph = gr;
 
     return it;
 }
@@ -244,17 +252,21 @@ LSUP_graph_add_iter (LSUP_GraphIterator *it, const LSUP_Triple *spo)
 
     LSUP_BufferTriple *sspo = LSUP_triple_serialize (spo);
     if (UNLIKELY (!sspo)) return LSUP_MEM_ERR;
+    const LSUP_StoreInt *sif = it->graph->store->sif;
 
-    LSUP_rc rc = it->store->sif->add_iter_fn (it->data, sspo);
+    LSUP_rc rc = sif->add_iter_fn (it->data, sspo);
     PCHECK (rc, finally);
 
     // Store datatype term permanently if the store supports it.
-    if (rc == LSUP_OK && it->store->sif->add_term_fn) {
+    if (rc == LSUP_OK && sif->add_term_fn) {
+        void *txn;
         for (int i = 0; i < 3; i++) {
             LSUP_Term *term = LSUP_triple_pos (spo, i);
             if (term->type == LSUP_TERM_LITERAL) {
                 LSUP_Buffer *ser_dtype = LSUP_term_serialize (term->datatype);
-                it->store->sif->add_term_fn (it->store->data, ser_dtype);
+                // Run add_term in the iterator's txn.
+                txn = sif->iter_txn_fn ? sif->iter_txn_fn (it->data) : NULL;
+                sif->add_term_fn ( it->graph->store->data, ser_dtype, txn);
                 LSUP_buffer_free (ser_dtype);
             }
         }
@@ -271,7 +283,7 @@ finally:
 void
 LSUP_graph_add_done (LSUP_GraphIterator *it)
 {
-    it->store->sif->add_done_fn (it->data);
+    it->graph->store->sif->add_done_fn (it->data);
     free (it);
 }
 
@@ -323,7 +335,8 @@ LSUP_graph_remove (
         *so = LSUP_term_serialize (o),
         *sc = LSUP_term_serialize (gr->uri);
 
-    rc = gr->store->sif->remove_fn (gr->store->data, ss, sp, so, sc, ct);
+    rc = gr->store->sif->remove_fn (
+            gr->store->data, ss, sp, so, sc, gr->txn, ct);
 
     LSUP_buffer_free (ss);
     LSUP_buffer_free (sp);
@@ -374,25 +387,27 @@ LSUP_graph_lookup (
     LSUP_GraphIterator *it;
     MALLOC_GUARD (it, NULL);
 
-    it->store = gr->store;
-
     LSUP_Buffer
         *ss = LSUP_term_serialize (s),
         *sp = LSUP_term_serialize (p),
         *so = LSUP_term_serialize (o),
         *sc = LSUP_term_serialize (gr->uri);
 
-    it->data = it->store->sif->lookup_fn (it->store->data, ss, sp, so, sc, ct);
-    if (UNLIKELY (!it->data)) {
-        free (it);
-        it = NULL;
-    }
+    it->data = gr->store->sif->lookup_fn (
+            gr->store->data, ss, sp, so, sc, gr->txn, ct);
 
     LSUP_buffer_free (ss);
     LSUP_buffer_free (sp);
     LSUP_buffer_free (so);
     LSUP_buffer_free (sc);
 
+    if (UNLIKELY (!it->data)) {
+        free (it);
+        return NULL;
+    }
+
+    it->graph = gr;
+
     return it;
 }
 
@@ -402,7 +417,7 @@ LSUP_graph_iter_next (LSUP_GraphIterator *it, LSUP_Triple *spo)
 {
     LSUP_Buffer *ss, *sp, *so;
     LSUP_BufferTriple *sspo;
-    if (it->store->sif->features & LSUP_STORE_COW) {
+    if (it->graph->store->sif->features & LSUP_STORE_COW) {
         CALLOC_GUARD (ss, LSUP_MEM_ERR);
         CALLOC_GUARD (sp, LSUP_MEM_ERR);
         CALLOC_GUARD (so, LSUP_MEM_ERR);
@@ -422,7 +437,7 @@ LSUP_graph_iter_next (LSUP_GraphIterator *it, LSUP_Triple *spo)
         if (!spo->o) return LSUP_ERROR;
     }
 
-    if (it->store->sif->features & LSUP_STORE_COW) {
+    if (it->graph->store->sif->features & LSUP_STORE_COW) {
         LSUP_btriple_free_shallow (sspo);
     } else {
         // TODO copy-on-retrieval stores. None yet.
@@ -435,7 +450,7 @@ LSUP_graph_iter_next (LSUP_GraphIterator *it, LSUP_Triple *spo)
 void
 LSUP_graph_iter_free (LSUP_GraphIterator *it)
 {
-    it->store->sif->lu_free_fn (it->data);
+    it->graph->store->sif->lu_free_fn (it->data);
     free (it);
 }
 
@@ -455,6 +470,33 @@ LSUP_graph_contains (const LSUP_Graph *gr, const LSUP_Triple *spo)
 }
 
 
+LSUP_rc
+LSUP_graph_begin (LSUP_Graph *gr, int flags) {
+    if (!(gr->store->sif->features & LSUP_STORE_TXN)) return LSUP_VALUE_ERR;
+
+    return gr->store->sif->txn_begin_fn(gr->store->data, flags, &gr->txn);
+}
+
+
+LSUP_rc
+LSUP_graph_commit (LSUP_Graph *gr)
+{
+    LSUP_rc rc = gr->store->sif->txn_commit_fn (gr->txn);
+
+    if (rc == LSUP_OK) gr->txn = NULL;
+
+    return rc;
+}
+
+
+void
+LSUP_graph_abort (LSUP_Graph *gr)
+{
+    gr->store->sif->txn_abort_fn (gr->txn);
+    gr->txn = NULL;
+}
+
+
 /*
  * Static functions.
  */
@@ -466,7 +508,7 @@ LSUP_graph_contains (const LSUP_Graph *gr, const LSUP_Triple *spo)
  */
 inline static LSUP_rc
 graph_iter_next_buffer (LSUP_GraphIterator *it, LSUP_BufferTriple *sspo)
-{ return it->store->sif->lu_next_fn (it->data, sspo, NULL); }
+{ return it->graph->store->sif->lu_next_fn (it->data, sspo, NULL); }
 
 
 /**

+ 28 - 21
src/store_htable.c

@@ -151,12 +151,12 @@ htiter_next_key (HTIterator *it);
  * @param[in] id Graph identifier. This may or may not be set. The store does
  *  not use this value internally, and does not check for duplicates.
  *
- * @param[in] size Initial size of the store (in number of triples). It may be
- *  0.
+ * @param[in] size Initial size of the store (in number of triples to
+ * preallocate). It may be 0.
  *
  * @return New graph store.
  */
-void *
+static void *
 htstore_new (const char *id, size_t size)
 {
     HTStore *ht;
@@ -177,7 +177,7 @@ htstore_new (const char *id, size_t size)
 
 
 #if 0
-LSUP_rc
+static LSUP_rc
 htstore_copy_contents (HTStore *dest, const HTStore *src)
 {
     size_t i = 0;
@@ -198,7 +198,7 @@ htstore_copy_contents (HTStore *dest, const HTStore *src)
 #endif
 
 
-void
+static void
 htstore_free (void *h)
 {
     HTStore *store = h;
@@ -208,7 +208,7 @@ htstore_free (void *h)
 }
 
 
-size_t
+static size_t
 htstore_size (const void *h)
 {
     const HTStore *store = h;
@@ -216,9 +216,10 @@ htstore_size (const void *h)
 }
 
 
-LSUP_rc
-htstore_add_term (void *h, const LSUP_Buffer *sterm)
+static LSUP_rc
+htstore_add_term (void *h, const LSUP_Buffer *sterm, void *_unused)
 {
+    (void) _unused;
     HTStore *store = h;
     IndexEntry entry_s = {
         .key = LSUP_buffer_hash (sterm),
@@ -237,10 +238,11 @@ htstore_add_term (void *h, const LSUP_Buffer *sterm)
 }
 
 
-void *
-htstore_add_init (void *h, const LSUP_Buffer *_unused)
+static void *
+htstore_add_init (void *h, const LSUP_Buffer *_unused, void *_unused2)
 {
     (void) _unused;
+    (void) _unused2;
     HTIterator *it;
     MALLOC_GUARD (it, NULL);
 
@@ -250,7 +252,7 @@ htstore_add_init (void *h, const LSUP_Buffer *_unused)
 }
 
 
-LSUP_rc
+static LSUP_rc
 htstore_add_iter (void *h, const LSUP_BufferTriple *sspo)
 {
     HTIterator *it = h;
@@ -265,13 +267,13 @@ htstore_add_iter (void *h, const LSUP_BufferTriple *sspo)
     if (rc != LSUP_OK) return rc;
 
     for (int i = 0; i < 3; i++)
-        htstore_add_term (it->store, LSUP_btriple_pos (sspo, i));
+        htstore_add_term (it->store, LSUP_btriple_pos (sspo, i), NULL);
 
     return rc;
 }
 
 
-LSUP_rc
+static LSUP_rc
 htstore_add_done (void *h)
 {
     free (h);
@@ -279,12 +281,13 @@ htstore_add_done (void *h)
 }
 
 
-void *
+static void *
 htstore_lookup (
         void *h,
         const LSUP_Buffer *ss, const LSUP_Buffer *sp, const LSUP_Buffer *so,
-        const LSUP_Buffer *sc, size_t *ct)
+        const LSUP_Buffer *sc, void *_unused, size_t *ct)
 {
+    (void) _unused;
     HTStore *store = h;
     HTIterator *it;
     CALLOC_GUARD (it, NULL);
@@ -348,16 +351,18 @@ htstore_lookup (
 }
 
 
-LSUP_rc
+static LSUP_rc
 htstore_remove(
         void *h, const LSUP_Buffer *ss, const LSUP_Buffer *sp,
-        const LSUP_Buffer *so,  const LSUP_Buffer *_unused, size_t *ct_p)
+        const LSUP_Buffer *so,  const LSUP_Buffer *_unused,
+        void *_unused2, size_t *ct_p)
 {
     (void) _unused;
+    (void) _unused2;
     HTStore *store = h;
     size_t ct;
 
-    HTIterator *it = htstore_lookup (store, ss, sp, so, NULL, &ct);
+    HTIterator *it = htstore_lookup (store, ss, sp, so, NULL, NULL, &ct);
     if (UNLIKELY (!it)) return LSUP_DB_ERR;
 
     LSUP_rc rc;
@@ -383,7 +388,7 @@ finally:
 }
 
 
-LSUP_rc
+static LSUP_rc
 htiter_next_key (HTIterator *it)
 {
     if (UNLIKELY (!it)) return LSUP_VALUE_ERR;
@@ -412,7 +417,7 @@ htiter_next_key (HTIterator *it)
 }
 
 
-LSUP_rc
+static LSUP_rc
 htiter_next (void *h, LSUP_BufferTriple *sspo, LSUP_Buffer **_unused)
 {
     (void) _unused;
@@ -447,7 +452,9 @@ const LSUP_StoreInt htstore_int = {
 };
 
 
-/* * * Statics * * */
+/*
+ * Other statics.
+ */
 
 inline static LSUP_rc
 tkey_to_strp (

+ 0 - 0
src/store_mdb.c


Some files were not shown because too many files changed in this diff