Sfoglia il codice sorgente

Merge branch 'store_interface' of scossu/lsup_rdf into master

scossu 4 giorni fa
parent
commit
3757b0855e
14 ha cambiato i file con 604 aggiunte e 284 eliminazioni
  1. 12 3
      Makefile
  2. 6 2
      README.md
  3. 1 1
      ext/hashmap
  4. 1 1
      ext/tpl
  5. 1 1
      include/lsup/graph.h
  6. 308 1
      include/lsup/store.h
  7. 43 167
      include/lsup/store_interface.h
  8. 2 2
      src/environment.c
  9. 25 30
      src/namespace.c
  10. 138 3
      src/store.c
  11. 4 4
      src/store_htable.c
  12. 3 3
      src/term.c
  13. 3 3
      test.c
  14. 57 63
      test/test_store.c

+ 12 - 3
Makefile

@@ -49,7 +49,7 @@ LSUP_SRC = $(wildcard src/*.c)
 SRC = $(EXT_SRC) $(LSUP_SRC)
 TEST_SRC = $(wildcard test/*.c) test.c
 
-EXT_OBJ := $(EXT_SRC:.c=.o)
+EXT_OBJ := $(EXT_SRC:%.c=$(BUILDDIR)/%.o)
 # TODO This is extremely convoluted, simplify if possible.
 CODEC_SRC := $(wildcard $(CODEC_DIR)/codec_*.c)
 CODEC_REL_SRC := $(CODEC_SRC:$(CODEC_DIR)/%=%)
@@ -70,6 +70,10 @@ DYN_DBG_LIB = $(DYN_LIB:.so=_dbg.so)
 LIBS = $(STATIC_LIB) $(DYN_LIB)
 DBG_LIBS = $(STATIC_DBG_LIB) $(DYN_DBG_LIB)
 
+$(info EXT_SRC: $(EXT_SRC))
+$(info EXT_OBJ: $(EXT_OBJ))
+$(info OBJ: $(OBJ))
+$(info DBG_OBJ: $(DBG_OBJ))
 #$(info LIBS: $(LIBS))
 #$(info DBG_LIBS: $(DBG_LIBS))
 
@@ -132,6 +136,12 @@ $(DYN_DBG_LIB): $(DBG_OBJ)
 	$(CC) -shared $(LDFLAGS) -o $@ $^ $(CODEC_DBG_OBJ)
 
 
+# External libraries.
+$(BUILDDIR)/ext/%.o: ext/%.c
+	mkdir -p $(dir $@)
+	$(CC) $(CFLAGS) -c $^ -o $@
+
+
 # Standard objects.
 $(BUILDDIR)/%.o: src/%.c
 	$(CC) $(CFLAGS) -c $^ -o $@
@@ -175,12 +185,11 @@ debug_install: debug ## Install debug libraries.
 
 .PHONY: clean
 clean: ## Clean up artifacts, including language parsers.
-	rm -f ./*.[aod] bin/* src/codec/*.out
 	rm -rf $(BUILDDIR)
+	rm -f bin/*
 	rm -f $(LIBS) $(DBG_LIBS)
 	rm -f include/codec/grammar_*.h
 	rm -f src/codec/grammar_*.c src/codec/parser_*.c
-	rm -rf venv/
 
 
 .PHONY: uninstall ## Uninstall library (not the dependencies).

+ 6 - 2
README.md

@@ -138,6 +138,10 @@ and other targets in the current Makefile.
 
 ### Environment Variables
 
+`LSUP_DEFAULT_CTX`: string to derive the default context URI for context-aware
+stores when the environment is initialized. This must be a fully qualified URI.
+If unspecified, the value of `LSUP_DEFAULT_CTX_LABEL` is used.
+
 `LSUP_MDB_STORE_PATH`: The file path for the persistent store back end. For
 production use it is strongly recommended to set this to a permanent location
 on the fastest storage volume available. If unset, the current directory will
@@ -154,8 +158,8 @@ be used. The directory must exist.
 
 If unspecified, it is set to 3.
 
-`LSUP_MDB_MAPSIZE` Virtual memory map size. It is recommended to leave this
-alone, unless you are running Valgrind or other tools that limit memory usage.
+`LSUP_MDB_MAPSIZE`: Virtual memory map size. It is recommended to leave this
+alone, except when running Valgrind or other tools that limit memory usage.
 The map size by itself does not preallocate any resources and is safe to
 increase beyond the physical capacity of the host system. By default, it is set
 to 1Tb for 64-bit systems and 4Gb for 32-bit systems.

+ 1 - 1
ext/hashmap

@@ -1 +1 @@
-Subproject commit d9b23a5a4df7e572ec897357292cf377eca9b229
+Subproject commit e6669b5e5d9c6a58af09c3795738120fe5db740e

+ 1 - 1
ext/tpl

@@ -1 +1 @@
-Subproject commit f8138ad393f4b1985c916029ab6d703e4e7a1c4c
+Subproject commit bfe4c3e1ea5f912e6a7240d851952dd4a335cb03

+ 1 - 1
include/lsup/graph.h

@@ -366,7 +366,7 @@ LSUP_graph_remove_txn (
  * @param[out] ct If not NULL, this handle is populated with the number of
  *  entries found.
  *
- * @return Pointer to a #LSUP_GraphIterator to be generated. It must be
+ * @return Pointer to a LSUP_GraphIterator to be generated. It must be
  *  freed with #LSUP_graph_iter_free() after use.
  */
 LSUP_GraphIterator *

+ 308 - 1
include/lsup/store.h

@@ -86,7 +86,7 @@ LSUP_store_type_label (LSUP_StoreType type);
 
 /** @brief Create a new store.
  *
- * The life cycle of a store may normally outspan the one of one or multiple
+ * The life cycle of a store normally outspans the one of one or multiple
  * graphs with the same back end, hence it is managed independently.
  *
  * @param[in] store_type Type of store backing the graph. One of the values of
@@ -122,6 +122,20 @@ void
 LSUP_store_free (LSUP_Store *store);
 
 
+size_t
+LSUP_store_size (const LSUP_Store *store);
+
+
+/// Feature flags belonging to the store interface.
+const LSUP_StoreFeature
+LSUP_store_features (const LSUP_Store *store);
+
+
+/// Store identifier.
+char *
+LSUP_store_id (const LSUP_Store *store);
+
+
 /** @brief Begin a transaction.
  *
  * If the store supports it, begin a transaction. Only one transaction may be
@@ -172,5 +186,298 @@ LSUP_store_commit (LSUP_Store *store, void *txn);
 void
 LSUP_store_abort (LSUP_Store *store, void *txn);
 
+
+/** @brief Update the context of triples in a context-aware store.
+ *
+ * When a context URI is updated, all relationships of triples to that context
+ * must be updated to reflect the new context.
+ *
+ * @param[in] store Store handle.
+ *
+ * @param[in] txn Transaction handle. If NULL, a new transaction is opened.
+ *  This is ignored by non-transactional back ends.
+ *
+ * @param[in] old_c Old context handle.
+ *
+ * @param[in] new_c New context handle.
+ *
+ * @return LSUP_OK on success; LSUP_NOACTION if no triples were found under the
+ *  given context; <0 on error.
+ */
+LSUP_rc
+LSUP_store_update_ctx_txn (
+        LSUP_Store *store, void *txn, const LSUP_Buffer *old_c,
+        const LSUP_Buffer *new_c);
+
+#define LSUP_store_update_ctx(store, ...) \
+    LSUP_store_update_txn (store, NULL, __VA_ARGS__)
+
+
+/** @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[in] store The store to add to.
+ *
+ * @param[in] txn Transaction handle. If NULL, a new transaction is opened.
+ *  This is ignored by non-transactional back ends.
+ *
+ * @param[in] sc 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.
+ */
+void *
+LSUP_store_add_init_txn (
+        LSUP_Store *store, void *txn, const LSUP_Buffer *sc);
+
+/// Non-transactional version of #LSUP_store_add_init_txn().
+#define LSUP_store_add_init(store, ...) \
+    LSUP_store_add_init_txn (store, NULL, __VA_ARGS__)
+
+
+/** @brief Add one triple into the store.
+ *
+ * This must be called after #store_add_init_fn_t, using the iterator
+ * yielded by that function. It may be called multiple times and must be
+ * followed by #store_add_done_fn_t or #store_add_abort_fn_t (if supported).
+ *
+ * @param[in] store Store handle.
+ *
+ * @param[in] it Iterator obtained by #LSUP_store_add_init().
+ *
+ * @param[in] sspo 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_store_add_iter (
+        LSUP_Store *store, void *it, const LSUP_BufferTriple *sspo);
+
+
+/** @brief Abort an add loop and free iterator.
+ *
+ * Usually called on an irrecoverable error from #store_add_iter_fn_t. None of
+ * the successful inserts in the same loop is retained.
+ *
+ * @param[in] store Store handle.
+ *
+ * @param[in] it Iterator obtained by #store_add_init_fn_t.
+ */
+void
+LSUP_store_add_abort (LSUP_Store *store, void *it);
+
+
+/** @brief Finalize an add loop and free iterator.
+ *
+ * This must be called after #store_add_iter_fn_t.
+ *
+ * @param[in] store Store handle.
+ *
+ * @param[in] it Iterator obtained by #LSUP_store_add_init().
+ *
+ * @return LSUP_OK on success, <0 on failure. In the latter case, a transaction
+ *  may be aborted in a transactional back end.
+ */
+LSUP_rc
+LSUP_store_add_done (LSUP_Store *store, void *it);
+
+
+/** @brief Add a single term to the store.
+ *
+ * @param[in] store Store handle.
+ *
+ * @param[in] txn Transaction handle. If NULL, a new transaction is opened.
+ *  This is ignored by non-transactional back ends.
+ *
+ * @param[in] sterm Serialized term to store.
+ */
+LSUP_rc
+LSUP_store_add_term_txn (LSUP_Store *store, void *txn, LSUP_Buffer *sterm);
+
+/// Non-transactional version of #LSUP_store_add_term_txn().
+#define LSUP_store_add_term(store, ...) \
+    LSUP_store_add_term_txn (store, NULL, __VA_ARGS__)
+
+
+/** @brief Look up triples by pattern matching.
+ *
+ * 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 witout context support will always ignore sc.
+ *
+ * @param[in] store The store to be queried.
+ *
+ * @param[in] txn Transaction handle. If NULL, a new transaction is opened.
+ *  This is ignored by non-transactional back ends.
+ *
+ * @param[in] ss Serialized s term.
+ *
+ * @param[in] sp Serialized p 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
+ * 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. In some implementations, 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
+ * #iter_free_fn_t() after use. If matches are found, the iterator points to
+ * the first result which can be retrieved with #iter_next_fn_t.
+ */
+void *
+LSUP_store_lookup_txn (
+        const LSUP_Store *store, void *txn,
+        const LSUP_Buffer *ss, const LSUP_Buffer *sp, const LSUP_Buffer *so,
+        const LSUP_Buffer *sc, size_t *ct);
+
+/// Non-transactional version of #LSUP_store_lookup_txn().
+#define LSUP_store_lookup(store, ...) \
+    LSUP_store_lookup_txn (store, NULL, __VA_ARGS__)
+
+
+/** @brief Yield the matching triples and advance the iterator.
+ *
+ * @note Iterators keep transactions open. Don't hold on to them longer than
+ * necessary.
+ *
+ * @note If the store interface has the LSUP_STORE_COW feature, the memory
+ * buffer referenced by the #LSUP_Buffer handle 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. Note that the #LSUP_Buffer
+ * handle must still be freed (with a plain `free()`), but not the underlying
+ * data buffer, since only the latter is owned by the back end. Implementations
+ * with the `LSUP_STORE_COW` feature SHOULD create buffers with the
+ * `LSUP_BUF_BORROWED` flag, so that they can be safely freed with
+ * #LSUP_buffer_free() and #LSUP_btriple_free().
+ *
+ * @param[in] store Store handle.
+ *
+ * @param[in] it Opaque iterator handle obtained with #store_lookup_fn_t.
+ *
+ * @param[out] sspo #LSUP_BufferTriple to be populated with three serialized
+ * terms if found. It may be NULL, in which case it is not populated.
+ *
+ * @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++));
+ *
+ * @return LSUP_OK if results were found; LSUP_END if no (more) results were
+ * found; LSUP_DB_ERR if a backend error occurred.
+ */
+LSUP_rc
+LSUP_store_iter_next (
+        LSUP_Store *store, void *it, LSUP_BufferTriple *sspo,
+        LSUP_Buffer **ctx);
+
+
+/** @brief Free an iterator allocated by a lookup.
+ *
+ * @param[in] store Store handle.
+ *
+ * @param[in] it Iterator pointer. It will be set to NULL after freeing.
+ */
+void
+LSUP_store_iter_free (LSUP_Store *store, void *it);
+
+
+/** @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] store Store handle.
+ *
+ * @param[in] it Iterator handle to get the transaction from.
+ *
+ * @return Transaction handle. DO NOT close this transaction directly.
+ */
+void *
+LSUP_store_iter_txn (const LSUP_Store *store, void *it);
+
+
+/** @brief Delete triples by pattern matching.
+ *
+ * @param[in] store The store to be queried.
+ *
+ * @param[in] txn Transaction handle. If NULL, a new transaction is opened.
+ *  This is ignored by non-transactional back ends.
+ *
+ * @param[in] ss Serialized s term.
+ *
+ * @param[in] sp Serialized p 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
+ *  without context are assigned the *default* context for the store.
+ *
+ * @param[out] ct If not NULL, this will be populated with the number of
+ *  triples actually deleted.
+ *
+ * @return LSUP_OK on success; LSUP_NOACTION if no triples were deleted;
+ *  <0 on error.
+ *
+ */
+LSUP_rc
+LSUP_store_remove_txn (
+        LSUP_Store *store, void *txn,
+        const LSUP_Buffer *ss, const LSUP_Buffer *sp, const LSUP_Buffer *so,
+        const LSUP_Buffer *sc, size_t *ct);
+
+/// Non-transactional version of #LSUP_store_remove_txn().
+#define LSUP_store_remove(store, ...) \
+    LSUP_store_remove_txn (store, NULL, __VA_ARGS__)
+
+
+/** @brief get index of all graph (context) URIs in a store.
+ *
+ * Only applicable to stores with the LSUP_STORE_CTX feature flag.
+ *
+ * @param[in] store Store handle.
+ *
+ * @param[in] txn Transaction handle. If NULL, a new transaction is opened.
+ *  This is ignored by non-transactional back ends.
+ *
+ * @return Set of all context URIs.
+ */
+LSUP_Buffer **
+LSUP_store_ctx_list_txn (LSUP_Store *store, void *txn);
+
+/// Non-transactional version of #LSUP_store_ctx_list_txn().
+#define LSUP_store_ctx_list(store) \
+    LSUP_store_ctx_list_txn (store, NULL)
+
+
 /// @}  END store
 #endif  /* LSUP_STORE_H */

+ 43 - 167
include/lsup/store_interface.h

@@ -106,22 +106,22 @@ typedef void * (*store_new_fn_t)(const char *id, size_t size);
 typedef void (*store_free_fn_t)(void *store);
 
 
-/** @brief Prototype: get the store ID.
+/** @brief Prototype: get store size.
  *
- * @param[in] store  Store handle.
+ * @param[in] store  The store to calculate size of.
  *
- * @return store ID string. This is a copy and should be freed after use.
+ * @return Number of stored SPO triples (across all contexts if supported).
  */
-typedef char * (*store_id_fn_t)(const void *store);
+typedef size_t (*store_size_fn_t)(const void *store);
 
 
-/** @brief Prototype: get store size.
+/** @brief Prototype: get the store ID.
  *
- * @param[in] store  The store to calculate size of.
+ * @param[in] store  Store handle.
  *
- * @return Number of stored SPO triples (across all contexts if supported).
+ * @return store ID string. This is a copy and should be freed after use.
  */
-typedef size_t (*store_size_fn_t)(const void *store);
+typedef char * (*store_id_fn_t)(const void *store);
 
 
 #if 0
@@ -175,22 +175,10 @@ typedef void (*store_txn_abort_fn_t)(void *txn);
 
 
 /** @brief Update the context of triples in a context-aware store.
- *
- * When a context URI is updated, all relationships of triples to that context
- * must be updated to reflect the new context.
  *
  * Only defined in stores with the LSUP_STORE_CTX feature.
  *
- * @param[in] store Store handle.
- *
- * @param[in] old_c Old context handle.
- *
- * @param[in] new_c New context handle.
- *
- * @param[in] udata Implementation-specific context data.
- *
- * @return LSUP_OK on success; LSUP_NOACTION if no triples were found under the
- *  given context; <0 on error.
+ * @sa #LSUP_store_update_ctx_txn()
  */
 typedef LSUP_rc (*store_update_ctx_fn_t)(
         void *store, const LSUP_Buffer *old_c, const LSUP_Buffer *new_c,
@@ -199,22 +187,7 @@ typedef LSUP_rc (*store_update_ctx_fn_t)(
 
 /** @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[in] store The store to add to.
- *
- * @param[in] sc 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.
- *
- *  @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.
+ * @sa #LSUP_store_add_init_txn()
  */
 typedef void * (*store_add_init_fn_t)(
         void *store, const LSUP_Buffer *sc, void *udata);
@@ -222,18 +195,7 @@ typedef void * (*store_add_init_fn_t)(
 
 /** @brief Add one triple into the store.
  *
- * This must be called after #store_add_init_fn_t, using the iterator
- * yielded by that function. It may be called multiple times and must be
- * followed by #store_add_done_fn_t or #store_add_abort_fn_t (if supported).
- *
- * @param[in] it Iterator obtained by #store_add_init_fn_t.
- *  The following members are of interest:
- *  it->i stores the total number of records inserted.
- *
- * @param[in] sspo 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.
+ * @sa LSUP_store_add_iter().
  */
 typedef LSUP_rc (*store_add_iter_fn_t)(
         void *it, const LSUP_BufferTriple * sspo);
@@ -241,91 +203,35 @@ typedef LSUP_rc (*store_add_iter_fn_t)(
 
 /** @brief Abort an add loop and free iterator.
  *
- * Usually called on an irrecoverable error from #store_add_iter_fn_t. None of
- * the successful inserts in the same loop is retained.
+ * Only available for stores with the LSUP_STORE_CTX feature.
  *
- * @param[in] it Iterator obtained by #store_add_init_fn_t.
+ * @sa LSUP_store_add_abort().
  */
 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 #store_add_iter_fn_t.
- *
- * @param[in] it Iterator obtained by #store_add_init_fn_t.
+ * @sa #LSUP_store_add_done().
  */
 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.
- *
- * @param[in] udata Implementation-defined data. See individual
- *  implementations' documentation.
+ * @sa #LSUP_store_add_term_txn().
  */
 typedef LSUP_rc (*store_add_term_fn_t)(
         void *store, const LSUP_Buffer *sterm, void *udata);
 
 
+/*
+ * Iterator function types.
+ */
+
 /** @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.
- *
- * 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 witout context support will always ignore sc.
- *
- * @param[in] store The store to be queried.
- *
- * @param[in] ss Serialized s term.
- *
- * @param[in] sp Serialized p 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
- * without context are assigned the *default* context for the store.
- *
- *  @param[in] udata User data. Consult individual store implementations for
- *   how this is interpreted.
- *
- * @param[out] ct If not NULL, this will be populated with the number of
- *  entries found. In some implementations, 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
- * #iter_free_fn_t() after use. If matches are found, the iterator points to
- * the first result which can be retrieved with #iter_next_fn_t.
+ * @sa #LSUP_store_lookup_txn().
  */
 typedef void * (*store_lookup_fn_t)(
         void *store,
@@ -334,6 +240,8 @@ typedef void * (*store_lookup_fn_t)(
 
 
 /** @brief Prototype: check for existence of a triple (T/F).
+ *
+ * @note Unused interface function.
  *
  * @param[in] store Store to be queried.
  *
@@ -348,55 +256,9 @@ typedef bool (*store_trp_exist_fn_t)(
         void *store, const LSUP_BufferTriple *sspo, const LSUP_Buffer *sc);
 
 
-/** @brief Prototype: delete triples by pattern matching.
- *
- * The ss, sp, so, sc terms act as a matching pattern as documented in
- * @sa #store_lookup_fn_t(). 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, void *udata, size_t *ct);
-
-
 /** @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 If the store interface has the LSUP_STORE_COW feature, the memory
- * buffer referenced by the #LSUP_Buffer handle 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. Note that the #LSUP_Buffer
- * handle must still be freed (with a plain `free()`), but not the underlying
- * data buffer, since only the latter is owned by the back end. Implementations
- * with the `LSUP_STORE_COW` feature SHOULD create buffers with the
- * `LSUP_BUF_BORROWED` flag, so that they can be safely freed with
- * #LSUP_buffer_free() and #LSUP_btriple_free().
- *
- * @param[in] it Opaque iterator handle obtained with #store_lookup_fn_t.
- *
- * @param[out] sspo #LSUP_BufferTriple to be populated with three serialized
- * terms if found. It may be NULL, in which case it is not populated.
- *
- * @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++));
- *
- * @return LSUP_OK if results were found; LSUP_END if no (more) results were
- * found; LSUP_DB_ERR if a backend error occurred.
+ * @sa LSUP_store_iter_next().
  */
 typedef LSUP_rc (*iter_next_fn_t)(
         void *it, LSUP_BufferTriple *sspo, LSUP_Buffer **ctx);
@@ -404,18 +266,31 @@ typedef LSUP_rc (*iter_next_fn_t)(
 
 /** @brief Prototype: free an iterator allocated by a lookup.
  *
- * @param[in] it Iterator pointer. It will be set to NULL after freeing.
+ * @sa #LSUP_store_iter_free().
  */
 typedef void (*iter_free_fn_t)(void * it);
 
 
-/** @brief Prototype: Get index of all graph (context) URIs in a store.
+/** @brief Prototype: get iterator active transaction handle.
  *
- * Only applicable to stores with the LSUP_STORE_CTX feature flag.
+ * @sa #LSUP_store_iter_txn().
+ */
+typedef void * (*iter_txn_fn_t)(void *it);
+
+
+/** @brief Prototype: delete triples by pattern matching.
  *
- * @param[in] store Store handle.
+ * @sa #LSUP_store_remove_txn().
+ */
+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, void *udata, size_t *ct);
+
+
+/** @brief Prototype: Get index of all graph (context) URIs in a store.
  *
- * @return Set of all context URIs.
+ * @sa #LSUP_store_ctx_list_txn()
  */
 typedef LSUP_Buffer ** (*store_ctx_list_fn_t)(void *store, void *txn);
 
@@ -457,7 +332,6 @@ typedef struct store_if_t {
     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.
 
     //Context setting.
     store_update_ctx_fn_t update_ctx_fn; ///< Update context URI.
@@ -484,6 +358,7 @@ typedef struct store_if_t {
     //store_trp_exist_fn_t exist_fn;      ///< Check if a triple exists.
     iter_next_fn_t      lu_next_fn;     ///< Advance the lookup iterator.
     iter_free_fn_t      lu_free_fn;     ///< Free the lookup iterator.
+    iter_txn_fn_t       iter_txn_fn;    ///< Get iterator's transaction.
 
     // Removal.
     store_remove_fn_t   remove_fn;      ///< Remove triples by pattern.
@@ -527,6 +402,7 @@ const LSUP_StoreInt my_store_int = {
     .lookup_fn      = my_lookup_fn,
     .lu_next_fn     = my_iter_next_fn,
     .lu_free_fn     = my_iter_free_fn,
+    .iter_txn_fn    = my_iter_txn_fn,
 
     .remove_fn      = my_remove_fn,
 

+ 2 - 2
src/environment.c

@@ -46,8 +46,8 @@ LSUP_init (void)
         LSUP_nsmap_add (LSUP_default_nsm, init_nsmap[i][0], init_nsmap[i][1]);
 
     // Default context URI.
-    char *default_ctx_str = getenv ("LSUP_DEFAULT_CTX");
-    if (!default_ctx_str ) default_ctx_str = DEFAULT_CTX_LABEL;
+    const char *default_ctx_str = getenv ("LSUP_DEFAULT_CTX");
+    if (!default_ctx_str) default_ctx_str = DEFAULT_CTX_LABEL;
     LSUP_default_ctx = LSUP_iriref_new (default_ctx_str, NULL);
     if (UNLIKELY (!LSUP_default_ctx)) return LSUP_ERROR;
     LSUP_default_ctx_buf = LSUP_term_serialize (LSUP_default_ctx);

+ 25 - 30
src/namespace.c

@@ -9,8 +9,6 @@ typedef struct ns_entry_t {
     char *              ns;         // Fully qualified NS.
 } NSEntry;
 
-typedef struct hashmap NSMap;
-
 /** @brief Iterator for dumping NS map.
  */
 struct dump_iter_t {
@@ -58,7 +56,7 @@ static bool nsmap_dump_ns_iter_fn (const void *item, void *udata)
  * API.
  */
 
-NSMap *
+LSUP_NSMap *
 LSUP_nsmap_new (void)
 {
     return hashmap_new (
@@ -68,12 +66,12 @@ LSUP_nsmap_new (void)
 
 
 void
-LSUP_nsmap_free (NSMap *map)
+LSUP_nsmap_free (LSUP_NSMap *map)
 { hashmap_free (map); }
 
 
 LSUP_rc
-LSUP_nsmap_add (NSMap *map, const char *pfx, const char *nsstr)
+LSUP_nsmap_add (LSUP_NSMap *map, const char *pfx, const char *nsstr)
 {
     NSEntry entry_s = {};
 
@@ -82,33 +80,30 @@ LSUP_nsmap_add (NSMap *map, const char *pfx, const char *nsstr)
                 "Prefix `%s` is longer than the maximum allowed size "
                 "(%d characters). Truncating.", pfx, PFX_LEN - 1);
     strncpy (entry_s.pfx, pfx, PFX_LEN -1);
-    char *ns = strdup (nsstr);
-
-    NSEntry *ret = hashmap_get (map, &entry_s);
-    if (!ret) {
-        entry_s.ns = ns;
-        hashmap_set (map, &entry_s);
-        if (hashmap_oom (map)) return LSUP_MEM_ERR;
-        LOG_DEBUG("Added prefix '%s' to NS map @%p.", entry_s.pfx, map);
-    } else {
+    entry_s.ns = strdup (nsstr);
+
+    const NSEntry *ret = hashmap_delete (map, &entry_s);
+    if (!ret) LOG_DEBUG("Adding prefix '%s' to NS map @%p.", entry_s.pfx, map);
+    else {
         LOG_DEBUG(
                 "Replacing NS '%s' with '%s' for prefix '%s'.",
-                ret->ns, ns, entry_s.pfx);
+                ret->ns, entry_s.ns, entry_s.pfx);
+        // Free replaced NS string.
         free (ret->ns);
-        ret->ns = ns;
     }
-    // Free replaced NS string.
+    hashmap_set (map, &entry_s);
+    if (hashmap_oom (map)) return LSUP_MEM_ERR;
 
     return LSUP_OK;
 }
 
 
 LSUP_rc
-LSUP_nsmap_remove (NSMap *map, const char *pfx)
+LSUP_nsmap_remove (LSUP_NSMap *map, const char *pfx)
 {
     NSEntry entry_s = {};
     strncpy (entry_s.pfx, pfx, PFX_LEN - 1);
-    NSEntry *entry = hashmap_delete (map, &entry_s);
+    const NSEntry *entry = hashmap_delete (map, &entry_s);
 
     if (!entry) return LSUP_NOACTION;
 
@@ -119,22 +114,22 @@ LSUP_nsmap_remove (NSMap *map, const char *pfx)
 
 
 const char *
-LSUP_nsmap_get_ns (const NSMap *map, const char *pfx)
+LSUP_nsmap_get_ns (const LSUP_NSMap *map, const char *pfx)
 {
     NSEntry entry_s = {};
     strncpy (entry_s.pfx, pfx, PFX_LEN - 1);
-    NSEntry *entry = hashmap_get ((NSMap *)map, &entry_s);
+    const NSEntry *entry = hashmap_get ((LSUP_NSMap *)map, &entry_s);
 
     return (entry) ? entry->ns : NULL;
 }
 
 
 const char *
-LSUP_nsmap_get_pfx (const NSMap *map, const char *ns)
+LSUP_nsmap_get_pfx (const LSUP_NSMap *map, const char *ns)
 {
     const NSEntry *entry;
     size_t i = 0;
-    while (hashmap_iter ((NSMap *)map, &i, (void **) &entry)) {
+    while (hashmap_iter ((LSUP_NSMap *)map, &i, (void **) &entry)) {
         if (strncmp (entry->ns, ns, strlen (ns)) == 0)
             return entry->pfx;
     }
@@ -145,7 +140,7 @@ LSUP_nsmap_get_pfx (const NSMap *map, const char *ns)
 
 LSUP_rc
 LSUP_nsmap_normalize_uri (
-        const NSMap *map, const char *pfx_uri, char **fq_uri_p)
+        const LSUP_NSMap *map, const char *pfx_uri, char **fq_uri_p)
 {
     LSUP_rc rc;
     char *fq_uri = NULL;
@@ -162,7 +157,7 @@ LSUP_nsmap_normalize_uri (
     strncpy (pfx, pfx_uri, pfx_len);
     pfx[pfx_len] = '\0';
 
-    const char *ns = LSUP_nsmap_get_ns ((NSMap *)map, pfx);
+    const char *ns = LSUP_nsmap_get_ns ((LSUP_NSMap *)map, pfx);
 
     if (ns) {
         // -1 for :, +1 for terminator.
@@ -192,7 +187,7 @@ no_prefix:
 
 LSUP_rc
 LSUP_nsmap_denormalize_uri (
-        const NSMap *map, const char *fq_uri, char **pfx_uri_p)
+        const LSUP_NSMap *map, const char *fq_uri, char **pfx_uri_p)
 {
     /*
      * This is different from LSUP_nsmap_get_ns, in that the URI being looked
@@ -205,7 +200,7 @@ LSUP_nsmap_denormalize_uri (
     const char *pfx = NULL;
     size_t i = 0, offset;
 
-    while (hashmap_iter ((NSMap *)map, &i, (void **) &entry)) {
+    while (hashmap_iter ((LSUP_NSMap *)map, &i, (void **) &entry)) {
         offset = strlen (entry->ns);
         if (strncmp (entry->ns, fq_uri, offset) == 0) {
             pfx = entry->pfx;
@@ -235,9 +230,9 @@ LSUP_nsmap_denormalize_uri (
 
 
 const char ***
-LSUP_nsmap_dump (const NSMap *map)
+LSUP_nsmap_dump (const LSUP_NSMap *map)
 {
-    size_t i = hashmap_count ((NSMap *) map);
+    size_t i = hashmap_count ((LSUP_NSMap *) map);
 
     const char ***data = malloc (2 * (i + 1) * sizeof (char *));
     if (UNLIKELY (!data)) return NULL;
@@ -248,7 +243,7 @@ LSUP_nsmap_dump (const NSMap *map)
     }
 
     struct dump_iter_t cur = {.i = 0, .data = data};
-    hashmap_scan ((NSMap *) map, nsmap_dump_ns_iter_fn, &cur);
+    hashmap_scan ((LSUP_NSMap *) map, nsmap_dump_ns_iter_fn, &cur);
     data[i] = NULL; // Sentinel
 
     return data;

+ 138 - 3
src/store.c

@@ -74,9 +74,25 @@ LSUP_store_free (LSUP_Store *store)
 }
 
 
+size_t
+LSUP_store_size (const LSUP_Store *store)
+{ return store->sif->size_fn (store->data); }
+
+
+const LSUP_StoreFeature
+LSUP_store_features (const LSUP_Store *store)
+{ return store->sif->features; }
+
+
+char *
+LSUP_store_id (const LSUP_Store *store)
+{ return store->sif->id_fn (store->data); }
+
+
 LSUP_rc
 LSUP_store_begin (LSUP_Store *store, int flags, void **txn) {
-    if (!(store->sif->features & LSUP_STORE_TXN)) return LSUP_VALUE_ERR;
+    if (UNLIKELY (!(store->sif->features & LSUP_STORE_TXN)))
+        return LSUP_NOT_IMPL_ERR;
 
     return store->sif->txn_begin_fn (store->data, flags, txn);
 }
@@ -85,7 +101,8 @@ LSUP_store_begin (LSUP_Store *store, int flags, void **txn) {
 LSUP_rc
 LSUP_store_commit (LSUP_Store *store, void *txn)
 {
-    if (!(store->sif->features & LSUP_STORE_TXN)) return LSUP_VALUE_ERR;
+    if (UNLIKELY (!(store->sif->features & LSUP_STORE_TXN)))
+        return LSUP_NOT_IMPL_ERR;
 
     return store->sif->txn_commit_fn (txn);
 }
@@ -93,4 +110,122 @@ LSUP_store_commit (LSUP_Store *store, void *txn)
 
 void
 LSUP_store_abort (LSUP_Store *store, void *txn)
-{ store->sif->txn_abort_fn (txn); }
+{
+    if (UNLIKELY (!(store->sif->features & LSUP_STORE_TXN))) return;
+
+    store->sif->txn_abort_fn (txn);
+}
+
+
+LSUP_rc
+LSUP_store_update_ctx_txn (
+        LSUP_Store *store, void *txn, const LSUP_Buffer *old_c,
+        const LSUP_Buffer *new_c)
+{
+    // TODO Handle txn better for non-txn stores.
+    if (UNLIKELY (!(store->sif->features & LSUP_STORE_CTX)))
+        return LSUP_NOT_IMPL_ERR;
+
+    return store->sif->update_ctx_fn (store->data, old_c, new_c, txn);
+}
+
+
+void *
+LSUP_store_add_init_txn (
+        LSUP_Store *store, void *txn, const LSUP_Buffer *sc)
+    // TODO Handle txn better for non-txn stores.
+{ return store->sif->add_init_fn (store->data, sc, txn); }
+
+
+LSUP_rc
+LSUP_store_add_iter (
+        LSUP_Store *store, void *it, const LSUP_BufferTriple *sspo)
+{ return store->sif->add_iter_fn (it, sspo); }
+
+
+void
+LSUP_store_add_abort (LSUP_Store *store, void *it)
+{
+    if (
+        UNLIKELY (!(store->sif->features & LSUP_STORE_TXN)) ||
+        UNLIKELY (!store->sif->add_abort_fn)
+    ) return;
+
+    store->sif->add_abort_fn (it);
+}
+
+
+LSUP_rc
+LSUP_store_add_done (LSUP_Store *store, void *it)
+{ return store->sif->add_done_fn (it); }
+
+
+LSUP_rc
+LSUP_store_add_term_txn (LSUP_Store *store, void *txn, LSUP_Buffer *sterm)
+{ return store->sif->add_term_fn (store, sterm, txn); }
+
+
+void *
+LSUP_store_lookup_txn (
+        const LSUP_Store *store, void *txn,
+        const LSUP_Buffer *ss, const LSUP_Buffer *sp, const LSUP_Buffer *so,
+        const LSUP_Buffer *sc, size_t *ct)
+{
+    if (UNLIKELY (!(store->sif->features & LSUP_STORE_CTX)) && sc) {
+        log_warn (
+                "Store does not support contexts. Ignoring passed context.");
+        sc = NULL;
+    }
+
+    return store->sif->lookup_fn (store->data, ss, sp, so, sc, txn, ct); }
+
+
+LSUP_rc LSUP_store_iter_next (
+        LSUP_Store *store, void *it, LSUP_BufferTriple *sspo,
+        LSUP_Buffer **ctx)
+{
+    if (UNLIKELY (!(store->sif->features & LSUP_STORE_CTX)) && ctx) {
+        log_warn (
+                "Store does not support contexts. Ignoring passed context.");
+        ctx = NULL;
+    }
+
+    return store->sif->lu_next_fn (it, sspo, ctx);
+}
+
+
+void
+LSUP_store_iter_free (LSUP_Store *store, void *it)
+{ store->sif->lu_free_fn (it); }
+
+
+void *
+LSUP_store_iter_txn (const LSUP_Store *store, void *it)
+{
+    if (UNLIKELY (!(store->sif->features & LSUP_STORE_TXN))) {
+        log_warn ("Store does not support transactions.");
+        return NULL;
+    }
+
+    return store->sif->iter_txn_fn (it);
+}
+
+
+LSUP_rc
+LSUP_store_remove_txn (
+        LSUP_Store *store, void *txn,
+        const LSUP_Buffer *ss, const LSUP_Buffer *sp, const LSUP_Buffer *so,
+        const LSUP_Buffer *sc, size_t *ct)
+{ return store->sif->remove_fn (store->data, ss, sp, so, sc, txn, ct); }
+
+
+LSUP_Buffer **
+LSUP_store_ctx_list_txn (LSUP_Store *store, void *txn)
+{
+    if (UNLIKELY (!(store->sif->features & LSUP_STORE_CTX))) {
+        log_error ("Store does not support contexts.");
+        return NULL;
+    }
+
+    return store->sif->ctx_list_fn (store->data, txn);
+}

+ 4 - 4
src/store_htable.c

@@ -131,7 +131,7 @@ tkey_to_strp (
 
 
 static LSUP_rc
-htstore_add_key_iter (HTIterator *it, const LSUP_Key *spok);
+add_key_iter (HTIterator *it, const LSUP_Key *spok);
 
 
 /** @brief Advance iterator by next key.
@@ -262,7 +262,7 @@ htstore_add_iter (void *h, const LSUP_BufferTriple *sspo)
         LSUP_buffer_hash (sspo->o),
     };
 
-    LSUP_rc rc = htstore_add_key_iter (it, spok);
+    LSUP_rc rc = add_key_iter (it, spok);
 
     if (rc != LSUP_OK) return rc;
 
@@ -460,7 +460,7 @@ tkey_to_strp (
         const HTStore *store, const LSUP_Key *spok, LSUP_BufferTriple *sspo)
 {
     // Data owned by the store.
-    IndexEntry *tmp;
+    const IndexEntry *tmp;
 
     for (int i = 0; i < 3; i++) {
         tmp = hashmap_get (store->idx, spok + i);
@@ -475,7 +475,7 @@ tkey_to_strp (
 
 
 static LSUP_rc
-htstore_add_key_iter (HTIterator *it, const LSUP_Key *spok)
+add_key_iter (HTIterator *it, const LSUP_Key *spok)
 {
     // Add triple.
     LOG_TRACE("Inserting spok: {%lx, %lx, %lx}", spok[0], spok[1], spok[2]);

+ 3 - 3
src/term.c

@@ -563,7 +563,7 @@ LSUP_term_set_add (LSUP_TermSet *ts, LSUP_Term *term, LSUP_Term **existing)
     LSUP_Hash key = LSUP_term_hash (term);
     KeyedTerm entry_s = {.key=key, .term=term};
 
-    KeyedTerm *ex = hashmap_get (ts, &entry_s);
+    const KeyedTerm *ex = hashmap_get (ts, &entry_s);
     if (ex) {
         if (existing) *existing = ex->term;
         return LSUP_NOACTION;
@@ -579,7 +579,7 @@ LSUP_term_set_add (LSUP_TermSet *ts, LSUP_Term *term, LSUP_Term **existing)
 const LSUP_Term *
 LSUP_term_set_get (LSUP_TermSet *ts, LSUP_Key key)
 {
-    KeyedTerm *entry = hashmap_get (ts, &(KeyedTerm){.key=key});
+    const KeyedTerm *entry = hashmap_get (ts, &(KeyedTerm){.key=key});
     if (entry) LOG_TRACE("ID found for key %lx: %s", key, entry->term->data);
     else LOG_TRACE("No ID found for key %lx.", key);
 
@@ -654,7 +654,7 @@ LSUP_link_map_add (
     // Keyed term to look up the link term and insert it, if necessary.
     KeyedTerm entry_s = {.key=LSUP_term_hash (term), .term=term};
 
-    Link *ex = hashmap_get (lmap->links, &(Link){.term=&entry_s});
+    const Link *ex = hashmap_get (lmap->links, &(Link){.term=&entry_s});
     if (ex) {
         // Add terms one by one to the existing term set.
         LOG_TRACE(

+ 3 - 3
test.c

@@ -22,8 +22,8 @@ int main(int argc, char **argv) {
 
     start = clock();
 
-    int rc = LSUP_init();
-    if (rc != LSUP_OK) return rc;
+    LSUP_rc rc = LSUP_init();
+    RCCK (rc);
 
     if (
         term_tests() ||
@@ -51,6 +51,6 @@ int main(int argc, char **argv) {
     log_info ("");
     log_info ("Run %d tests in %lu ms.", tests_run, (size_t) wallclock);
 
-    return rc;
+    return (int)rc;
 }
 

+ 57 - 63
test/test_store.c

@@ -32,17 +32,17 @@ static int test_triple_store()
     }
 
     // Test adding.
-    void *it = store->sif->add_init_fn (store->data, NULL, NULL);
+    void *it = LSUP_store_add_init (store, NULL);
     size_t ins = 0;
     for (size_t i = 0; i < NUM_TRP; i++) {
-        LSUP_rc rc = store->sif->add_iter_fn (it, ser_trp + i);
+        LSUP_rc rc = LSUP_store_add_iter (store, it, ser_trp + i);
         ASSERT (rc >= 0, "Error inserting triples!");
         if (rc == LSUP_OK) ins++;
     }
-    store->sif->add_done_fn (it);
+    LSUP_store_add_done (store, it);
 
     EXPECT_INT_EQ (ins, 8);
-    EXPECT_INT_EQ (store->sif->size_fn (store->data), ins);
+    EXPECT_INT_EQ (LSUP_store_size (store), ins);
 
     // Test lookups.
     LSUP_Buffer *lut[12][3] = {
@@ -86,11 +86,11 @@ static int test_triple_store()
 
         5,
         // Lookup on nonexisting context is ignored by non-context store.
-        store->sif->features & LSUP_STORE_CTX ? 0 : 1,
+        LSUP_store_features (store) & LSUP_STORE_CTX ? 0 : 1,
         1, 0, 1,
 
         2, 1, 2,
-        store->sif->features & LSUP_STORE_CTX ? 0 : 2,
+        LSUP_store_features (store) & LSUP_STORE_CTX ? 0 : 2,
 
         1, 0,
     };
@@ -99,13 +99,12 @@ static int test_triple_store()
         size_t ct;
         log_info ("Testing triple lookup #%d.", i);
 
-        void *it = store->sif->lookup_fn (
-                store->data, lut[i][0], lut[i][1], lut[i][2], luc[i],
-                NULL, &ct);
+        void *it = LSUP_store_lookup (
+                store, lut[i][0], lut[i][1], lut[i][2], luc[i], &ct);
         ASSERT (it != NULL, "Error creating iterator!");
         EXPECT_INT_EQ (ct, results[i]);
 
-        store->sif->lu_free_fn (it);
+        LSUP_store_iter_free (store, it);
     }
 
     for (int i = 0; i < NUM_TRP; i++) {
@@ -131,7 +130,7 @@ static int test_quad_store()
 {
     LSUP_Store *store = LSUP_store_new (store_type, store_id, 0, true);
     ASSERT (store != NULL, "Error creating store!"); \
-    if (!(store->sif->features & LSUP_STORE_TXN)) {
+    if (!(LSUP_store_features (store) & LSUP_STORE_TXN)) {
         LSUP_store_free (store);
         return 0;
     }
@@ -152,15 +151,15 @@ static int test_quad_store()
     size_t ins;
 
     // Only triples 0÷5 in default context.
-    it = store->sif->add_init_fn (store->data, NULL, NULL);
+    it = LSUP_store_add_init_txn (store, NULL, NULL);
     ins = 0;
     for (size_t i = 0; i < 6; i++) {
         log_info ("Inserting triple #%d in default context.", i);
-        LSUP_rc rc = store->sif->add_iter_fn (it, ser_trp + i);
+        LSUP_rc rc = LSUP_store_add_iter (store, it, ser_trp + i);
         EXPECT_PASS (rc);
         if (rc == LSUP_OK) ins++;
     }
-    store->sif->add_done_fn (it);
+    LSUP_store_add_done (store, it);
     EXPECT_INT_EQ (ins, 6);
 
     LSUP_Buffer *sc1 = LSUP_default_ctx_buf;
@@ -169,20 +168,20 @@ static int test_quad_store()
     LSUP_Buffer *sc2 = LSUP_term_serialize (ctx2);
 
     // Only triples 4÷9 in context 2 (effectively 4 non-duplicates).
-    it = store->sif->add_init_fn (store->data, sc2, NULL);
+    it = LSUP_store_add_init (store, sc2);
     ASSERT (it != NULL, "Error creating iterator!");
     ins = 0;
     for (size_t i = 4; i < 10; i++) {
         log_info ("Inserting triple #%d in context 2.", i);
-        LSUP_rc rc = store->sif->add_iter_fn (it, ser_trp + i);
+        LSUP_rc rc = LSUP_store_add_iter (store, it, ser_trp + i);
         ASSERT (rc == LSUP_OK || rc == LSUP_NOACTION, "Error adding triples!");
         if (rc == LSUP_OK) ins++;
     }
-    store->sif->add_done_fn (it);
+    LSUP_store_add_done (store, it);
     EXPECT_INT_EQ (ins, 4);
 
     // 6 triples in ctx1 + 6 in ctx2 - 2 duplicates
-    EXPECT_INT_EQ (store->sif->size_fn (store->data), 10);
+    EXPECT_INT_EQ (LSUP_store_size (store), 10);
 
     // This context has no triples.
     LSUP_Term *ctx3 = LSUP_iriref_new("urn:c:3", NULL);
@@ -320,24 +319,23 @@ static int test_quad_store()
         size_t ct;
 
         log_info ("Checking triple #%d.", i);
-        void *it = store->sif->lookup_fn (
-                store->data, lut[i][0], lut[i][1], lut[i][2], luc[i],
-                NULL, &ct);
+        void *it = LSUP_store_lookup (
+                store, lut[i][0], lut[i][1], lut[i][2], luc[i], &ct);
         ASSERT (it != NULL, "Lookup error!");
         EXPECT_INT_EQ (ct, results[i]);
 
-        store->sif->lu_free_fn (it);
+        LSUP_store_iter_free (store, it);
     }
 
     // Check triple contexts.
     for (int i = 0; i < 10; i++) {
-        void *it = store->sif->lookup_fn (
-                store->data, ser_trp[i].s, ser_trp[i].p, ser_trp[i].o,
-                NULL, NULL, NULL);
+        void *it = LSUP_store_lookup_txn (
+                store, NULL, ser_trp[i].s, ser_trp[i].p, ser_trp[i].o,
+                NULL, NULL);
         log_info ("Checking contexts for triple %d.", i);
         LSUP_Buffer *ctx_a;
-        EXPECT_PASS (store->sif->lu_next_fn (it, NULL, &ctx_a));
-        store->sif->lu_free_fn (it);
+        EXPECT_PASS (LSUP_store_iter_next (store, it, NULL, &ctx_a));
+        LSUP_store_iter_free (store, it);
 
         ASSERT (ctx_a != NULL, "No contexts found!");
 
@@ -369,7 +367,7 @@ static int test_txn_commit (void)
 {
     LSUP_Store *store = LSUP_store_new (store_type, store_id, 0, true);
     ASSERT (store != NULL, "Error creating store!"); \
-    if (!(store->sif->features & LSUP_STORE_TXN)) {
+    if (!(LSUP_store_features (store) & LSUP_STORE_TXN)) {
         LSUP_store_free (store);
         return 0;
     }
@@ -402,44 +400,42 @@ static int test_txn_commit (void)
     }
 
     void *it;
-    // Start adding then commit.
-    it = store->sif->add_init_fn (store->data, NULL, NULL);
+    // Start adding then abort.
+    it = LSUP_store_add_init_txn (store, NULL, NULL);
     for (size_t i = 0; i < NUM_TRP; i++) {
-        LSUP_rc rc = store->sif->add_iter_fn (it, ser_trp + i);
+        LSUP_rc rc = LSUP_store_add_iter (store, it, ser_trp + i);
         ASSERT (rc >= 0, "Error inserting triples!");
     }
-    store->sif->add_abort_fn (it);
+    LSUP_store_add_abort (store, it);
 
-    EXPECT_INT_EQ (store->sif->size_fn (store->data), 0);
+    EXPECT_INT_EQ (LSUP_store_size (store), 0);
 
     // Add within a transaction, commit, then commit parent transaction.
     void *txn;
-    EXPECT_PASS (store->sif->txn_begin_fn (store->data, 0, &txn));
-    it = store->sif->add_init_fn (store->data, NULL, txn);
+    EXPECT_PASS (LSUP_store_begin (store, 0, &txn));
+    it = LSUP_store_add_init_txn (store, txn, NULL);
     for (size_t i = 0; i < NUM_TRP; i++) {
-        LSUP_rc rc = store->sif->add_iter_fn (it, ser_trp + i);
+        LSUP_rc rc = LSUP_store_add_iter (store, it, ser_trp + i);
         ASSERT (rc >= 0, "Error inserting triples!");
     }
-    store->sif->add_done_fn (it);
+    LSUP_store_add_done (store, it);
 
     // Triples are added in child txn but parent is still open.
     // Size function always calculates outside of all transactions.
-    EXPECT_INT_EQ (store->sif->size_fn (store->data), 0);
+    EXPECT_INT_EQ (LSUP_store_size (store), 0);
 
     size_t ct;
-    it = store->sif->lookup_fn (
-            store->data, NULL, NULL, NULL, NULL, txn, &ct);
-    store->sif->lu_free_fn (it);
+    it = LSUP_store_lookup_txn (store, txn, NULL, NULL, NULL, NULL, &ct);
+    LSUP_store_iter_free (store, it);
     // Should show triples added within the parent txn.
     EXPECT_INT_EQ (ct, 8);
 
     // commit child txn operations.
-    EXPECT_PASS (store->sif->txn_commit_fn (txn));
-    it = store->sif->lookup_fn (
-            store->data, NULL, NULL, NULL, NULL, NULL, &ct);
-    store->sif->lu_free_fn (it);
+    EXPECT_PASS (LSUP_store_commit (store, txn));
+    it = LSUP_store_lookup (store, NULL, NULL, NULL, NULL, &ct);
+    LSUP_store_iter_free (store, it);
     EXPECT_INT_EQ (ct, 8);
-    EXPECT_INT_EQ (store->sif->size_fn (store->data), 8);
+    EXPECT_INT_EQ (LSUP_store_size (store), 8);
 
     for (int i = 0; i < NUM_TRP; i++) {
         LSUP_buffer_free (ser_trp[i].s);
@@ -458,7 +454,7 @@ static int test_txn_abort (void)
 {
     LSUP_Store *store = LSUP_store_new (store_type, store_id, 0, true);
     ASSERT (store != NULL, "Error creating store!"); \
-    if (!(store->sif->features & LSUP_STORE_TXN)) {
+    if (!(LSUP_store_features (store) & LSUP_STORE_TXN)) {
         LSUP_store_free (store);
         return 0;
     }
@@ -476,41 +472,39 @@ static int test_txn_abort (void)
 
     void *it;
     // Start adding then abort.
-    it = store->sif->add_init_fn (store->data, NULL, NULL);
+    it = LSUP_store_add_init (store, NULL);
     for (size_t i = 0; i < NUM_TRP; i++) {
-        LSUP_rc rc = store->sif->add_iter_fn (it, ser_trp + i);
+        LSUP_rc rc = LSUP_store_add_iter (store, it, ser_trp + i);
         ASSERT (rc >= 0, "Error inserting triples!");
     }
-    store->sif->add_abort_fn (it);
+    LSUP_store_add_abort (store, it);
 
-    EXPECT_INT_EQ (store->sif->size_fn (store->data), 0);
+    EXPECT_INT_EQ (LSUP_store_size (store), 0);
 
     // Add within a transaction, commit, then abort parent transaction.
     void *txn;
-    EXPECT_PASS (store->sif->txn_begin_fn (store->data, 0, &txn));
-    it = store->sif->add_init_fn (store->data, NULL, txn);
+    EXPECT_PASS (LSUP_store_begin (store, 0, &txn));
+    it = LSUP_store_add_init_txn (store, txn, NULL);
     for (size_t i = 0; i < NUM_TRP; i++) {
-        LSUP_rc rc = store->sif->add_iter_fn (it, ser_trp + i);
+        LSUP_rc rc = LSUP_store_add_iter (store, it, ser_trp + i);
         ASSERT (rc >= 0, "Error inserting triples!");
     }
-    store->sif->add_done_fn (it);
+    LSUP_store_add_done (store, it);
 
     // Triples are added in child txn but parent is still open.
     // Size function always calculates outside of all transactions.
-    EXPECT_INT_EQ (store->sif->size_fn (store->data), 0);
+    EXPECT_INT_EQ (LSUP_store_size (store), 0);
 
     size_t ct;
-    it = store->sif->lookup_fn (
-            store->data, NULL, NULL, NULL, NULL, txn, &ct);
-    store->sif->lu_free_fn (it);
+    it = LSUP_store_lookup_txn (store, txn, NULL, NULL, NULL, NULL, &ct);
+    LSUP_store_iter_free (store, it);
     // Should show triples added within the parent txn.
     EXPECT_INT_EQ (ct, 8);
 
     // Discard child txn operations.
-    store->sif->txn_abort_fn (txn);
-    it = store->sif->lookup_fn (
-            store->data, NULL, NULL, NULL, NULL, NULL, &ct);
-    store->sif->lu_free_fn (it);
+    LSUP_store_abort (store, txn);
+    it = LSUP_store_lookup (store, NULL, NULL, NULL, NULL, &ct);
+    LSUP_store_iter_free (store, it);
     EXPECT_INT_EQ (ct, 0);
 
     for (int i = 0; i < NUM_TRP; i++) {