Переглянути джерело

Pass all tests without mem store:

* Avoid transaction conflicts in mdb store.
* Clean up databases properly on test.
* Add triple constructors.
* Make key_to_sterm static and mark sterm_to_key unused in mdb store.
Stefano Cossu 4 роки тому
батько
коміт
a901e7c0ba
6 змінених файлів з 290 додано та 175 видалено
  1. 2 1
      include/graph.h
  2. 4 0
      include/store_mdb.h
  3. 34 3
      include/triple.h
  4. 116 105
      src/graph.c
  5. 52 66
      src/store_mdb.c
  6. 82 0
      src/triple.c

+ 2 - 1
include/graph.h

@@ -191,7 +191,8 @@ LSUP_graph_lookup(const LSUP_Graph *gr, const LSUP_Triple *spo);
  *
  * @param it[in] Iterator handle obtained through #LSUP_graph_lookup.
  *
- * @param spo[out] Triple to be populated with the next result.
+ * @param spo[out] Triple to be populated with the next result. May be NULL
+ *  (e.g. for counters).
  *
  * @return LSUP_OK if a result was found; LSUP_END if the end of the match list
  *  was reached.

+ 4 - 0
include/store_mdb.h

@@ -128,6 +128,10 @@ LSUP_mdbstore_add_init(LSUP_MDBStore *store, const LSUP_Buffer *sc);
  * yielded by that function. It may be called multiple times and must be
  * followed by #LSUP_mdbstore_add_done.
  *
+ * NOTE: at the moment #LSUP_mdbstore_remove() or another
+ * #LSUP_mdbstore_init() cannot be called between #LSUP_mdbstore_add_init and
+ * #LSUP_mdbstore_add_abort or #LSUP_mdbstore_add_done. FIXME
+ *
  * @param it[in] Iterator obtained by #LSUP_mdbstore_add_init.
  *  The following members are of interest:
  *  it->i stores the total number of records inserted.

+ 34 - 3
include/triple.h

@@ -22,6 +22,22 @@ typedef enum {
 } LSUP_TriplePos;
 
 
+LSUP_Triple *
+LSUP_triple_new();
+
+
+LSUP_SerTriple *
+LSUP_striple_new();
+
+
+LSUP_Triple *
+LSUP_triple_new_from_striple(const LSUP_SerTriple *sspo);
+
+
+LSUP_SerTriple *
+LSUP_striple_new_from_triple(const LSUP_Triple *spo);
+
+
 /** @brief Serialize a RDF triple into a buffer triple.
  *
  * The internal structure must be freed with #LSUP_striple_done after use.
@@ -55,7 +71,7 @@ LSUP_triple_deserialize(const LSUP_SerTriple *sspo, LSUP_Triple *spo);
  * @param spo[in] Triple to be freed.
  */
 void
-LSUP_triple_done(LSUP_Triple *spo);
+LSUP_triple_done (LSUP_Triple *spo);
 
 
 /** @brief Free the internal pointers of a buffer triple.
@@ -63,7 +79,23 @@ LSUP_triple_done(LSUP_Triple *spo);
  * @param sspo[in] Buffer triple to be freed.
  */
 void
-LSUP_striple_done(LSUP_SerTriple *sspo);
+LSUP_striple_done (LSUP_SerTriple *sspo);
+
+
+/** @brief Free a triple and all its internal pointers.
+ *
+ * @param spo[in] Triple to be freed.
+ */
+void
+LSUP_triple_free (LSUP_Triple *spo);
+
+
+/** @brief Free a buffer triple and all its internal pointers.
+ *
+ * @param sspo[in] Buffer triple to be freed.
+ */
+void
+LSUP_striple_free (LSUP_SerTriple *sspo);
 
 
 #define _FN_BODY \
@@ -101,7 +133,6 @@ LSUP_triple_pos(const LSUP_Triple *trp, LSUP_TriplePos n)
 inline LSUP_Buffer *
 LSUP_striple_pos(const LSUP_SerTriple *trp, LSUP_TriplePos n)
 { _FN_BODY }
-
 #undef _FN_BODY
 
 #endif

+ 116 - 105
src/graph.c

@@ -52,12 +52,12 @@ typedef struct GraphIterator {
 /**
  * Extern inline functions.
  */
-size_t LSUP_graph_size(const LSUP_Graph *gr);
+size_t LSUP_graph_size (const LSUP_Graph *gr);
 
 
 /* * * Static prototypes. * * */
 
-static inline void mdbstore_init();
+static inline LSUP_rc mdbstore_init();
 
 
 /* * * Post-lookup callback prototypes * * */
@@ -72,14 +72,14 @@ int match_rm_fn(
         void *ctx);
 
 static LSUP_rc
-graph_iter_next_buffer(GraphIterator *it, LSUP_SerTriple *sspo);
+graph_iter_next_buffer (GraphIterator *it, LSUP_SerTriple *sspo);
 
 
 /* Atexit functions. */
-void ctx_cleanup() { LSUP_buffer_done(default_ctx); }
+void ctx_cleanup() { LSUP_buffer_done (default_ctx); }
 
 
-static inline bool is_null_trp(const LSUP_TripleKey *trp)
+static inline bool is_null_trp (const LSUP_TripleKey *trp)
 {
     return (
             *trp[0] == NULL_KEY &&
@@ -97,23 +97,23 @@ check_backend (LSUP_store_type be)
 /* * * GRAPH * * */
 
 Graph *
-LSUP_graph_new(const LSUP_store_type store_type)
+LSUP_graph_new (const LSUP_store_type store_type)
 {
-    if (!check_backend(store_type)) return NULL;
+    if (!check_backend (store_type)) return NULL;
 
     LSUP_Graph *gr;
-    CRITICAL(gr = malloc(sizeof(LSUP_Graph)));
-    gr->uri = LSUP_uri_new(NULL);
+    CRITICAL (gr = malloc (sizeof (LSUP_Graph)));
+    gr->uri = LSUP_uri_new (NULL);
 
-    mdbstore_init();
+    if (mdbstore_init() != LSUP_OK) return NULL;
 
     if (store_type == LSUP_STORE_MEM) {
-        // TODO uncomment gr->ht_store = LSUP_htstore_new(0);
+        // TODO uncomment gr->ht_store = LSUP_htstore_new (0);
         // if (!gr->ht_store) return NULL;
 
     } else if (store_type == LSUP_STORE_MDB) {
         gr->mdb_store = LSUP_mdbstore_new(
-                getenv("LSUP_MDB_STORE_PATH"), default_ctx);
+                getenv ("LSUP_MDB_STORE_PATH"), default_ctx);
         if (!gr->mdb_store) return NULL;
 
     } else {
@@ -132,18 +132,18 @@ LSUP_graph_new(const LSUP_store_type store_type)
  * The destination graph is not initialized here, so the copy is cumulative.
  */
 static LSUP_rc
-graph_copy_contents(const LSUP_Graph *src, LSUP_Graph *dest)
+graph_copy_contents (const LSUP_Graph *src, LSUP_Graph *dest)
 {
     LSUP_rc rc = LSUP_NOACTION;
     const LSUP_Triple trp = {NULL, NULL, NULL};
 
-    GraphIterator *it = LSUP_graph_lookup(src, &trp);
+    GraphIterator *it = LSUP_graph_lookup (src, &trp);
 
     LSUP_SerTriple sspo;
 
-    while (graph_iter_next_buffer(it, &sspo) != LSUP_END) {
-        TRACE("Inserting triple #%lu\n", it->i);
-        LSUP_rc add_rc = LSUP_graph_add(dest, NULL, 0, &sspo, 1, NULL);
+    while (graph_iter_next_buffer (it, &sspo) != LSUP_END) {
+        TRACE ("Inserting triple #%lu\n", it->i);
+        LSUP_rc add_rc = LSUP_graph_add (dest, NULL, 0, &sspo, 1, NULL);
         if (LIKELY (add_rc == LSUP_OK)) rc = LSUP_OK;
         else if (add_rc < 0) return add_rc;
     }
@@ -153,12 +153,12 @@ graph_copy_contents(const LSUP_Graph *src, LSUP_Graph *dest)
 
 
 LSUP_Graph *
-LSUP_graph_copy(const Graph *src)
+LSUP_graph_copy (const Graph *src)
 {
-    LSUP_Graph *dest = LSUP_graph_new(src->store_type);
+    LSUP_Graph *dest = LSUP_graph_new (src->store_type);
     if (UNLIKELY (!dest)) return NULL;
 
-    LSUP_rc rc = graph_copy_contents(src, dest);
+    LSUP_rc rc = graph_copy_contents (src, dest);
     if (UNLIKELY (rc != LSUP_OK)) return NULL;
 
     return dest;
@@ -187,8 +187,8 @@ LSUP_graph_bool_op(
         return NULL;
     }
 
-    LSUP_Graph *res = LSUP_graph_new(LSUP_STORE_MEM);
-    res->ht_store = LSUP_htstore_bool_op(op, gr1->ht_store, gr2->ht_store);
+    LSUP_Graph *res = LSUP_graph_new (LSUP_STORE_MEM);
+    res->ht_store = LSUP_htstore_bool_op (op, gr1->ht_store, gr2->ht_store);
 
     return res;
 }
@@ -196,58 +196,58 @@ LSUP_graph_bool_op(
 
 
 void
-LSUP_graph_free(LSUP_Graph *gr)
+LSUP_graph_free (LSUP_Graph *gr)
 {
-    if (LIKELY(gr != NULL)) {
-        LSUP_term_free(gr->uri);
+    if (LIKELY (gr != NULL)) {
+        LSUP_term_free (gr->uri);
 
         if (gr->store_type == LSUP_STORE_MEM)
-            NULL;// TODO uncomment LSUP_htstore_free(gr->ht_store);
+            NULL;// TODO uncomment LSUP_htstore_free (gr->ht_store);
         else
-            LSUP_mdbstore_free(gr->mdb_store);
+            LSUP_mdbstore_free (gr->mdb_store);
 
-        free(gr);
+        free (gr);
     }
 }
 
 
 LSUP_Term *
-LSUP_graph_uri(const LSUP_Graph *gr) { return gr->uri; }
+LSUP_graph_uri (const LSUP_Graph *gr) { return gr->uri; }
 
 
 LSUP_rc
-LSUP_graph_set_uri(LSUP_Graph *gr, const char *uri)
-{ return LSUP_uri_init(gr->uri, uri); }
+LSUP_graph_set_uri (LSUP_Graph *gr, const char *uri)
+{ return LSUP_uri_init (gr->uri, uri); }
 
 
 LSUP_rc
-LSUP_graph_resize(LSUP_Graph *gr, size_t size)
+LSUP_graph_resize (LSUP_Graph *gr, size_t size)
 {
     return LSUP_OK; // TODO remove line.
     if (gr->store_type == LSUP_STORE_MEM)
-        return 0;// TODO uncomment LSUP_htstore_resize(gr->ht_store, size);
+        return 0;// TODO uncomment LSUP_htstore_resize (gr->ht_store, size);
 
     return LSUP_VALUE_ERR;
 }
 
 
 size_t
-LSUP_graph_capacity(const Graph *gr)
+LSUP_graph_capacity (const Graph *gr)
 {
-    if(gr->store_type == LSUP_STORE_MEM)
-        return 0;// TODO uncomment LSUP_htstore_capacity(gr->ht_store);
+    if (gr->store_type == LSUP_STORE_MEM)
+        return 0;// TODO uncomment LSUP_htstore_capacity (gr->ht_store);
 
     return LSUP_NOT_IMPL_ERR;
 }
 
 
 size_t
-LSUP_graph_size(const Graph *gr)
+LSUP_graph_size (const Graph *gr)
 {
-    if(gr->store_type == LSUP_STORE_MEM)
-        return 0;// TODO uncomment LSUP_htstore_size(gr->ht_store);
+    if (gr->store_type == LSUP_STORE_MEM)
+        return 0;// TODO uncomment LSUP_htstore_size (gr->ht_store);
 
-    return LSUP_mdbstore_size(gr->mdb_store);
+    return LSUP_mdbstore_size (gr->mdb_store);
 }
 
 
@@ -269,11 +269,11 @@ LSUP_graph_add(
         return LSUP_NOT_IMPL_ERR;
     /* TODO
         // Resize all at once if needed.
-        htsize_t prealloc = LSUP_htstore_size(gr->ht_store) + trp_ct + strp_ct;
-        if (LSUP_htstore_capacity(gr->ht_store) < prealloc) {
-            rc = LSUP_htstore_resize(gr->ht_store, prealloc * PREALLOC_FACTOR);
+        htsize_t prealloc = LSUP_htstore_size (gr->ht_store) + trp_ct + strp_ct;
+        if (LSUP_htstore_capacity (gr->ht_store) < prealloc) {
+            rc = LSUP_htstore_resize (gr->ht_store, prealloc * PREALLOC_FACTOR);
 
-            if (UNLIKELY(rc != LSUP_OK)) return rc;
+            if (UNLIKELY (rc != LSUP_OK)) return rc;
         }
 
         rc = LSUP_NOACTION;
@@ -282,16 +282,16 @@ LSUP_graph_add(
         if (trp_ct > 0) {
             for (size_t i = 0; i < trp_ct; i++) {
                 LSUP_SerTriple sspo;
-                LSUP_triple_serialize(trp + i, &sspo);
+                LSUP_triple_serialize (trp + i, &sspo);
 
-                TRACE("Inserting triple #%lu\n", i);
-                LSUP_rc db_rc = LSUP_htstore_add(gr->ht_store, &sspo);
-                if (LIKELY(db_rc == LSUP_OK)) {
+                TRACE ("Inserting triple #%lu\n", i);
+                LSUP_rc db_rc = LSUP_htstore_add (gr->ht_store, &sspo);
+                if (LIKELY (db_rc == LSUP_OK)) {
                     rc = LSUP_OK;
                     (*inserted) ++;
                 }
 
-                LSUP_striple_done(&sspo);
+                LSUP_striple_done (&sspo);
 
                 if (UNLIKELY (db_rc < 0)) return db_rc;
             }
@@ -299,8 +299,8 @@ LSUP_graph_add(
 
         // Insert serialized triples.
         for (size_t i = 0; i < strp_ct; i++) {
-            TRACE("Inserting triple #%lu\n", i);
-            LSUP_rc db_rc = LSUP_htstore_add(gr->ht_store, strp + i);
+            TRACE ("Inserting triple #%lu\n", i);
+            LSUP_rc db_rc = LSUP_htstore_add (gr->ht_store, strp + i);
 
             if (LIKELY (db_rc == LSUP_OK)) {
                 rc = LSUP_OK;
@@ -315,26 +315,25 @@ LSUP_graph_add(
         rc = LSUP_NOACTION;
 
         LSUP_Buffer sc;
-        LSUP_term_serialize(gr->uri, &sc);
+        LSUP_term_serialize (gr->uri, &sc);
 
-        LSUP_MDBIterator *it = LSUP_mdbstore_add_init(gr->mdb_store, &sc);
-        LSUP_buffer_done(&sc);
+        LSUP_MDBIterator *it = LSUP_mdbstore_add_init (gr->mdb_store, &sc);
+        LSUP_buffer_done (&sc);
 
         // Serialize and insert RDF triples.
         if (trp_ct > 0) {
             for (size_t i = 0; i < trp_ct; i++) {
-                LSUP_SerTriple sspo;
-                LSUP_triple_serialize(trp + i, &sspo);
+                LSUP_SerTriple *sspo = LSUP_striple_new_from_triple (trp + i);
 
-                TRACE("Inserting triple #%lu\n", i);
-                LSUP_rc db_rc = LSUP_mdbstore_add_iter(it, &sspo);
+                TRACE ("Inserting triple #%lu\n", i);
+                LSUP_rc db_rc = LSUP_mdbstore_add_iter (it, sspo);
 
                 if (LIKELY (db_rc == LSUP_OK)) {
                     rc = LSUP_OK;
                     (*inserted) ++;
                 }
 
-                LSUP_striple_done(&sspo);
+                LSUP_striple_free (sspo);
 
                 if (UNLIKELY (db_rc < 0)) return db_rc;
             }
@@ -342,8 +341,8 @@ LSUP_graph_add(
 
         // Insert serialized triples.
         for (size_t i = 0; i < strp_ct; i++) {
-            TRACE("Inserting triple #%lu\n", i);
-            LSUP_rc db_rc = LSUP_mdbstore_add_iter(it, strp + i);
+            TRACE ("Inserting triple #%lu\n", i);
+            LSUP_rc db_rc = LSUP_mdbstore_add_iter (it, strp + i);
 
             if (LIKELY (db_rc == LSUP_OK)) {
                 rc = LSUP_OK;
@@ -352,7 +351,7 @@ LSUP_graph_add(
             if (UNLIKELY (db_rc < 0)) return db_rc;
         }
 
-        LSUP_mdbstore_add_done(it);
+        LSUP_mdbstore_add_done (it);
 
         return rc;
     }
@@ -362,47 +361,46 @@ LSUP_graph_add(
 
 
 LSUP_rc
-LSUP_graph_remove(Graph *gr, const LSUP_Triple *spo, size_t *ct)
+LSUP_graph_remove (Graph *gr, const LSUP_Triple *spo, size_t *ct)
 {
     LSUP_rc rc;
 
     LSUP_SerTriple sspo;
-    LSUP_triple_serialize(spo, &sspo);
-    LSUP_Buffer *sc = LSUP_buffer_new_from_term(gr->uri);
+    LSUP_triple_serialize (spo, &sspo);
+    LSUP_Buffer *sc = LSUP_buffer_new_from_term (gr->uri);
 
     if (gr->store_type == LSUP_STORE_MEM)
-        rc = 0;// TODO uncomment LSUP_htstore_remove(gr->ht_store, &sspo, ct);
+        rc = 0;// TODO uncomment LSUP_htstore_remove (gr->ht_store, &sspo, ct);
     else
-        rc = LSUP_mdbstore_remove(gr->mdb_store, &sspo, sc, ct);
+        rc = LSUP_mdbstore_remove (gr->mdb_store, &sspo, sc, ct);
 
-    LSUP_striple_done(&sspo);
-    LSUP_buffer_free(sc);
+    LSUP_striple_done (&sspo);
+    LSUP_buffer_free (sc);
 
     return rc;
 }
 
 
 GraphIterator *
-LSUP_graph_lookup(const Graph *gr, const LSUP_Triple *spo)
+LSUP_graph_lookup (const Graph *gr, const LSUP_Triple *spo)
 {
     GraphIterator *it;
-    CRITICAL(it = malloc(sizeof(GraphIterator)));
+    CRITICAL (it = malloc (sizeof (GraphIterator)));
 
     it->graph = gr;
 
-    LSUP_SerTriple sspo;
-    LSUP_triple_serialize(spo, &sspo);
-    LSUP_Buffer *sc = LSUP_buffer_new_from_term(gr->uri);
+    LSUP_SerTriple *sspo = LSUP_striple_new_from_triple (spo);
+    LSUP_Buffer *sc = LSUP_buffer_new_from_term (gr->uri);
 
     if (gr->store_type == LSUP_STORE_MEM) {
-        // TODO uncomment it->ht_iter = LSUP_htstore_lookup(gr->ht_store, &sspo, &it->ct);
+        // TODO uncomment it->ht_iter = LSUP_htstore_lookup (gr->ht_store, sspo, &it->ct);
 
     } else {
-        it->mdb_iter = LSUP_mdbstore_lookup(gr->mdb_store, &sspo, sc, &it->ct);
+        it->mdb_iter = LSUP_mdbstore_lookup (gr->mdb_store, sspo, sc, &it->ct);
     }
 
-    LSUP_striple_done(&sspo);
-    LSUP_buffer_free(sc);
+    LSUP_striple_free (sspo);
+    LSUP_buffer_free (sc);
 
     return it;
 }
@@ -414,55 +412,63 @@ LSUP_graph_lookup(const Graph *gr, const LSUP_Triple *spo)
  * functions without serializing and deserializing triples.
  */
 static LSUP_rc
-graph_iter_next_buffer(GraphIterator *it, LSUP_SerTriple *sspo)
+graph_iter_next_buffer (GraphIterator *it, LSUP_SerTriple *sspo)
 {
     LSUP_rc rc;
 
     if (it->graph->store_type == LSUP_STORE_MEM)
-        rc = 0;// TODO uncomment LSUP_htiter_next(it->ht_iter, sspo);
-    else rc = LSUP_mdbiter_next(it->mdb_iter, sspo);
+        rc = 0;// TODO uncomment LSUP_htiter_next (it->ht_iter, sspo);
+    else rc = LSUP_mdbiter_next (it->mdb_iter, sspo);
 
     return rc;
 }
 
 
 LSUP_rc
-LSUP_graph_iter_next(GraphIterator *it, LSUP_Triple *spo)
+LSUP_graph_iter_next (GraphIterator *it, LSUP_Triple *spo)
 {
-    LSUP_SerTriple sspo;
-    LSUP_rc rc = graph_iter_next_buffer(it, &sspo);
+    LSUP_SerTriple *sspo = LSUP_striple_new();
+    LSUP_rc rc = graph_iter_next_buffer (it, sspo);
 
     if (rc == LSUP_OK) {
-        LSUP_term_deserialize(sspo.s, spo->s);
-        LSUP_term_deserialize(sspo.p, spo->p);
-        LSUP_term_deserialize(sspo.o, spo->o);
+        if (spo) {
+            LSUP_term_deserialize (sspo->s, spo->s);
+            LSUP_term_deserialize (sspo->p, spo->p);
+            LSUP_term_deserialize (sspo->o, spo->o);
+        }
 
         it->i++;
     }
 
+    // Addresses of triple buffers are owned by the DB. Do not free.
+    free (sspo->s);
+    free (sspo->p);
+    free (sspo->o);
+    free (sspo);
+
     return rc;
 }
 
 
 void
-LSUP_graph_iter_free(GraphIterator *it)
+LSUP_graph_iter_free (GraphIterator *it)
 {
     if (it->graph->store_type == LSUP_STORE_MEM)
-        NULL;// TODO uncomment LSUP_htiter_free(it->ht_iter);
+        NULL;// TODO uncomment LSUP_htiter_free (it->ht_iter);
     else
-        LSUP_mdbiter_free(it->mdb_iter);
+        LSUP_mdbiter_free (it->mdb_iter);
 
-    free(it);
+    free (it);
 }
 
 
 bool
-LSUP_graph_contains(const LSUP_Graph *gr, const LSUP_Triple *spo)
+LSUP_graph_contains (const LSUP_Graph *gr, const LSUP_Triple *spo)
 {
-    GraphIterator *it = LSUP_graph_lookup(gr, spo);
-    bool rc = LSUP_graph_iter_next(it, NULL) != LSUP_NORESULT;
+    GraphIterator *it = LSUP_graph_lookup (gr, spo);
+    bool rc = LSUP_graph_iter_next (it, NULL) != LSUP_NORESULT;
 
-    LSUP_graph_iter_free(it);
+    LSUP_graph_iter_free (it);
 
     return rc;
 }
@@ -470,25 +476,30 @@ LSUP_graph_contains(const LSUP_Graph *gr, const LSUP_Triple *spo)
 
 /* * * Static functions * * */
 
-/** @brief Initialize default context and ramdisk only once per process.
+/** @brief Initialize default context and MDB environments.
+ *
+ * This is done only once per process.
  *
  * The ramdisk store persists after the application is closed, but will be
  * wiped clean the next time this function is called.
  */
-static inline void mdbstore_init()
+static inline LSUP_rc
+mdbstore_init()
 {
-    if(UNLIKELY(!default_ctx)) {
+    if (UNLIKELY (!default_ctx)) {
         // RAM disk store.
         char *path = MDB_RAMDISK_PATH;
-        LSUP_mdbstore_setup (&path, true);
+        if (LSUP_mdbstore_setup (&path, true) != LSUP_OK) return LSUP_DB_ERR;
 
         // Permanent store.
-        //path = getenv ("LSUP_MDB_STORE_PATH");
-        //LSUP_mdbstore_setup (&path);
+        path = getenv ("LSUP_MDB_STORE_PATH");
+        if (LSUP_mdbstore_setup (&path, false) != LSUP_OK) return LSUP_DB_ERR;
 
-        LSUP_Term *default_ctx_uri = LSUP_uri_new(default_ctx_label);
-        default_ctx = LSUP_buffer_new_from_term(default_ctx_uri);
-        LSUP_term_free(default_ctx_uri);
-        atexit(ctx_cleanup);
+        LSUP_Term *default_ctx_uri = LSUP_uri_new (default_ctx_label);
+        default_ctx = LSUP_buffer_new_from_term (default_ctx_uri);
+        LSUP_term_free (default_ctx_uri);
+        atexit (ctx_cleanup);
     }
+
+    return LSUP_OK;
 }

+ 52 - 66
src/store_mdb.c

@@ -198,8 +198,7 @@ static const uint8_t lookup_ordering_2bound[3][3] = {
  * Static prototypes.
  */
 static int index_triple(
-        LSUP_MDBStore *store, StoreOp op,
-        LSUP_TripleKey spok, LSUP_Key ck, MDB_txn *txn);
+        LSUP_MDBStore *store, StoreOp op, LSUP_TripleKey spok, LSUP_Key ck);
 
 inline static LSUP_rc lookup_0bound(
         MDBStore *store, MDBIterator *it, size_t *ct);
@@ -216,7 +215,7 @@ inline static LSUP_rc lookup_3bound(
  */
 
 LSUP_rc
-LSUP_mdbstore_setup(char **path, bool clear)
+LSUP_mdbstore_setup (char **path, bool clear)
 {
     int rc;
 
@@ -235,12 +234,8 @@ LSUP_mdbstore_setup(char **path, bool clear)
     // TODO Verify that a writable directory exists or can be created.
     //struct stat path_stat;
 
-    if (clear) {
-        rm_r (*path);
-        if (mkdir(*path, ENV_DIR_MODE) != 0) return LSUP_IO_ERR;
-    }
-
-    if (mkdir_p(*path, ENV_DIR_MODE) != 0) return LSUP_IO_ERR;
+    if (clear) rm_r (*path);
+    if (mkdir_p (*path, ENV_DIR_MODE) != 0) return LSUP_IO_ERR;
 
     // Open a temporary environment and txn to create the DBs.
     MDB_env *env;
@@ -340,23 +335,19 @@ LSUP_mdbstore_stats(LSUP_MDBStore *store)
 size_t
 LSUP_mdbstore_size(LSUP_MDBStore *store)
 {
+    // Size is calculated outside of any pending write txn.
+
     if(!(store->state & LSSTORE_INIT)) return 0;
 
-    bool txn_pending = false;
-    if (!store->txn) {
-        mdb_txn_begin(store->env, NULL, 0, &store->txn);
-        txn_pending = true;
-    }
+    MDB_txn *txn;
+    mdb_txn_begin(store->env, NULL, MDB_RDONLY, &txn);
 
     MDB_stat stat;
-    int rc = mdb_stat(store->txn, store->dbi[IDX_SPO_C], &stat);
+    int rc = mdb_stat(txn, store->dbi[IDX_SPO_C], &stat);
     TRACE("Stat rc: %d\n", rc);
     // TODO error handling.
 
-    if(txn_pending) {
-        mdb_txn_abort(store->txn);
-        store->txn = NULL;
-    }
+    mdb_txn_abort(txn);
 
     return stat.ms_entries;
 }
@@ -374,14 +365,8 @@ LSUP_mdbstore_add_init(LSUP_MDBStore *store, const LSUP_Buffer *sc)
     it->store = store;
     it->i = 0;
 
-    // If the main store transaction is open, use that, otherwise open a new
-    // RW child transaction owned by the iterator.
-    if (!it->store->txn) {
-        mdb_txn_begin(store->env, NULL, 0, &it->store->txn);
-        it->txn = it->store->txn;
-    } else {
-        mdb_txn_begin(store->env, it->store->txn, 0, &it->txn);
-    }
+    // No other write transaction may be open.
+    mdb_txn_begin(store->env, NULL, 0, &it->store->txn);
 
     // Take care of context first.
     // Serialize and hash.
@@ -400,7 +385,7 @@ LSUP_mdbstore_add_init(LSUP_MDBStore *store, const LSUP_Buffer *sc)
         it->data.mv_size = sc->size;
 
         if (mdb_put(
-                store->txn, store->dbi[IDX_T_ST],
+                it->store->txn, it->store->dbi[IDX_T_ST],
                 &it->key, &it->data, MDB_NOOVERWRITE) != MDB_SUCCESS)
             return NULL;
     }
@@ -432,7 +417,7 @@ LSUP_mdbstore_add_iter(MDBIterator *it, const LSUP_SerTriple *sspo)
         it->data.mv_size = st->size;
 
         db_rc = mdb_put(
-                it->txn, it->store->dbi[IDX_T_ST],
+                it->store->txn, it->store->dbi[IDX_T_ST],
                 &it->key, &it->data, MDB_NOOVERWRITE);
         if (db_rc != MDB_SUCCESS && db_rc != MDB_KEYEXIST) {
             return LSUP_DB_ERR;
@@ -450,14 +435,14 @@ LSUP_mdbstore_add_iter(MDBIterator *it, const LSUP_SerTriple *sspo)
     it->data.mv_size = it->ck == NULL_KEY ? 0 : KLEN;
 
     db_rc = mdb_put(
-            it->txn, it->store->dbi[IDX_SPO_C],
+            it->store->txn, it->store->dbi[IDX_SPO_C],
             &it->key, &it->data, MDB_NODUPDATA);
 
     if (db_rc == MDB_KEYEXIST) return LSUP_NOACTION;
     if (db_rc != MDB_SUCCESS) return LSUP_DB_ERR;
 
     // Index.
-    rc = index_triple (it->store, OP_ADD, spok, it->ck, it->txn);
+    rc = index_triple (it->store, OP_ADD, spok, it->ck);
     if (rc == LSUP_OK) it->i++;
 
     return rc;
@@ -469,13 +454,12 @@ LSUP_mdbstore_add_done (MDBIterator *it)
 {
     LSUP_rc rc = LSUP_OK;
 
-    if (mdb_txn_commit (it->txn) != MDB_SUCCESS) {
-        mdb_txn_abort (it->txn);
+    if (mdb_txn_commit (it->store->txn) != MDB_SUCCESS) {
+        mdb_txn_abort (it->store->txn);
         rc = LSUP_DB_ERR;
     }
 
-    if (it->txn == it->store->txn) it->store->txn = NULL;
-    it->txn = NULL;
+    it->store->txn = NULL;
 
     free (it);
 
@@ -486,10 +470,9 @@ LSUP_mdbstore_add_done (MDBIterator *it)
 void
 LSUP_mdbstore_add_abort (MDBIterator *it)
 {
-    mdb_txn_abort (it->txn);
+    mdb_txn_abort (it->store->txn);
 
-    if (it->txn == it->store->txn) it->store->txn = NULL;
-    it->txn = NULL;
+    it->store->txn = NULL;
 
     free (it);
 }
@@ -510,15 +493,14 @@ LSUP_mdbstore_add (
             return rc;
         }
     }
-
     *inserted = it->i;
 
     return LSUP_mdbstore_add_done(it);
 }
 
 
-LSUP_Key
-LSUP_mdbstore_sterm_to_key(
+static LSUP_Key __attribute__ ((unused))
+sterm_to_key (
         LSUP_MDBStore *store, const LSUP_Buffer *sterm)
 {
     // TODO this will be replaced by a lookup when 128-bit hash is introduced.
@@ -526,30 +508,35 @@ LSUP_mdbstore_sterm_to_key(
 }
 
 
-LSUP_rc
-LSUP_mdbstore_key_to_sterm(
-        LSUP_MDBStore *store, const LSUP_Key key, LSUP_Buffer *sterm)
+static LSUP_rc
+key_to_sterm(
+        LSUP_MDBStore *store, const LSUP_Key key, LSUP_Buffer *sterm,
+        MDB_txn *txn)
 {
     LSUP_rc rc = LSUP_NORESULT;
+    int db_rc;
+    bool txn_dirty = false;
 
-    MDB_txn *txn;
-    if (store->txn) txn = store->txn;
-    else mdb_txn_begin(store->env, NULL, MDB_RDONLY, &txn);
+    if (!txn) {
+        db_rc = mdb_txn_begin(store->env, NULL, MDB_RDONLY, &txn);
+        if (db_rc != MDB_SUCCESS) return LSUP_DB_ERR;
+        txn_dirty = true;
+    }
 
     MDB_val key_v, data_v;
     key_v.mv_data = (void*)&key;
     key_v.mv_size = KLEN;
 
-    int mdb_rc = mdb_get(txn, store->dbi[IDX_T_ST], &key_v, &data_v);
+    db_rc = mdb_get(txn, store->dbi[IDX_T_ST], &key_v, &data_v);
 
-    if (mdb_rc == MDB_SUCCESS) {
+    if (db_rc == MDB_SUCCESS) {
         sterm->addr = data_v.mv_data;
         sterm->size = data_v.mv_size;
         rc = LSUP_OK;
     }
-    else if (UNLIKELY(mdb_rc != MDB_NOTFOUND)) rc = LSUP_ERROR;
+    else if (UNLIKELY(db_rc != MDB_NOTFOUND)) rc = LSUP_ERROR;
 
-    if (txn != store->txn) mdb_txn_abort(txn);
+    if (txn_dirty) mdb_txn_abort(txn);
 
     return rc;
 }
@@ -700,9 +687,9 @@ LSUP_mdbiter_next(LSUP_MDBIterator *it, LSUP_SerTriple *sspo)
     LSUP_rc rc = mdbiter_next_key(it);
 
     if (sspo && rc == LSUP_OK) {
-        LSUP_mdbstore_key_to_sterm(it->store, it->spok[0], sspo->s);
-        LSUP_mdbstore_key_to_sterm(it->store, it->spok[1], sspo->p);
-        LSUP_mdbstore_key_to_sterm(it->store, it->spok[2], sspo->o);
+        key_to_sterm(it->store, it->spok[0], sspo->s, it->txn);
+        key_to_sterm(it->store, it->spok[1], sspo->p, it->txn);
+        key_to_sterm(it->store, it->spok[2], sspo->o, it->txn);
 
         // TODO error handling.
     }
@@ -786,7 +773,7 @@ LSUP_mdbstore_remove(
         if (rc == MDB_SUCCESS) continue;
         if (UNLIKELY(rc != MDB_NOTFOUND)) goto _remove_abort;
 
-        index_triple(store, OP_REMOVE, it->spok, ck, NULL);
+        index_triple(store, OP_REMOVE, it->spok, ck);
     }
 
     if(UNLIKELY(mdb_txn_commit(txn) != MDB_SUCCESS)) {
@@ -812,12 +799,10 @@ _remove_abort:
  * @param op[in] Store operation. One of OP_ADD or OP_REMOVE.
  * @param spok[in] Triple key to index.
  * @param ck[in] Context to index, may be NULL.
- * @param txn[in] If not NULL, use this transaction instead of the store one.
  */
 static LSUP_rc
 index_triple(
-        LSUP_MDBStore *store, StoreOp op,
-        LSUP_TripleKey spok, LSUP_Key ck, MDB_txn *txn)
+        LSUP_MDBStore *store, StoreOp op, LSUP_TripleKey spok, LSUP_Key ck)
 {
     int db_rc;
     LSUP_rc rc = LSUP_NOACTION;
@@ -825,8 +810,6 @@ index_triple(
 
     printf("Indexing triple: %lx %lx %lx\n", spok[0], spok[1], spok[2]);
 
-    if (!txn) txn = store->txn;
-
     // Index c:spo.
     if (op == OP_REMOVE) {
         if (ck != NULL_KEY) {
@@ -837,7 +820,7 @@ index_triple(
             v2.mv_data = spok;
             v2.mv_size = TRP_KLEN;
 
-            mdb_cursor_open(txn, store->dbi[IDX_C_SPO], &cur);
+            mdb_cursor_open(store->txn, store->dbi[IDX_C_SPO], &cur);
             if (mdb_cursor_get(cur, &v1, &v2, MDB_GET_BOTH) == MDB_SUCCESS) {
                 db_rc = mdb_cursor_del (cur, 0);
                 if (db_rc != MDB_SUCCESS) return LSUP_DB_ERR;
@@ -856,7 +839,8 @@ index_triple(
             v2.mv_size = TRP_KLEN;
 
             db_rc = mdb_put(
-                    txn, store->dbi[IDX_C_SPO], &v1, &v2, MDB_NODUPDATA);
+                    store->txn, store->dbi[IDX_C_SPO],
+                    &v1, &v2, MDB_NODUPDATA);
             if (db_rc != MDB_SUCCESS) return LSUP_DB_ERR;
             if (db_rc != MDB_KEYEXIST) rc = LSUP_OK;
         }
@@ -882,7 +866,8 @@ index_triple(
 
         if (op == OP_REMOVE) {
             MDB_cursor *cur1, *cur2;
-            mdb_cursor_open(txn, store->dbi[lookup_indices[i]], &cur1);
+            mdb_cursor_open(
+                    store->txn, store->dbi[lookup_indices[i]], &cur1);
 
             db_rc = mdb_cursor_get(cur1, &v1, &v2, MDB_GET_BOTH);
             if (db_rc == MDB_SUCCESS) mdb_cursor_del(cur1, 0);
@@ -893,7 +878,8 @@ index_triple(
             v1.mv_data = spok + i;
             v2.mv_data = dbl_keys[i];
 
-            mdb_cursor_open(txn, store->dbi[lookup_indices[i + 3]], &cur2);
+            mdb_cursor_open(
+                    store->txn, store->dbi[lookup_indices[i + 3]], &cur2);
 
             db_rc = mdb_cursor_get(cur2, &v2, &v1, MDB_GET_BOTH);
             if (db_rc == MDB_SUCCESS) mdb_cursor_del(cur2, 0);
@@ -909,7 +895,7 @@ index_triple(
                     "%lx: %lx %lx\n", *(size_t*)(v1.mv_data),
                     *(size_t*)(v2.mv_data), *(size_t*)(v2.mv_data) + 1);
 
-            db_rc = mdb_put(txn, db1, &v1, &v2, MDB_NODUPDATA);
+            db_rc = mdb_put(store->txn, db1, &v1, &v2, MDB_NODUPDATA);
 
             if (db_rc == MDB_SUCCESS) rc = LSUP_OK;
             else if (db_rc != MDB_KEYEXIST) return LSUP_DB_ERR;
@@ -920,7 +906,7 @@ index_triple(
                     "%lx %lx: %lx\n", *(size_t*)(v2.mv_data),
                     *(size_t*)(v2.mv_data) + 1, *(size_t*)(v1.mv_data));
 
-            db_rc = mdb_put(txn, db2, &v2, &v1, MDB_NODUPDATA);
+            db_rc = mdb_put(store->txn, db2, &v2, &v1, MDB_NODUPDATA);
 
             if (db_rc == MDB_SUCCESS) rc = LSUP_OK;
             else if (db_rc != MDB_KEYEXIST) return LSUP_DB_ERR;

+ 82 - 0
src/triple.c

@@ -5,6 +5,62 @@ LSUP_Term *LSUP_triple_pos (const LSUP_Triple *trp, LSUP_TriplePos n);
 LSUP_Buffer *LSUP_striple_pos (const LSUP_SerTriple *trp, LSUP_TriplePos n);
 
 
+LSUP_Triple *
+LSUP_triple_new()
+{
+    LSUP_Triple *spo = malloc (sizeof (*spo));
+    if (!spo) return NULL;
+
+    spo->s = malloc (sizeof (LSUP_Term));
+    spo->p = malloc (sizeof (LSUP_Term));
+    spo->o = malloc (sizeof (LSUP_Term));
+
+    return spo;
+}
+
+
+LSUP_SerTriple *
+LSUP_striple_new()
+{
+    LSUP_SerTriple *sspo = malloc (sizeof (*sspo));
+    if (!sspo) return NULL;
+
+    sspo->s = malloc (sizeof (LSUP_Buffer));
+    sspo->p = malloc (sizeof (LSUP_Buffer));
+    sspo->o = malloc (sizeof (LSUP_Buffer));
+
+    return sspo;
+}
+
+
+LSUP_Triple *
+LSUP_triple_new_from_striple(const LSUP_SerTriple *sspo)
+{
+    LSUP_Triple *spo = malloc (sizeof (*spo));
+    if (!spo) return NULL;
+
+    spo->s = LSUP_term_new_from_buffer (sspo->s);
+    spo->p = LSUP_term_new_from_buffer (sspo->p);
+    spo->o = LSUP_term_new_from_buffer (sspo->o);
+
+    return spo;
+}
+
+
+LSUP_SerTriple *
+LSUP_striple_new_from_triple(const LSUP_Triple *spo)
+{
+    LSUP_SerTriple *sspo = malloc (sizeof (*sspo));
+    if (!sspo) return NULL;
+
+    sspo->s = LSUP_buffer_new_from_term (spo->s);
+    sspo->p = LSUP_buffer_new_from_term (spo->p);
+    sspo->o = LSUP_buffer_new_from_term (spo->o);
+
+    return sspo;
+}
+
+
 LSUP_rc
 LSUP_triple_serialize(const LSUP_Triple *spo, LSUP_SerTriple *sspo)
 {
@@ -57,3 +113,29 @@ LSUP_striple_done(LSUP_SerTriple *sspo)
     LSUP_buffer_done(sspo->p);
     LSUP_buffer_done(sspo->o);
 }
+
+
+void
+LSUP_triple_free (LSUP_Triple *spo)
+{
+    if (UNLIKELY(!spo)) return;
+
+    LSUP_term_free(spo->s);
+    LSUP_term_free(spo->p);
+    LSUP_term_free(spo->o);
+
+    free (spo);
+}
+
+
+void
+LSUP_striple_free(LSUP_SerTriple *sspo)
+{
+    if (UNLIKELY(!sspo)) return;
+
+    LSUP_buffer_free(sspo->s);
+    LSUP_buffer_free(sspo->p);
+    LSUP_buffer_free(sspo->o);
+
+    free (sspo);
+}