store_mdb.c 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354
  1. #include <ftw.h>
  2. #include "store_mdb.h"
  3. /**
  4. * Number of DBs defined.
  5. */
  6. #define N_DB 12
  7. /**
  8. * Memory map size.
  9. */
  10. #if (defined DEBUG)
  11. #define DEFAULT_MAPSIZE 1<<24 // 16Mb (limit for Valgrind)
  12. #elif !(defined __LP64__ || defined __LLP64__) || \
  13. defined _WIN32 && !defined _WIN64
  14. #define DEFAULT_MAPSIZE 1<<31 // 2Gb (limit for 32-bit systems)
  15. #else
  16. #define DEFAULT_MAPSIZE 1UL<<40 // 1Tb
  17. #endif
  18. #define ENV_DIR_MODE 0750
  19. #define ENV_FILE_MODE 0640
  20. typedef char DbLabel[8];
  21. // TODO Most of these are no longer used. Clean up.
  22. typedef enum {
  23. LSSTORE_INIT = 1, // Is the store environment set up on disk?
  24. LSSTORE_OPEN = 3, // Is the environment open? Assumes init is set.
  25. LSSTORE_DIRTY_TXN = 4, // Main txn was opened in a subroutine.
  26. } StoreState;
  27. typedef enum {
  28. OP_ADD,
  29. OP_REMOVE,
  30. } StoreOp;
  31. typedef struct MDBStore {
  32. MDB_env * env; // Environment handle.
  33. MDB_txn * txn; // Current transaction.
  34. MDB_dbi dbi[N_DB]; // DB handles. Refer to DbIdx enum.
  35. LSUP_Buffer * default_ctx;// Default ctx as a serialized URI.
  36. StoreState state; // Store state.
  37. } MDBStore;
  38. /** @brief Iterator operation.
  39. *
  40. * Function executed for each iteration of a #MDBIterator. It assumes that a
  41. * result triple has already been found and is ready to be composed and
  42. * yielded.
  43. *
  44. * Upon call, the rc value of the iterator structure is set to the MDB_* rc
  45. * value for the next result. It is up to the caller to evaluate this value
  46. * and decide whether to call the function again.
  47. */
  48. typedef void (*iter_op_fn_t)(struct MDBIterator *it);
  49. /** @brief Triple iterator.
  50. */
  51. typedef struct MDBIterator {
  52. MDBStore * store; // MDB store pointer.
  53. MDB_txn * txn; // MDB transaction.
  54. MDB_cursor * cur; // MDB cursor.
  55. MDB_val key, data; // Internal data handlers.
  56. LSUP_TripleKey spok; // Triple to be populated with match.
  57. LSUP_Key ck; // Ctx key to filter by. May be NULL_TRP.
  58. iter_op_fn_t iter_op_fn; // Function used to look up next match.
  59. const uint8_t * term_order; // Term order used in 1-2bound look-ups.
  60. LSUP_Key luk[3]; // 0÷3 lookup keys.
  61. size_t i; // Internal counter for paged lookups.
  62. int rc; // MDB_* return code for the next result.
  63. StoreState state; // State flags.
  64. } MDBIterator;
  65. /*
  66. * TODO At the moment up to 64-bit key / hash values are allowed. Later on,
  67. * 128-bit keys should be allowed by compile options, and that will no longer
  68. * be compatible with integer keys and data. When 128-bit keys are supported,
  69. * integer keys should remain available for code compiled with 64-bit keys.
  70. */
  71. #define DUPSORT_MASK MDB_DUPSORT
  72. #define DUPFIXED_MASK MDB_DUPSORT | MDB_DUPFIXED
  73. #define INT_KEY_MASK MDB_INTEGERKEY
  74. #define INT_DUP_KEY_MASK MDB_DUPSORT | MDB_DUPFIXED | MDB_INTEGERKEY
  75. #define INT_DUPDATA_MASK MDB_DUPSORT | MDB_DUPFIXED | MDB_INTEGERDUP
  76. /**
  77. * Main DBs. These are the master information containers.
  78. *
  79. * Data columns are: identifier prefix, DB label, flags.
  80. */
  81. #define MAIN_TABLE \
  82. ENTRY( T_ST, "t:st", 0 ) /* Key to ser. term */ \
  83. ENTRY( SPO_C, "spo:c", DUPFIXED_MASK ) /* Triple to context */ \
  84. ENTRY( C_, "c:", 0 ) /* Track empty ctx */ \
  85. ENTRY( PFX_NS, "pfx:ns", 0 ) /* Prefix to NS */ \
  86. /**
  87. * Lookup DBs. These are indices and may be destroyed and rebuilt.
  88. */
  89. #define LOOKUP_TABLE \
  90. ENTRY( S_PO, "s:po", DUPFIXED_MASK ) /* 1-bound lookup */ \
  91. ENTRY( P_SO, "p:so", DUPFIXED_MASK ) /* 1-bound lookup */ \
  92. ENTRY( O_SP, "o:sp", DUPFIXED_MASK ) /* 1-bound lookup */ \
  93. ENTRY( PO_S, "po:s", DUPFIXED_MASK ) /* 2-bound lookup */ \
  94. ENTRY( SO_P, "so:p", DUPFIXED_MASK ) /* 2-bound lookup */ \
  95. ENTRY( SP_O, "sp:o", DUPFIXED_MASK ) /* 2-bound lookup */ \
  96. ENTRY( C_SPO, "c:spo", DUPFIXED_MASK ) /* Context lookup */ \
  97. ENTRY( NS_PFX, "ns:pfx", DUPSORT_MASK ) /* NS to prefix */ \
  98. /**
  99. * DB labels. They are prefixed with DB_
  100. */
  101. #define ENTRY(a, b, c) static const DbLabel DB_##a = b;
  102. MAIN_TABLE
  103. LOOKUP_TABLE
  104. #undef ENTRY
  105. /**
  106. * Numeric index of each DB. Prefixed with IDX_
  107. *
  108. * These index numbers are referred to in all the arrays defeined below. They
  109. * are independent from the LMDB dbi values which are considered opaque here.
  110. */
  111. typedef enum {
  112. #define ENTRY(a, b, c) IDX_##a,
  113. MAIN_TABLE
  114. LOOKUP_TABLE
  115. #undef ENTRY
  116. } DBIdx;
  117. /**
  118. * DB labels.
  119. */
  120. static const char *db_labels[N_DB] = {
  121. #define ENTRY(a, b, c) DB_##a,
  122. MAIN_TABLE
  123. LOOKUP_TABLE
  124. #undef ENTRY
  125. };
  126. /**
  127. * DB flags. These are aligned with the dbi_labels index.
  128. */
  129. static const unsigned int db_flags[N_DB] = {
  130. #define ENTRY(a, b, c) c,
  131. MAIN_TABLE
  132. LOOKUP_TABLE
  133. #undef ENTRY
  134. };
  135. /**
  136. * 1-bound and 2-bound lookup indices.
  137. *
  138. * N.B. Only the first 6 (1-bound and 2-bound term lookup) are used.
  139. * The others are added just because they belong logically to the lookup table.
  140. */
  141. static DBIdx lookup_indices[9] = {
  142. #define ENTRY(a, b, c) IDX_##a,
  143. LOOKUP_TABLE
  144. #undef ENTRY
  145. };
  146. static const uint8_t lookup_ordering_1bound[3][3] = {
  147. {0, 1, 2}, // s:po
  148. {1, 0, 2}, // p:so
  149. {2, 0, 1}, // o:sp
  150. };
  151. static const uint8_t lookup_ordering_2bound[3][3] = {
  152. {1, 2, 0}, // po:s
  153. {0, 2, 1}, // so:p
  154. {0, 1, 2}, // sp:o
  155. };
  156. /**
  157. * Static prototypes.
  158. */
  159. static int index_triple(
  160. LSUP_MDBStore *store, StoreOp op, LSUP_TripleKey spok, LSUP_Key ck);
  161. inline static LSUP_rc lookup_0bound(
  162. MDBStore *store, MDBIterator *it, size_t *ct);
  163. inline static LSUP_rc lookup_1bound(
  164. MDBStore *store, uint8_t idx0, MDBIterator *it, size_t *ct);
  165. inline static LSUP_rc lookup_2bound(
  166. MDBStore *store, uint8_t idx0, uint8_t idx1,
  167. MDBIterator *it, size_t *ct);
  168. inline static LSUP_rc lookup_3bound(
  169. MDBStore *store, MDBIterator *it, size_t *ct);
  170. /**
  171. * API.
  172. */
  173. LSUP_rc
  174. LSUP_mdbstore_setup (char *path, bool clear)
  175. {
  176. int rc;
  177. // Set environment path.
  178. if (!path) return LSUP_ERROR;
  179. // TODO Verify that a writable directory exists or can be created.
  180. //struct stat path_stat;
  181. if (clear) rm_r (path);
  182. if (mkdir_p (path, ENV_DIR_MODE) != 0) return LSUP_IO_ERR;
  183. // Open a temporary environment and txn to create the DBs.
  184. MDB_env *env;
  185. mdb_env_create (&env);
  186. mdb_env_set_maxdbs (env, N_DB);
  187. mdb_env_open (env, path, 0, ENV_FILE_MODE);
  188. MDB_txn *txn;
  189. mdb_txn_begin (env, NULL, 0, &txn);
  190. for (int i = 0; i < N_DB; i++) {
  191. TRACE ("Creating DB %s", db_labels[i]);
  192. MDB_dbi dbi;
  193. rc = mdb_dbi_open (txn, db_labels[i], db_flags[i] | MDB_CREATE, &dbi);
  194. if (rc != MDB_SUCCESS) return rc;
  195. }
  196. mdb_txn_commit (txn);
  197. mdb_env_close (env);
  198. return rc;
  199. }
  200. MDBStore *
  201. LSUP_mdbstore_new (const char *path, const LSUP_Buffer *default_ctx)
  202. {
  203. int db_rc;
  204. LSUP_MDBStore *store;
  205. MALLOC_GUARD (store, NULL);
  206. db_rc = mdb_env_create (&store->env);
  207. TRACE ("create rc: %d", db_rc);
  208. store->default_ctx = (
  209. default_ctx ?
  210. LSUP_buffer_new (default_ctx->size, default_ctx->addr) : NULL);
  211. // Set map size.
  212. size_t mapsize;
  213. char *env_mapsize = getenv ("LSUP_MDB_MAPSIZE");
  214. if (env_mapsize == NULL) mapsize = DEFAULT_MAPSIZE;
  215. else sscanf (env_mapsize, "%lu", &mapsize);
  216. TRACE(
  217. "Setting environment map size at %s to %lu bytes.\n",
  218. path, mapsize);
  219. db_rc = mdb_env_set_mapsize (store->env, mapsize);
  220. db_rc = mdb_env_set_maxdbs (store->env, N_DB);
  221. if (UNLIKELY (db_rc != MDB_SUCCESS)) return NULL;
  222. db_rc = mdb_env_open (store->env, path, 0, ENV_FILE_MODE);
  223. if (UNLIKELY (db_rc != MDB_SUCCESS)) return NULL;
  224. // Assign DB handles to store->dbi.
  225. MDB_txn *txn;
  226. mdb_txn_begin (store->env, NULL, 0, &txn);
  227. for (int i = 0; i < N_DB; i++) {
  228. db_rc = mdb_dbi_open (txn, db_labels[i], db_flags[i], store->dbi + i);
  229. if (UNLIKELY (db_rc != MDB_SUCCESS)) {
  230. mdb_txn_abort (txn);
  231. return NULL;
  232. }
  233. }
  234. mdb_txn_commit (txn);
  235. store->state |= LSSTORE_OPEN;
  236. store->txn = NULL;
  237. return store;
  238. }
  239. void
  240. LSUP_mdbstore_free (LSUP_MDBStore *store)
  241. {
  242. if (store->state & LSSTORE_OPEN) {
  243. const char *path;
  244. mdb_env_get_path (store->env, &path);
  245. TRACE ("Closing MDB env at %s.\n", path);
  246. mdb_env_close (store->env);
  247. }
  248. if (store->default_ctx) {
  249. LSUP_buffer_done (store->default_ctx);
  250. free (store->default_ctx);
  251. }
  252. free (store);
  253. }
  254. LSUP_rc
  255. LSUP_mdbstore_stat (LSUP_MDBStore *store, MDB_stat *stat)
  256. {
  257. if (!(store->state & LSSTORE_INIT)) return 0;
  258. MDB_txn *txn;
  259. mdb_txn_begin (store->env, NULL, MDB_RDONLY, &txn);
  260. if (mdb_stat (txn, store->dbi[IDX_SPO_C], stat) != MDB_SUCCESS)
  261. return LSUP_DB_ERR;
  262. mdb_txn_abort (txn);
  263. return LSUP_OK;
  264. }
  265. size_t
  266. LSUP_mdbstore_size (LSUP_MDBStore *store)
  267. {
  268. // Size is calculated outside of any pending write txn.
  269. MDB_stat stat;
  270. if (LSUP_mdbstore_stat (store, &stat) != LSUP_OK) return 0;
  271. return stat.ms_entries;
  272. }
  273. MDBIterator *
  274. LSUP_mdbstore_add_init (LSUP_MDBStore *store, const LSUP_Buffer *sc)
  275. {
  276. /* An iterator is used here. Some members are a bit misused but it does
  277. * its job without having to define a very similar struct.
  278. */
  279. MDBIterator *it;
  280. MALLOC_GUARD (it, NULL);
  281. it->store = store;
  282. it->i = 0;
  283. // No other write transaction may be open.
  284. mdb_txn_begin (store->env, NULL, 0, &it->store->txn);
  285. // Take care of context first.
  286. // Serialize and hash.
  287. it->ck = NULL_KEY;
  288. if (store->default_ctx != NULL) {
  289. if (sc == NULL) sc = store->default_ctx;
  290. it->ck = LSUP_buffer_hash (sc);
  291. // Insert t:st for context.
  292. //TRACE ("Adding context: %s", sc);
  293. it->key.mv_data = &it->ck;
  294. it->key.mv_size = KLEN;
  295. it->data.mv_data = sc->addr;
  296. it->data.mv_size = sc->size;
  297. if (mdb_put(
  298. it->store->txn, it->store->dbi[IDX_T_ST],
  299. &it->key, &it->data, MDB_NOOVERWRITE) != MDB_SUCCESS)
  300. return NULL;
  301. }
  302. return it;
  303. }
  304. LSUP_rc
  305. LSUP_mdbstore_add_iter (MDBIterator *it, const LSUP_SerTriple *sspo)
  306. {
  307. int db_rc;
  308. LSUP_TripleKey spok = NULL_TRP;
  309. // Add triple.
  310. for (int i = 0; i < 3; i++) {
  311. LSUP_Buffer *st = LSUP_striple_pos (sspo, i);
  312. #ifdef DEBUG3
  313. printf ("Inserting term: ");
  314. LSUP_buffer_print (st);
  315. printf ("\n");
  316. #endif
  317. spok[i] = LSUP_buffer_hash (st);
  318. it->key.mv_data = spok + i;
  319. it->key.mv_size = KLEN;
  320. it->data.mv_data = st->addr;
  321. it->data.mv_size = st->size;
  322. db_rc = mdb_put(
  323. it->store->txn, it->store->dbi[IDX_T_ST],
  324. &it->key, &it->data, MDB_NOOVERWRITE);
  325. if (db_rc != MDB_SUCCESS && db_rc != MDB_KEYEXIST) {
  326. fprintf (
  327. stderr, "MDB error while inserting term: %s\n",
  328. mdb_strerror(db_rc));
  329. return LSUP_DB_ERR;
  330. }
  331. }
  332. TRACE ("Inserting spok: {%lx, %lx, %lx}", spok[0], spok[1], spok[2]);
  333. // Insert spo:c.
  334. it->key.mv_data = spok;
  335. it->key.mv_size = TRP_KLEN;
  336. // In triple mode, data is empty (= NULL_KEY).
  337. it->data.mv_data = &it->ck;
  338. it->data.mv_size = it->ck == NULL_KEY ? 0 : KLEN;
  339. db_rc = mdb_put(
  340. it->store->txn, it->store->dbi[IDX_SPO_C],
  341. &it->key, &it->data, MDB_NODUPDATA);
  342. if (db_rc == MDB_KEYEXIST) return LSUP_NOACTION;
  343. if (db_rc != MDB_SUCCESS) {
  344. fprintf (
  345. stderr, "MDB error while inserting triple: %s\n",
  346. mdb_strerror(db_rc));
  347. return LSUP_DB_ERR;
  348. }
  349. // Index.
  350. LSUP_rc rc = index_triple (it->store, OP_ADD, spok, it->ck);
  351. if (rc == LSUP_OK) it->i++;
  352. return rc;
  353. }
  354. LSUP_rc
  355. LSUP_mdbstore_add_done (MDBIterator *it)
  356. {
  357. LSUP_rc rc = LSUP_OK;
  358. if (mdb_txn_commit (it->store->txn) != MDB_SUCCESS) {
  359. mdb_txn_abort (it->store->txn);
  360. rc = LSUP_DB_ERR;
  361. }
  362. it->store->txn = NULL;
  363. free (it);
  364. return rc;
  365. }
  366. void
  367. LSUP_mdbstore_add_abort (MDBIterator *it)
  368. {
  369. mdb_txn_abort (it->store->txn);
  370. it->store->txn = NULL;
  371. free (it);
  372. }
  373. LSUP_rc
  374. LSUP_mdbstore_add (
  375. LSUP_MDBStore *store, const LSUP_Buffer *sc,
  376. const LSUP_SerTriple strp[], const size_t ct, size_t *inserted)
  377. {
  378. MDBIterator *it = LSUP_mdbstore_add_init (store, sc);
  379. if (UNLIKELY (!it)) return LSUP_DB_ERR;
  380. for (size_t i = 0; i < ct; i++) {
  381. LSUP_rc rc = LSUP_mdbstore_add_iter (it, strp + i);
  382. if (UNLIKELY (rc < 0)) {
  383. LSUP_mdbstore_add_abort (it);
  384. return rc;
  385. }
  386. }
  387. *inserted = it->i;
  388. return LSUP_mdbstore_add_done (it);
  389. }
  390. static LSUP_Key __attribute__ ((unused))
  391. sterm_to_key (
  392. LSUP_MDBStore *store, const LSUP_Buffer *sterm)
  393. {
  394. // TODO this will be replaced by a lookup when 128-bit hash is introduced.
  395. return LSUP_buffer_hash (sterm);
  396. }
  397. static LSUP_rc
  398. key_to_sterm(
  399. LSUP_MDBStore *store, const LSUP_Key key, LSUP_Buffer *sterm,
  400. MDB_txn *txn)
  401. {
  402. LSUP_rc rc = LSUP_NORESULT;
  403. int db_rc;
  404. bool txn_dirty = false;
  405. if (!txn) {
  406. db_rc = mdb_txn_begin (store->env, NULL, MDB_RDONLY, &txn);
  407. if (db_rc != MDB_SUCCESS) return LSUP_DB_ERR;
  408. txn_dirty = true;
  409. }
  410. MDB_val key_v, data_v;
  411. key_v.mv_data = (void*)&key;
  412. key_v.mv_size = KLEN;
  413. db_rc = mdb_get (txn, store->dbi[IDX_T_ST], &key_v, &data_v);
  414. if (db_rc == MDB_SUCCESS) {
  415. sterm->addr = data_v.mv_data;
  416. sterm->size = data_v.mv_size;
  417. rc = LSUP_OK;
  418. } else if (db_rc == MDB_NOTFOUND) {
  419. free (sterm->addr);
  420. sterm->addr = NULL;
  421. sterm->size = 0;
  422. } else rc = LSUP_ERROR;
  423. if (txn_dirty) mdb_txn_abort (txn);
  424. return rc;
  425. }
  426. MDBIterator *
  427. LSUP_mdbstore_lookup(
  428. LSUP_MDBStore *store, const LSUP_SerTriple *sspo,
  429. const LSUP_Buffer *sc, size_t *ct)
  430. {
  431. LSUP_TripleKey spok = {
  432. LSUP_buffer_hash (sspo->s),
  433. LSUP_buffer_hash (sspo->p),
  434. LSUP_buffer_hash (sspo->o),
  435. };
  436. LSUP_MDBIterator *it;
  437. CALLOC_GUARD (it, NULL);
  438. it->store = store;
  439. it->ck = store->default_ctx ? LSUP_buffer_hash (sc) : NULL_KEY;
  440. if (ct) *ct = 0;
  441. uint8_t idx0, idx1;
  442. // s p o (all terms bound)
  443. if (spok[0] != NULL_KEY && spok[1] != NULL_KEY && spok[2] != NULL_KEY) {
  444. it->luk[0] = spok[0];
  445. it->luk[1] = spok[1];
  446. it->luk[2] = spok[2];
  447. RCNL (lookup_3bound (store, it, ct));
  448. } else if (spok[0] != NULL_KEY) {
  449. it->luk[0] = spok[0];
  450. idx0 = 0;
  451. // s p ?
  452. if (spok[1] != NULL_KEY) {
  453. it->luk[1] = spok[1];
  454. idx1 = 1;
  455. RCNL (lookup_2bound (store, idx0, idx1, it, ct));
  456. // s ? o
  457. } else if (spok[2] != NULL_KEY) {
  458. it->luk[1] = spok[2];
  459. idx1 = 2;
  460. RCNL (lookup_2bound (store, idx0, idx1, it, ct));
  461. // s ? ?
  462. } else RCNL (lookup_1bound (store, idx0, it, ct));
  463. } else if (spok[1] != NULL_KEY) {
  464. it->luk[0] = spok[1];
  465. idx0 = 1;
  466. // ? p o
  467. if (spok[2] != NULL_KEY) {
  468. it->luk[1] = spok[2];
  469. idx1 = 2;
  470. RCNL (lookup_2bound (store, idx0, idx1, it, ct));
  471. // ? p ?
  472. } else RCNL (lookup_1bound (store, idx0, it, ct));
  473. // ? ? o
  474. } else if (spok[2] != NULL_KEY) {
  475. it->luk[0] = spok[2];
  476. idx0 = 2;
  477. RCNL (lookup_1bound (store, idx0, it, ct));
  478. // ? ? ? (all terms unbound)
  479. } else RCNL (lookup_0bound (store, it, ct));
  480. return it;
  481. }
  482. inline static LSUP_rc
  483. mdbiter_next_key (LSUP_MDBIterator *it)
  484. {
  485. if (UNLIKELY (!it)) return LSUP_VALUE_ERR;
  486. // Only advance if the previous it->rc wasn't already at the end.
  487. if (it->rc == MDB_NOTFOUND) return LSUP_END;
  488. if (UNLIKELY (it->rc != MDB_SUCCESS)) {
  489. fprintf (
  490. stderr, "%s:%d [%s]: Database error: %s\n",
  491. __FILE__, __LINE__, __func__, mdb_strerror (it->rc));
  492. return LSUP_DB_ERR;
  493. }
  494. LSUP_rc rc;
  495. /* Retrieve current value and advance cursor to the next result.
  496. * it->rc is set to the result of the next iteration.
  497. */
  498. it->iter_op_fn (it);
  499. TRACE (
  500. "Found spok: {%lx, %lx, %lx}",
  501. it->spok[0], it->spok[1], it->spok[2]);
  502. if (it->ck) {
  503. rc = LSUP_NORESULT; // Intermediary value, will never be returned.
  504. MDB_cursor *cur;
  505. MDB_val key, data;
  506. int db_rc;
  507. db_rc = mdb_cursor_open (it->txn, it->store->dbi[IDX_SPO_C], &cur);
  508. if (UNLIKELY (db_rc != MDB_SUCCESS)) {
  509. fprintf (
  510. stderr, "%s:%d [%s]: Database error: %s\n",
  511. __FILE__, __LINE__, __func__, mdb_strerror (db_rc));
  512. return LSUP_DB_ERR;
  513. }
  514. key.mv_size = TRP_KLEN;
  515. data.mv_data = &it->ck;
  516. data.mv_size = KLEN;
  517. while (rc == LSUP_NORESULT) {
  518. //TRACE (STR, "begin ctx loop.");
  519. // If ctx is specified, look if the matching triple is associated
  520. // with it. If not, move on to the next triple.
  521. // The loop normally exits when a triple with matching ctx is found
  522. // (LSUP_OK), if there are no more triples (LSUP_END), or if there
  523. // is an error (LSUP_DB_ERR).
  524. key.mv_data = it->spok;
  525. db_rc = mdb_cursor_get (cur, &key, &data, MDB_GET_BOTH);
  526. if (db_rc == MDB_SUCCESS) {
  527. rc = LSUP_OK;
  528. //TRACE (STR, "Triple found for context.");
  529. }
  530. else if (db_rc == MDB_NOTFOUND) {
  531. //TRACE (STR, "No triples found for context.");
  532. if (it->rc == MDB_NOTFOUND) rc = LSUP_END;
  533. else it->iter_op_fn (it);
  534. } else {
  535. fprintf (stderr, mdb_strerror (it->rc));
  536. rc = LSUP_DB_ERR;
  537. }
  538. }
  539. mdb_cursor_close (cur);
  540. } else rc = LSUP_OK;
  541. return rc;
  542. }
  543. LSUP_rc
  544. LSUP_mdbiter_next (LSUP_MDBIterator *it, LSUP_SerTriple *sspo)
  545. {
  546. LSUP_rc rc = mdbiter_next_key (it);
  547. if (sspo && rc == LSUP_OK) {
  548. key_to_sterm (it->store, it->spok[0], sspo->s, it->txn);
  549. key_to_sterm (it->store, it->spok[1], sspo->p, it->txn);
  550. key_to_sterm (it->store, it->spok[2], sspo->o, it->txn);
  551. // TODO error handling.
  552. }
  553. return rc;
  554. }
  555. size_t
  556. LSUP_mdbiter_cur (LSUP_MDBIterator *it)
  557. { return it->i; }
  558. void
  559. LSUP_mdbiter_free (MDBIterator *it)
  560. {
  561. if (it) {
  562. if (it->cur) mdb_cursor_close (it->cur);
  563. if (it->store->txn != it->txn) mdb_txn_abort (it->txn);
  564. free (it);
  565. }
  566. }
  567. LSUP_rc
  568. LSUP_mdbstore_remove(
  569. MDBStore *store, const LSUP_SerTriple *sspo,
  570. const LSUP_Buffer *sc, size_t *ct)
  571. {
  572. LSUP_rc rc = LSUP_NOACTION;
  573. LSUP_Key ck = NULL_KEY;
  574. if (store->default_ctx != NULL) {
  575. if (sc == NULL) sc = store->default_ctx;
  576. ck = LSUP_buffer_hash (sc);
  577. }
  578. MDB_txn *txn;
  579. mdb_txn_begin (store->env, NULL, 0, &txn);
  580. MDB_cursor *dcur, *icur;
  581. mdb_cursor_open (txn, store->dbi[IDX_SPO_C], &dcur);
  582. mdb_cursor_open (txn, store->dbi[IDX_C_SPO], &icur);
  583. MDB_val spok_v, ck_v;
  584. spok_v.mv_size = TRP_KLEN;
  585. ck_v.mv_size = KLEN;
  586. ck_v.mv_data = &ck;
  587. LSUP_MDBIterator *it = LSUP_mdbstore_lookup (store, sspo, sc, ct);
  588. if (UNLIKELY (!it)) return LSUP_DB_ERR;
  589. if (ct) TRACE ("Found %lu triples to remove.", *ct);
  590. while (mdbiter_next_key (it) == LSUP_OK) {
  591. spok_v.mv_data = it->spok;
  592. rc = mdb_cursor_get (dcur, &spok_v, &ck_v, MDB_GET_BOTH);
  593. if (rc == MDB_NOTFOUND) continue;
  594. if (UNLIKELY (rc != MDB_SUCCESS)) goto _remove_abort;
  595. TRACE (
  596. "Removing {%lx, %lx, %lx}",
  597. it->spok[0], it->spok[1], it->spok[2]);
  598. // Delete spo:c entry.
  599. rc = mdb_cursor_del (dcur, 0);
  600. if (UNLIKELY (rc != MDB_SUCCESS)) goto _remove_abort;
  601. // Restore ck address after each delete.
  602. spok_v.mv_data = it->spok;
  603. ck_v.mv_data = &ck;
  604. // Delete c:spo entry.
  605. rc = mdb_cursor_get (icur, &ck_v, &spok_v, MDB_GET_BOTH);
  606. if (rc == MDB_NOTFOUND) continue;
  607. if (UNLIKELY (rc != MDB_SUCCESS)) goto _remove_abort;
  608. rc = mdb_cursor_del (icur, 0);
  609. if (UNLIKELY (rc != MDB_SUCCESS)) goto _remove_abort;
  610. spok_v.mv_data = it->spok;
  611. ck_v.mv_data = &ck;
  612. // If there are no more contexts associated with this triple,
  613. // remove from indices.
  614. rc = mdb_cursor_get (dcur, &spok_v, NULL, MDB_SET);
  615. if (rc == MDB_SUCCESS) continue;
  616. if (UNLIKELY (rc != MDB_NOTFOUND)) goto _remove_abort;
  617. index_triple (store, OP_REMOVE, it->spok, ck);
  618. }
  619. LSUP_mdbiter_free (it);
  620. if (UNLIKELY (mdb_txn_commit (txn) != MDB_SUCCESS)) {
  621. rc = LSUP_TXN_ERR;
  622. goto _remove_abort;
  623. }
  624. return rc;
  625. _remove_abort:
  626. mdb_txn_abort (txn);
  627. fprintf (
  628. stderr, "%s:%d [%s]: Database error: %s\n",
  629. __FILE__, __LINE__, __func__, mdb_strerror (rc));
  630. return rc;
  631. }
  632. /* * * Static functions. * * */
  633. /** @brief Index an added or removed triple.
  634. *
  635. * @param store[in] MDB store to index.
  636. * @param op[in] Store operation. One of OP_ADD or OP_REMOVE.
  637. * @param spok[in] Triple key to index.
  638. * @param ck[in] Context to index, may be NULL.
  639. */
  640. static LSUP_rc
  641. index_triple(
  642. LSUP_MDBStore *store, StoreOp op, LSUP_TripleKey spok, LSUP_Key ck)
  643. {
  644. int db_rc;
  645. LSUP_rc rc = LSUP_NOACTION;
  646. MDB_val v1, v2;
  647. TRACE ("Indexing triple: %lx %lx %lx\n", spok[0], spok[1], spok[2]);
  648. // Index c:spo.
  649. if (op == OP_REMOVE) {
  650. TRACE (STR, "Indexing op: REMOVE");
  651. if (ck != NULL_KEY) {
  652. MDB_cursor *cur;
  653. v1.mv_data = &ck;
  654. v1.mv_size = KLEN;
  655. v2.mv_data = spok;
  656. v2.mv_size = TRP_KLEN;
  657. mdb_cursor_open (store->txn, store->dbi[IDX_C_SPO], &cur);
  658. if (mdb_cursor_get (cur, &v1, &v2, MDB_GET_BOTH) == MDB_SUCCESS) {
  659. db_rc = mdb_cursor_del (cur, 0);
  660. if (db_rc != MDB_SUCCESS) return LSUP_DB_ERR;
  661. rc = LSUP_OK;
  662. }
  663. mdb_cursor_close (cur);
  664. }
  665. } else if (op == OP_ADD) {
  666. TRACE (STR, "Indexing op: ADD");
  667. if (ck != NULL_KEY) {
  668. v1.mv_data = &ck;
  669. v1.mv_size = KLEN;
  670. v2.mv_data = spok;
  671. v2.mv_size = TRP_KLEN;
  672. db_rc = mdb_put(
  673. store->txn, store->dbi[IDX_C_SPO],
  674. &v1, &v2, MDB_NODUPDATA);
  675. if (db_rc != MDB_SUCCESS) return LSUP_DB_ERR;
  676. if (db_rc != MDB_KEYEXIST) rc = LSUP_OK;
  677. }
  678. } else return LSUP_VALUE_ERR;
  679. LSUP_DoubleKey dbl_keys[3] = {
  680. {spok[1], spok[2]}, // po
  681. {spok[0], spok[2]}, // so
  682. {spok[0], spok[1]}, // sp
  683. };
  684. // Add terms to index.
  685. v1.mv_size = KLEN;
  686. v2.mv_size = DBL_KLEN;
  687. for (int i = 0; i < 3; i++) {
  688. MDB_dbi db1 = store->dbi[lookup_indices[i]]; // s:po, p:so, o:sp
  689. MDB_dbi db2 = store->dbi[lookup_indices[i + 3]]; // po:s, so:p, sp:o
  690. v1.mv_data = spok + i;
  691. v2.mv_data = dbl_keys[i];
  692. if (op == OP_REMOVE) {
  693. MDB_cursor *cur1, *cur2;
  694. mdb_cursor_open(
  695. store->txn, store->dbi[lookup_indices[i]], &cur1);
  696. db_rc = mdb_cursor_get (cur1, &v1, &v2, MDB_GET_BOTH);
  697. if (db_rc == MDB_SUCCESS) mdb_cursor_del (cur1, 0);
  698. mdb_cursor_close (cur1);
  699. // Restore pointers invalidated after delete.
  700. v1.mv_data = spok + i;
  701. v2.mv_data = dbl_keys[i];
  702. mdb_cursor_open(
  703. store->txn, store->dbi[lookup_indices[i + 3]], &cur2);
  704. db_rc = mdb_cursor_get (cur2, &v2, &v1, MDB_GET_BOTH);
  705. if (db_rc == MDB_SUCCESS) mdb_cursor_del (cur2, 0);
  706. // TODO error handling.
  707. rc = LSUP_OK;
  708. mdb_cursor_close (cur2);
  709. } else { // OP_ADD is guaranteed.
  710. // 1-bound index.
  711. /*
  712. TRACE ("Indexing in %s: ", db_labels[lookup_indices[i]]);
  713. TRACE(
  714. "%lx: %lx %lx\n", *(size_t*)(v1.mv_data),
  715. *(size_t*)(v2.mv_data), *(size_t*)(v2.mv_data) + 1);
  716. */
  717. db_rc = mdb_put (store->txn, db1, &v1, &v2, MDB_NODUPDATA);
  718. if (db_rc == MDB_SUCCESS) rc = LSUP_OK;
  719. else if (db_rc != MDB_KEYEXIST) return LSUP_DB_ERR;
  720. // 2-bound index.
  721. /*
  722. TRACE ("Indexing in %s: ", db_labels[lookup_indices[i + 3]]);
  723. TRACE(
  724. "%lx %lx: %lx\n", *(size_t*)(v2.mv_data),
  725. *(size_t*)(v2.mv_data) + 1, *(size_t*)(v1.mv_data));
  726. */
  727. db_rc = mdb_put (store->txn, db2, &v2, &v1, MDB_NODUPDATA);
  728. if (db_rc == MDB_SUCCESS) rc = LSUP_OK;
  729. else if (db_rc != MDB_KEYEXIST) return LSUP_DB_ERR;
  730. }
  731. }
  732. return rc;
  733. }
  734. /* * * Term-specific iterators. * * */
  735. /** @brief Advance 0-bound iterator.
  736. *
  737. * Cursor: spo:c
  738. */
  739. inline static void
  740. it_next_0bound (MDBIterator *it)
  741. {
  742. memcpy (it->spok, it->data.mv_data, sizeof (LSUP_TripleKey));
  743. it->rc = mdb_cursor_get (it->cur, &it->key, NULL, MDB_NEXT);
  744. }
  745. /** @brief Advance 1-bound iterator.
  746. *
  747. * Uses paged data in a nested loop.
  748. *
  749. * Cursor: s:po, p:so, or o:sp.
  750. */
  751. inline static void
  752. it_next_1bound (MDBIterator *it)
  753. {
  754. LSUP_DoubleKey *lu_dset = it->data.mv_data;
  755. it->spok[it->term_order[0]] = it->luk[0];
  756. it->spok[it->term_order[1]] = lu_dset[it->i][0];
  757. it->spok[it->term_order[2]] = lu_dset[it->i][1];
  758. TRACE(
  759. "Composed triple: {%lx %lx %lx}",
  760. it->spok[0], it->spok[1], it->spok[2]);
  761. // Ensure next block within the same page is not beyond the last.
  762. if (it->i < it->data.mv_size / DBL_KLEN - 1) {
  763. it->i ++;
  764. //TRACE ("Increasing page cursor to %lu.", it->i);
  765. //TRACE ("it->rc: %d", it->rc);
  766. } else {
  767. // If the last block in the page is being yielded,
  768. // move cursor to beginning of next page.
  769. it->i = 0;
  770. //TRACE ("Reset page cursor to %lu.", it->i);
  771. it->rc = mdb_cursor_get (
  772. it->cur, &it->key, &it->data, MDB_NEXT_MULTIPLE);
  773. }
  774. }
  775. /** @brief Advance 2-bound iterator.
  776. *
  777. * Uses paged data in a nested loop.
  778. *
  779. * Cursor: po:s, so:p, or sp:o.
  780. */
  781. inline static void
  782. it_next_2bound (MDBIterator *it)
  783. {
  784. LSUP_Key *lu_dset = it->data.mv_data;
  785. it->spok[it->term_order[0]] = it->luk[0];
  786. it->spok[it->term_order[1]] = it->luk[1];
  787. it->spok[it->term_order[2]] = lu_dset[it->i];
  788. // Ensure next block within the same page is not beyond the last.
  789. if (it->i < it->data.mv_size / KLEN - 1)
  790. it->i ++;
  791. else {
  792. // If the last block in the page is being yielded,
  793. // move cursor to beginning of next page.
  794. it->i = 0;
  795. it->rc = mdb_cursor_get (it->cur, &it->key, &it->data, MDB_NEXT_MULTIPLE);
  796. }
  797. }
  798. /** @brief Advance 3-bound iterator.
  799. *
  800. * This is a special case of 0÷1 results; either there was one matching triple,
  801. * which was already set in the first result, or there was none, i.e. it->rc is
  802. * already MDB_NOTFOUND and this function will not be called.
  803. */
  804. inline static void
  805. it_next_3bound (MDBIterator *it)
  806. {
  807. it->rc = MDB_NOTFOUND;
  808. }
  809. /* * * Term-specific lookups. * * */
  810. inline static LSUP_rc
  811. lookup_0bound (MDBStore *store, MDBIterator *it, size_t *ct)
  812. {
  813. if (store->txn) it->txn = store->txn;
  814. else {
  815. it->rc = mdb_txn_begin (store->env, NULL, MDB_RDONLY, &it->txn);
  816. if (it->rc != MDB_SUCCESS) {
  817. fprintf (
  818. stderr, "%s:%d [%s]: Database error: %s\n",
  819. __FILE__, __LINE__, __func__, mdb_strerror (it->rc));
  820. return LSUP_DB_ERR;
  821. }
  822. }
  823. if (ct) {
  824. if (it->ck != NULL_KEY) {
  825. // Look up by given context.
  826. it->rc = mdb_cursor_open (it->txn, store->dbi[IDX_C_SPO], &it->cur);
  827. it->key.mv_data = &it->ck;
  828. it->key.mv_size = KLEN;
  829. it->rc = mdb_cursor_get (it->cur, &it->key, NULL, MDB_SET);
  830. if (it->rc == MDB_SUCCESS) mdb_cursor_count (it->cur, ct);
  831. mdb_cursor_close (it->cur);
  832. it->cur = NULL;
  833. } else {
  834. // Look up all contexts.
  835. MDB_stat stat;
  836. mdb_stat (it->txn, store->dbi[IDX_S_PO], &stat);
  837. *ct = stat.ms_entries;
  838. }
  839. TRACE ("Found %lu keys.", *ct);
  840. }
  841. mdb_cursor_open (it->txn, store->dbi[IDX_SPO_C], &it->cur);
  842. it->rc = mdb_cursor_get (it->cur, &it->key, &it->data, MDB_FIRST);
  843. mdb_cursor_close (it->cur);
  844. it->cur = NULL;
  845. it->iter_op_fn = it_next_0bound;
  846. if (it->rc != MDB_SUCCESS && it->rc != MDB_NOTFOUND) {
  847. fprintf (
  848. stderr, "%s:%d [%s]: Database error: %s\n",
  849. __FILE__, __LINE__, __func__, mdb_strerror (it->rc));
  850. return LSUP_DB_ERR;
  851. }
  852. return LSUP_OK;
  853. }
  854. inline static LSUP_rc
  855. lookup_1bound (MDBStore *store, uint8_t idx0, MDBIterator *it, size_t *ct)
  856. {
  857. it->term_order = (const uint8_t*)lookup_ordering_1bound[idx0];
  858. TRACE ("Looking up 1 bound term: %lx\n", it->luk[0]);
  859. if (!it->txn) {
  860. if (store->txn) it->txn = store->txn;
  861. else {
  862. it->rc = mdb_txn_begin (store->env, NULL, MDB_RDONLY, &it->txn);
  863. if (it->rc != MDB_SUCCESS) {
  864. fprintf (
  865. stderr, "%s:%d [%s]: Database error: %s",
  866. __FILE__, __LINE__, __func__, mdb_strerror (it->rc));
  867. return LSUP_DB_ERR;
  868. }
  869. }
  870. }
  871. mdb_cursor_open (it->txn, store->dbi[lookup_indices[idx0]], &it->cur);
  872. it->key.mv_data = it->luk;
  873. it->key.mv_size = KLEN;
  874. if (ct) {
  875. // If a context is specified, the only way to count triples matching
  876. // the context is to loop over them.
  877. if (it->ck != NULL_KEY) {
  878. TRACE ("Counting in context: %lx\n", it->ck);
  879. MDBIterator *ct_it;
  880. MALLOC_GUARD (ct_it, LSUP_MEM_ERR);
  881. ct_it->luk[0] = it->luk[0];
  882. /*
  883. LSUP_TripleKey ct_spok;
  884. memcpy (ct_it->spok, ct_spok, sizeof (LSUP_TripleKey));
  885. */
  886. ct_it->ck = it->ck;
  887. ct_it->store = it->store;
  888. ct_it->txn = it->txn;
  889. ct_it->key = it->key;
  890. ct_it->data = it->data;
  891. ct_it->i = 0;
  892. LSUP_rc rc = lookup_1bound (store, idx0, ct_it, NULL);
  893. if (rc < 0) return rc;
  894. while (LSUP_mdbiter_next (ct_it, NULL) != LSUP_END) {
  895. (*ct)++;
  896. TRACE ("Counter increased to %lu.", *ct);
  897. }
  898. // Free the counter iterator without freeing the shared txn.
  899. if (ct_it->cur) mdb_cursor_close (ct_it->cur);
  900. free (ct_it);
  901. } else {
  902. it->rc = mdb_cursor_get (it->cur, &it->key, &it->data, MDB_SET);
  903. if (it->rc == MDB_SUCCESS) mdb_cursor_count (it->cur, ct);
  904. }
  905. }
  906. it->i = 0;
  907. it->iter_op_fn = it_next_1bound;
  908. it->rc = mdb_cursor_get (it->cur, &it->key, &it->data, MDB_SET);
  909. if (it->rc == MDB_SUCCESS)
  910. it->rc = mdb_cursor_get (it->cur, &it->key, &it->data, MDB_GET_MULTIPLE);
  911. if (it->rc != MDB_SUCCESS && it->rc != MDB_NOTFOUND) {
  912. fprintf (
  913. stderr, "%s:%d [%s]: Database error: %s",
  914. __FILE__, __LINE__, __func__, mdb_strerror (it->rc));
  915. return LSUP_DB_ERR;
  916. }
  917. return LSUP_OK;
  918. }
  919. inline static LSUP_rc
  920. lookup_2bound(
  921. MDBStore *store, uint8_t idx0, uint8_t idx1,
  922. MDBIterator *it, size_t *ct)
  923. {
  924. uint8_t luk1_offset, luk2_offset;
  925. MDB_dbi dbi = 0;
  926. // Establish lookup ordering with some awkward offset math.
  927. for (int i = 0; i < 3; i++) {
  928. if (
  929. (
  930. idx0 == lookup_ordering_2bound[i][0] &&
  931. idx1 == lookup_ordering_2bound[i][1]
  932. ) || (
  933. idx0 == lookup_ordering_2bound[i][1] &&
  934. idx1 == lookup_ordering_2bound[i][0]
  935. )
  936. ) {
  937. it->term_order = (const uint8_t*)lookup_ordering_2bound[i];
  938. if (it->term_order[0] == idx0) {
  939. luk1_offset = 0;
  940. luk2_offset = 1;
  941. } else {
  942. luk1_offset = 1;
  943. luk2_offset = 0;
  944. }
  945. dbi = store->dbi[lookup_indices[i + 3]];
  946. TRACE(
  947. "Looking up 2 bound in %s\n",
  948. db_labels[lookup_indices[i + 3]]);
  949. break;
  950. }
  951. }
  952. if (dbi == 0) {
  953. TRACE(
  954. "Values %d and %d not found in lookup keys.",
  955. idx0, idx1);
  956. return LSUP_VALUE_ERR;
  957. }
  958. // Compose term keys in lookup key.
  959. LSUP_DoubleKey luk;
  960. luk[luk1_offset] = it->luk[0];
  961. luk[luk2_offset] = it->luk[1];
  962. if (!it->txn) {
  963. if (store->txn) it->txn = store->txn;
  964. else {
  965. it->rc = mdb_txn_begin (store->env, NULL, MDB_RDONLY, &it->txn);
  966. if (it->rc != MDB_SUCCESS) {
  967. fprintf (
  968. stderr, "%s:%d [%s]: Database error: %s",
  969. __FILE__, __LINE__, __func__, mdb_strerror (it->rc));
  970. return LSUP_DB_ERR;
  971. }
  972. }
  973. }
  974. it->key.mv_data = luk;
  975. it->key.mv_size = DBL_KLEN;
  976. mdb_cursor_open (it->txn, dbi, &it->cur);
  977. it->rc = mdb_cursor_get (it->cur, &it->key, &it->data, MDB_SET);
  978. if (ct) {
  979. // If a context is specified, the only way to count triples matching
  980. // the context is to loop over them.
  981. if (it->ck != NULL_KEY) {
  982. MDBIterator *ct_it;
  983. MALLOC_GUARD (ct_it, LSUP_MEM_ERR);
  984. ct_it->luk[0] = it->luk[0];
  985. ct_it->luk[1] = it->luk[1];
  986. /*
  987. LSUP_TripleKey ct_spok;
  988. memcpy (ct_it->spok, ct_spok, sizeof (LSUP_TripleKey));
  989. */
  990. ct_it->ck = it->ck;
  991. ct_it->store = it->store;
  992. ct_it->txn = it->txn;
  993. lookup_2bound (store, idx0, idx1, ct_it, NULL);
  994. while (LSUP_mdbiter_next (ct_it, NULL) != LSUP_END) {
  995. ct[0] ++;
  996. }
  997. // Free the counter iterator without freeing the shared txn.
  998. if (ct_it->cur) mdb_cursor_close (ct_it->cur);
  999. free (ct_it);
  1000. } else {
  1001. it->rc = mdb_cursor_get (it->cur, &it->key, &it->data, MDB_SET);
  1002. if (it->rc == MDB_SUCCESS) mdb_cursor_count (it->cur, ct);
  1003. }
  1004. }
  1005. it->i = 0;
  1006. it->iter_op_fn = it_next_2bound;
  1007. it->rc = mdb_cursor_get (it->cur, &it->key, &it->data, MDB_SET);
  1008. if (it->rc == MDB_SUCCESS)
  1009. it->rc = mdb_cursor_get (it->cur, &it->key, &it->data, MDB_GET_MULTIPLE);
  1010. if (it->rc != MDB_SUCCESS && it->rc != MDB_NOTFOUND) {
  1011. fprintf (
  1012. stderr, "%s:%d [%s]: Database error: %s\n",
  1013. __FILE__, __LINE__, __func__, mdb_strerror (it->rc));
  1014. return LSUP_DB_ERR;
  1015. }
  1016. return LSUP_OK;
  1017. }
  1018. inline static LSUP_rc
  1019. lookup_3bound (MDBStore *store, MDBIterator *it, size_t *ct)
  1020. {
  1021. TRACE(
  1022. "Looking up 3 bound: {%lx, %lx, %lx}",
  1023. it->luk[0], it->luk[1], it->luk[2]);
  1024. if (store->txn) it->txn = store->txn;
  1025. else {
  1026. it->rc = mdb_txn_begin (store->env, NULL, MDB_RDONLY, &it->txn);
  1027. if (it->rc != MDB_SUCCESS) {
  1028. fprintf (
  1029. stderr, "%s:%d [%s]: Database error: %s\n",
  1030. __FILE__, __LINE__, __func__, mdb_strerror (it->rc));
  1031. return LSUP_DB_ERR;
  1032. }
  1033. }
  1034. it->key.mv_data = it->luk;
  1035. if (it->ck != NULL_KEY) {
  1036. it->rc = mdb_cursor_open (it->txn, store->dbi[IDX_SPO_C], &it->cur);
  1037. it->key.mv_size = TRP_KLEN;
  1038. it->data.mv_data = &it->ck;
  1039. it->data.mv_size = KLEN;
  1040. } else {
  1041. it->rc = mdb_cursor_open (it->txn, store->dbi[IDX_S_PO], &it->cur);
  1042. it->key.mv_size = KLEN;
  1043. it->data.mv_data = it->luk + 1;
  1044. it->data.mv_size = DBL_KLEN;
  1045. }
  1046. it->rc = mdb_cursor_get (it->cur, &it->key, &it->data, MDB_GET_BOTH);
  1047. if (it->rc != MDB_SUCCESS && it->rc != MDB_NOTFOUND) {
  1048. fprintf (
  1049. stderr, "%s:%d [%s]: Database error: %s\n",
  1050. __FILE__, __LINE__, __func__, mdb_strerror (it->rc));
  1051. return LSUP_DB_ERR;
  1052. }
  1053. mdb_cursor_close (it->cur);
  1054. it->cur = NULL;
  1055. if (ct && it->rc == MDB_SUCCESS) *ct = 1;
  1056. it->iter_op_fn = it_next_3bound;
  1057. memcpy (it->spok, it->luk, sizeof (LSUP_TripleKey));
  1058. return LSUP_OK;
  1059. }