store_mdb.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999
  1. #include <sys/stat.h>
  2. #include <errno.h>
  3. #include <ftw.h>
  4. #include "store_mdb.h"
  5. /*
  6. * TODO At the moment up to 64-bit key / hash values are allowed. Later on,
  7. * 128-bit keys should be allowed by compile options, and that will no longer
  8. * be compatible with integer keys and data. When 128-bit keys are supported,
  9. * integer keys should remain available for code compiled with 64-bit keys.
  10. */
  11. #define INT_KEY_MASK MDB_INTEGERKEY
  12. #define INT_DUP_KEY_MASK MDB_DUPSORT | MDB_DUPFIXED | MDB_INTEGERKEY
  13. #define INT_DUPDATA_MASK MDB_DUPSORT | MDB_DUPFIXED | MDB_INTEGERDUP
  14. /**
  15. * Number of DBs defined.
  16. */
  17. #define N_DB 12
  18. #define DEFAULT_ENV_PATH "./mdb_store"
  19. #define ENV_DIR_MODE 0750
  20. #define ENV_FILE_MODE 0640
  21. typedef char DbLabel[8];
  22. // TODO Most of these are no longer used. Clean up.
  23. typedef enum {
  24. LSSTORE_INIT = 1, // Is the store environment set up on disk?
  25. LSSTORE_OPEN = 3, // Is the environment open? Assumes init is set.
  26. LSSTORE_DB_CREATED = 5, // Are DBs created? Assumes init is set.
  27. } StoreState;
  28. typedef enum {
  29. OP_ADD,
  30. OP_REMOVE,
  31. } StoreOp;
  32. struct MDBStore {
  33. MDB_env * env; // Environment handle.
  34. MDB_txn * txn; // Current transaction. If RW, it may have
  35. // nested transactions.
  36. MDB_dbi dbi[N_DB]; // DB handles. Refer to DbIdx enum.
  37. LSUP_Buffer * default_ctx;// Default context as a serialized URI.
  38. StoreState state; // Store state (initialized, open etc.)
  39. };
  40. /** @brief Common match callback arguments.
  41. */
  42. struct MatchArgs {
  43. LSUP_Key luks[2];
  44. uint8_t idx0, idx1;
  45. size_t *ct;
  46. mdb_store_match_fn_t callback_fn;
  47. void *ctx;
  48. };
  49. /**
  50. * Main DBs. These are the master information containers.
  51. *
  52. * Data columns are: identifier prefix, DB label, flags.
  53. */
  54. #define MAIN_TABLE \
  55. ENTRY( T_ST, "t:st", INT_KEY_MASK ) /* Key to ser. term */ \
  56. ENTRY( SPO_C, "spo:c", INT_DUPDATA_MASK) /* Triple to context */ \
  57. ENTRY( C_, "c:", INT_KEY_MASK ) /* Track empty contexts */\
  58. ENTRY( PFX_NS, "pfx:ns", 0 ) /* Prefix to NS */ \
  59. /**
  60. * Lookup DBs. These are indices and may be destroyed and rebuilt.
  61. */
  62. #define LOOKUP_TABLE \
  63. ENTRY( S_PO, "s:po", INT_DUP_KEY_MASK) /* 1-bound lookup */ \
  64. ENTRY( P_SO, "p:so", INT_DUP_KEY_MASK) /* 1-bound lookup */ \
  65. ENTRY( O_SP, "o:sp", INT_DUP_KEY_MASK) /* 1-bound lookup */ \
  66. ENTRY( PO_S, "po:s", INT_DUPDATA_MASK) /* 2-bound lookup */ \
  67. ENTRY( SO_P, "so:p", INT_DUPDATA_MASK) /* 2-bound lookup */ \
  68. ENTRY( SP_O, "sp:o", INT_DUPDATA_MASK) /* 2-bound lookup */ \
  69. ENTRY( C_SPO, "c:spo", INT_DUP_KEY_MASK) /* Context lookup */ \
  70. ENTRY( NS_PFX, "ns:pfx", 0 ) /* NS to prefix */ \
  71. /**
  72. * DB labels. They are prefixed with DB_
  73. */
  74. #define ENTRY(a, b, c) static const DbLabel DB_##a = b;
  75. MAIN_TABLE
  76. LOOKUP_TABLE
  77. #undef ENTRY
  78. /**
  79. * Numeric index of each DB. Prefixed with IDX_
  80. *
  81. * These index numbers are referred to in all the arrays defeined below. They
  82. * are independent from the LMDB dbi values which are considered opaque here.
  83. */
  84. typedef enum {
  85. #define ENTRY(a, b, c) IDX_##a,
  86. MAIN_TABLE
  87. LOOKUP_TABLE
  88. #undef ENTRY
  89. } DBIdx;
  90. /**
  91. * DB labels.
  92. */
  93. static const char *db_labels[N_DB] = {
  94. #define ENTRY(a, b, c) DB_##a,
  95. MAIN_TABLE
  96. LOOKUP_TABLE
  97. #undef ENTRY
  98. };
  99. /**
  100. * DB flags. These are aligned with the dbi_labels index.
  101. */
  102. static const unsigned int db_flags[N_DB] = {
  103. #define ENTRY(a, b, c) c,
  104. MAIN_TABLE
  105. LOOKUP_TABLE
  106. #undef ENTRY
  107. };
  108. /**
  109. * 1-bound and 2-bound lookup indices.
  110. *
  111. * N.B. Only the first 6 (1-bound and 2-bound term lookup) are used.
  112. * The others are added just because they belong logically to the lookup table.
  113. */
  114. static DBIdx lookup_indices[9] = {
  115. #define ENTRY(a, b, c) IDX_##a,
  116. LOOKUP_TABLE
  117. #undef ENTRY
  118. };
  119. /**
  120. * Order in which keys are looked up if two terms are bound.
  121. * The indices with the smallest average number of values per key should be
  122. * looked up first.
  123. *
  124. * 0 = s:po
  125. * 1 = p:so
  126. * 2 = o:sp
  127. */
  128. static const uint8_t lookup_rank[3] = {0, 2, 1};
  129. static const uint8_t lookup_ordering_1bound[3][3] = {
  130. {0, 1, 2}, // s:po
  131. {1, 0, 2}, // p:so
  132. {2, 0, 1}, // o:sp
  133. };
  134. static const uint8_t lookup_ordering_2bound[3][3] = {
  135. {1, 2, 0}, // po:s
  136. {0, 2, 1}, // so:p
  137. {0, 1, 2}, // sp:o
  138. };
  139. /**
  140. * Static prototypes.
  141. */
  142. static int dbi_init(LSUP_MDBStore *store);
  143. static int index_triple(
  144. LSUP_MDBStore *store, StoreOp op,
  145. LSUP_TripleKey spok, LSUP_Key ck);
  146. inline static LSUP_rc match_callback_0bound(
  147. struct MDBStore *store, struct MatchArgs *args);
  148. inline static LSUP_rc match_callback_1bound(
  149. struct MDBStore *store, struct MatchArgs *args);
  150. inline static LSUP_rc match_callback_2bound(
  151. struct MDBStore *store, struct MatchArgs *args);
  152. inline static LSUP_rc match_callback_3bound(
  153. struct MDBStore *store, struct MatchArgs *args);
  154. /* TODO
  155. inline static int check_txn_open(MDB_txn *txn, bool write);
  156. */
  157. /* TODO
  158. static int unlink_cb(
  159. const char *fpath, const struct stat *sb,
  160. int typeflag, struct FTW *ftwbuf);
  161. static int rmrf(char *path);
  162. */
  163. /**
  164. * API.
  165. */
  166. LSUP_rc
  167. LSUP_store_setup(char **path/*, bool clear*/) // TODO clear
  168. {
  169. int rc;
  170. // Set environment path.
  171. if (path == NULL || (*path = getenv("LSUP_STORE_PATH")) == NULL) {
  172. printf(
  173. "WARNING: `LSUP_STORE_PATH' environment variable is not set. "
  174. "A local directory will be used to store the LSUP data.");
  175. *path = DEFAULT_ENV_PATH;
  176. }
  177. // Verify that a writable directory exists or can be created.
  178. struct stat path_stat;
  179. rc = stat(*path, &path_stat);
  180. if (rc == ENOENT) {
  181. if (mkdir(*path, ENV_DIR_MODE) != 0) abort();
  182. } else if (!S_ISDIR(path_stat.st_mode)) {
  183. printf("%s is not a valid directory.\n", path);
  184. abort();
  185. /* TODO clear
  186. } else {
  187. if (clear) {
  188. rmrf(*path);
  189. if (mkdir(*path, ENV_DIR_MODE) != 0) abort();
  190. }
  191. */
  192. }
  193. // Open a temporary environment and txn to create the DBs.
  194. MDB_env *env;
  195. mdb_env_create(&env);
  196. mdb_env_open(env, *path, 0, ENV_FILE_MODE);
  197. MDB_txn *txn;
  198. mdb_txn_begin(env, NULL, 0, &txn);
  199. for (int i = 0; i < N_DB; i++) {
  200. MDB_dbi dbi;
  201. mdb_dbi_open(txn, db_labels[i], db_flags[i] | MDB_CREATE, &dbi);
  202. mdb_dbi_close(env, dbi);
  203. }
  204. mdb_txn_commit(txn);
  205. mdb_env_close(env);
  206. return rc;
  207. }
  208. LSUP_rc
  209. LSUP_store_open(
  210. LSUP_MDBStore *store, const char *path, LSUP_Buffer *default_ctx)
  211. {
  212. if(store->state & LSSTORE_OPEN) return LSUP_NOACTION;
  213. if (default_ctx == NULL) store->default_ctx = NULL;
  214. else {
  215. CRITICAL(store->default_ctx = malloc(sizeof(LSUP_Buffer)));
  216. LSUP_buffer_copy(store->default_ctx, default_ctx);
  217. }
  218. // Set map size.
  219. size_t mapsize;
  220. char *env_mapsize = getenv("LSUP_MDB_MAPSIZE");
  221. if (env_mapsize == NULL) {
  222. mapsize = 0x10000000000; // 1Tb
  223. } else {
  224. sscanf(env_mapsize, "%lu", &mapsize);
  225. }
  226. mdb_env_set_mapsize(store->env, mapsize);
  227. mdb_env_set_maxdbs(store->env, N_DB);
  228. int rc = mdb_env_open(store->env, path, 0, ENV_FILE_MODE);
  229. // Assign DB handles to store->dbi.
  230. MDB_txn *txn;
  231. mdb_txn_begin(store->env, NULL, 0, &txn);
  232. for (int i = 0; i < N_DB; i++) {
  233. mdb_dbi_open(
  234. txn, db_labels[i], db_flags[i], store->dbi + i);
  235. }
  236. mdb_txn_commit(txn);
  237. store->state = LSSTORE_OPEN;
  238. return rc;
  239. }
  240. LSUP_rc
  241. LSUP_store_stats(LSUP_MDBStore *store)
  242. {
  243. // TODO
  244. MDB_stat env_stat, db_stats[N_DB];
  245. return 0;
  246. }
  247. size_t
  248. LSUP_store_size(LSUP_MDBStore *store)
  249. {
  250. if(!(store->state & LSSTORE_INIT)) return 0;
  251. MDB_stat stat;
  252. mdb_stat(store->txn, store->dbi[IDX_SPO_C], &stat);
  253. return stat.ms_entries;
  254. }
  255. LSUP_rc
  256. LSUP_store_add(
  257. LSUP_MDBStore *store, const LSUP_Buffer *sc,
  258. const LSUP_SerTerm **data, const size_t data_size)
  259. {
  260. MDB_val key_v, data_v;
  261. bool txn_pending = false;
  262. if (!store->txn) {
  263. mdb_txn_begin(store->env, NULL, MDB_RDONLY, &store->txn);
  264. txn_pending = true;
  265. }
  266. // Take care of context first.
  267. // Serialize and hash.
  268. LSUP_Key ck = NULL_KEY;
  269. if (store->default_ctx != NULL) {
  270. if (sc == NULL) sc = store->default_ctx;
  271. ck = LSUP_sterm_to_key(sc);
  272. // Insert t:st for context.
  273. TRACE("Adding context: %s", sc);
  274. key_v.mv_data = &ck;
  275. key_v.mv_size = KLEN;
  276. data_v.mv_data = sc->addr;
  277. data_v.mv_size = sc->size;
  278. mdb_put(
  279. store->txn, store->dbi[IDX_T_ST],
  280. &key_v, &data_v, MDB_NOOVERWRITE);
  281. }
  282. int rc = LSUP_NOACTION;
  283. for (size_t i = 0; i < data_size && rc == LSUP_OK; i++) {
  284. const LSUP_SerTerm *sspo = data[i];
  285. /*
  286. LSUP_SerTerm sspo[3];
  287. CHECK(LSUP_term_serialize(spo->s, sspo), _add_free_sterm1);
  288. CHECK(LSUP_term_serialize(spo->p, sspo + 1), _add_free_sterm2);
  289. CHECK(LSUP_term_serialize(spo->s, sspo + 2), _add_free_sterms);
  290. */
  291. LSUP_TripleKey spok = NULL_TRP;
  292. // Add triple.
  293. TRACE("Inserting spok: {%lx, %lx, %lx}", spok[0], spok[1], spok[2]);
  294. int put_rc[2] = {LSUP_OK, MDB_KEYEXIST};
  295. // Insert t:st for s, p, o
  296. for (int j = 0; j < 3; j++) {
  297. spok[i] = LSUP_sterm_to_key(sspo + i);
  298. key_v.mv_data = spok + i;
  299. key_v.mv_size = KLEN;
  300. data_v.mv_data = (sspo + i)->addr;
  301. data_v.mv_size = (sspo + i)->size;
  302. MCHECK(
  303. mdb_put(
  304. store->txn, store->dbi[IDX_T_ST],
  305. &key_v, &data_v, MDB_NOOVERWRITE),
  306. put_rc, _add_close_txn);
  307. }
  308. // Insert spo:c
  309. key_v.mv_data = spok;
  310. key_v.mv_size = TRP_KLEN;
  311. // In triple mode, data is empty.
  312. data_v.mv_data = ck == NULL_KEY ? NULL : &ck;
  313. data_v.mv_size = ck == NULL_KEY ? 0 : KLEN;
  314. MCHECK(
  315. mdb_put(
  316. store->txn, store->dbi[IDX_SPO_C],
  317. &key_v, &data_v, MDB_NODUPDATA),
  318. put_rc, _add_close_txn);
  319. // Index.
  320. PCHECK(index_triple(store, OP_ADD, spok, ck), _add_close_txn);
  321. // Free serialized terms.
  322. /*
  323. _add_free_sterms:
  324. LSUP_buffer_done(sspo + 2);
  325. _add_free_sterm2:
  326. LSUP_buffer_done(sspo + 1);
  327. _add_free_sterm1:
  328. LSUP_buffer_done(sspo);
  329. */
  330. }
  331. _add_close_txn:
  332. // Only return commit rc if it fails.
  333. if (txn_pending) {
  334. if (rc >= LSUP_OK) {
  335. int txn_rc;
  336. if((txn_rc = mdb_txn_commit(store->txn)) != MDB_SUCCESS) {
  337. mdb_txn_abort(store->txn);
  338. rc = txn_rc;
  339. }
  340. } else mdb_txn_abort(store->txn);
  341. store->txn = NULL;
  342. }
  343. return rc;
  344. }
  345. LSUP_Key
  346. LSUP_store_sterm_to_key(
  347. LSUP_MDBStore *store, const LSUP_SerTerm *sterm)
  348. {
  349. // TODO this will be replaced by a lookup when 128-bit hash is introduced.
  350. return LSUP_sterm_to_key(sterm);
  351. }
  352. /*
  353. LSUP_Key
  354. LSUP_store_get_key(
  355. LSUP_MDBStore *store, const LSUP_SerTerm *sterm)
  356. {
  357. }
  358. */
  359. LSUP_rc
  360. LSUP_store_key_to_sterm(
  361. LSUP_MDBStore *store, const LSUP_Key key, LSUP_SerTerm *sterm)
  362. {
  363. LSUP_rc rc = LSUP_NORESULT;
  364. MDB_txn *txn;
  365. mdb_txn_begin(store->env, NULL, MDB_RDONLY, &txn);
  366. MDB_val key_v, data_v;
  367. key_v.mv_data = (void*)&key;
  368. key_v.mv_size = KLEN;
  369. int mdb_rc = mdb_get(txn, store->dbi[IDX_T_ST], &key_v, &data_v);
  370. if (mdb_rc == MDB_SUCCESS) {
  371. sterm->addr = data_v.mv_data;
  372. sterm->size = data_v.mv_size;
  373. rc = LSUP_OK;
  374. }
  375. else if (UNLIKELY(mdb_rc != MDB_NOTFOUND)) rc = LSUP_ERROR;
  376. mdb_txn_abort(txn);
  377. return rc;
  378. }
  379. LSUP_rc
  380. LSUP_store_match_callback(
  381. LSUP_MDBStore *store, LSUP_SerTerm sspoc[], size_t *ct,
  382. mdb_store_match_fn_t callback_fn, void *ctx)
  383. {
  384. LSUP_TripleKey spok = {
  385. LSUP_sterm_to_key(sspoc),
  386. LSUP_sterm_to_key(sspoc + 1),
  387. LSUP_sterm_to_key(sspoc + 2),
  388. };
  389. LSUP_Key ck = store->default_ctx ? LSUP_sterm_to_key(sspoc + 3) : NULL_KEY;
  390. struct MatchArgs args_s;
  391. struct MatchArgs *args = &args_s;
  392. args->ct = ct;
  393. args->callback_fn = callback_fn;
  394. args->ctx = ctx;
  395. // s p o (all terms bound)
  396. if (spok[0] != NULL_KEY && spok[1] != NULL_KEY && spok[2] != NULL_KEY) {
  397. return match_callback_3bound(store, args);
  398. }
  399. else if (spok[0] != NULL_KEY) {
  400. args->luks[0] = spok[0];
  401. args->idx0 = 0;
  402. if (spok[1] != NULL_KEY) { // s p ?
  403. args->luks[1] = spok[1];
  404. args->idx1 = 1;
  405. return match_callback_2bound(store, args);
  406. } else if (spok[2] != NULL_KEY) { // s ? o
  407. args->luks[1] = spok[2];
  408. args->idx1 = 2;
  409. return match_callback_2bound(store, args);
  410. } else { // s ? ?
  411. return match_callback_1bound(store, args);
  412. }
  413. } else if (spok[1] != NULL_KEY) {
  414. args->luks[0] = spok[1];
  415. if (spok[2] != NULL_KEY) { // ? p o
  416. args->luks[1] = spok[2];
  417. args->idx1 = 2;
  418. return match_callback_2bound(store, args);
  419. } else { // ? p ?
  420. args->idx0 = 1;
  421. return match_callback_1bound(store, args);
  422. }
  423. } else if (spok[2] != NULL_KEY) { // ? ? o
  424. args->luks[0] = spok[2];
  425. args->idx0 = 2;
  426. return match_callback_1bound(store, args);
  427. } else { // ? ? ? (all terms unbound)
  428. return match_callback_0bound(store, args);
  429. }
  430. }
  431. LSUP_rc
  432. LSUP_store_remove(
  433. LSUP_MDBStore *store, const LSUP_Buffer *sc,
  434. LSUP_TripleKey data[], size_t data_size)
  435. {
  436. LSUP_rc rc = LSUP_NOACTION;
  437. LSUP_Key ck = NULL_KEY;
  438. if (store->default_ctx != NULL) {
  439. if (sc == NULL) sc = store->default_ctx;
  440. ck = LSUP_sterm_to_key(sc);
  441. }
  442. MDB_txn *txn;
  443. mdb_txn_begin(store->env, NULL, 0, &txn);
  444. MDB_cursor *dcur, *icur;
  445. mdb_cursor_open(txn, store->dbi[IDX_SPO_C], &dcur);
  446. mdb_cursor_open(txn, store->dbi[IDX_C_SPO], &icur);
  447. MDB_val spok_v, ck_v;
  448. LSUP_TripleKey spok_cur;
  449. spok_v.mv_size = TRP_KLEN;
  450. ck_v.mv_size = KLEN;
  451. for(size_t i = 0; i < data_size; i++) {
  452. spok_v.mv_data = data + i;
  453. rc = mdb_cursor_get(dcur, &spok_v, &ck_v, MDB_GET_BOTH);
  454. if (rc == MDB_NOTFOUND) continue;
  455. if (UNLIKELY(rc != MDB_SUCCESS)) goto _remove_abort;
  456. // Delete spo:c entry.
  457. mdb_cursor_del(dcur, 0);
  458. // Restore ck address after each delete.
  459. ck_v.mv_data = &ck;
  460. // Delete c::spo entry.
  461. rc = mdb_cursor_get(icur, &ck_v, &spok_v, MDB_GET_BOTH);
  462. if (rc == MDB_NOTFOUND) continue;
  463. if (UNLIKELY(rc != MDB_SUCCESS)) goto _remove_abort;
  464. mdb_cursor_del(icur, 0);
  465. spok_v.mv_data = data + i;
  466. // If there are no more contexts associated with this triple,
  467. // remove from indices.
  468. rc = mdb_cursor_get(dcur, &spok_v, NULL, MDB_SET);
  469. if (rc == MDB_SUCCESS) continue;
  470. if (UNLIKELY(rc != MDB_NOTFOUND)) goto _remove_abort;
  471. index_triple(store, OP_REMOVE, data[i], ck);
  472. }
  473. if(UNLIKELY(mdb_txn_commit(txn) != MDB_SUCCESS)) {
  474. rc = LSUP_TXN_ERR;
  475. goto _remove_abort;
  476. }
  477. return rc;
  478. _remove_abort:
  479. mdb_txn_abort(txn);
  480. return rc;
  481. }
  482. void
  483. LSUP_store_done(LSUP_MDBStore *store)
  484. {
  485. if (store->state & LSSTORE_OPEN) {
  486. TRACE(STR, "Closing MDB env.\n");
  487. mdb_env_close(store->env);
  488. }
  489. if (store->default_ctx != NULL) LSUP_buffer_done(store->default_ctx);
  490. }
  491. /* * * Static functions. * * */
  492. /* TODO
  493. static int
  494. unlink_cb(
  495. const char *fpath, const struct stat *sb,
  496. int typeflag, struct FTW *ftwbuf)
  497. {
  498. int rv = remove(fpath);
  499. if (rv)
  500. perror(fpath);
  501. return rv;
  502. }
  503. static int rmrf(char *path)
  504. { return nftw(path, unlink_cb, 64, FTW_DEPTH | FTW_PHYS); }
  505. */
  506. /**
  507. * @brief Open and allocate DB handles in an array.
  508. *
  509. * @param bool create [in]: If true, the DBs are created. This is only needed
  510. * on bootstrap.
  511. */
  512. static LSUP_rc dbi_init(LSUP_MDBStore *store)
  513. {
  514. bool db_created = store->state & LSSTORE_DB_CREATED;
  515. MDB_txn *txn;
  516. unsigned int txn_flags = db_created ? MDB_RDONLY : 0;
  517. unsigned int create_flag = db_created ? 0 : MDB_CREATE;
  518. mdb_txn_begin(store->env, NULL, txn_flags, &txn);
  519. for (int i = 0; i < N_DB; i++) {
  520. mdb_dbi_open(
  521. txn, db_labels[i], db_flags[i] | create_flag, store->dbi + i);
  522. }
  523. store->state |= LSSTORE_DB_CREATED;
  524. mdb_txn_commit(txn);
  525. return 0;
  526. }
  527. /* TODO
  528. inline static int
  529. check_txn_open(MDB_txn *txn, bool write)
  530. {
  531. if (txn == NULL) {
  532. mdb_txn_begin(LSUP_mdbenv, NULL, write ? 0 : MDB_RDONLY, &txn);
  533. return LSUP_OK;
  534. }
  535. return LSUP_NOACTION;
  536. }
  537. */
  538. static LSUP_rc
  539. index_triple(
  540. LSUP_MDBStore *store, StoreOp op,
  541. LSUP_TripleKey spok, LSUP_Key ck)
  542. {
  543. int rc = LSUP_NOACTION;
  544. MDB_val v1, v2;
  545. // Index c:spo.
  546. if (op == OP_REMOVE) {
  547. if (ck != NULL_KEY) {
  548. MDB_cursor *cur;
  549. v1.mv_data = &ck;
  550. v1.mv_size = KLEN;
  551. v2.mv_data = spok;
  552. v2.mv_size = TRP_KLEN;
  553. mdb_cursor_open(store->txn, store->dbi[IDX_C_SPO], &cur);
  554. rc = mdb_cursor_get(cur, &v1, &v2, MDB_GET_BOTH);
  555. if(rc == MDB_SUCCESS) mdb_cursor_del(cur, 0);
  556. mdb_cursor_close(cur);
  557. }
  558. } else if (op == OP_ADD) {
  559. if (ck != NULL_KEY) {
  560. v1.mv_data = &ck;
  561. v1.mv_size = KLEN;
  562. v2.mv_data = spok;
  563. v2.mv_size = TRP_KLEN;
  564. mdb_put(
  565. store->txn, store->dbi[IDX_C_SPO],
  566. &v1, &v2, MDB_NODUPDATA);
  567. }
  568. } else return LSUP_VALUE_ERR;
  569. LSUP_DoubleKey dbl_keys[3] = {
  570. {spok[1], spok[2]}, // po
  571. {spok[0], spok[2]}, // so
  572. {spok[0], spok[1]}, // sp
  573. };
  574. // Add terms to index.
  575. v1.mv_size = KLEN;
  576. v2.mv_size = DBL_KLEN;
  577. for (int i = 0; i < 3; i++) {
  578. MDB_dbi db1 = lookup_indices[i]; // s:po, p:so, o:sp
  579. MDB_dbi db2 = lookup_indices[i + 3]; // po:s, so:p, sp:o
  580. v1.mv_data = spok + i;
  581. v2.mv_data = dbl_keys[i];
  582. if (op == OP_REMOVE) {
  583. MDB_cursor *cur1, *cur2;
  584. mdb_cursor_open(store->txn, store->dbi[lookup_indices[i]], &cur1);
  585. rc = mdb_cursor_get(cur1, &v1, &v2, MDB_GET_BOTH);
  586. if (rc == MDB_SUCCESS) mdb_cursor_del(cur1, 0);
  587. mdb_cursor_close(cur1);
  588. // Restore pointers invalidated after delete.
  589. v1.mv_data = spok + i;
  590. v2.mv_data = dbl_keys[i];
  591. mdb_cursor_open(
  592. store->txn, store->dbi[lookup_indices[i + 3]], &cur2);
  593. rc = mdb_cursor_get(cur2, &v2, &v1, MDB_GET_BOTH);
  594. if (rc == MDB_SUCCESS) mdb_cursor_del(cur2, 0);
  595. mdb_cursor_close(cur2);
  596. } else { // OP_ADD is guaranteed.
  597. mdb_put(store->txn, db1, &v1, &v2, MDB_NODUPDATA);
  598. mdb_put(store->txn, db2, &v2, &v1, MDB_NODUPDATA);
  599. }
  600. }
  601. return rc;
  602. }
  603. /* * * Match callbacks. * * */
  604. inline static LSUP_rc
  605. match_callback_0bound(struct MDBStore *store, struct MatchArgs *args)
  606. {
  607. int rc = LSUP_NORESULT;
  608. MDB_txn *txn;
  609. if(store->txn) txn = store->txn;
  610. else mdb_txn_begin(store->env, NULL, MDB_RDONLY, &txn);
  611. MDB_val key_v;
  612. if(args->ct) {
  613. MDB_stat stat;
  614. mdb_stat(store->txn, store->dbi[IDX_SPO_C], &stat);
  615. *args->ct = stat.ms_entries;
  616. }
  617. MDB_cursor *cur;
  618. mdb_cursor_open(txn, store->dbi[IDX_SPO_C], &cur);
  619. if(args->callback_fn) {
  620. rc = mdb_cursor_get(cur, &key_v, NULL, MDB_FIRST);
  621. while (rc != MDB_NOTFOUND) {
  622. LSUP_TripleKey spok;
  623. rc = args->callback_fn(spok, args->ctx);
  624. if (rc < 0) goto _match0b_abort;
  625. }
  626. }
  627. _match0b_abort:
  628. mdb_cursor_close(cur);
  629. if (txn != store->txn) mdb_txn_abort(txn);
  630. return rc;
  631. }
  632. inline static LSUP_rc
  633. match_callback_1bound(struct MDBStore *store, struct MatchArgs *args)
  634. {
  635. int rc = LSUP_NORESULT;
  636. const uint8_t *term_order = lookup_ordering_1bound[args->idx0];
  637. MDB_txn *txn;
  638. if(store->txn) txn = store->txn;
  639. else mdb_txn_begin(store->env, NULL, MDB_RDONLY, &txn);
  640. MDB_cursor *cur;
  641. mdb_cursor_open(txn, store->dbi[args->idx0], &cur);
  642. MDB_val key_v, data_v;
  643. key_v.mv_data = &args->luks;
  644. key_v.mv_size = KLEN;
  645. if(args->ct) {
  646. mdb_cursor_get(cur, &key_v, &data_v, MDB_SET);
  647. mdb_cursor_count(cur, args->ct);
  648. }
  649. if(args->callback_fn) {
  650. rc = mdb_cursor_get(cur, &key_v, &data_v, MDB_SET);
  651. if (rc == MDB_SUCCESS)
  652. rc = mdb_cursor_get(cur, &key_v, &data_v, MDB_GET_MULTIPLE);
  653. while (rc != MDB_NOTFOUND) {
  654. LSUP_Key **lu_dset = data_v.mv_data;
  655. for (int i = 0; i < data_v.mv_size / DBL_KLEN; i++) {
  656. // Build triple key from lookup key and result keys.
  657. LSUP_TripleKey spok;
  658. spok[term_order[0]] = args->luks[0];
  659. spok[term_order[1]] = lu_dset[i][0];
  660. spok[term_order[2]] = lu_dset[i][1];
  661. rc = args->callback_fn(spok, args->ctx);
  662. if (rc < 0) goto _match1b_abort;
  663. }
  664. rc = mdb_cursor_get(cur, &key_v, &data_v, MDB_NEXT_MULTIPLE);
  665. }
  666. }
  667. _match1b_abort:
  668. mdb_cursor_close(cur);
  669. if (txn != store->txn) mdb_txn_abort(txn);
  670. return rc;
  671. }
  672. inline static LSUP_rc
  673. match_callback_2bound(struct MDBStore *store, struct MatchArgs *args)
  674. {
  675. int rc = LSUP_NORESULT;
  676. uint8_t luk1_offset, luk2_offset;
  677. const uint8_t *term_order;
  678. MDB_dbi dbi = 0;
  679. // Establish lookup ordering with some awkward offset math.
  680. for(int i = 0; i < 3; i++) {
  681. if (
  682. (
  683. args->idx0 == lookup_ordering_2bound[i][0] &&
  684. args->idx1 == lookup_ordering_2bound[i][1]
  685. ) || (
  686. args->idx0 == lookup_ordering_2bound[i][1] &&
  687. args->idx1 == lookup_ordering_2bound[i][0]
  688. )
  689. ) {
  690. term_order = lookup_ordering_2bound[i];
  691. if (term_order[0] == args->idx0) {
  692. luk1_offset = 0;
  693. luk2_offset = 1;
  694. } else {
  695. luk1_offset = 1;
  696. luk2_offset = 0;
  697. }
  698. dbi = store->dbi[lookup_indices[i + 3]];
  699. break;
  700. }
  701. }
  702. if (dbi == 0) {
  703. TRACE(
  704. "Values %d and %d not found in lookup keys.",
  705. args->idx0, args->idx1);
  706. return LSUP_VALUE_ERR;
  707. }
  708. // Compose term keys in lookup key.
  709. LSUP_DoubleKey luk;
  710. luk[luk1_offset] = args->luks[0];
  711. luk[luk2_offset] = args->luks[1];
  712. MDB_txn *txn;
  713. if(store->txn) txn = store->txn;
  714. else mdb_txn_begin(store->env, NULL, MDB_RDONLY, &txn);
  715. MDB_cursor *cur;
  716. mdb_cursor_open(txn, store->dbi[dbi], &cur);
  717. MDB_val key_v, data_v;
  718. key_v.mv_data = luk;
  719. key_v.mv_size = DBL_KLEN;
  720. if(args->ct) {
  721. mdb_cursor_get(cur, &key_v, &data_v, MDB_SET);
  722. mdb_cursor_count(cur, args->ct);
  723. }
  724. if(args->callback_fn) {
  725. rc = mdb_cursor_get(cur, &key_v, &data_v, MDB_SET);
  726. if (rc == MDB_SUCCESS)
  727. rc = mdb_cursor_get(cur, &key_v, &data_v, MDB_GET_MULTIPLE);
  728. while (rc != MDB_NOTFOUND) {
  729. LSUP_Key *lu_dset = data_v.mv_data;
  730. for (int i = 0; i < data_v.mv_size / DBL_KLEN; i++) {
  731. // Build triple key from lookup key and result keys.
  732. LSUP_TripleKey spok;
  733. spok[term_order[0]] = luk[0];
  734. spok[term_order[1]] = luk[1];
  735. spok[term_order[2]] = lu_dset[i];
  736. rc = args->callback_fn(spok, args->ctx);
  737. if (rc < 0) goto _match2b_abort;
  738. }
  739. rc = mdb_cursor_get(cur, &key_v, &data_v, MDB_NEXT_MULTIPLE);
  740. }
  741. }
  742. _match2b_abort:
  743. mdb_cursor_close(cur);
  744. if (txn != store->txn) mdb_txn_abort(txn);
  745. return rc;
  746. }
  747. inline static LSUP_rc
  748. match_callback_3bound(struct MDBStore *store, struct MatchArgs *args)
  749. {
  750. int rc = LSUP_NORESULT;
  751. MDB_txn *txn;
  752. if(store->txn)
  753. txn = store->txn;
  754. else
  755. mdb_txn_begin(store->env, NULL, MDB_RDONLY, &txn);
  756. MDB_cursor *cur;
  757. rc = mdb_cursor_open(txn, store->dbi[IDX_SPO_C], &cur);
  758. MDB_val key_v, data_v;
  759. key_v.mv_data = args->luks;
  760. key_v.mv_size = TRP_KLEN;
  761. if(args->ct) {
  762. if (mdb_cursor_get(cur, &key_v, NULL, MDB_SET) == MDB_SUCCESS)
  763. rc = mdb_cursor_count(cur, args->ct);
  764. }
  765. if(args->callback_fn) {
  766. rc = mdb_cursor_get(cur, &key_v, NULL, MDB_FIRST);
  767. while (rc != MDB_NOTFOUND) {
  768. LSUP_TripleKey spok;
  769. rc = args->callback_fn(spok, args->ctx);
  770. if (rc < 0) goto _match3b_abort;
  771. }
  772. }
  773. _match3b_abort:
  774. mdb_cursor_close(cur);
  775. if (txn != store->txn) mdb_txn_abort(txn);
  776. return rc;
  777. }