Jelajahi Sumber

Implement all store ops as functions in store module.

scossu 4 hari lalu
induk
melakukan
c1c7673995
4 mengubah file dengan 518 tambahan dan 234 penghapusan
  1. 280 1
      include/lsup/store.h
  2. 43 167
      include/lsup/store_interface.h
  3. 138 3
      src/store.c
  4. 57 63
      test/test_store.c

+ 280 - 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,270 @@ 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] 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.
+ */
+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] 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.
+ */
+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] 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.
+ */
+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 #store_add_init_fn_t.
+ */
+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] sterm Serialized term to store.
+ *
+ * @param[in] udata Implementation-defined data. See individual
+ *  implementations' documentation.
+ */
+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] 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.
+ */
+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] 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] 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] 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.
+ *
+ * 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.
+ */
+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.
+ *
+ * @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,
 

+ 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);
+}

+ 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++) {