Browse Source

Write up all lookup functions.

Stefano Cossu 4 years ago
parent
commit
429b6fa875
3 changed files with 328 additions and 108 deletions
  1. 45 16
      include/store_mdb.h
  2. 275 88
      src/store_mdb.c
  3. 8 4
      test/test_store_mdb.c

+ 45 - 16
include/store_mdb.h

@@ -27,8 +27,9 @@
 
 typedef char DbLabel[8];
 typedef struct MDBStore LSUP_MDBStore;
+typedef struct MDBIterator LSUP_MDBIterator;
 
-typedef LSUP_rc (*mdb_store_match_fn_t)(const LSUP_TripleKey spok, void *data);
+typedef LSUP_rc (*store_match_fn_t)(const LSUP_TripleKey spok, void *data);
 
 // TODO Introduce compile-time LSUP_BIG_STORE option to define two store
 // options: false: 64-bit hashes, uint32 keys, max 4G entries; true:
@@ -120,8 +121,8 @@ LSUP_rc LSUP_store_add(
 
 /** @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.
+ * This function may return a count of matches and/or an iterator of results as
+ * serialized triples.
  *
  * @param store[in] The store to be queried.
  *
@@ -129,26 +130,54 @@ LSUP_rc LSUP_store_add(
  * terms. Any and all of these may be NULL, which indicates an unbound query
  * term. Stores with context not set will always ignore the fourth term.
  *
+ * @param it[out] Pointer to a pointer to an #LSUP_MDBIterator that will be
+ * populated with a result iterator. This is always created even if no matches
+ * are found and must be freed with #LSUP_store_it_free after use. If matches
+ * are found, the iterator points to the first result which can be retrieved
+ * with #LSUP_store_it_next.
+ *
  * @param ct[out] If not NULL, this will be populated with the number of
- *  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.
+ *  entries found. It is very inexpensive to set for lookups without context,
+ *  much less so for 1-bound and 2-bound context lookups, in which cases it
+ *  should be set only if needed.
+ *
+ * @return LSUP_OK if entries were found, LSUP_NORESULT if none were found.
+ */
+LSUP_rc LSUP_store_lookup(
+        LSUP_MDBStore *store, LSUP_SerTerm *sspoc[],
+        LSUP_MDBIterator **it, size_t *ct);
+
+
+/** @brief Get iterator results and advance the cursor.
+ *
+ * This function also checks if the matching triple is associated with a
+ * context, if one was specified. If no associated contexts are found, the next
+ * triple is searched, until the end of the results.
  *
- * @param callback[in] This function is applied to the matching
- *  triple keys. It may be NULL if the function is called only to count the
- *  matches; which in most cases will be much faster.
+ * NOTE: Iterators keep LMDB cursors and (read only) transactions open. Don't
+ * hold on to them longer than necessary.
  *
- * @param data[in,out] Arbitrary data to be used in the callback function.
+ * NOTE: The memory pointed to by the individual LSUP_SerTerm pointers is
+ * owned by the database. It must not be written to or freed. To modify
+ * the data or use them beyond the caller's scope, this memory must be copied.
  *
- * @return LSUP_OK if entries were found, LSUP_NORESULT if none were found; if
- *  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.
+ * @param it[in] Opaque iterator handle obtained with #LSUP_store_lookup.
+ *
+ * @param sspo[out] #LSUP_SerTriple to be populated with three serialized terms
+ * if found, NULL if not found.
+ *
+ * @return LSUP_OK if results were found; LSUP_END otherwise. TODO handle
+ * errors.
  */
-LSUP_rc LSUP_store_lookup(
-        LSUP_MDBStore *store, LSUP_SerTerm *sspoc[], size_t *ct,
-        mdb_store_match_fn_t callback_fn, void *data);
+LSUP_rc LSUP_store_it_next(LSUP_MDBIterator *it, LSUP_SerTerm **sspo);
 
 
+/** @brief Free an iterator allocated by a lookup.
+ *
+ * @param it[in] Iterator pointer. It will be set to NULL after freeing.
+ */
+void LSUP_store_it_free(struct MDBIterator *it);
+
 
 /** @brief Contexts that a triple key appears in.
  *

+ 275 - 88
src/store_mdb.c

@@ -56,10 +56,34 @@ typedef struct LookupArgs {
                                     // represent, respectively.
     LSUP_Key ck;                    // Context key to restrict results to.
     size_t *ct;                     // Result count.
-    mdb_store_match_fn_t callback_fn; // Callback function to apply to matches.
+    struct MDBIterator *it;         // Iterator returned on matches.
     void *data;                     // Arbitrary data usable by the callback.
 } LookupArgs;
 
+
+/** @brief Iterator operation.
+ *
+ * Function executed for each iteration of a #MDBIterator.
+ */
+typedef LSUP_rc (*iter_op_fn_t)(struct MDBIterator *it);
+
+
+/** @brief Triple iterator.
+ */
+struct MDBIterator {
+    struct MDBStore *store;     // MDB store pointer.
+    MDB_cursor *cur;            // MDB cursor.
+    MDB_val *key, *data;        // Internal data handlers.
+    LSUP_TripleKey *spok;       // Triple to be populated with match.
+    LSUP_Key ck;                // Context key to filter by. May be NULL_TRP.
+    iter_op_fn_t iter_op_fn;    // Function used to look up next match.
+    uint8_t term_order[3];      // Term order used in 1-2bound look-ups.
+    LSUP_Key luks[3];           // 0÷3 lookup keys.
+    size_t i;                   // Internal counter for paged lookups.
+    int rc;                     // MDB_* return code for the next result.
+};
+
+
 /*
  * 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
@@ -154,11 +178,13 @@ static DBIdx lookup_indices[9] = {
  * The indices with the smallest average number of values per key should be
  * looked up first.
  *
+ * TODO deprecate?
+ *
  * 0 = s:po
  * 1 = p:so
  * 2 = o:sp
  */
-static const uint8_t lookup_rank[3] = {0, 2, 1};
+// static const uint8_t lookup_rank[3] = {0, 2, 1};
 
 static const uint8_t lookup_ordering_1bound[3][3] = {
     {0, 1, 2}, // s:po
@@ -219,8 +245,8 @@ LSUP_store_setup(char **path/*, bool clear*/) // TODO clear
                 *path);
     }
 
-    // Verify that a writable directory exists or can be created.
-    struct stat path_stat;
+    // TODO Verify that a writable directory exists or can be created.
+    //struct stat path_stat;
 
     /*
     // TODO clear
@@ -325,7 +351,7 @@ LSUP_rc
 LSUP_store_stats(LSUP_MDBStore *store)
 {
     // TODO
-    MDB_stat env_stat, db_stats[N_DB];
+    // MDB_stat env_stat, db_stats[N_DB];
     return 0;
 }
 
@@ -464,16 +490,6 @@ LSUP_store_sterm_to_key(
 }
 
 
-/*
-LSUP_Key
-LSUP_store_get_key(
-        LSUP_MDBStore *store, const LSUP_SerTerm *sterm)
-{
-
-}
-*/
-
-
 LSUP_rc
 LSUP_store_key_to_sterm(
         LSUP_MDBStore *store, const LSUP_Key key, LSUP_SerTerm *sterm)
@@ -481,7 +497,8 @@ LSUP_store_key_to_sterm(
     LSUP_rc rc = LSUP_NORESULT;
 
     MDB_txn *txn;
-    mdb_txn_begin(store->env, NULL, MDB_RDONLY, &txn);
+    if (store->txn) txn = store->txn;
+    else mdb_txn_begin(store->env, NULL, MDB_RDONLY, &txn);
 
     MDB_val key_v, data_v;
     key_v.mv_data = (void*)&key;
@@ -496,7 +513,7 @@ LSUP_store_key_to_sterm(
     }
     else if (UNLIKELY(mdb_rc != MDB_NOTFOUND)) rc = LSUP_ERROR;
 
-    mdb_txn_abort(txn);
+    if (txn != store->txn) mdb_txn_abort(txn);
 
     return rc;
 }
@@ -504,8 +521,8 @@ LSUP_store_key_to_sterm(
 
 LSUP_rc
 LSUP_store_lookup(
-        LSUP_MDBStore *store, LSUP_SerTerm *sspoc[], size_t *ct,
-        mdb_store_match_fn_t callback_fn, void *data)
+        LSUP_MDBStore *store, LSUP_SerTerm *sspoc[],
+        struct MDBIterator **it, size_t *ct)
 {
     LSUP_TripleKey spok = {
         LSUP_sterm_to_key(sspoc[0]),
@@ -520,8 +537,9 @@ LSUP_store_lookup(
 
     args->ct = ct;
     *args->ct = 0;
-    args->callback_fn = callback_fn;
-    args->data = data;
+
+    CRITICAL(args->it = *it = malloc(sizeof(struct MDBIterator)));
+    args->it->store = store;
 
     // s p o (all terms bound)
     if (spok[0] != NULL_KEY && spok[1] != NULL_KEY && spok[2] != NULL_KEY) {
@@ -573,6 +591,72 @@ LSUP_store_lookup(
 }
 
 
+/** @brief Yield the matching triples and advance the iterator.
+ */
+LSUP_rc
+LSUP_store_it_next(LSUP_MDBIterator *it, LSUP_SerTerm **sspo)
+{
+    LSUP_rc rc = (
+            it->rc == MDB_NOTFOUND ?
+            LSUP_END : it->iter_op_fn(it));
+
+    if (it->ck && rc != LSUP_END) {
+        MDB_cursor *cur;
+        MDB_val key, data;
+
+        mdb_cursor_open
+            (mdb_cursor_txn(it->cur), it->store->dbi[IDX_SPO_C], &cur);
+
+        key.mv_size = TRP_KLEN;
+        data.mv_data = &it->ck;
+        data.mv_size = KLEN;
+
+        do {
+            // If ctx is specified, look if the matching triple is associated
+            // with it. If not, move on to the next triple.
+            // The loop normally exits when a triple with matching ctx is found
+            // (LSUP_OK), or if there are no more triples (LSUP_END).
+            key.mv_data = it->spok;
+
+            if (
+                mdb_cursor_get(cur, &key, &data, MDB_GET_BOTH) == MDB_NOTFOUND
+            ) {
+                if (it->iter_op_fn(it) == LSUP_END) rc = LSUP_END;
+                else rc = LSUP_NORESULT;
+            }
+
+        } while (rc == LSUP_NORESULT);
+
+        mdb_cursor_close(cur);
+    }
+
+    if (rc == LSUP_OK) {
+        LSUP_store_key_to_sterm(it->store, *it->spok[0], *sspo);
+        LSUP_store_key_to_sterm(it->store, *it->spok[1], *sspo + 1);
+        LSUP_store_key_to_sterm(it->store, *it->spok[2], *sspo + 2);
+
+    // TODO error handling.
+    } else *sspo = NULL;
+
+    return rc;
+}
+
+
+void
+LSUP_store_it_free(struct MDBIterator *it)
+{
+    if (it) {
+        MDB_txn *cur_txn = mdb_cursor_txn(it->cur);
+
+        mdb_cursor_close(it->cur);
+        if(it->store->txn != cur_txn) mdb_txn_abort(cur_txn);
+
+        free(it);
+        it = NULL;
+    }
+}
+
+
 LSUP_rc
 LSUP_store_remove(
         LSUP_MDBStore *store, const LSUP_Buffer *sc,
@@ -596,7 +680,6 @@ LSUP_store_remove(
     mdb_cursor_open(txn, store->dbi[IDX_C_SPO], &icur);
 
     MDB_val spok_v, ck_v;
-    LSUP_TripleKey spok_cur;
 
     spok_v.mv_size = TRP_KLEN;
     ck_v.mv_size = KLEN;
@@ -785,7 +868,100 @@ index_triple(
 }
 
 
-/* * * Match callbacks. * * */
+/* * * Term-specific iterators. * * */
+
+/** @brief Advance 0-bound iterator.
+ *
+ * Cursor: spo:c
+ */
+inline static LSUP_rc
+it_next_0bound(struct MDBIterator *it)
+{
+    LSUP_Key *spok = (LSUP_Key*)it->data->mv_data;
+
+    *it->spok[0] = spok[0];
+    *it->spok[1] = spok[1];
+    *it->spok[2] = spok[2];
+
+    it->rc = mdb_cursor_get(it->cur, it->key, NULL, MDB_NEXT);
+
+    return LSUP_OK;
+}
+
+
+/** @brief Advance 1-bound iterator.
+ *
+ * Uses paged data in a nested loop.
+ *
+ * Cursor: s:po, p:so, or o:sp.
+ */
+inline static LSUP_rc
+it_next_1bound(struct MDBIterator *it)
+{
+    LSUP_Key **lu_dset = it->data->mv_data;
+
+    *it->spok[it->term_order[0]] = it->luks[0];
+    *it->spok[it->term_order[1]] = lu_dset[it->i][0];
+    *it->spok[it->term_order[2]] = lu_dset[it->i][1];
+
+    // Ensure next block within the same page is not beyond the last.
+    if(it->i < it->data->mv_size / DBL_KLEN - 1)
+        it->i ++;
+    else {
+        // If the last block in the page is being yielded,
+        // move cursor to beginning of next page.
+        it->i = 0;
+        it->rc = mdb_cursor_get(it->cur, it->key, it->data, MDB_NEXT_MULTIPLE);
+    }
+
+    return LSUP_OK;
+}
+
+
+/** @brief Advance 2-bound iterator.
+ *
+ * Uses paged data in a nested loop.
+ *
+ * Cursor: po:s, so:p, or sp:o.
+ */
+inline static LSUP_rc
+it_next_2bound(struct MDBIterator *it)
+{
+    LSUP_Key *lu_dset = it->data->mv_data;
+
+    *it->spok[it->term_order[0]] = it->luks[0];
+    *it->spok[it->term_order[1]] = it->luks[1];
+    *it->spok[it->term_order[2]] = lu_dset[it->i];
+
+    // Ensure next block within the same page is not beyond the last.
+    if(it->i < it->data->mv_size / DBL_KLEN - 1)
+        it->i ++;
+    else {
+        // If the last block in the page is being yielded,
+        // move cursor to beginning of next page.
+        it->i = 0;
+        it->rc = mdb_cursor_get(it->cur, it->key, it->data, MDB_NEXT_MULTIPLE);
+    }
+
+    return LSUP_OK;
+}
+
+
+/** @brief Advance 3-bound iterator.
+ *
+ * This is a special case of 0÷1 results; either there was one matching triple,
+ * which was already set in the first result, or there was none, i.e. it->rc is
+ * already MDB_NOTFOUND and this function will not be called.
+ */
+inline static LSUP_rc
+it_next_3bound(struct MDBIterator *it)
+{
+    it->rc = MDB_NOTFOUND;
+    return LSUP_OK;
+}
+
+
+/* * * Term-specific lookups. * * */
 
 inline static LSUP_rc
 lookup_0bound(struct MDBStore *store, LookupArgs *args)
@@ -820,23 +996,20 @@ lookup_0bound(struct MDBStore *store, LookupArgs *args)
 
             *args->ct = stat.ms_entries;
         }
-
-        if (!args->callback_fn && *args->ct > 0) rc = LSUP_OK;
     }
 
-    mdb_cursor_open(txn, store->dbi[IDX_S_PO], &cur);
+    args->it->ck = args->ck;
+    memcpy(args->it->key, &key_v, sizeof(MDB_val));
 
-    if(args->callback_fn) {
-        rc = mdb_cursor_get(cur, &key_v, NULL, MDB_FIRST);
-        while (rc != MDB_NOTFOUND) {
-            LSUP_TripleKey spok;
+    mdb_cursor_open(txn, store->dbi[IDX_SPO_C], &args->it->cur);
 
-            rc = args->callback_fn(spok, args->data);
-            if (rc < 0) goto _match0b_abort;
-        }
-    }
+    args->it->rc = mdb_cursor_get(
+            args->it->cur, args->it->key, args->it->data, MDB_FIRST);
+
+    args->it->iter_op_fn = it_next_0bound;
+
+    return args->it->rc == MDB_SUCCESS ? LSUP_OK : LSUP_NORESULT;
 
-_match0b_abort:
     mdb_cursor_close(cur);
     if (txn != store->txn) mdb_txn_abort(txn);
 
@@ -869,35 +1042,42 @@ lookup_1bound(struct MDBStore *store, LookupArgs *args)
     key_v.mv_size = KLEN;
 
     if(args->ct) {
-        db_rc = mdb_cursor_get(cur, &key_v, &data_v, MDB_SET);
-        if (db_rc != MDB_SUCCESS) return rc;
-
-        if (!args->callback_fn && *args->ct > 0) rc = LSUP_OK;
-    }
-
-    if(args->callback_fn) {
-        db_rc = mdb_cursor_get(cur, &key_v, &data_v, MDB_SET);
-        if (db_rc == MDB_SUCCESS)
+        if (args->ck != NULL_KEY) {
             db_rc = mdb_cursor_get(cur, &key_v, &data_v, MDB_GET_MULTIPLE);
+            while (db_rc != MDB_NOTFOUND) {
+            }
+        } else {
+            db_rc = mdb_cursor_get(cur, &key_v, &data_v, MDB_SET);
+            if (db_rc != MDB_SUCCESS) return rc;
 
-        while (db_rc != MDB_NOTFOUND) {
-            LSUP_Key **lu_dset = data_v.mv_data;
-            for (int i = 0; i < data_v.mv_size / DBL_KLEN; i++) {
-                // Build triple key from lookup key and result keys.
-                LSUP_TripleKey spok;
+            db_rc = mdb_cursor_count(cur, args->ct);
+        }
 
-                spok[term_order[0]] = args->luks[0];
-                spok[term_order[1]] = lu_dset[i][0];
-                spok[term_order[2]] = lu_dset[i][1];
+        if (!args->it && *args->ct > 0) rc = LSUP_OK;
+    }
 
-                rc = args->callback_fn(spok, args->data);
-                if (rc < 0) goto _match1b_abort;
-            }
-            db_rc = mdb_cursor_get(cur, &key_v, &data_v, MDB_NEXT_MULTIPLE);
-        }
+    if(args->it) {
+        args->it->ck = args->ck;
+        args->it->cur = cur;
+        memcpy(args->it->key, &key_v, sizeof(MDB_val));
+        memcpy(args->it->data, &data_v, sizeof(MDB_val));
+        args->it->term_order[0] = term_order[0];
+        args->it->term_order[1] = term_order[1];
+        args->it->term_order[2] = term_order[2];
+        args->it->luks[0] = args->luks[0];
+        args->it->i = 0;
+        args->it->iter_op_fn = it_next_1bound;
+
+        args->it->rc = mdb_cursor_get(
+                args->it->cur, args->it->key, args->it->data, MDB_SET);
+        if (args->it->rc == MDB_SUCCESS)
+            args->it->rc = mdb_cursor_get(
+                    args->it->cur, args->it->key, args->it->data,
+                    MDB_GET_MULTIPLE);
+
+        return args->it->rc == MDB_SUCCESS ? LSUP_OK : LSUP_NORESULT;
     }
 
-_match1b_abort:
     mdb_cursor_close(cur);
     if (txn != store->txn) mdb_txn_abort(txn);
 
@@ -969,32 +1149,32 @@ lookup_2bound(struct MDBStore *store, LookupArgs *args)
         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->it && *args->ct > 0) rc = LSUP_OK;
     }
 
-    if(args->callback_fn) {
-        rc = mdb_cursor_get(cur, &key_v, &data_v, MDB_SET);
-        if (rc == MDB_SUCCESS)
-            rc = mdb_cursor_get(cur, &key_v, &data_v, MDB_GET_MULTIPLE);
-
-        while (rc != MDB_NOTFOUND) {
-            LSUP_Key *lu_dset = data_v.mv_data;
-            for (int i = 0; i < data_v.mv_size / DBL_KLEN; i++) {
-                // Build triple key from lookup key and result keys.
-                LSUP_TripleKey spok;
-
-                spok[term_order[0]] = luk[0];
-                spok[term_order[1]] = luk[1];
-                spok[term_order[2]] = lu_dset[i];
-
-                rc = args->callback_fn(spok, args->data);
-                if (rc < 0) goto _match2b_abort;
-            }
-            rc = mdb_cursor_get(cur, &key_v, &data_v, MDB_NEXT_MULTIPLE);
-        }
+    if(args->it) {
+        args->it->ck = args->ck;
+        args->it->cur = cur;
+        memcpy(args->it->key, &key_v, sizeof(MDB_val));
+        memcpy(args->it->data, &data_v, sizeof(MDB_val));
+        args->it->term_order[0] = term_order[0];
+        args->it->term_order[1] = term_order[1];
+        args->it->term_order[2] = term_order[2];
+        args->it->luks[0] = args->luks[0];
+        args->it->luks[1] = args->luks[1];
+        args->it->i = 0;
+        args->it->iter_op_fn = it_next_2bound;
+
+        args->it->rc = mdb_cursor_get(
+                args->it->cur, args->it->key, args->it->data, MDB_SET);
+        if (args->it->rc == MDB_SUCCESS)
+            args->it->rc = mdb_cursor_get(
+                    args->it->cur, args->it->key, args->it->data,
+                    MDB_GET_MULTIPLE);
+
+        return args->it->rc == MDB_SUCCESS ? LSUP_OK : LSUP_NORESULT;
     }
 
-_match2b_abort:
     mdb_cursor_close(cur);
     if (txn != store->txn) mdb_txn_abort(txn);
 
@@ -1028,20 +1208,27 @@ lookup_3bound(struct MDBStore *store, LookupArgs *args)
     if(args->ct) {
         if (db_rc == MDB_SUCCESS) {
             *args->ct = 1;
-            if (!args->callback_fn) rc = LSUP_OK;
+            if (!args->it) rc = LSUP_OK;
         }
     }
 
-    if(args->callback_fn) {
-        while (db_rc != MDB_NOTFOUND) {
-            LSUP_TripleKey spok;
-
-            rc = args->callback_fn(spok, args->data);
-            if (rc < 0) goto _match3b_abort;
+    if(args->it) {
+        args->it->rc = db_rc;
+        if (args->it->rc == MDB_NOTFOUND) {
+            rc = LSUP_NORESULT;
+        } else {
+            args->it->ck = args->ck;
+            args->it->iter_op_fn = it_next_3bound;
+            *args->it->spok[0] = args->luks[0];
+            *args->it->spok[1] = args->luks[1];
+            *args->it->spok[2] = args->luks[2];
+            args->it->rc = LSUP_OK;
+            rc = LSUP_OK;
         }
+
+        return rc;
     }
 
-_match3b_abort:
     mdb_cursor_close(cur);
     if (txn != store->txn) mdb_txn_abort(txn);
 

+ 8 - 4
test/test_store_mdb.c

@@ -51,7 +51,6 @@ static int test_triple_store()
     EXPECT_INT_EQ(LSUP_store_size(store), 8);
 
     // Test lookups.
-    size_t ct;
     LSUP_SerTerm *lut[12][4] = {
         {NULL, NULL, NULL, NULL},
 
@@ -78,8 +77,11 @@ static int test_triple_store()
     };
 
     for (int i = 0; i < 12; i++) {
+        size_t ct;
+        LSUP_MDBIterator *it;
+
         TRACE("Testing triple lookup #%d.\n", i);
-        int rc = LSUP_store_lookup(store, lut[i], &ct, NULL, NULL);
+        int rc = LSUP_store_lookup(store, lut[i], &it, &ct);
         if (ct > 0) EXPECT_INT_EQ(rc, LSUP_OK);
         else if (ct == 0) EXPECT_INT_EQ(rc, LSUP_NORESULT);
 
@@ -153,7 +155,6 @@ static int test_quad_store()
     LSUP_SerTerm *sc3 = &sc3_s;
     LSUP_term_serialize(ctx2, &sc3_s);
 
-    size_t ct;
     LSUP_SerTerm *lut[41][4] = {
         // Any context
         {NULL, NULL, NULL, NULL},
@@ -246,6 +247,9 @@ static int test_quad_store()
     };
 
     for (int i = 0; i < 41; i++) {
+        size_t ct;
+        LSUP_MDBIterator *it;
+
         printf("Testing triple lookup #%d: {", i);
 
         if(lut[i][0]) LSUP_buffer_print(lut[i][0]);
@@ -264,7 +268,7 @@ static int test_quad_store()
         else printf("NULL");
         printf("}\n");
 
-        int rc = LSUP_store_lookup(store, lut[i], &ct, NULL, NULL);
+        int rc = LSUP_store_lookup(store, lut[i], &it, &ct);
         if (ct > 0) EXPECT_INT_EQ(rc, LSUP_OK);
         else if (ct == 0) EXPECT_INT_EQ(rc, LSUP_NORESULT);