|
@@ -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);
|
|
|
|