store_mdb.c 25 KB

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