Explorar el Código

WIP Checkpoint before getting too deep into changes.

Stefano Cossu hace 3 años
padre
commit
b6643a018f
Se han modificado 8 ficheros con 661 adiciones y 553 borrados
  1. 2 0
      include/data/bootstrap.h
  2. 5 5
      include/environment.h
  3. 12 7
      include/graph.h
  4. 384 2
      include/store.h
  5. 2 334
      include/store_mdb.h
  6. 2 4
      src/environment.c
  7. 22 31
      src/graph.c
  8. 232 170
      src/store_mdb.c

+ 2 - 0
include/data/bootstrap.h

@@ -1,6 +1,7 @@
 #ifndef LSUP_INIT_DATA_H
 #define LSUP_INIT_DATA_H
 
+#define LSUP_NS "urn:lsup:"
 
 /** @brief Initial namespace map.
  */
@@ -8,6 +9,7 @@ const char *init_nsmap[][2] = {
     {"dc",       "http://purl.org/dc/elements/1.1/"},
     {"dcterms",  "http://purl.org/dc/terms/"},
     {"foaf",     "http://xmlns.com/foaf/0.1/"},
+    {"lsup",     LSUP_NS},
     {"rdf",      "http://www.w3.org/1999/02/22-rdf-syntax-ns#"},
     {"rdfs",     "http://www.w3.org/2000/01/rdf-schema#"},
     {"xsd",      "http://www.w3.org/2001/XMLSchema#"},

+ 5 - 5
include/environment.h

@@ -18,16 +18,16 @@
  * Multiple environments may be opened within one program. One environment,
  * the default one, must always be opened before performing any operation with
  * the library.
+ *
+ * The namespace map is used by all volatile (not LSUP_STORE_PERM) stores
  */
 typedef struct env_t {
-    LSUP_Buffer *       default_ctx;            // Default context URI.
-    LSUP_MDBStore *     mdb_store;              // MDB permanent store handle.
-    LSUP_MDBStore *     mdb_store_ramdisk;      // MDB RAM disk store handle.
-    LSUP_NSMap *        nsm;                    // Namespace prefix map.
+    LSUP_Buffer *       default_ctx;            ///> Default context URI.
+    LSUP_NSMap *        nsm;                    ///> Namespace prefix map.
 } LSUP_Env;
 
 
-/** @brief Environment variable that gets passed around.
+/** @brief Default environment that gets created with #LSUP_init().
  */
 extern LSUP_Env *LSUP_default_env;
 

+ 12 - 7
include/graph.h

@@ -5,17 +5,22 @@
 #include "term.h"
 
 /*
- * Define backend types and checks.
+ * Define backend types.
+ *
+ * Add new store implementations to this table.
  */
 
-#define BACKEND_TBL                                              \
-    ENTRY(  MEM,       0)/* Memory backend, hash map. */         \
-    ENTRY(  MDB,       1)/* LMDB back end on persistent disk. */ \
-    ENTRY(  MDB_TMP,   2)/* LMDB back end on RAM disk. */        \
+#define BACKEND_TBL                                                           \
+/*          #enum pfx   #store          #iterator   */\
+    /*ENTRY(  HTABLE,     htstore_int,    htiter_int )*/  \
+    ENTRY(  MDB,        mdbstore_int,   mdbiter_int ) \
+    ENTRY(  MDB_TMP,    mdbstore_int,   mdbiter_int ) \
 
 
-typedef enum LSUP_store_type {
-#define ENTRY(a, b) LSUP_STORE_##a = b,
+/** @brief Store types. All prefixed with `LSUP_STORE_`.
+ */
+typedef enum {
+#define ENTRY(a, b, c) LSUP_STORE_##a,
     BACKEND_TBL
 #undef ENTRY
 } LSUP_store_type;

+ 384 - 2
include/store.h

@@ -1,14 +1,41 @@
 /** @file store.h
  *
- * @brief Common back end store definitions.
+ * @brief Store back end interfaces.
  *
- * New store implementations should include this header.
+ * The basic interfaces for store and store iterator implementations are
+ * defined here. New store implementations should include this header and
+ * implement four basic elements:
+ *
+ * - A structure representing the store back end and one for the iterator.
+ *   These structures will be opaque to all external modules and their layouts
+ *   are entirely up to the implementer.
+ *
+ * - The LSUP_StoreInt and LSUP_StoreIteratorInt interfaces as well as the
+ *   functions defined in the interfaces necessary to interact with the store.
+ *
+ * See the `store_htable.{c,h}` and `store_mdb.{c,h}` files for examples of
+ * fully functioning implementations.
+ *
+ * The #LSUP_Store structure defines a store back end for raw buffer triples.
+ * Nothing in the store should hint at RDF triples—it should accept and output
+ * exclusively raw byte buffers (LSUP_Buffer). A store may have any of the
+ * `LSUP_STORE_*` faeture flags which should be reflected by how its members
+ * are implemented.
+ *
+ * The #LSUP_StoreIterator structure defines a store iterator implementation
+ * that is designed to work with the store implementation. It must define a
+ * "next" function for retrieving a triple in a lookup and a "free" function
+ * to deallocate the iterator after use. Functions for creating iterators for
+ * lookup and incremental addition are defined in the associated store.
  */
 
 
 #ifndef _LSUP_STORE_H
 #define _LSUP_STORE_H
 
+#include "namespace.h"
+#include "data/bootstrap.h"
+
 
 /*
  * Store feature flags.
@@ -26,4 +53,359 @@
 #define LSUP_STORE_TXN      1<<3    /// Supports manual transaction handling.
 #define LSUP_STORE_NET      1<<4    /// Store is over a network protocol.
 
+
+/*
+ * 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
+ * necessary.
+ *
+ * NOTE: The memory pointed to by the individual LSUP_Buffer pointers is
+ * owned by the database. It must not be written to or freed. To modify
+ * the data or use them beyond the caller's scope, this memory must be copied.
+ *
+ * @param[in] it Opaque iterator handle obtained with the store's #lookup_fn.
+ *
+ * @param[out] sspo #LSUP_BufferTriple to be populated with three serialized
+ * terms if found, NULL if not found. Internal callers (e.g. counters) may pass
+ * NULL if they don't need the serialized triples.
+ *
+ * @param[out] ctx If not NULL, it is populated with a NULL-terminated array of
+ *  LSUP_Buffer structs, one for each context associated with the matching
+ *  triple. These contexts are the same regardless of the context filter used
+ *  in the lookup. The array is freed with a simple #free(). This parameter
+ *  is ignored by implementations without the LSUP_STORE_CTX feature.
+ *
+ *  To iterate over the context array, use this loop:
+ *
+ *      size_t i = 0;
+ *      while (ctx[i].addr)
+ *          do_something(ctx + i++); // Buffer data are RO.
+ *
+ * @return LSUP_OK if results were found; LSUP_END if no (more) results were
+ * found; LSUP_DB_ERR if a backend error occurred.
+ */
+typedef LSUP_rc (*iter_next_fn_t)(
+        void *it, LSUP_BufferTriple *sspo, LSUP_Buffer **ctx);
+
+
+/** @brief Prototype: free an iterator allocated by a lookup.
+ *
+ * @param it[in] Iterator pointer. It will be set to NULL after freeing.
+ */
+typedef void (*iter_free_fn_t)(void * it);
+
+
+/*
+ * Store function types.
+ */
+
+/** @brief Prototype: create any environment necessary for the store to work.
+ *
+ * @param[in] clear Whether to remove a previous environment at this location.
+ *
+ * @param[in,out] path Path of the suggested directory to use. It may be NULL,
+ *  in which case it will be set either to the environment variable
+ *  LSUP_STORE_PATH, or if that is not set, a default local path.
+ */
+typedef LSUP_rc (*store_setup_fn_t)(const char *path, bool clear);
+
+
+/** @brief Prototype: create a new store.
+ *
+ * @param[in] Location for the new store. This depends on the implementation,
+ *  which may ignore it (e.g. an in-memory store). Implementations should
+ *  provide instructions on how to use and interpret this parameter.
+ *
+ * @param[in] default_ctx Default context IRI (serialized). It should only be
+ *  considered by implementations with the LSUP_STORE_CTX feature.
+ *
+ * @return New store handle.
+ */
+typedef void * (*store_new_fn_t)(
+        const char *location, const LSUP_Buffer *default_ctx);
+
+
+/** @brief Prototype: free store handle.
+ *
+ * @param[in] store Store handle.
+ *
+ */
+typedef void (*store_free_fn_t)(void *store);
+
+
+/** @brief Prototype: get store size.
+ *
+ * @param store[in] The store to calculate size of.
+ *
+ * @return Number of stored SPO triples (across all contexts if supported).
+ */
+typedef size_t (*store_size_fn_t)(void *store);
+
+
+/** @brief Print stats about a store.
+ *
+ * TODO
+ *
+ * @param store[in] The store to get stats for.
+ */
+/* TODO
+typedef LSUP_rc (*store_stat_fn_t)(void *store, void *stat);
+*/
+
+
+/** @brief Initialize bulk triple load.
+ *
+ * This is the first step of a bulk load. It is best used when the data at hand
+ * need to be pre-processed, which can be done in the same loop as the next
+ * step to keep memory usage low.
+ *
+ * @param store[in] The store to add to.
+ *
+ * @param sc[in] Context as a serialized term. If this is NULL, and the
+ *  default context is not NULL, triples will be added to the default context
+ *  for the store, If the default context for the store is NULL, regardless of
+ *  the value of sc, triples will be added with no context. Only meaningful
+ *  for stores with the LSUP_STORE_CTX feature.
+ *
+ * @return Iterator handle to be passed to the following load steps.
+ */
+typedef void * (*store_add_init_fn_t)(void *store, const LSUP_Buffer * sc);
+
+
+/** @brief Add one triple into the store.
+ *
+ * This must be called after #add_init_fn, using the iterator
+ * 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.
+ *  The following members are of interest:
+ *  it->i stores the total number of records inserted.
+ *
+ * @param sspo[in] Serialized triple to be added.
+ *
+ * @return LSUP_OK if the triple was inserted; LSUP_NOACTION if the triple
+ *  already existed; LSUP_DB_ERR if an MDB error occurred.
+ */
+typedef LSUP_rc (*store_add_iter_fn_t)(void *it, const LSUP_BufferTriple * sspo);
+
+
+/** @brief Abort an add loop and free iterator.
+ *
+ * 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.
+ */
+typedef void (*store_add_abort_fn_t)(void *it);
+
+
+/** @brief Finalize an add loop and free iterator.
+ *
+ * This must be called after #add_iter_fn.
+ *
+ * @param it[in] Iterator obtained by #LSUP_mdbstore_add_init.
+ */
+typedef LSUP_rc (*store_add_done_fn_t)(void *it);
+
+
+/** @brief Add a single term to the store.
+ *
+ * @param[in] store Store handle.
+ *
+ * @param[in] sterm Serialized term to store.
+ */
+typedef LSUP_rc (*store_add_term_fn_t)(void *store, const LSUP_Buffer *sterm);
+
+
+/** @brief Prototype: look up triples by pattern matching.
+ *
+ * This function may return a count of matches and/or an iterator of results as
+ * serialized triples.
+ *
+ * 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.
+ *
+ * @param[in] store The store to be queried.
+ *
+ * @param[in] ss Buffer representing the serialized s term.
+ *
+ * @param[in] sp Buffer representing the serialized p term.
+ *
+ * @param[in] so Buffer representing the 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
+ * without context are assigned the *default* context for the store.
+ *
+ * @param[out] ct If not NULL, this will be populated with the number of
+ *  entries found. It is very inexpensive to set for lookups without context,
+ *  much less so for 1-bound and 2-bound context lookups, in which cases it
+ *  should be set only if needed.
+ *
+ * @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
+ * the first result which can be retrieved with #iter_next_fn().
+ */
+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);
+
+
+/** @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
+ * deleted.
+ */
+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);
+
+
+/** @brief Put an in-memory namespace map into a permanent back end.
+ *
+ * This is only available in stores with the LSUP_STORE_PERM feature.
+ *
+ * Existing prefixes and namespaces are not updated. Thus, if the following are
+ * already stored:
+ *
+ * ns1: <urn:ns:a#>
+ * ns2: <urn:ns:b#>
+ *
+ * Neither of the following will be inserted:
+ *
+ * ns3: <urn:ns:a#>
+ * ns2: <urn:ns:c#>
+ *
+ * @param[in] store MDB store to update.
+ *
+ * @param[out] nsm Namespace map handle to store.
+ *
+ * @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);
+
+
+/** @brief Get the store's namespace prefix map.
+ *
+ * @param[in] store MDB store to query.
+ *
+ * @return NS map or NULL on error.
+ */
+typedef LSUP_NSMap * (*store_nsm_get_fn_t)(void *store);
+
+
+/*
+ * Iterface type definitions.
+ */
+
+/** @brief Store interface.
+ */
+typedef struct store_t {
+    // Basic properties.
+    char                name[16];       ///> Store type name.
+    int                 features;       ///> Feature flags.
+
+    // Allocation, setup and deallocation.
+    store_setup_fn_t    setup_fn;       ///> Optional function called before
+                                        ///>    new_fn.
+    store_new_fn_t      new_fn;         ///> Create a new store instance.
+    store_free_fn_t     free_fn;        ///> Free the store.
+
+    // Metadata.
+    store_size_fn_t     size_fn;        ///> Number of triples in the store.
+    //store_stat_fn       stat_fn;        ///> Store statistics. Optional.
+
+    // 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_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.
+
+    // Look up.
+    store_lookup_fn_t   lookup_fn;      ///> Look up triples by pattern.
+
+    // Removal.
+    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.
+                                        ///>    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.
+                                        ///>    Only available (and mandatory)
+                                        ///>    in stores with the
+                                        ///>    LSUP_STORE_IDX feature.
+} LSUP_StoreInt;
+
+
+/** @brief Store iterator interface.
+ */
+typedef struct store_it_t {
+    char                name[16];       ///> Iterator type name.
+
+    iter_next_fn_t      iter_fn;        ///> Advance to next step and yield
+                                        ///>    one triple key if found.
+    iter_free_fn_t      free_fn;        ///> Free the iterator.
+} LSUP_StoreIteratorInt;
+
+
+/*
+ * Template for a new store and iterator implementation.
+ * These should be placed in the .c file where the interface functions are
+ * defined, and declared as `extern` in the related .h file.
+
+const LSUP_StoreInt my_store_int = {
+    .name           = "My Store", // Truncated to 15 chars.
+    .features       = LSUP_STORE_PERM | LSUP_STORE_IDX,
+
+    .setup_fn       = my_setup_fn,
+    .new_fn         = my_new_fn,
+    .free_fn        = my_free_fn,
+
+    .size_fn        = my_size_fn,
+
+    .add_init_fn    = my_init_fn,
+    .add_iter_fn    = my_iter_fn,
+    .add_abort_fn   = my_add_abort_fn,
+    .add_done_fn    = my_add_done_fn,
+    .add_term_fn    = my_add_term_fn,
+
+    .lookup_fn      = my_lookup_fn,
+
+    .remove_fn      = my_remove_fn,
+
+    .nsm_put_fn     = my_nsm_put_fn,
+    .nsm_get_fn     = my_nsm_get_fn,
+};
+
+const LSUP_StoreIteratorInt my_it_int = {
+    .name           = "My Iterator",
+
+    .iter_next_fn_t = my_iter_next_fn,
+    .iter_free_fn_t = my_iter_free_fn,
+};
+*/
+
 #endif  /* _LSUP_STORE_H */

+ 2 - 334
include/store_mdb.h

@@ -25,7 +25,6 @@
 #include "lmdb.h"
 
 #include "buffer.h"
-#include "namespace.h"
 #include "store.h"
 
 
@@ -36,338 +35,7 @@
 #define LSUP_MDB_RAMDISK_PATH TMPDIR "/lsup_mem_graph"
 
 
-typedef char DbLabel[8];
-typedef struct mdbstore_t LSUP_MDBStore;
-typedef struct mdbstore_iter_t LSUP_MDBIterator;
-
-typedef LSUP_rc (*store_match_fn_t)(const LSUP_TripleKey spok, void *data);
-
-// TODO Introduce compile-time LSUP_BIG_STORE option to define two store
-// options: false: 64-bit hashes, uint32 keys, max 4G entries; true:
-// 128-bit hashes, size_t keys, max MAX_SIZE entries, larger and slower.
-// Ideally this could be specified at runtime to handle different stores with
-// different sizes, but code could become more complex.
-
-
-/** @brief Create the MDB environment and databases on disk.
- *
- * This function takes care of creaating the environment path if not existing,
- * and checking that it's a writable directory. If the path is not specified
- * in the LSUP_STORE_PATH environment variable, a default directory is used.
- *
- * @param[in] clear Whether to remove a previous environment at this location.
- *
- * @param[in,out] path Path of the suggested directory to use. It may be NULL,
- *  in which case it will be set either to the environment variable
- *  LSUP_STORE_PATH, or if that is not set, a default local path.
- */
-LSUP_rc LSUP_mdbstore_setup (const char *path, bool clear);
-
-
-/** @brief Open an MDB store.
- *
- * The store must have been set up with #LSUP_mdbstore_setup.
- *
- * Some environment variables affect various store parameters:
- *
- * - LSUP_MDB_MAPSIZE Long int specifying the size of the memory map. Usually
- *   it is not necessary to modify this, unless one is operating under memory
- *   and disk constraints. The default map size is 1Tb.
- *
- * @param[in,out] store Uninitialized store struct pointer.
- *
- * @param[in] path MDB environment path. This must be the path given by
- * #LSUP_mdbstore_setup.
- *
- * @param[in] default_ctx Serialized URI to be used as a default context for
- *  triples inserted without a context specified. If NULL, the store operates
- *  in triple mode.
- */
-LSUP_MDBStore *
-LSUP_mdbstore_new (const char *path, const LSUP_Buffer *default_ctx);
-
-
-/** @brief Close a store and free its handle.
- *
- * @param[in] store Store pointer.
- *
- */
-void LSUP_mdbstore_free (LSUP_MDBStore *store);
-
-
-/** @brief Store feature flags.
- *
- * @param[in] store Store handle.
- *
- * @return A combination of LSUP_STORE_* feature flags.
- */
-int LSUP_mdbstore_features (LSUP_MDBStore *store);
-
-
-/** @brief Print stats about a store and its databases.
- *
- * TODO
- *
- * @param store[in] The store to get stats for.
- */
-LSUP_rc LSUP_mdbstore_stat (LSUP_MDBStore *store, MDB_stat *stat);
-
-
-/** @brief Store size.
- *
- * @param store[in] The store to calculate size of.
- *
- * @return Number of stored SPO triples across all contexts.
- */
-size_t LSUP_mdbstore_size (LSUP_MDBStore *store);
-
-
-/** @brief Initialize bulk triple load.
- *
- * This is the first step of a bulk load. It is best used when the data at hand
- * need to be pre-processed, which can be done in the same loop as the next
- * step to keep memory usage low.
- *
- * @param store[in] The store to add to.
- *
- * @param sc[in] Context as a serialized term. If this is NULL, and the
- *  default context is not NULL, triples will be added to the default context
- *  for the store, If the default context for the store is NULL, regardless of
- *  the value of sc, triples will be added with no context.
- *
- * @param it[out] Pointer to an iterator pointer to be passed to the following
- *  load steps.
- */
-LSUP_MDBIterator *
-LSUP_mdbstore_add_init (LSUP_MDBStore *store, const LSUP_Buffer *sc);
-
-
-/** @brief Add one triple into the store.
- *
- * This must be called after #LSUP_mdbstore_add_init, using the iterator
- * 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.
- *
- * @param sspo[in] Serialized triple to be added.
- *
- * @return LSUP_OK if the triple was inserted; LSUP_NOACTION if the triple
- *  already existed; LSUP_DB_ERR if an MDB error occurred.
- */
-LSUP_rc
-LSUP_mdbstore_add_iter (LSUP_MDBIterator *it, const LSUP_BufferTriple *sspo);
-
-
-/** @brief Finalize an add loop and free iterator.
- *
- * If a count of inserted records is needed, #LSUP_mdbiter_cur must be called
- * before this function.
- *
- * This must be called after #LSUP_mdbstore_add_iter.
- *
- * @param it[in] Iterator obtained by #LSUP_mdbstore_add_init.
- */
-LSUP_rc
-LSUP_mdbstore_add_done (LSUP_MDBIterator *it);
-
-
-/** @brief Abort an add loop and free iterator.
- *
- * Usually called on an irrecoverable error from LSUP_mdb_add_iter. None of the
- * successful inserts in the same loop is retained.
- *
- * @param it[in] Iterator obtained by #LSUP_mdbstore_add_init.
- */
-void
-LSUP_mdbstore_add_abort (LSUP_MDBIterator *it);
-
-
-/** @brief Add a batch of triples with optional context to the store.
- *
- * This is a shortcut for calling #LSUP_mdbstore_add_init,
- * #LSUP_mdbstore_add_iter and #LSUP_mdbstore_add_done in a sequence
- * when an array of pre-serialized triples is available.
- *
- * @param store[in] The store to add to.
- *
- * @param sc[in] Context as a serialized term. If this is NULL, and the
- *  default context is not NULL, triples will be added to the default context
- *  for the store. If the default context for the store is NULL, regardless of
- *  the value of sc, triples will be added with no context.
-
- * @param data[in] Triples to be inserted as a 2D array of triples in the shape
- * of data[n][3], where n is the value of data_size.
- *
- * @param inserted[out] If not NULL, it will be filled with the count of
- *  effectively inserted triples.
- *
- * @param data_size[in] Number of triples to be inserted.
- */
-LSUP_rc LSUP_mdbstore_add(
-        LSUP_MDBStore *store, const LSUP_Buffer *sc,
-        const LSUP_BufferTriple strp[], const size_t ct, size_t *inserted);
-
-
-/** @brief Delete triples by pattern matching.
- *
- * The ss, sp, so, sc terms act as a matching pattern as documented in
- * #LSUP_mdbstore_lookup. if not NULL, ct yields the number of triples actually
- * deleted.
- */
-LSUP_rc
-LSUP_mdbstore_remove(
-        LSUP_MDBStore *store, const LSUP_Buffer *ss, const LSUP_Buffer *sp,
-        const LSUP_Buffer *so, const LSUP_Buffer *sc, size_t *ct);
-
-
-/** @brief Look up matching triples and optional context.
- *
- * This function may return a count of matches and/or an iterator of results as
- * serialized triples.
- *
- * Any and all of the terms may be NULL, which indicates an unbound query
- * term. Stores with context not set will always ignore the fourth term.
- *
- * @param[in] store The store to be queried.
- *
- * @param[in] ss Buffer representing the serialized s term.
- *
- * @param[in] sp Buffer representing the serialized p term.
- *
- * @param[in] so Buffer representing the 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 without
- *  context are assigned the *default* context, indicated by the "default_ctx"
- *  member of the store struct.
- *
- * @param[out] it Pointer to an #LSUP_MDBIterator 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 the first result which can be retrieved with
- * #LSUP_mdbiter_next().
- *
- * @param[out] ct If not NULL, this will be populated with the number of
- *  entries found. It is very inexpensive to set for lookups without context,
- *  much less so for 1-bound and 2-bound context lookups, in which cases it
- *  should be set only if needed.
- *
- * @return LSUP_OK if entries were found, LSUP_NORESULT if none were found.
- */
-LSUP_MDBIterator *
-LSUP_mdbstore_lookup(
-        LSUP_MDBStore *store, const LSUP_Buffer *ss, const LSUP_Buffer *sp,
-        const LSUP_Buffer *so, const LSUP_Buffer *sc, size_t *ct);
-
-
-/** @brief Yield the matching triples and advance the iterator.
- *
- * This function also checks if the matching triple is associated with a
- * context, if one was specified. If no associated contexts are found, the next
- * triple is searched, until the end of the results.
- *
- * NOTE: Iterators keep LMDB cursors and (read only) transactions open. Don't
- * hold on to them longer than necessary.
- *
- * NOTE: The memory pointed to by the individual LSUP_Buffer pointers is
- * owned by the database. It must not be written to or freed. To modify
- * the data or use them beyond the caller's scope, this memory must be copied.
- *
- * @param[in] it Opaque iterator handle obtained with #LSUP_mdbstore_lookup.
- *
- * @param[out] sspo #LSUP_BufferTriple to be populated with three serialized
- * terms if found, NULL if not found. Internal callers (e.g. counters) may pass
- * NULL if they don't need the serialized triples.
- *
- * @param[out] ctx If not NULL, it is populated with a NULL-terminated array of
- *  LSUP_Buffer structs, one for each context associated with the matching
- *  triple. These contexts are the same regardless of the context filter used
- *  in the lookup. The array is freed with a simple #free().
- *
- *  To iterate over the context array, use this loop:
- *
- *      size_t i = 0;
- *      while (ctx[i].addr)
- *          do_something(ctx + i++); // Buffer data are memory-mapped and RO.
- *
- * @return LSUP_OK if results were found; LSUP_END if no (more) results were
- * found; LSUP_DB_ERR if a MDB_* error occurred.
- */
-LSUP_rc LSUP_mdbiter_next (
-        LSUP_MDBIterator *it, LSUP_BufferTriple *sspo, LSUP_Buffer **ctx);
-
-
-/** @brief Free an iterator allocated by a lookup.
- *
- * @param it[in] Iterator pointer. It will be set to NULL after freeing.
- */
-void LSUP_mdbiter_free (LSUP_MDBIterator *it);
-
-
-/** @brief Get all namespace prefixes in the store.
- *
- * @param[in] store MDB store to query.
- *
- * @param[out] nsm Pointer to namespace map to generate.
- *
- * @return LSUP_OK on success; LSUP_DB_ERR on MDB error.
- */
-LSUP_rc
-LSUP_mdbstore_nsm_get (LSUP_MDBStore *store, LSUP_NSMap **nsm);
-
-
-/** @brief Store an in-memory namespace map into the permanent back end.
- *
- * Existing prefixes and namespaces are not updated. Thus, if the following are
- * already stored:
- *
- * ns1: <urn:ns:a#>
- * ns2: <urn:ns:b#>
- *
- * Neither of the following will be inserted:
- *
- * ns3: <urn:ns:a#>
- * ns2: <urn:ns:c#>
- *
- * @param[in] store MDB store to update.
- *
- * @param[out] nsm Namespace map handle to store.
- *
- * @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.
- */
-LSUP_rc
-LSUP_mdbstore_nsm_store (LSUP_MDBStore *store, const LSUP_NSMap *nsm);
-
-
-/** @brief Add a single term to the store.
- *
- * @param[in] store MDB store handle.
- *
- * @param[in] sterm Serialized term to store.
- */
-LSUP_rc
-LSUP_mdbstore_add_term (LSUP_MDBStore *store, const LSUP_Buffer *sterm);
-
-
-/** @brief Whether a term key exists.
- *
- * @param[in] store MDB store to search in.
- *
- * @param[in] key Key to look up.
- *
- * @return 1 if the term exists, 0 if it does not exist; <0 on error.
- */
-int
-LSUP_mdbstore_tkey_exists (LSUP_MDBStore *store, LSUP_Key tkey);
-
+extern const LSUP_StoreInt mdbstore_int;       /// MDB store interface.
+extern const LSUP_StoreIteratorInt mdbiter_int;  /// MDB iterator interface.
 
 #endif  /* _LSUP_STORE_MDB_H */

+ 2 - 4
src/environment.c

@@ -7,12 +7,10 @@
 /**
  * Static handles.
  */
-#define DEFAULT_CTX_LABEL "urn:lsup:default"
+#define DEFAULT_CTX_LABEL LSUP_NS "default"
 
 
-/** @brief Environment "singleton".
- */
-LSUP_Env *LSUP_default_env = NULL;
+LSUP_Env *LSUP_default_env = NULL; /// Environment "singleton".
 
 
 /*

+ 22 - 31
src/graph.c

@@ -2,34 +2,27 @@
 #include "store_mdb.h"
 #include "graph.h"
 
-
 /*
  * Data types.
  */
 
 typedef struct Graph {
-    const LSUP_Env *        env;            // LSUP environment.
-    LSUP_store_type         store_type;     // Back end type: in-memory or MDB.
-    LSUP_Term               *uri;           // Graph "name" (URI)
-    union {
-        LSUP_HTStore *      ht_store;
-        LSUP_MDBStore *     mdb_store;
-    };                                      // Back end, defined by store_type.
-    LSUP_NSMap *            nsm;            // Namespace map. NOTE: This is
-                                            // NULL for MDB* stores. To get a
-                                            // proper NSMap, always call
-                                            // #LSUP_graph_namespace() for all
-                                            // types of graphs.
+    const LSUP_Env *        env;            ///> LSUP environment.
+    LSUP_Term               *uri;           ///> Graph "name" (URI)
+    const LSUP_StoreInt *   interface;      ///> Store interface.
+    void *                  backend;        ///> Store back end struct.
+    LSUP_NSMap *            nsm;            ///> Namespace map. NOTE: This is
+                                            ///> NULL for MDB* stores. To get
+                                            ///> a proper NSMap, always call
+                                            ///> #LSUP_graph_namespace() for
+                                            ///> all types of graphs.
 } Graph;
 
-
 typedef struct GraphIterator {
-    const Graph *           graph;      // Parent graph.
-    union {                             // Internal store iterator.
-        LSUP_HTIterator *   ht_iter;
-        LSUP_MDBIterator *  mdb_iter;
-    };
-    size_t                  ct;         // Total matches.
+    const Graph *                   graph;      ///> Parent graph.
+    const LSUP_StoreIteratorInt *   interface;  ///> Iterator interface.
+    void *                          backend;    ///> Iterator back end struct.
+    size_t                          ct;         ///> Total matches.
 } GraphIterator;
 
 
@@ -43,7 +36,7 @@ graph_iter_next_buffer (GraphIterator *it, LSUP_BufferTriple *sspo);
 static LSUP_rc
 graph_copy_contents (const LSUP_Graph *src, LSUP_Graph *dest);
 
-#define ENTRY(a, b) (be) == (LSUP_STORE_##a) ||
+#define ENTRY(a, b, c) (be) == (LSUP_STORE_##a) ||
 static inline bool
 check_backend (LSUP_store_type be)
 { return (BACKEND_TBL false); }
@@ -68,20 +61,18 @@ LSUP_graph_new_env (
     MALLOC_GUARD (gr, NULL);
 
     gr->uri = uri;
-    gr->store_type = store_type;
     gr->env = env;
     gr->nsm = env->nsm;
 
-    if (gr->store_type == LSUP_STORE_MEM) {
-        gr->ht_store = LSUP_htstore_new(0);
-        if (UNLIKELY (!gr->ht_store)) return NULL;
-
-    } else if (gr->store_type == LSUP_STORE_MDB) {
-        gr->mdb_store = env->mdb_store;
+    switch (store_type) {
+#define ENTRY(a, b, c) case LSUP_STORE_##a: gr->interface = &b; break;
+BACKEND_TBL
+#undef ENTRY
+        default:
+            log_error ("Not a valid store type: %d", store_type); return NULL;
+    };
 
-    } else { // LSUP_STORE_MDB_TMP
-        gr->mdb_store = env->mdb_store_ramdisk;
-    }
+    gr->backend = gr->interface->new_fn();
 
     log_debug ("Graph created.");
     return gr;

+ 232 - 170
src/store_mdb.c

@@ -1,7 +1,6 @@
 #include <ftw.h>
 
 #include "store_mdb.h"
-#include "data/bootstrap.h"
 
 /**
  * Number of DBs defined. See MAIN_TABLE and LOOKUP_TABLE defines below.
@@ -29,6 +28,8 @@
  */
 
 typedef char DbLabel[8];
+typedef struct mdbstore_t MDBStore;
+typedef struct mdbstore_iter_t MDBIterator;
 
 typedef enum {
     LSSTORE_INIT         = 1, // Is the store environment set up on disk?
@@ -41,14 +42,14 @@ typedef enum {
     OP_REMOVE,
 } StoreOp;
 
-typedef struct mdbstore_t {
+struct mdbstore_t {
     MDB_env *           env;            // Environment handle.
     MDB_txn *           txn;            // Current transaction.
     MDB_dbi             dbi[N_DB];      // DB handles. Refer to DbIdx enum.
     LSUP_Buffer *       default_ctx;    // Default ctx as a serialized URI.
     StoreState          state;          // Store state.
     int                 features;       // Store feature flags.
-} MDBStore;
+};
 
 /** @brief Iterator operation.
  *
@@ -60,11 +61,11 @@ typedef struct mdbstore_t {
  * value for the next result. It is up to the caller to evaluate this value
  * and decide whether to call the function again.
  */
-typedef void (*iter_op_fn_t)(LSUP_MDBIterator *it);
+typedef void (*iter_op_fn_t)(MDBIterator *it);
 
 
 /// Triple iterator.
-typedef struct mdbstore_iter_t {
+struct mdbstore_iter_t {
     MDBStore *          store;      ///< MDB store handle.
     MDB_txn *           txn;        ///< MDB transaction.
     MDB_cursor *        cur;        ///< MDB cursor.
@@ -81,7 +82,7 @@ typedef struct mdbstore_iter_t {
     size_t              ct;         ///< Current count of records inserted or
                                     ///< results found.
     int                 rc;         ///< MDB_* return code for the next result.
-} MDBIterator;
+};
 
 
 /*
@@ -94,29 +95,29 @@ typedef struct mdbstore_iter_t {
 /**
  * Main DBs. These are the master information containers.
  *
- * Data columns are: identifier prefix, DB label, flags.
- *
  * The number of entries must match the N_DB constant defined above.
  */
 #define MAIN_TABLE \
-    ENTRY(  T_ST,    "t:st",    0               )   /* Key to ser. term */  \
-    ENTRY(  SPO_C,   "spo:c",   DUPFIXED_MASK   )   /* Triple to context */ \
-    ENTRY(  C_,      "c:",      0               )   /* Track empty ctx */   \
-    ENTRY(  PFX_NS,  "pfx:ns",  0               )   /* Prefix to NS */      \
-    ENTRY(  IDK_ID,  "idk:id",  0               )   /* ID key to ID */      \
+/*          #ID pfx #DB label   #Flags  */ \
+    ENTRY(  T_ST,   "t:st",     0               )   /* Key to ser. term */  \
+    ENTRY(  SPO_C,  "spo:c",    DUPFIXED_MASK   )   /* Triple to context */ \
+    ENTRY(  C_,     "c:",       0               )   /* Track empty ctx */   \
+    ENTRY(  PFX_NS, "pfx:ns",   0               )   /* Prefix to NS */      \
+    ENTRY(  IDK_ID, "idk:id",   0               )   /* ID key to ID */      \
 
 /**
  * Lookup DBs. These are indices and may be destroyed and rebuilt.
  */
 #define LOOKUP_TABLE \
-    ENTRY(  S_PO,    "s:po",    DUPFIXED_MASK   )   /* 1-bound lookup */    \
-    ENTRY(  P_SO,    "p:so",    DUPFIXED_MASK   )   /* 1-bound lookup */    \
-    ENTRY(  O_SP,    "o:sp",    DUPFIXED_MASK   )   /* 1-bound lookup */    \
-    ENTRY(  PO_S,    "po:s",    DUPFIXED_MASK   )   /* 2-bound lookup */    \
-    ENTRY(  SO_P,    "so:p",    DUPFIXED_MASK   )   /* 2-bound lookup */    \
-    ENTRY(  SP_O,    "sp:o",    DUPFIXED_MASK   )   /* 2-bound lookup */    \
-    ENTRY(  C_SPO,   "c:spo",   DUPFIXED_MASK   )   /* Context lookup */    \
-    ENTRY(  NS_PFX,  "ns:pfx",  DUPSORT_MASK    )   /* NS to prefix */      \
+/*          #ID pfx #DB label  #Flags   */ \
+    ENTRY(  S_PO,    "s:po",   DUPFIXED_MASK    )   /* 1-bound lookup */    \
+    ENTRY(  P_SO,    "p:so",   DUPFIXED_MASK    )   /* 1-bound lookup */    \
+    ENTRY(  O_SP,    "o:sp",   DUPFIXED_MASK    )   /* 1-bound lookup */    \
+    ENTRY(  PO_S,    "po:s",   DUPFIXED_MASK    )   /* 2-bound lookup */    \
+    ENTRY(  SO_P,    "so:p",   DUPFIXED_MASK    )   /* 2-bound lookup */    \
+    ENTRY(  SP_O,    "sp:o",   DUPFIXED_MASK    )   /* 2-bound lookup */    \
+    ENTRY(  C_SPO,   "c:spo",  DUPFIXED_MASK    )   /* Context lookup */    \
+    ENTRY(  NS_PFX,  "ns:pfx", DUPSORT_MASK     )   /* NS to prefix */      \
 
 /**
  * DB labels. They are prefixed with DB_
@@ -188,7 +189,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);
+        MDBStore *store, StoreOp op, LSUP_TripleKey spok, LSUP_Key ck);
 
 inline static LSUP_rc lookup_0bound (MDBIterator *it, size_t *ct);
 inline static LSUP_rc lookup_1bound (
@@ -202,8 +203,119 @@ inline static LSUP_rc lookup_3bound(MDBIterator *it, size_t *ct);
  * API.
  */
 
+LSUP_NSMap *
+mdbstore_nsm_get (void *h)
+{
+    MDBStore *store = h;
+    LSUP_NSMap *nsm = LSUP_nsmap_new();
+    if (UNLIKELY (!nsm)) return NULL;
+
+    MDB_txn *txn;
+    mdb_txn_begin (store->env, NULL, MDB_RDONLY, &txn);
+
+    MDB_cursor *cur;
+    if (mdb_cursor_open (txn, store->dbi[IDX_PFX_NS], &cur) != MDB_SUCCESS) {
+        mdb_txn_abort (txn);
+        return NULL;
+    }
+
+    MDB_val ns_v, pfx_v;
+    if (mdb_cursor_get (cur, &pfx_v, &ns_v, MDB_FIRST) != MDB_SUCCESS)
+        goto finally;
+
+    do {
+        ns_pfx pfx;
+        char *ns = malloc (ns_v.mv_size);
+
+        strncpy (pfx, pfx_v.mv_data, pfx_v.mv_size);
+        strncpy (ns, ns_v.mv_data, ns_v.mv_size);
+        LSUP_nsmap_add (nsm, pfx, ns);
+
+        free (ns);
+    } while (mdb_cursor_get (
+                cur, &pfx_v, &ns_v, MDB_NEXT_NODUP) == MDB_SUCCESS);
+
+finally:
+    mdb_cursor_close (cur);
+    mdb_txn_abort (txn);
+
+    return nsm;
+}
+
+
 LSUP_rc
-LSUP_mdbstore_setup (const char *path, bool clear)
+mdbstore_nsm_put (void *h, const LSUP_NSMap *nsm)
+{
+    MDBStore *store = h;
+    MDB_txn *txn;
+    RCCK (mdb_txn_begin (store->env, store->txn, 0, &txn));
+
+    LSUP_rc rc = LSUP_NOACTION;
+    int db_rc;
+
+    MDB_cursor *dcur = NULL, *icur = NULL;
+    if (
+        mdb_cursor_open (txn, store->dbi[IDX_PFX_NS], &dcur) != MDB_SUCCESS
+        ||
+        mdb_cursor_open (txn, store->dbi[IDX_NS_PFX], &icur) != MDB_SUCCESS
+    ) {
+        mdb_txn_abort (txn);
+        return LSUP_DB_ERR;
+    }
+
+    MDB_val pfx_v, ns_v;
+    const char ***nsm_data = LSUP_nsmap_dump (nsm);
+
+    for (size_t i = 0; nsm_data[i] != NULL; i++) {
+        // At least 1 action. If not OK, it will change during the iteration.
+        if (i == 0) rc = LSUP_OK;
+        // On previous error, just clean up the NSM data array.
+        if (rc < 0) goto loop_end;
+
+        pfx_v.mv_data = (void *) nsm_data[i][0];
+        pfx_v.mv_size = strlen (nsm_data[i][0]) + 1;
+        ns_v.mv_data = (void *) nsm_data[i][1];
+        ns_v.mv_size = strlen (nsm_data[i][1]) + 1;
+
+        // If either ns or pfx exist, skip.
+        if (
+            mdb_cursor_get (dcur, &pfx_v, &ns_v, MDB_SET) != MDB_NOTFOUND
+            ||
+            mdb_cursor_get (icur, &ns_v, &pfx_v, MDB_SET) != MDB_NOTFOUND
+        ) {
+            rc = LSUP_CONFLICT;
+            goto loop_end;
+        }
+
+        db_rc = mdb_cursor_put (dcur, &pfx_v, &ns_v, 0);
+        db_rc |= mdb_cursor_put (icur, &ns_v, &pfx_v, 0);
+        if (db_rc != MDB_SUCCESS) {
+            log_error ("DB error: %s", LSUP_strerror (db_rc));
+            rc = LSUP_DB_ERR;
+        }
+loop_end:
+        free (nsm_data[i]);
+    }
+    free (nsm_data);
+
+    if (UNLIKELY (rc != LSUP_OK)) mdb_txn_abort (txn);
+    else if (UNLIKELY (mdb_txn_commit (txn) != MDB_SUCCESS)) {
+        mdb_txn_abort (txn);
+        rc = LSUP_TXN_ERR;
+    }
+
+    return rc;
+}
+
+
+/** @brief Create the MDB environment and databases on disk.
+ *
+ * This function takes care of creaating the environment path if not existing,
+ * and checking that it's a writable directory. If the path is not specified
+ * in the LSUP_STORE_PATH environment variable, a default directory is used.
+ */
+LSUP_rc
+mdbstore_setup (const char *path, bool clear)
 {
     int rc;
 
@@ -243,15 +355,22 @@ LSUP_mdbstore_setup (const char *path, bool clear)
 }
 
 
-MDBStore *
-LSUP_mdbstore_new (const char *path, const LSUP_Buffer *default_ctx)
+/** @brief Open an MDB store.
+ *
+ * The store must have been set up with #mdbstore_setup.
+ *
+ * Some environment variables affect various store parameters:
+ *
+ * - LSUP_MDB_MAPSIZE Long int specifying the size of the memory map. Usually
+ *   it is not necessary to modify this, unless one is operating under memory
+ *   and disk constraints. The default map size is 1Tb.
+ */
+void *
+mdbstore_new (const char *path, const LSUP_Buffer *default_ctx)
 {
     int db_rc;
-    LSUP_MDBStore *store;
+    MDBStore *store;
     MALLOC_GUARD (store, NULL);
-    store->features = LSUP_STORE_CTX | LSUP_STORE_IDX;
-    if (strcmp (path, LSUP_MDB_RAMDISK_PATH) != 0)
-            store->features |= LSUP_STORE_PERM;
 
     db_rc = mdb_env_create (&store->env);
     log_trace ("create rc: %d", db_rc);
@@ -298,7 +417,7 @@ LSUP_mdbstore_new (const char *path, const LSUP_Buffer *default_ctx)
             for (int i = 0; init_nsmap[i][0] != NULL; i++)
                 LSUP_nsmap_add (nsm, init_nsmap[i][0], init_nsmap[i][1]);
 
-            LSUP_mdbstore_nsm_store (store, nsm);
+            mdbstore_nsm_put (store, nsm);
 
             LSUP_nsmap_free (nsm);
         }
@@ -318,8 +437,9 @@ fail:
 
 
 void
-LSUP_mdbstore_free (LSUP_MDBStore *store)
+mdbstore_free (void *h)
 {
+    MDBStore *store = h;
     if (store->state & LSSTORE_OPEN) {
         const char *path;
         mdb_env_get_path (store->env, &path);
@@ -336,12 +456,12 @@ LSUP_mdbstore_free (LSUP_MDBStore *store)
 }
 
 
-int LSUP_mdbstore_features (LSUP_MDBStore *store)
+int mdbstore_features (MDBStore *store)
 { return store->features; }
 
 
 LSUP_rc
-LSUP_mdbstore_stat (LSUP_MDBStore *store, MDB_stat *stat)
+mdbstore_stat (MDBStore *store, MDB_stat *stat)
 {
     if (!(store->state & LSSTORE_INIT)) return 0;
 
@@ -357,20 +477,22 @@ LSUP_mdbstore_stat (LSUP_MDBStore *store, MDB_stat *stat)
 
 
 size_t
-LSUP_mdbstore_size (LSUP_MDBStore *store)
+mdbstore_size (void *h)
 {
+    MDBStore *store = h;
     // Size is calculated outside of any pending write txn.
 
     MDB_stat stat;
-    if (LSUP_mdbstore_stat (store, &stat) != LSUP_OK) return 0;
+    if (mdbstore_stat (store, &stat) != LSUP_OK) return 0;
 
     return stat.ms_entries;
 }
 
 
-MDBIterator *
-LSUP_mdbstore_add_init (LSUP_MDBStore *store, const LSUP_Buffer *sc)
+void *
+mdbstore_add_init (void *h, const LSUP_Buffer *sc)
 {
+    MDBStore *store = h;
     /* An iterator is used here. Some members are a bit misused but it does
      * its job without having to define a very similar struct.
      */
@@ -413,9 +535,16 @@ LSUP_mdbstore_add_init (LSUP_MDBStore *store, const LSUP_Buffer *sc)
 }
 
 
+/*
+ * NOTE: at the moment #mdbstore_remove() or another
+ * #mdbstore_init() cannot be called between #mdbstore_add_init and
+ * #mdbstore_add_abort or #mdbstore_add_done. FIXME
+ *
+ */
 LSUP_rc
-LSUP_mdbstore_add_iter (MDBIterator *it, const LSUP_BufferTriple *sspo)
+mdbstore_add_iter (void *h, const LSUP_BufferTriple *sspo)
 {
+    MDBIterator *it = h;
     int db_rc;
     LSUP_TripleKey spok = NULL_TRP;
 
@@ -472,8 +601,9 @@ LSUP_mdbstore_add_iter (MDBIterator *it, const LSUP_BufferTriple *sspo)
 
 
 LSUP_rc
-LSUP_mdbstore_add_done (MDBIterator *it)
+mdbstore_add_done (void *h)
 {
+    MDBIterator *it = h;
     LSUP_rc rc = LSUP_OK;
 
     if (mdb_txn_commit (it->store->txn) != MDB_SUCCESS) {
@@ -490,8 +620,9 @@ LSUP_mdbstore_add_done (MDBIterator *it)
 
 
 void
-LSUP_mdbstore_add_abort (MDBIterator *it)
+mdbstore_add_abort (void *h)
 {
+    MDBIterator *it = h;
     mdb_txn_abort (it->store->txn);
 
     it->store->txn = NULL;
@@ -500,29 +631,31 @@ LSUP_mdbstore_add_abort (MDBIterator *it)
 }
 
 
+/* TODO deprecate. Use low-level instead and abstract at graph level. */
 LSUP_rc
-LSUP_mdbstore_add (
-        LSUP_MDBStore *store, const LSUP_Buffer *sc,
+mdbstore_add (
+        void *h, const LSUP_Buffer *sc,
         const LSUP_BufferTriple strp[], const size_t ct, size_t *inserted)
 {
-    MDBIterator *it = LSUP_mdbstore_add_init (store, sc);
+    MDBStore *store = h;
+    MDBIterator *it = mdbstore_add_init (store, sc);
     if (UNLIKELY (!it)) return LSUP_DB_ERR;
 
     for (size_t i = 0; i < ct; i++) {
-        LSUP_rc rc = LSUP_mdbstore_add_iter (it, strp + i);
+        LSUP_rc rc = mdbstore_add_iter (it, strp + i);
         if (UNLIKELY (rc < 0)) {
-            LSUP_mdbstore_add_abort (it);
+            mdbstore_add_abort (it);
             return rc;
         }
     }
     *inserted = it->i;
 
-    return LSUP_mdbstore_add_done (it);
+    return mdbstore_add_done (it);
 }
 
 
 static LSUP_rc
-key_to_sterm (LSUP_MDBIterator *it, const LSUP_Key key, LSUP_Buffer *sterm)
+key_to_sterm (MDBIterator *it, const LSUP_Key key, LSUP_Buffer *sterm)
 {
     LSUP_rc rc = LSUP_NORESULT;
     int db_rc;
@@ -546,18 +679,19 @@ key_to_sterm (LSUP_MDBIterator *it, const LSUP_Key key, LSUP_Buffer *sterm)
 }
 
 
-MDBIterator *
-LSUP_mdbstore_lookup(
-        LSUP_MDBStore *store, const LSUP_Buffer *ss, const LSUP_Buffer *sp,
+void *
+mdbstore_lookup(
+        void *h, const LSUP_Buffer *ss, const LSUP_Buffer *sp,
         const LSUP_Buffer *so, const LSUP_Buffer *sc, size_t *ct)
 {
+    MDBStore *store = h;
     LSUP_TripleKey spok = {
         LSUP_buffer_hash (ss),
         LSUP_buffer_hash (sp),
         LSUP_buffer_hash (so),
     };
 
-    LSUP_MDBIterator *it;
+    MDBIterator *it;
     CALLOC_GUARD (it, NULL);
 
     it->store = store;
@@ -644,7 +778,7 @@ LSUP_mdbstore_lookup(
  * in, if not NULL.
  */
 inline static LSUP_rc
-mdbiter_next_key (LSUP_MDBIterator *it)
+mdbiter_next_key (MDBIterator *it)
 {
     if (UNLIKELY (!it)) return LSUP_VALUE_ERR;
 
@@ -736,9 +870,10 @@ mdbiter_next_key (LSUP_MDBIterator *it)
 
 
 LSUP_rc
-LSUP_mdbiter_next (
-        LSUP_MDBIterator *it, LSUP_BufferTriple *sspo, LSUP_Buffer **ctx_p)
+mdbiter_next (
+        void *h, LSUP_BufferTriple *sspo, LSUP_Buffer **ctx_p)
 {
+    MDBIterator *it = h;
     LSUP_rc rc = mdbiter_next_key (it);
 
     if (rc == LSUP_OK) {
@@ -774,8 +909,9 @@ LSUP_mdbiter_next (
 
 
 void
-LSUP_mdbiter_free (MDBIterator *it)
+mdbiter_free (void *h)
 {
+    MDBIterator *it = h;
     if (!it) return;
 
     if (it->cur) mdb_cursor_close (it->cur);
@@ -788,10 +924,11 @@ LSUP_mdbiter_free (MDBIterator *it)
 
 
 LSUP_rc
-LSUP_mdbstore_remove(
-        LSUP_MDBStore *store, const LSUP_Buffer *ss, const LSUP_Buffer *sp,
+mdbstore_remove(
+        void *h, const LSUP_Buffer *ss, const LSUP_Buffer *sp,
         const LSUP_Buffer *so, const LSUP_Buffer *sc, size_t *ct)
 {
+    MDBStore *store = h;
     LSUP_rc rc = LSUP_NOACTION, db_rc;
 
     LSUP_Key ck = NULL_KEY;
@@ -815,7 +952,7 @@ LSUP_mdbstore_remove(
     ck_v.mv_size = KLEN;
     ck_v.mv_data = &ck;
 
-    LSUP_MDBIterator *it = LSUP_mdbstore_lookup (store, ss, sp, so, sc, ct);
+    MDBIterator *it = mdbstore_lookup (store, ss, sp, so, sc, ct);
     if (UNLIKELY (!it)) return LSUP_DB_ERR;
     if (ct) log_debug ("Found %lu triples to remove.", *ct);
 
@@ -858,7 +995,7 @@ LSUP_mdbstore_remove(
         rc = index_triple (store, OP_REMOVE, it->spok, ck);
     }
 
-    LSUP_mdbiter_free (it);
+    mdbiter_free (it);
 
     if (UNLIKELY (mdb_txn_commit (store->txn) != MDB_SUCCESS)) {
         rc = LSUP_TXN_ERR;
@@ -878,114 +1015,8 @@ fail:
 }
 
 
-LSUP_rc
-LSUP_mdbstore_nsm_get (LSUP_MDBStore *store, LSUP_NSMap **nsm_p)
-{
-    LSUP_rc rc = LSUP_NORESULT;
-    LSUP_NSMap *nsm = LSUP_nsmap_new();
-    if (UNLIKELY (!nsm)) return LSUP_MEM_ERR;
-
-    *nsm_p = nsm;
-
-    MDB_txn *txn;
-    mdb_txn_begin (store->env, NULL, MDB_RDONLY, &txn);
-
-    MDB_cursor *cur;
-    if (mdb_cursor_open (txn, store->dbi[IDX_PFX_NS], &cur) != MDB_SUCCESS) {
-        mdb_txn_abort (txn);
-        return LSUP_DB_ERR;
-    }
-
-    MDB_val ns_v, pfx_v;
-    if (mdb_cursor_get (cur, &pfx_v, &ns_v, MDB_FIRST) != MDB_SUCCESS)
-        goto finally;
-
-    do {
-        ns_pfx pfx;
-        char *ns = malloc (ns_v.mv_size);
-
-        strncpy (pfx, pfx_v.mv_data, pfx_v.mv_size);
-        strncpy (ns, ns_v.mv_data, ns_v.mv_size);
-        LSUP_nsmap_add (nsm, pfx, ns);
-
-        free (ns);
-    } while (mdb_cursor_get (
-                cur, &pfx_v, &ns_v, MDB_NEXT_NODUP) == MDB_SUCCESS);
-
-finally:
-    mdb_cursor_close (cur);
-    mdb_txn_abort (txn);
-
-    return rc;
-}
-
-
-LSUP_rc
-LSUP_mdbstore_nsm_store (LSUP_MDBStore *store, const LSUP_NSMap *nsm)
-{
-    MDB_txn *txn;
-    RCCK (mdb_txn_begin (store->env, store->txn, 0, &txn));
-
-    LSUP_rc rc = LSUP_NOACTION;
-    int db_rc;
-
-    MDB_cursor *dcur = NULL, *icur = NULL;
-    if (
-        mdb_cursor_open (txn, store->dbi[IDX_PFX_NS], &dcur) != MDB_SUCCESS
-        ||
-        mdb_cursor_open (txn, store->dbi[IDX_NS_PFX], &icur) != MDB_SUCCESS
-    ) {
-        mdb_txn_abort (txn);
-        return LSUP_DB_ERR;
-    }
-
-    MDB_val pfx_v, ns_v;
-    const char ***nsm_data = LSUP_nsmap_dump (nsm);
-
-    for (size_t i = 0; nsm_data[i] != NULL; i++) {
-        // At least 1 action. If not OK, it will change during the iteration.
-        if (i == 0) rc = LSUP_OK;
-        // On previous error, just clean up the NSM data array.
-        if (rc < 0) goto loop_end;
-
-        pfx_v.mv_data = (void *) nsm_data[i][0];
-        pfx_v.mv_size = strlen (nsm_data[i][0]) + 1;
-        ns_v.mv_data = (void *) nsm_data[i][1];
-        ns_v.mv_size = strlen (nsm_data[i][1]) + 1;
-
-        // If either ns or pfx exist, skip.
-        if (
-            mdb_cursor_get (dcur, &pfx_v, &ns_v, MDB_SET) != MDB_NOTFOUND
-            ||
-            mdb_cursor_get (icur, &ns_v, &pfx_v, MDB_SET) != MDB_NOTFOUND
-        ) {
-            rc = LSUP_CONFLICT;
-            goto loop_end;
-        }
-
-        db_rc = mdb_cursor_put (dcur, &pfx_v, &ns_v, 0);
-        db_rc |= mdb_cursor_put (icur, &ns_v, &pfx_v, 0);
-        if (db_rc != MDB_SUCCESS) {
-            log_error ("DB error: %s", LSUP_strerror (db_rc));
-            rc = LSUP_DB_ERR;
-        }
-loop_end:
-        free (nsm_data[i]);
-    }
-    free (nsm_data);
-
-    if (UNLIKELY (rc != LSUP_OK)) mdb_txn_abort (txn);
-    else if (UNLIKELY (mdb_txn_commit (txn) != MDB_SUCCESS)) {
-        mdb_txn_abort (txn);
-        rc = LSUP_TXN_ERR;
-    }
-
-    return rc;
-}
-
-
 int
-LSUP_mdbstore_tkey_exists (LSUP_MDBStore *store, LSUP_Key tkey)
+mdbstore_tkey_exists (MDBStore *store, LSUP_Key tkey)
 {
     int db_rc, rc;
     MDB_val key, data;
@@ -1015,8 +1046,9 @@ LSUP_mdbstore_tkey_exists (LSUP_MDBStore *store, LSUP_Key tkey)
 
 
 LSUP_rc
-LSUP_mdbstore_add_term (LSUP_MDBStore *store, const LSUP_Buffer *sterm)
+mdbstore_add_term (void *h, const LSUP_Buffer *sterm)
 {
+    MDBStore *store = h;
     int db_rc;
     MDB_val key, data;
 
@@ -1053,8 +1085,39 @@ fail:
 }
 
 
-/* * * Static functions. * * */
+const LSUP_StoreInt mdbstore_int = {
+    .name           = "MDB Store",
+    .features       = LSUP_STORE_PERM | LSUP_STORE_CTX | LSUP_STORE_IDX
+                      | LSUP_STORE_TXN,
+
+    .setup_fn       = mdbstore_setup,
+    .new_fn         = mdbstore_new,
+    .free_fn        = mdbstore_free,
+
+    .size_fn        = mdbstore_size,
+
+    .add_init_fn    = mdbstore_add_init,
+    .add_iter_fn    = mdbstore_add_iter,
+    .add_abort_fn   = mdbstore_add_abort,
+    .add_done_fn    = mdbstore_add_done,
+    .add_term_fn    = mdbstore_add_term,
+
+    .lookup_fn      = mdbstore_lookup,
 
+    .remove_fn      = mdbstore_remove,
+
+    .nsm_put_fn     = mdbstore_nsm_put,
+    .nsm_get_fn     = mdbstore_nsm_get,
+};
+
+const LSUP_StoreIteratorInt mdbiter_int = {
+    .name           = "MDB Iterator",
+    .iter_fn        = mdbiter_next,
+    .free_fn        = mdbiter_free,
+};
+
+
+/* * * Static functions. * * */
 
 /** @brief Index an added or removed triple.
  *
@@ -1064,8 +1127,7 @@ fail:
  * @param ck[in] Context to index, may be NULL.
  */
 static LSUP_rc
-index_triple(
-        LSUP_MDBStore *store, StoreOp op, LSUP_TripleKey spok, LSUP_Key ck)
+index_triple(MDBStore *store, StoreOp op, LSUP_TripleKey spok, LSUP_Key ck)
 {
     int db_rc;
     LSUP_rc rc = LSUP_NOACTION;