Parcourir la source

Test and fix lookups in triple mode.

Stefano Cossu il y a 3 ans
Parent
commit
664565f018
5 fichiers modifiés avec 160 ajouts et 69 suppressions
  1. 1 0
      include/core.h
  2. 12 7
      include/store_mdb.h
  3. 104 59
      src/store_mdb.c
  4. 3 3
      test/assets.h
  5. 40 0
      test/test_store_mdb.c

+ 1 - 0
include/core.h

@@ -63,6 +63,7 @@ typedef enum {
     LSUP_PARSE_ERR      = -88802,
     LSUP_VALUE_ERR      = -88803,
     LSUP_TXN_ERR        = -88804,
+    LSUP_DB_ERR         = -88805,
 } LSUP_rc;
 
 typedef size_t LSUP_Key;

+ 12 - 7
include/store_mdb.h

@@ -118,7 +118,10 @@ LSUP_rc LSUP_store_add(
         const LSUP_SerTriple *data, const size_t data_size);
 
 
-/** @brief Perform an arbitraty function on matching triples and context.
+/** @brief Look up matching triples and optional context.
+ *
+ * This function may return a count of matches and/or run a callback function
+ * on each of the matching triples.
  *
  * @param store[in] The store to be queried.
  *
@@ -127,13 +130,13 @@ LSUP_rc LSUP_store_add(
  * term. Stores with context not set will always ignore the fourth term.
  *
  * @param ct[out] If not NULL, this will be populated with the number of
- *  entries found. This can be used when calling this function twice: once to
- *  pre-allocate some memory based on the results obtained, the second one to
+ *  entries found. This can be used when calling this function once to
+ *  pre-allocate some memory based on the results obtained, then again to
  *  perform an operation on the allocated memory.
  *
  * @param callback[in] This function is applied to the matching
- *  triple keys. This may be NULL if the function is called only to count the
- *  matches.
+ *  triple keys. It may be NULL if the function is called only to count the
+ *  matches; which in most cases will be much faster.
  *
  * @param ctx[in] Arbitrary data to be used in the callback function.
  *
@@ -141,7 +144,9 @@ LSUP_rc LSUP_store_add(
  *  a callback was applied, and an error (<0) code was returned in any of the
  *  interactions, that code is returned and the callback execution is stopped.
  */
-LSUP_rc LSUP_store_match_callback(
-        LSUP_MDBStore *store, LSUP_SerTerm sspoc[], size_t *ct,
+LSUP_rc LSUP_store_lookup(
+        LSUP_MDBStore *store, LSUP_SerTerm *sspoc[], size_t *ct,
         mdb_store_match_fn_t callback_fn, void *ctx);
+
+
 #endif

+ 104 - 59
src/store_mdb.c

@@ -2,16 +2,6 @@
 
 #include "store_mdb.h"
 
-/*
- * TODO At the moment up to 64-bit key / hash values are allowed. Later on,
- * 128-bit keys should be allowed by compile options, and that will no longer
- * be compatible with integer keys and data. When 128-bit keys are supported,
- * integer keys should remain available for code compiled with 64-bit keys.
- */
-#define INT_KEY_MASK        MDB_INTEGERKEY
-#define INT_DUP_KEY_MASK    MDB_DUPSORT | MDB_DUPFIXED | MDB_INTEGERKEY
-#define INT_DUPDATA_MASK    MDB_DUPSORT | MDB_DUPFIXED | MDB_INTEGERDUP
-
 /**
  * Number of DBs defined.
  */
@@ -70,28 +60,40 @@ struct MatchArgs {
 
 static LSUP_Buffer *default_ctx = NULL; // Default context URI for quad store.
 
+/*
+ * TODO At the moment up to 64-bit key / hash values are allowed. Later on,
+ * 128-bit keys should be allowed by compile options, and that will no longer
+ * be compatible with integer keys and data. When 128-bit keys are supported,
+ * integer keys should remain available for code compiled with 64-bit keys.
+ */
+#define DUPSORT_MASK        MDB_DUPSORT
+#define DUPFIXED_MASK       MDB_DUPSORT | MDB_DUPFIXED
+#define INT_KEY_MASK        MDB_INTEGERKEY
+#define INT_DUP_KEY_MASK    MDB_DUPSORT | MDB_DUPFIXED | MDB_INTEGERKEY
+#define INT_DUPDATA_MASK    MDB_DUPSORT | MDB_DUPFIXED | MDB_INTEGERDUP
+
 /**
  * Main DBs. These are the master information containers.
  *
  * Data columns are: identifier prefix, DB label, flags.
  */
 #define MAIN_TABLE \
-    ENTRY(  T_ST,    "t:st",    INT_KEY_MASK    )   /* Key to ser. term */  \
-    ENTRY(  SPO_C,   "spo:c",   INT_DUPDATA_MASK)   /* Triple to context */ \
-    ENTRY(  C_,      "c:",      INT_KEY_MASK    )   /* Track empty contexts */\
+    ENTRY(  T_ST,    "t:st",    0               )   /* Key to ser. term */  \
+    ENTRY(  SPO_C,   "spo:c",   DUPFIXED_MASK   )   /* Triple to context */ \
+    ENTRY(  C_,      "c:",      0               )   /* Track empty contexts */\
     ENTRY(  PFX_NS,  "pfx:ns",  0               )   /* Prefix to NS */      \
 
 /**
  * Lookup DBs. These are indices and may be destroyed and rebuilt.
  */
 #define LOOKUP_TABLE \
-    ENTRY(  S_PO,    "s:po",    INT_DUP_KEY_MASK)   /* 1-bound lookup */    \
-    ENTRY(  P_SO,    "p:so",    INT_DUP_KEY_MASK)   /* 1-bound lookup */    \
-    ENTRY(  O_SP,    "o:sp",    INT_DUP_KEY_MASK)   /* 1-bound lookup */    \
-    ENTRY(  PO_S,    "po:s",    INT_DUPDATA_MASK)   /* 2-bound lookup */    \
-    ENTRY(  SO_P,    "so:p",    INT_DUPDATA_MASK)   /* 2-bound lookup */    \
-    ENTRY(  SP_O,    "sp:o",    INT_DUPDATA_MASK)   /* 2-bound lookup */    \
-    ENTRY(  C_SPO,   "c:spo",   INT_DUP_KEY_MASK)   /* Context lookup */    \
+    ENTRY(  S_PO,    "s:po",    DUPFIXED_MASK   )   /* 1-bound lookup */    \
+    ENTRY(  P_SO,    "p:so",    DUPFIXED_MASK   )   /* 1-bound lookup */    \
+    ENTRY(  O_SP,    "o:sp",    DUPFIXED_MASK   )   /* 1-bound lookup */    \
+    ENTRY(  PO_S,    "po:s",    DUPFIXED_MASK   )   /* 2-bound lookup */    \
+    ENTRY(  SO_P,    "so:p",    DUPFIXED_MASK   )   /* 2-bound lookup */    \
+    ENTRY(  SP_O,    "sp:o",    DUPFIXED_MASK   )   /* 2-bound lookup */    \
+    ENTRY(  C_SPO,   "c:spo",   DUPFIXED_MASK   )   /* Context lookup */    \
     ENTRY(  NS_PFX,  "ns:pfx",  0               )   /* NS to prefix */      \
 
 /**
@@ -178,13 +180,13 @@ static int index_triple(
         LSUP_MDBStore *store, StoreOp op,
         LSUP_TripleKey spok, LSUP_Key ck);
 
-inline static LSUP_rc match_callback_0bound(
+inline static LSUP_rc lookup_0bound(
         struct MDBStore *store, struct MatchArgs *args);
-inline static LSUP_rc match_callback_1bound(
+inline static LSUP_rc lookup_1bound(
         struct MDBStore *store, struct MatchArgs *args);
-inline static LSUP_rc match_callback_2bound(
+inline static LSUP_rc lookup_2bound(
         struct MDBStore *store, struct MatchArgs *args);
-inline static LSUP_rc match_callback_3bound(
+inline static LSUP_rc lookup_3bound(
         struct MDBStore *store, struct MatchArgs *args);
 /* TODO
 inline static int check_txn_open(MDB_txn *txn, bool write);
@@ -333,8 +335,21 @@ LSUP_store_size(LSUP_MDBStore *store)
 {
     if(!(store->state & LSSTORE_INIT)) return 0;
 
+    bool txn_pending = false;
+    if (!store->txn) {
+        mdb_txn_begin(store->env, NULL, 0, &store->txn);
+        txn_pending = true;
+    }
+
     MDB_stat stat;
-    mdb_stat(store->txn, store->dbi[IDX_SPO_C], &stat);
+    int rc = mdb_stat(store->txn, store->dbi[IDX_SPO_C], &stat);
+    TRACE("Stat rc: %d\n", rc);
+    // TODO error handling.
+
+    if(txn_pending) {
+        mdb_txn_abort(store->txn);
+        store->txn = NULL;
+    }
 
     return stat.ms_entries;
 }
@@ -488,27 +503,28 @@ LSUP_store_key_to_sterm(
 
 
 LSUP_rc
-LSUP_store_match_callback(
-        LSUP_MDBStore *store, LSUP_SerTerm sspoc[], size_t *ct,
+LSUP_store_lookup(
+        LSUP_MDBStore *store, LSUP_SerTerm *sspoc[], size_t *ct,
         mdb_store_match_fn_t callback_fn, void *ctx)
 {
     LSUP_TripleKey spok = {
-        LSUP_sterm_to_key(sspoc),
-        LSUP_sterm_to_key(sspoc + 1),
-        LSUP_sterm_to_key(sspoc + 2),
+        LSUP_sterm_to_key(sspoc[0]),
+        LSUP_sterm_to_key(sspoc[1]),
+        LSUP_sterm_to_key(sspoc[2]),
     };
 
-    LSUP_Key ck = store->default_ctx ? LSUP_sterm_to_key(sspoc + 3) : NULL_KEY;
+    LSUP_Key ck = store->default_ctx ? LSUP_sterm_to_key(sspoc[3]) : NULL_KEY;
 
     struct MatchArgs args_s;
     struct MatchArgs *args = &args_s;
     args->ct = ct;
+    *args->ct = 0;
     args->callback_fn = callback_fn;
     args->ctx = ctx;
 
     // s p o (all terms bound)
     if (spok[0] != NULL_KEY && spok[1] != NULL_KEY && spok[2] != NULL_KEY) {
-        return match_callback_3bound(store, args);
+        return lookup_3bound(store, args);
     }
 
     else if (spok[0] != NULL_KEY) {
@@ -518,15 +534,15 @@ LSUP_store_match_callback(
         if (spok[1] != NULL_KEY) { // s p ?
             args->luks[1] = spok[1];
             args->idx1 = 1;
-            return match_callback_2bound(store, args);
+            return lookup_2bound(store, args);
 
         } else if (spok[2] != NULL_KEY) { // s ? o
             args->luks[1] = spok[2];
             args->idx1 = 2;
-            return match_callback_2bound(store, args);
+            return lookup_2bound(store, args);
 
         } else { // s ? ?
-            return match_callback_1bound(store, args);
+            return lookup_1bound(store, args);
         }
 
     } else if (spok[1] != NULL_KEY) {
@@ -535,20 +551,20 @@ LSUP_store_match_callback(
         if (spok[2] != NULL_KEY) { // ? p o
             args->luks[1] = spok[2];
             args->idx1 = 2;
-            return match_callback_2bound(store, args);
+            return lookup_2bound(store, args);
 
         } else { // ? p ?
             args->idx0 = 1;
-            return match_callback_1bound(store, args);
+            return lookup_1bound(store, args);
         }
 
     } else if (spok[2] != NULL_KEY) { // ? ? o
         args->luks[0] = spok[2];
         args->idx0 = 2;
-        return match_callback_1bound(store, args);
+        return lookup_1bound(store, args);
 
     } else { // ? ? ? (all terms unbound)
-        return match_callback_0bound(store, args);
+        return lookup_0bound(store, args);
     }
 }
 
@@ -668,6 +684,8 @@ index_triple(
     int rc = LSUP_NOACTION;
     MDB_val v1, v2;
 
+    printf("Indexing triple: %lx %lx %lx\n", spok[0], spok[1], spok[2]);
+
     // Index c:spo.
     if (op == OP_REMOVE) {
         if (ck != NULL_KEY) {
@@ -739,16 +757,26 @@ index_triple(
             mdb_cursor_close(cur2);
 
         } else { // OP_ADD is guaranteed.
-            CHECK(
-                    mdb_put(store->txn, db1, &v1, &v2, MDB_NODUPDATA),
-                    db_rc, _index_triple_exit);
-            CHECK(
-                    mdb_put(store->txn, db2, &v2, &v1, MDB_NODUPDATA),
-                    db_rc, _index_triple_exit);
+            printf("Indexing in %s: ", db_labels[lookup_indices[i]]);
+            printf(
+                    "%lx: %lx %lx\n", *(size_t*)(v1.mv_data),
+                    *(size_t*)(v2.mv_data), *(size_t*)(v2.mv_data) + 1);
+
+            db_rc = mdb_put(store->txn, db1, &v1, &v2, MDB_NODUPDATA);
+            if (db_rc != MDB_SUCCESS && db_rc != MDB_KEYEXIST)
+                return LSUP_DB_ERR;
+
+            printf("Indexing in %s: ", db_labels[lookup_indices[i + 3]]);
+            printf(
+                    "%lx %lx: %lx\n", *(size_t*)(v2.mv_data),
+                    *(size_t*)(v2.mv_data) + 1, *(size_t*)(v1.mv_data));
+
+            db_rc = mdb_put(store->txn, db2, &v2, &v1, MDB_NODUPDATA);
+            if (db_rc != MDB_SUCCESS && db_rc != MDB_KEYEXIST)
+                return LSUP_DB_ERR;
         }
     }
 
-_index_triple_exit:
     return rc;
 }
 
@@ -756,7 +784,7 @@ _index_triple_exit:
 /* * * Match callbacks. * * */
 
 inline static LSUP_rc
-match_callback_0bound(struct MDBStore *store, struct MatchArgs *args)
+lookup_0bound(struct MDBStore *store, struct MatchArgs *args)
 {
     int rc = LSUP_NORESULT;
 
@@ -768,9 +796,11 @@ match_callback_0bound(struct MDBStore *store, struct MatchArgs *args)
 
     if(args->ct) {
         MDB_stat stat;
-        mdb_stat(store->txn, store->dbi[IDX_SPO_C], &stat);
+        mdb_stat(txn, store->dbi[IDX_SPO_C], &stat);
 
         *args->ct = stat.ms_entries;
+
+        if (!args->callback_fn && *args->ct > 0) rc = LSUP_OK;
     }
 
     MDB_cursor *cur;
@@ -795,26 +825,34 @@ _match0b_abort:
 
 
 inline static LSUP_rc
-match_callback_1bound(struct MDBStore *store, struct MatchArgs *args)
+lookup_1bound(struct MDBStore *store, struct MatchArgs *args)
 {
-    int rc = LSUP_NORESULT;
+    int rc = LSUP_NORESULT, db_rc;
 
     const uint8_t *term_order = lookup_ordering_1bound[args->idx0];
 
+    TRACE("Looking up 1 bound term: %lx\n", args->luks[0]);
+
     MDB_txn *txn;
     if(store->txn) txn = store->txn;
-    else mdb_txn_begin(store->env, NULL, MDB_RDONLY, &txn);
+    else {
+        db_rc = mdb_txn_begin(store->env, NULL, MDB_RDONLY, &txn);
+        if (db_rc != MDB_SUCCESS) abort();
+    }
+
 
     MDB_cursor *cur;
-    mdb_cursor_open(txn, store->dbi[args->idx0], &cur);
+    mdb_cursor_open(txn, store->dbi[lookup_indices[args->idx0]], &cur);
 
     MDB_val key_v, data_v;
-    key_v.mv_data = &args->luks;
+    key_v.mv_data = args->luks;
     key_v.mv_size = KLEN;
 
     if(args->ct) {
-        mdb_cursor_get(cur, &key_v, &data_v, MDB_SET);
-        mdb_cursor_count(cur, args->ct);
+        db_rc = mdb_cursor_get(cur, &key_v, &data_v, MDB_SET);
+        db_rc = mdb_cursor_count(cur, args->ct);
+
+        if (!args->callback_fn && *args->ct > 0) rc = LSUP_OK;
     }
 
     if(args->callback_fn) {
@@ -848,7 +886,7 @@ _match1b_abort:
 
 
 inline static LSUP_rc
-match_callback_2bound(struct MDBStore *store, struct MatchArgs *args)
+lookup_2bound(struct MDBStore *store, struct MatchArgs *args)
 {
     int rc = LSUP_NORESULT;
 
@@ -876,6 +914,9 @@ match_callback_2bound(struct MDBStore *store, struct MatchArgs *args)
                 luk2_offset = 0;
             }
             dbi = store->dbi[lookup_indices[i + 3]];
+            TRACE(
+                    "Looking up 2 bound in %s\n",
+                    db_labels[lookup_indices[i + 3]]);
 
             break;
         }
@@ -898,7 +939,7 @@ match_callback_2bound(struct MDBStore *store, struct MatchArgs *args)
     else mdb_txn_begin(store->env, NULL, MDB_RDONLY, &txn);
 
     MDB_cursor *cur;
-    mdb_cursor_open(txn, store->dbi[dbi], &cur);
+    mdb_cursor_open(txn, dbi, &cur);
 
     MDB_val key_v, data_v;
     key_v.mv_data = luk;
@@ -907,6 +948,8 @@ match_callback_2bound(struct MDBStore *store, struct MatchArgs *args)
     if(args->ct) {
         mdb_cursor_get(cur, &key_v, &data_v, MDB_SET);
         mdb_cursor_count(cur, args->ct);
+
+        if (!args->callback_fn && *args->ct > 0) rc = LSUP_OK;
     }
 
     if(args->callback_fn) {
@@ -940,7 +983,7 @@ _match2b_abort:
 
 
 inline static LSUP_rc
-match_callback_3bound(struct MDBStore *store, struct MatchArgs *args)
+lookup_3bound(struct MDBStore *store, struct MatchArgs *args)
 {
     int rc = LSUP_NORESULT;
 
@@ -959,7 +1002,9 @@ match_callback_3bound(struct MDBStore *store, struct MatchArgs *args)
 
     if(args->ct) {
         if (mdb_cursor_get(cur, &key_v, NULL, MDB_SET) == MDB_SUCCESS)
-            rc = mdb_cursor_count(cur, args->ct);
+            mdb_cursor_count(cur, args->ct);
+
+        if (!args->callback_fn && *args->ct > 0) rc = LSUP_OK;
     }
 
     if(args->callback_fn) {

+ 3 - 3
test/assets.h

@@ -36,17 +36,17 @@ LSUP_Triple *create_triples()
     trp[5].s = LSUP_term_new(LSUP_TERM_URI, "urn:s:0", NULL, NULL);
     trp[5].p = LSUP_term_new(LSUP_TERM_URI, "urn:p:5", NULL, NULL);
     trp[5].o = LSUP_term_new(
-            LSUP_TERM_LITERAL, "String 2", "xsd:string", NULL);
+            LSUP_TERM_LITERAL, "String 1", "xsd:string", NULL);
 
     trp[6].s = LSUP_term_new(LSUP_TERM_URI, "urn:s:1", NULL, NULL);
     trp[6].p = LSUP_term_new(LSUP_TERM_URI, "urn:p:6", NULL, NULL);
     trp[6].o = LSUP_term_new(
-            LSUP_TERM_LITERAL, "String 3", "xsd:string", "es-ES");
+            LSUP_TERM_LITERAL, "String 1", "xsd:string", "es-ES");
 
     // Let's reuse pointers. Do not double-free.
     trp[7].s = trp[0].s; // <urn:s:0>
     trp[7].p = trp[2].p; // <urn:p:2>
-    trp[7].o = trp[5].o; // "String 2"^^xsd:string
+    trp[7].o = trp[5].o; // "String 1"^^xsd:string
 
     // Duplicate of trp[7]. Do not double-free.
     trp[8].s = trp[0].s;

+ 40 - 0
test/test_store_mdb.c

@@ -46,7 +46,45 @@ static int test_triple_store()
         }
     }
 
+    // Test adding.
     EXPECT_PASS(LSUP_store_add(store, NULL, ser_trp, NUM_TRP));
+    EXPECT_INT_EQ(LSUP_store_size(store), 8);
+
+    // Test lookups.
+    size_t ct;
+    LSUP_SerTerm *lut[12][4] = {
+        {NULL, NULL, NULL, NULL},
+
+        {ser_trp[0].s, NULL, NULL, NULL},
+        {ser_trp[2].s, NULL, NULL, ser_trp[2].s},
+        {NULL, ser_trp[0].p, NULL, NULL},
+        {NULL, ser_trp[0].s, NULL, NULL},
+        {NULL, NULL, ser_trp[6].o, NULL},
+
+        {ser_trp[4].s, ser_trp[4].p, NULL, NULL},
+        {NULL, ser_trp[7].p, ser_trp[7].o, NULL},
+        {ser_trp[5].s, NULL, ser_trp[5].o, NULL},
+        {ser_trp[6].s, NULL, ser_trp[5].o, ser_trp[2].s},
+
+        {ser_trp[4].s, ser_trp[4].p, ser_trp[4].o, NULL},
+        {ser_trp[4].s, ser_trp[4].p, ser_trp[5].o, NULL},
+    };
+
+    size_t results[12] = {
+        8,
+        5, 1, 1, 0, 1,
+        2, 1, 2, 0,
+        1, 0,
+    };
+
+    for (int i = 0; i < 8; i++) {
+        TRACE("Testing triple lookup #%d.\n", i);
+        int rc = LSUP_store_lookup(store, lut[i], &ct, NULL, NULL);
+        if (ct > 0) EXPECT_INT_EQ(rc, LSUP_OK);
+        else if (ct == 0) EXPECT_INT_EQ(rc, LSUP_NORESULT);
+
+        EXPECT_INT_EQ(ct, results[i]);
+    }
 
     for (int i = 0; i < NUM_TRP; i++) {
         LSUP_buffer_done(ser_trp[i].s);
@@ -103,6 +141,8 @@ static int test_quad_store()
     // Use specific context.
     EXPECT_PASS(LSUP_store_add(store, &sc2_s, ser_trp, NUM_TRP));
 
+    EXPECT_INT_EQ(LSUP_store_size(store), 16);
+
     for (int i = 0; i < NUM_TRP; i++) {
         LSUP_buffer_done(ser_trp[i].s);
         LSUP_buffer_done(ser_trp[i].p);