瀏覽代碼

Add nsm_update for store interface; rework store tests.

scossu 1 天之前
父節點
當前提交
493c1571ae
共有 9 個文件被更改,包括 161 次插入126 次删除
  1. 1 1
      Makefile
  2. 27 12
      include/lsup/core.h
  3. 9 1
      include/lsup/namespace.h
  4. 19 25
      include/lsup/store_interface.h
  5. 1 1
      src/graph.c
  6. 1 0
      src/namespace.c
  7. 50 43
      src/store_mdb.c
  8. 21 8
      test/test_graph.c
  9. 32 35
      test/test_store.c

+ 1 - 1
Makefile

@@ -220,7 +220,7 @@ memcheck:
 	valgrind \
 	--leak-check=full --show-leak-kinds=all --track-origins=yes \
 	--log-file=$(VALGRIND_DUMP) \
-	./bin/test
+	./bin/test || true
 	@echo "Memcheck complete. Valgrind log is at $(VALGRIND_DUMP)"
 
 

+ 27 - 12
include/lsup/core.h

@@ -268,7 +268,7 @@ LSUP_strerror (LSUP_rc rc);
  */
 #define LOG_RC(rc) do {                                             \
     if ((rc) < 0) log_error (LSUP_strerror (rc));                   \
-    else if ((rc) > 0) LOG_DEBUG (LSUP_strerror (rc));               \
+    else if ((rc) > 0) log_warn (LSUP_strerror (rc));              \
 } while (0);
 
 /// Jump to `marker` if `exp` does not return `LSUP_OK`.
@@ -276,8 +276,9 @@ LSUP_strerror (LSUP_rc rc);
     LSUP_rc _rc = (exp);                                            \
     LOG_RC(_rc);                                                    \
     if (UNLIKELY (_rc != LSUP_OK)) {                                \
-        log_error ("*** PREMATURE EXIT due to error:");             \
-        LOG_RC(_rc);                                                \
+        log_error (                                                 \
+                "*** PREMATURE EXIT due to error: %s",              \
+                LSUP_strerror (_rc));                               \
         goto marker;                                                \
     }                                                               \
 } while (0);
@@ -286,18 +287,29 @@ LSUP_strerror (LSUP_rc rc);
 #define PCHECK(exp, marker) do {                                    \
     LSUP_rc _rc = (exp);                                            \
     if (UNLIKELY (_rc < LSUP_OK)) {                                 \
-        log_error ("*** PREMATURE EXIT due to error:");             \
+        log_error (                                                 \
+                "*** PREMATURE EXIT due to error: %s",              \
+                LSUP_strerror (_rc));                               \
         LOG_RC(_rc);                                                \
         goto marker;                                                \
     }                                                               \
 } while (0);
 
+/// Jump to `marker` if `exp` returns a negative value (skip warnings).
+#define NLCHECK(exp, marker) do {                                   \
+    if (UNLIKELY ((exp) == NULL)) {                                 \
+        log_error ("*** PREMATURE EXIT due to NULL result.");       \
+        goto marker;                                                \
+    }                                                               \
+} while (0);
+
 /// Return `exp` return value if it is of `LSUP_rc` type and nonzero.
 #define RCCK(exp) do {                                              \
     LSUP_rc _rc = (exp);                                            \
     if (UNLIKELY (_rc != LSUP_OK)) {                                \
-        log_error ("*** PREMATURE EXIT due to error:");             \
-        LOG_RC(_rc);                                                \
+        log_error (                                                 \
+                "*** PREMATURE EXIT due to error: %s",              \
+                LSUP_strerror (_rc));                               \
         return _rc;                                                 \
     }                                                               \
 } while (0);
@@ -306,8 +318,9 @@ LSUP_strerror (LSUP_rc rc);
 #define PRCCK(exp) do {                                             \
     LSUP_rc _rc = (exp);                                            \
     if (UNLIKELY (_rc < LSUP_OK)) {                                 \
-        log_error ("*** PREMATURE EXIT due to error:");             \
-        LOG_RC(_rc);                                                \
+        log_error (                                                 \
+                "*** PREMATURE EXIT due to error: %s",              \
+                LSUP_strerror (_rc));                               \
         return _rc;                                                 \
     }                                                               \
 } while (0);
@@ -316,8 +329,9 @@ LSUP_strerror (LSUP_rc rc);
 #define RCNL(exp) do {                                              \
     LSUP_rc _rc = (exp);                                            \
     if (UNLIKELY (_rc != LSUP_OK)) {                                \
-        log_error ("*** PREMATURE EXIT due to error:");             \
-        LOG_RC(_rc);                                                \
+        log_error (                                                 \
+                "*** PREMATURE EXIT due to error: %s",              \
+                LSUP_strerror (_rc));                               \
         return NULL;                                                \
     }                                                               \
 } while (0);
@@ -326,8 +340,9 @@ LSUP_strerror (LSUP_rc rc);
 #define PRCNL(exp) do {                                             \
     LSUP_rc _rc = (exp);                                            \
     if (UNLIKELY (_rc < LSUP_OK)) {                                 \
-        log_error ("*** PREMATURE EXIT due to error:");             \
-        LOG_RC(_rc);                                                \
+        log_error (                                                 \
+                "*** PREMATURE EXIT due to error: %s",              \
+                LSUP_strerror (_rc));                               \
         return NULL;                                                \
     }                                                               \
 } while (0);

+ 9 - 1
include/lsup/namespace.h

@@ -150,7 +150,15 @@ LSUP_nsmap_denormalize_uri (
  *
  * @return 2-dimensional array of strings, with as many rows as namespace
  *  entries, and two columns: the first for the namespace prefix, the second
- *  for the namespace. The last entry is NULL.
+ *  for the namespace. The last entry is NULL. Prefix and namespace strings
+ *  pointed to by the array are owned by the namespace map, but the individual
+ *  pointers must be freed along with the parent array. E.g.:
+ *
+ *      const char ***nsm_data = LSUP_nsmap_dump (nsm);
+ *      // [...]
+ *      for (size_t i = 0; nsm_data[i] != NULL; i++)
+ *          free (nsm_data[i]);
+ *      free (nsm_data);
  */
 const char ***
 LSUP_nsmap_dump (const LSUP_NSMap *map);

+ 19 - 25
include/lsup/store_interface.h

@@ -58,6 +58,7 @@ typedef enum {
     LSUP_STORE_IDX      = 1<<2,   ///< Store is fully SPO(C)-indexed.
     LSUP_STORE_TXN      = 1<<3,   ///< Supports transaction handling.
     LSUP_STORE_COW      = 1<<4,   ///< Copy on write. @sa #iter_next_fn_t()
+    LSUP_STORE_OWN_NSM  = 1<<5,   ///< Store has its own namespace map.
 } LSUP_StoreFeature;
 
 
@@ -363,34 +364,27 @@ typedef LSUP_rc (*store_remove_fn_t)(
         const LSUP_Buffer *sc, void *udata, size_t *ct);
 
 
-/** @brief Put an in-memory namespace map into a permanent back end.
+/** @brief Update the namespace map of a store.
  *
- * 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#>
+ * This is only available in stores with the LSUP_STORE_OWN_NSM feature.
  *
  * @param[in] store Store back end to update.
  *
- * @param[out] nsm Namespace map handle to store.
+ * @param[in] pfx Prefix to update. If the key is already existing, it is
+ *  updated or deleted (see `ns` parameter).
+ *
+ * @param[in] ns Namespace to relate to the prefix. If this value is NULL, the
+ *  prefix mapping is removed, if existing, or otherwise quietly skipped.
  *
- * @param[in] udata User-defined data. Consult individual implementations for
- *  details.
+ * @param[in] udata Opaque implementation-specific data handler. Consult each
+ *  implementation for details.
  *
- * @return LSUP_OK if all terms were updated; LSUP_CONFLICT if one or more
- *  namespaces or terms were not updated because they already existed; <0 if
- *  an error occurred.
+ * @return LSUP_OK if a mapping was added, updated, or deleted; LSUP_NOACTION
+ * if NULL was passed for `ns` and no corresponding prefix was found to delete;
+ * <0 if an error occurred.
  */
-typedef LSUP_rc (*store_nsm_put_fn_t)(
-        void *store, const LSUP_NSMap * nsm, void *udata);
+typedef LSUP_rc (*store_nsm_update_fn_t)(
+        void *store, const char *pfx, const char *ns, void *udata);
 
 
 /** @brief Get the store's namespace prefix map.
@@ -528,16 +522,16 @@ typedef struct store_if_t {
     store_remove_fn_t   remove_fn;      ///< Remove triples by pattern.
 
     // Namespace prefix mapping.
-    store_nsm_put_fn_t  nsm_put_fn;     ///< Add a ns/pfx pair to the map.
+    store_nsm_update_fn_t  nsm_update_fn; ///< Add a ns/pfx pair to the map.
                                         ///<
                                         ///< Only available (and mandatory)
                                         ///< in stores with the
-                                        ///< #LSUP_STORE_IDX feature.
+                                        ///< #LSUP_STORE_OWN_NSM feature.
     store_nsm_get_fn_t  nsm_get_fn;     ///< Get the store's namespace map.
                                         ///<
                                         ///< Only available (and mandatory)
                                         ///< in stores with the
-                                        ///< #LSUP_STORE_IDX feature.
+                                        ///< #LSUP_STORE_OWN_NSM feature.
     store_ctx_list_fn_t ctx_list_fn;    ///< Get all context URIs in a store.
                                         ///<
                                         ///< Only applicable to stores with
@@ -579,7 +573,7 @@ const LSUP_StoreInt my_store_int = {
 
     .remove_fn      = my_remove_fn,
 
-    .nsm_put_fn     = my_nsm_put_fn,
+    .nsm_update_fn  = my_nsm_update_fn,
     .nsm_get_fn     = my_nsm_get_fn,
 
     .ctx_list_fn   = my_ctx_list_fn,

+ 1 - 1
src/graph.c

@@ -51,7 +51,7 @@ LSUP_graph_new (LSUP_Store *store, const char *uri_str, LSUP_NSMap *nsm)
     gr->store = (store) ? store : LSUP_store_new (
             LSUP_STORE_HTABLE, NULL, 0, false);
 
-    if (gr->store->sif->nsm_get_fn)
+    if (gr->store->sif->features & LSUP_STORE_OWN_NSM)
         gr->nsm = gr->store->sif->nsm_get_fn (gr->store->data);
     else gr->nsm = nsm ? nsm : LSUP_default_nsm;
 

+ 1 - 0
src/namespace.c

@@ -41,6 +41,7 @@ static void nsmap_free_fn (void *item)
 { free (((NSEntry *) item)->ns); }
 
 
+// TODO expose in API as first-class iterator function.
 static bool nsmap_dump_ns_iter_fn (const void *item, void *udata)
 {
     const NSEntry *entry = item;

+ 50 - 43
src/store_mdb.c

@@ -256,7 +256,7 @@ finally:
 
 
 static LSUP_rc
-nsm_put (MDB_env *env, MDB_txn *p_txn, const LSUP_NSMap *nsm)
+nsm_update (MDB_env *env, const char *pfx, const char *ns, MDB_txn *p_txn)
 {
     LSUP_rc rc = LSUP_NOACTION;
     int db_rc;
@@ -274,53 +274,43 @@ nsm_put (MDB_env *env, MDB_txn *p_txn, const LSUP_NSMap *nsm)
     RCCK (mdb_cursor_open (txn, idbi, &icur));
 
     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 *) pfx;
+    pfx_v.mv_size = strlen (pfx) + 1;
 
-        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 (ns) {
+        // Add or update prefix mapping.
+        ns_v.mv_data = (void *) ns;
+        ns_v.mv_size = strlen (ns) + 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;
-        }
+        CHECK ((rc = mdb_cursor_put (dcur, &pfx_v, &ns_v, 0)), fail);
+        CHECK ((rc = mdb_cursor_put (icur, &ns_v, &pfx_v, 0)), fail);
 
-        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]);
+    } else {
+        // Delete prefix.
+        if (mdb_cursor_get (dcur, &pfx_v, &ns_v, MDB_SET_KEY) == MDB_NOTFOUND)
+            goto success;
+        CHECK ((rc = mdb_cursor_del (dcur, 0)), fail);
+        if (mdb_cursor_get (icur, &ns_v, &pfx_v, MDB_GET_BOTH) == MDB_NOTFOUND)
+            goto success;
+        CHECK ((rc = mdb_cursor_del (icur, 0)), fail);
     }
-    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;
-    }
+success:
+    CHECK (mdb_txn_commit (txn), fail);
+
+    return rc;
+
+fail:
+    mdb_txn_abort (txn);
 
     return rc;
 }
 
 
 static LSUP_rc
-mdbstore_nsm_put (void *h, const LSUP_NSMap *nsm, void *th)
-{ return nsm_put (((MDBStore *)h)->env, (MDB_txn *)th, nsm); }
+mdbstore_nsm_update (void *h, const char *pfx, const char *ns, void *th)
+{ return nsm_update (((MDBStore *)h)->env, pfx, ns, (MDB_txn *)th); }
 
 
 static const char *
@@ -384,10 +374,22 @@ mdbstore_setup (const char *id, bool clear)
     CHECK (mdb_dbi_open (
                 txn, db_labels[IDX_PFX_NS], db_flags[IDX_PFX_NS], &dbi), fail);
     CHECK (mdb_stat (txn, dbi, &stat), fail);
+
+    const char ***nsm_data = NULL;
     if (stat.ms_entries == 0) {
-        LOG_DEBUG ("Loading initial data into %s", id);
+        LOG_DEBUG ("Loading initial data into %s", path);
         // Load initial NS map.
-        PCHECK (nsm_put (env, txn, LSUP_default_nsm), fail);
+        nsm_data = LSUP_nsmap_dump (LSUP_default_nsm);
+        if (UNLIKELY (!nsm_data)) {
+            rc = LSUP_ERROR;
+            goto loop_fail;
+        }
+        for (size_t i = 0; nsm_data[i] != NULL; i++)
+            PCHECK (nsm_update (
+                        env, nsm_data[i][0], nsm_data[i][1], txn), loop_fail);
+        for (size_t i = 0; nsm_data[i] != NULL; i++)
+            free (nsm_data[i]);
+        free (nsm_data);
 
         // Index default context.
         CHECK (mdb_dbi_open (
@@ -412,7 +414,12 @@ mdbstore_setup (const char *id, bool clear)
 
     return clear ? LSUP_OK : rc;
 
+loop_fail:
+    for (size_t i = 0; nsm_data[i] != NULL; i++)
+        free (nsm_data[i]);
+    free (nsm_data);
 fail:
+    if (rc >= 0) rc = LSUP_DB_ERR;
     mdb_txn_abort (txn);
     return rc;
 }
@@ -445,9 +452,9 @@ mdbstore_new (const char *id, size_t _unused)
     size_t mapsize;
     char *env_mapsize = getenv ("LSUP_MDB_MAPSIZE");
     if (env_mapsize == NULL) mapsize = DEFAULT_MAPSIZE;
-    else sscanf (env_mapsize, "%lu", &mapsize);
+    else sscanf (env_mapsize, "%zu", &mapsize);
     log_info (
-            "Setting environment map size at %s to %lu Mb.",
+            "Setting environment map size at %s to %zu Mb.",
             path, mapsize / 1024 / 1024);
     CHECK (mdb_env_set_mapsize (store->env, mapsize), fail);
     CHECK (mdb_env_set_maxdbs (store->env, N_DB), fail);
@@ -1337,7 +1344,7 @@ fail:
 const LSUP_StoreInt mdbstore_int = {
     .name           = "MDB Store",
     .features       = LSUP_STORE_PERM | LSUP_STORE_CTX | LSUP_STORE_IDX
-                      | LSUP_STORE_TXN | LSUP_STORE_COW,
+                      | LSUP_STORE_TXN | LSUP_STORE_COW | LSUP_STORE_OWN_NSM,
 
     .setup_fn       = mdbstore_setup,
     .new_fn         = mdbstore_new,
@@ -1364,10 +1371,10 @@ const LSUP_StoreInt mdbstore_int = {
 
     .remove_fn      = mdbstore_remove,
 
-    .nsm_put_fn     = mdbstore_nsm_put,
+    .nsm_update_fn  = mdbstore_nsm_update,
     .nsm_get_fn     = mdbstore_nsm_get,
 
-    .ctx_list_fn   = mdbstore_ctx_list,
+    .ctx_list_fn    = mdbstore_ctx_list,
 };
 
 

+ 21 - 8
test/test_graph.c

@@ -43,18 +43,31 @@ _graph_ns_uri (LSUP_StoreType type)
 {
     const LSUP_StoreInt *sif = LSUP_store_int (type);
 
-    LSUP_NSMap *nsm = LSUP_nsmap_new();
-    LSUP_nsmap_add (nsm, "ns1", "urn:ns1#");
-    LSUP_nsmap_add (nsm, "ns2", "urn:ns2#");
-    LSUP_nsmap_add (nsm, "ns3", "urn:ns3#");
+    const char *nsdata[][2] = {
+        {"ns1", "urn:ns1#"},
+        {"ns2", "urn:ns2#"},
+        {"ns3", "urn:ns3#"},
+        {NULL}
+    };
 
-    LSUP_Graph *gr;
     LSUP_Store *store = NULL;
-    if (sif->features & LSUP_STORE_PERM) {
+    if (sif->features & LSUP_STORE_PERM)
         store = LSUP_store_new (type, NULL, 0, true);
-        store->sif->nsm_put_fn (store->data, nsm, NULL);
+
+    log_info("Testing NSM for graph store type: %d", type);
+
+    LSUP_NSMap *nsm = LSUP_nsmap_new();
+    if (sif->features & LSUP_STORE_OWN_NSM) {
+        for (int i = 0; nsdata[i][0]; i++) {
+            log_debug ("ns i: %d", i);
+            EXPECT_PASS (store->sif->nsm_update_fn (
+                    store->data, nsdata[i][0], nsdata[i][1], NULL));
+        }
+    } else {
+        for (int i = 0; nsdata[i][0]; i++)
+            EXPECT_PASS (LSUP_nsmap_add (nsm, nsdata[i][0], nsdata[i][1]));
     }
-    gr = LSUP_graph_new (store, "ns1:gr1", nsm);
+    LSUP_Graph *gr = LSUP_graph_new (store, "ns1:gr1", nsm);
     ASSERT (gr != NULL, "Error creating graph!");
 
     ASSERT (

+ 32 - 35
test/test_store.c

@@ -3,11 +3,11 @@
 #include "test.h"
 
 
-#define STORE_ID_MDB "file:///tmp/testdb";
-#define STORE_ID_HTABLE LSUP_NS "dummy";
+#define STORE_ID_MDB "file:///tmp/testdb"
+#define STORE_ID_HTABLE LSUP_NS "dummy"
 
-static LSUP_Store store_s;
-static LSUP_Store *store = &store_s;
+static LSUP_StoreType store_type;
+static char *store_id;
 
 
 /** @brief Test triple store.
@@ -17,12 +17,10 @@ static LSUP_Store *store = &store_s;
  */
 static int test_triple_store()
 {
-    if (store->sif->setup_fn)
-        EXPECT_PASS (store->sif->setup_fn (store->id, true));
+    LSUP_Store *store = LSUP_store_new (store_type, store_id, 0, true);
+    ASSERT (store != NULL, "Error creating store!"); \
 
     log_info ("Testing triple store for %s", store->id);
-    store->data = store->sif->new_fn (store->id, 0);
-    ASSERT (store->data != NULL, "Error creating store data back end!");
 
     LSUP_Triple **trp = create_triples();
     LSUP_BufferTriple ser_trp[NUM_TRP];
@@ -116,8 +114,8 @@ static int test_triple_store()
         LSUP_buffer_free (ser_trp[i].o);
     }
 
-    store->sif->free_fn (store->data);
     free_triples (trp);
+    LSUP_store_free (store);
 
     return 0;
 }
@@ -131,16 +129,16 @@ static int test_triple_store()
  */
 static int test_quad_store()
 {
-    if (!(store->sif->features & LSUP_STORE_CTX)) return 0;
+    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)) {
+        LSUP_store_free (store);
+        return 0;
+    }
 
-    if (store->sif->setup_fn)
-        EXPECT_PASS (store->sif->setup_fn (store->id, true));
 
     log_info ("Testing quad store for %s", store->id);
 
-    store->data = store->sif->new_fn (store->id, 0);
-    ASSERT (store->data != NULL, "Error creating store data back end!");
-
     LSUP_Triple **trp = create_triples();
     LSUP_BufferTriple ser_trp[NUM_TRP];
 
@@ -361,8 +359,7 @@ static int test_quad_store()
     LSUP_buffer_free (sc2);
     LSUP_buffer_free (sc3);
     free_triples (trp);
-
-    store->sif->free_fn (store->data);
+    LSUP_store_free (store);
 
     return 0;
 }
@@ -370,7 +367,12 @@ static int test_quad_store()
 
 static int test_txn_commit (void)
 {
-    if (!(store->sif->features & LSUP_STORE_TXN)) return 0;
+    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)) {
+        LSUP_store_free (store);
+        return 0;
+    }
 
     ASSERT (
             store->sif->txn_begin_fn != NULL,
@@ -388,12 +390,7 @@ static int test_txn_commit (void)
             store->sif->iter_txn_fn != NULL,
             "Iterator transaction function is NULL!");
 
-    if (store->sif->setup_fn)
-        EXPECT_PASS (store->sif->setup_fn (store->id, true));
-
     log_info ("Testing transaction control for %s", store->id);
-    store->data = store->sif->new_fn (store->id, 0);
-    ASSERT (store->data != NULL, "Error creating store data back end!");
 
     LSUP_Triple **trp = create_triples();
     LSUP_BufferTriple ser_trp[NUM_TRP];
@@ -450,8 +447,8 @@ static int test_txn_commit (void)
         LSUP_buffer_free (ser_trp[i].o);
     }
 
-    store->sif->free_fn (store->data);
     free_triples (trp);
+    LSUP_store_free (store);
 
     return 0;
 }
@@ -459,14 +456,14 @@ static int test_txn_commit (void)
 
 static int test_txn_abort (void)
 {
-    if (!(store->sif->features & LSUP_STORE_TXN)) return 0;
-
-    if (store->sif->setup_fn)
-        EXPECT_PASS (store->sif->setup_fn (store->id, true));
+    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)) {
+        LSUP_store_free (store);
+        return 0;
+    }
 
     log_info ("Testing transaction control for %s", store->id);
-    store->data = store->sif->new_fn (store->id, 0);
-    ASSERT (store->data != NULL, "Error creating store data back end!");
 
     LSUP_Triple **trp = create_triples();
     LSUP_BufferTriple ser_trp[NUM_TRP];
@@ -522,8 +519,8 @@ static int test_txn_abort (void)
         LSUP_buffer_free (ser_trp[i].o);
     }
 
-    store->sif->free_fn (store->data);
     free_triples (trp);
+    LSUP_store_free (store);
 
     return 0;
 }
@@ -532,13 +529,13 @@ static int test_txn_abort (void)
 int store_tests()
 {
 #define ENTRY(a, b) \
-    store->type = LSUP_STORE_##a; \
-    store->id = STORE_ID_##a; \
-    store->sif = &b; \
+    store_type = LSUP_STORE_##a; \
+    store_id = STORE_ID_##a; \
     RUN (test_triple_store); \
     RUN (test_quad_store); \
     RUN (test_txn_commit); \
-    RUN (test_txn_abort);
+    RUN (test_txn_abort);  \
+
 BACKEND_TBL
 #undef ENTRY
     return 0;