Volksdata 1.0b10
RDF library
Loading...
Searching...
No Matches
store_mdb.c
Go to the documentation of this file.
2
6#define N_DB 9
7
11#if (defined DEBUG || defined TESTING)
12#define DEFAULT_MAPSIZE 1<<24 // 16Mb (limit for Valgrind)
13#elif !(defined __LP64__ || defined __LLP64__) || \
14 defined _WIN32 && !defined _WIN64
15#define DEFAULT_MAPSIZE 1<<31 // 2Gb (limit for 32-bit systems)
16#else
17#define DEFAULT_MAPSIZE 1UL<<40 // 1Tb
18#endif
19
20#define ENV_DIR_MODE 0750
21#define ENV_FILE_MODE 0640
22
23/*
24 * Data types.
25 */
26
27typedef char DbLabel[8];
28typedef struct mdbstore_iter_t MDBIterator;
29
31typedef enum {
32 LSSTORE_OPEN = 1<<0,
34
36typedef enum {
38 //<
39 //< The iterator has begun a new
40 //< transaction on initialization
41 //< which needs to be closed. If
42 //< false, the iterator is using an
43 //< existing transaction which will
44 //< not be closed with
45 //< #mdbiter_free().
46} IterFlags;
47
48typedef enum {
51} StoreOp;
52
53typedef struct mdbstore_t {
54 MDB_env * env;
55 MDB_dbi dbi[N_DB];
57} MDBStore;
58
69typedef void (*iter_op_fn_t)(MDBIterator *it);
70
71
73typedef struct mdbstore_iter_t {
76 MDB_txn * txn;
77 MDB_cursor * cur;
78 MDB_cursor * ctx_cur;
79 MDB_val key;
80 MDB_val data;
87 const uint8_t * term_order;
90 size_t i;
91 size_t ct;
93 int rc;
95
96
97/*
98 * Static variables.
99 */
100
101#define DUPFIXED_MASK MDB_DUPSORT | MDB_DUPFIXED
102
108#define MAIN_TABLE \
109/* #ID pfx #DB label #Flags */ \
110 ENTRY( T_ST, "t:st", 0 ) /* Key to ser. term */ \
111 ENTRY( SPO_C, "spo:c", DUPFIXED_MASK ) /* Triple to context */ \
112
113
116#define LOOKUP_TABLE \
117/* #ID pfx #DB label #Flags */ \
118 ENTRY( S_PO, "s:po", DUPFIXED_MASK ) /* 1-bound lookup */ \
119 ENTRY( P_SO, "p:so", DUPFIXED_MASK ) /* 1-bound lookup */ \
120 ENTRY( O_SP, "o:sp", DUPFIXED_MASK ) /* 1-bound lookup */ \
121 ENTRY( PO_S, "po:s", DUPFIXED_MASK ) /* 2-bound lookup */ \
122 ENTRY( SO_P, "so:p", DUPFIXED_MASK ) /* 2-bound lookup */ \
123 ENTRY( SP_O, "sp:o", DUPFIXED_MASK ) /* 2-bound lookup */ \
124 ENTRY( C_SPO, "c:spo", DUPFIXED_MASK ) /* Context lookup */ \
125
126
129#define ENTRY(a, b, c) static const DbLabel DB_##a = b;
132#undef ENTRY
133
134/*
135 * Numeric index of each DB. Prefixed with IDX_
136 *
137 * These index numbers are referred to in all the arrays defeined below. They
138 * are independent from the LMDB dbi values which are considered opaque here.
139 */
140typedef enum {
141#define ENTRY(a, b, c) IDX_##a,
144#undef ENTRY
145} DBIdx;
146
150static const char *db_labels[N_DB] = {
151#define ENTRY(a, b, c) DB_##a,
154#undef ENTRY
155};
156
157/*
158 * DB flags. These are aligned with the dbi_labels index.
159 */
160static const unsigned int db_flags[N_DB] = {
161#define ENTRY(a, b, c) c,
164#undef ENTRY
165};
166
167/*
168 * 1-bound and 2-bound lookup indices.
169 *
170 * N.B. Only the first 6 (1-bound and 2-bound term lookup) are used.
171 * The others are added just because they belong logically to the lookup table.
172 */
173static DBIdx lookup_indices[9] = {
174#define ENTRY(a, b, c) IDX_##a,
176#undef ENTRY
177};
178
179static const uint8_t lookup_ordering_1bound[3][3] = {
180 {0, 1, 2}, // s:po
181 {1, 0, 2}, // p:so
182 {2, 0, 1}, // o:sp
183};
184
185static const uint8_t lookup_ordering_2bound[3][3] = {
186 {1, 2, 0}, // po:s
187 {0, 2, 1}, // so:p
188 {0, 1, 2}, // sp:o
189};
190
191
192/*
193 * Static prototypes.
194 */
195static int index_triple(
196 MDBStore *store, StoreOp op, VOLK_TripleKey spok, VOLK_Key ck,
197 MDB_txn *txn);
198static VOLK_rc mdbstore_add_term (void *h, const VOLK_Buffer *sterm, void *th);
199
200inline static VOLK_rc lookup_0bound (MDBIterator *it, size_t *ct);
201inline static VOLK_rc lookup_1bound (
202 uint8_t idx0, MDBIterator *it, size_t *ct);
203inline static VOLK_rc lookup_2bound (
204 uint8_t idx0, uint8_t idx1, MDBIterator *it, size_t *ct);
205inline static VOLK_rc lookup_3bound(MDBIterator *it, size_t *ct);
206
207
208static const char *
209mdbstore_path_from_id (const char *id)
210{
211 // Set environment path.
212 if (!id) id = getenv ("VOLK_MDB_STORE_URN");
213 if (!id) {
215 log_info (
216 "`VOLK_MDB_STORE_URN' environment variable is not "
217 "set. The default URN %s has been set as the store ID.", id
218 );
219 } else if (strncmp ("file://", id, 7) != 0) {
220 log_error ("MDB store ID must be in the `file://<abs_path>` format.");
221
222 return NULL;
223 }
224
225 return id + 7;
226}
227
228
229/*
230 * Inliners.
231 */
232
233static inline VOLK_rc
234txn_begin (MDB_env *env, MDB_txn *p, unsigned int f, MDB_txn **tp) {
235 RCCK (mdb_txn_begin (env, p, f, tp));
236 const char *path;
237 RCCK (mdb_env_get_path (env, &path));
238 log_debug (
239 "BEGIN %s transaction %p child of %p in env %s",
240 f == 0 ? "RW" : "RO", *tp, p, path);
241
242 return VOLK_OK;
243}
244
245
246static inline VOLK_rc
247txn_commit (MDB_txn *t) {
248 log_debug ("COMMIT transaction %p", t);
249 return mdb_txn_commit (t);
250}
251
252
256
263static VOLK_rc
264mdbstore_setup (const char *id, bool clear)
265{
266 if (!VOLK_env_is_init) return VOLK_ENV_ERR;
267
268 const char *path = mdbstore_path_from_id (id);
269 if (!path) return VOLK_VALUE_ERR;
270
271 // If the directory exists (unless clear == true), do nothing.
272 if (clear) rm_r (path);
273 VOLK_rc rc = mkdir_p (path, ENV_DIR_MODE);
274 log_debug ("Create dir rc: %s", VOLK_strerror (rc));
275 PRCCK (rc);
276
277 // Open a temporary environment and txn to create the DBs.
278 MDB_env *env;
279 RCCK (mdb_env_create (&env));
280
281 RCCK (mdb_env_set_maxdbs (env, N_DB));
282 RCCK (mdb_env_open (env, path, 0, ENV_FILE_MODE));
283 log_debug ("Environment opened at %s.", path);
284
285 MDB_txn *txn;
286 RCCK (txn_begin (env, NULL, 0, &txn));
287 MDB_dbi dbi;
288 for (int i = 0; i < N_DB; i++) {
289 log_trace ("Creating DB %s", db_labels[i]);
290 RCCK (
291 mdb_dbi_open (txn, db_labels[i], db_flags[i] | MDB_CREATE, &dbi)
292 );
293 }
294
295 // Bootstrap the permanent store with initial data.
296 MDB_stat stat;
297 CHECK (mdb_dbi_open (
298 txn, db_labels[IDX_T_ST], db_flags[IDX_T_ST], &dbi), fail);
299 CHECK (mdb_stat (txn, dbi, &stat), fail);
300
301 if (stat.ms_entries == 0) {
302 log_debug ("Loading initial data into %s", path);
303 // Index default context.
304 MDB_cursor *cur;
305 CHECK (mdb_cursor_open (txn, dbi, &cur), fail);
307 MDB_val key, data;
308 key.mv_data = &k;
309 key.mv_size = sizeof (k);
310
311 data.mv_data = VOLK_default_ctx_buf->addr;
312 data.mv_size = VOLK_default_ctx_buf->size;
313
314 VOLK_rc db_rc = mdb_cursor_put (cur, &key, &data, 0);
315 CHECK (db_rc, fail);
316 }
317
318 CHECK (txn_commit (txn), fail);
319 mdb_env_close (env);
320
321 return clear ? VOLK_OK : rc;
322
323fail:
324 if (rc >= 0) rc = VOLK_DB_ERR;
325 mdb_txn_abort (txn);
326 return rc;
327}
328
329
340static void *
341mdbstore_new (const char *id, size_t _unused)
342{
343 if (!VOLK_env_is_init) {
344 log_error (VOLK_strerror (VOLK_ENV_ERR));
345 return NULL;
346 }
347
348 (void) _unused;
349 const char *path = mdbstore_path_from_id (id);
350 if (!path) return NULL;
351
352 MDBStore *store;
353 CALLOC_GUARD (store, NULL);
354
355 RCNL (mdb_env_create (&store->env));
356 MDB_txn *txn = NULL;
357
358 // Set map size.
359 size_t mapsize;
360 char *env_mapsize = getenv ("VOLK_MDB_MAPSIZE");
361 if (env_mapsize == NULL) mapsize = DEFAULT_MAPSIZE;
362 else sscanf (env_mapsize, "%zu", &mapsize);
363 log_debug (
364 "Setting environment map size at %s to %zu Mb.",
365 path, mapsize / 1024 / 1024);
366 CHECK (mdb_env_set_mapsize (store->env, mapsize), fail);
367 CHECK (mdb_env_set_maxdbs (store->env, N_DB), fail);
368 CHECK (mdb_env_open (store->env, path, 0, ENV_FILE_MODE), fail);
369
370 // Assign DB handles to store->dbi.
371 CHECK (txn_begin (store->env, NULL, 0, &txn), fail);
372 for (int i = 0; i < N_DB; i++)
373 CHECK (mdb_dbi_open (
374 txn, db_labels[i], db_flags[i], store->dbi + i), fail);
375
376 store->flags |= LSSTORE_OPEN;
377 CHECK (txn_commit(txn), fail);
378 txn = NULL;
379
380 log_info ("Created MDB environment at %s", path);
381
382 return store;
383
384fail:
385 if (txn) mdb_txn_abort (txn);
386 mdb_env_close (store->env);
387
388 return NULL;
389}
390
391
392static void
393mdbstore_free (void *h)
394{
395 MDBStore *store = h;
396 if (store->flags & LSSTORE_OPEN) {
397 const char *path;
398 mdb_env_get_path (store->env, &path);
399 log_info ("Closing MDB environment at %s.", path);
400 mdb_env_close (store->env);
401 }
402
403 free (store);
404}
405
406
407static VOLK_rc
408mdbstore_stat (const MDBStore *store, MDB_stat *stat)
409{
410 if (!(store->flags & LSSTORE_OPEN)) return 0;
411
412 MDB_txn *txn;
413 RCCK (txn_begin (store->env, NULL, MDB_RDONLY, &txn));
414
415 if (mdb_stat (txn, store->dbi[IDX_SPO_C], stat) != MDB_SUCCESS)
416 return VOLK_DB_ERR;
417 mdb_txn_abort (txn);
418
419 return VOLK_OK;
420}
421
422
423static size_t
424mdbstore_size (const void *h)
425{
426 const MDBStore *store = h;
427 // Size is calculated outside of any pending write txn.
428
429 MDB_stat stat;
430 if (mdbstore_stat (store, &stat) != VOLK_OK) return 0;
431
432 return stat.ms_entries;
433}
434
435
436char *
437mdbstore_id (const void *h)
438{
439 const MDBStore *store = h;
440 const char *path;
441
442 RCNL (mdb_env_get_path (store->env, &path));
443
444 char *id = malloc (strlen (path) + 8);
445 sprintf (id, "file://%s", path);
446
447 return id;
448}
449
450
451static VOLK_rc
452mdbstore_txn_begin (void *h, VOLK_StoreFlags flags, void **th)
453{
454 MDBStore *store = h;
455 flags = flags & VOLK_STORE_TXN_RO ? MDB_RDONLY : 0;
456
457 RCCK (txn_begin (store->env, NULL, flags, (MDB_txn **) th));
458
459 return VOLK_OK;
460}
461
462
463static VOLK_rc
464mdbstore_txn_commit (void *th)
465{
466 RCCK (txn_commit (th));
467
468 return VOLK_OK;
469}
470
471
472static void
473mdbstore_txn_abort (void *th)
474{ mdb_txn_abort (th); }
475
476
477static void *
478mdbiter_txn (void *h)
479{ return ((MDBIterator *) h)->txn; }
480
481
493static void *
494mdbstore_add_init (void *h, const VOLK_Buffer *sc, void *th)
495{
496 MDBStore *store = h;
497 /* An iterator is used here. Some members are a bit misused but it does
498 * its job without having to define a very similar struct.
499 */
500 MDBIterator *it;
501 MALLOC_GUARD (it, NULL);
502
503 it->store = store;
504 it->i = 0;
505
506 CHECK (txn_begin (store->env, th, 0, &it->txn), fail);
507
508 if (sc) {
509 // Store context if it's not the default one.
510 it->luc = VOLK_buffer_hash (sc);
511
512 // Insert t:st for context.
513 //log_debug ("Adding context: %s", sc);
514 it->key.mv_data = &it->luc;
515 it->key.mv_size = KLEN;
516 it->data.mv_data = sc->addr;
517 it->data.mv_size = sc->size;
518
519 int db_rc = mdb_put (
520 it->txn, it->store->dbi[IDX_T_ST],
521 &it->key, &it->data, MDB_NOOVERWRITE);
522 if (db_rc != MDB_SUCCESS && db_rc != MDB_KEYEXIST) {
523 log_error (VOLK_strerror (db_rc));
524 mdb_txn_abort (it->txn);
525 return NULL;
526 }
527 } else {
528 log_debug ("No context passed to iterator, using default.");
530 }
531
532 return it;
533
534fail:
535 free (it);
536
537 return NULL;
538}
539
540
541/*
542 * NOTE: at the moment #mdbstore_remove() or another
543 * #mdbstore_init() cannot be called between #mdbstore_add_init and
544 * #mdbstore_add_abort or #mdbstore_add_done. FIXME
545 *
546 */
547static VOLK_rc
548mdbstore_add_iter (void *h, const VOLK_BufferTriple *sspo)
549{
550 if (UNLIKELY (!h)) return VOLK_VALUE_ERR;
551
552 MDBIterator *it = h;
553 int db_rc = VOLK_NOACTION;
555
556 // Add triple terms.
557 for (int i = 0; i < 3; i++) {
558 VOLK_Buffer *st = VOLK_btriple_pos (sspo, i);
559
560 spok[i] = VOLK_buffer_hash (st);
561
562 it->key.mv_data = spok + i;
563 it->key.mv_size = KLEN;
564 it->data.mv_data = st->addr;
565 it->data.mv_size = st->size;
566
567 db_rc = mdb_put(
568 it->txn, it->store->dbi[IDX_T_ST],
569 &it->key, &it->data, MDB_NOOVERWRITE);
570 if (db_rc != MDB_SUCCESS && db_rc != MDB_KEYEXIST) {
571 LOG_RC (db_rc);
572 return VOLK_DB_ERR;
573 }
574 }
575
576 log_trace ("Inserting spok: {%x, %x, %x}", spok[0], spok[1], spok[2]);
577 log_trace ("Into context: %x", it->luc);
578
579 // Insert spo:c.
580 it->key.mv_data = spok;
581 it->key.mv_size = TRP_KLEN;
582
583 // In triple mode, data is empty (= NULL_KEY).
584 it->data.mv_data = &it->luc;
585 it->data.mv_size = it->luc == NULL_KEY ? 0 : KLEN;
586
587 db_rc = mdb_put(
588 it->txn, it->store->dbi[IDX_SPO_C],
589 &it->key, &it->data, MDB_NODUPDATA);
590
591 if (db_rc == MDB_KEYEXIST) return VOLK_NOACTION;
592 if (db_rc != MDB_SUCCESS) {
593 log_error (
594 "MDB error while inserting triple: %s", VOLK_strerror(db_rc));
595 return VOLK_DB_ERR;
596 }
597
598 // Index.
599 VOLK_rc rc = index_triple (it->store, OP_ADD, spok, it->luc, it->txn);
600 if (rc == VOLK_OK) it->i++;
601
602 return rc;
603}
604
605
606static VOLK_rc
607mdbstore_add_done (void *h)
608{
609 MDBIterator *it = h;
610 VOLK_rc rc = VOLK_OK;
611 log_debug ("Committing add transaction.");
612
613 if (txn_commit (it->txn) != MDB_SUCCESS) {
614 log_error ("Error committing transaction. Aborting instead.");
615 mdb_txn_abort (it->txn);
616 rc = VOLK_TXN_ERR;
617 }
618
619 free (it);
620
621 RCCK (rc);
622 return rc;
623}
624
625
626static void
627mdbstore_add_abort (void *h)
628{
629 MDBIterator *it = h;
630 mdb_txn_abort (it->txn);
631
632 free (it);
633}
634
635
636static VOLK_rc
637key_to_sterm (
638 MDBStore *store, MDB_txn *txn, const VOLK_Key key, VOLK_Buffer *sterm)
639{
641 int db_rc;
642
643 MDB_val key_v, data_v;
644 key_v.mv_data = (void*)&key;
645 key_v.mv_size = KLEN;
646
647 db_rc = mdb_get (txn, store->dbi[IDX_T_ST], &key_v, &data_v);
648
649 sterm->flags |= VOLK_BUF_BORROWED;
650 if (db_rc == MDB_SUCCESS) {
651 sterm->addr = data_v.mv_data;
652 sterm->size = data_v.mv_size;
653 rc = VOLK_OK;
654 } else if (db_rc == MDB_NOTFOUND) {
655 sterm->addr = NULL;
656 sterm->size = 0;
657 } else rc = VOLK_DB_ERR;
658
659 return rc;
660}
661
662
663static void *
664mdbstore_lookup (
665 void *h, const VOLK_Buffer *ss, const VOLK_Buffer *sp,
666 const VOLK_Buffer *so, const VOLK_Buffer *sc, void *th, size_t *ct)
667{
668 VOLK_TripleKey spok = {
669 VOLK_buffer_hash (ss),
670 VOLK_buffer_hash (sp),
671 VOLK_buffer_hash (so),
672 };
673
674 MDBIterator *it;
675 CALLOC_GUARD (it, NULL);
676
677 it->store = h;
678 it->luc = VOLK_buffer_hash (sc);
679 log_debug ("Lookup context: %x", it->luc);
680
681 if (ct) *ct = 0;
682
683 uint8_t idx0, idx1;
684
685 if (th) it->txn = th;
686 else {
687 RCNL (txn_begin (it->store->env, NULL, MDB_RDONLY, &it->txn));
688 log_trace ("Opening new lookup transaction @%p", it->txn);
689 it->flags |= ITER_OPEN_TXN;
690 }
691
692 // Context index loop.
693 RCNL (mdb_cursor_open (
694 it->txn, it->store->dbi[IDX_SPO_C], &it->ctx_cur));
695
696 /*
697 * Lookup decision tree.
698 */
699 // s p o (all terms bound)
700 if (spok[0] != NULL_KEY && spok[1] != NULL_KEY && spok[2] != NULL_KEY) {
701 it->luk[0] = spok[0];
702 it->luk[1] = spok[1];
703 it->luk[2] = spok[2];
704 PRCNL (lookup_3bound (it, ct));
705
706 } else if (spok[0] != NULL_KEY) {
707 it->luk[0] = spok[0];
708 idx0 = 0;
709
710 // s p ?
711 if (spok[1] != NULL_KEY) {
712 it->luk[1] = spok[1];
713 idx1 = 1;
714 PRCNL (lookup_2bound (idx0, idx1, it, ct));
715
716 // s ? o
717 } else if (spok[2] != NULL_KEY) {
718 it->luk[1] = spok[2];
719 idx1 = 2;
720 PRCNL (lookup_2bound (idx0, idx1, it, ct));
721
722 // s ? ?
723 } else PRCNL (lookup_1bound (idx0, it, ct));
724
725 } else if (spok[1] != NULL_KEY) {
726 it->luk[0] = spok[1];
727 idx0 = 1;
728
729 // ? p o
730 if (spok[2] != NULL_KEY) {
731 it->luk[1] = spok[2];
732 idx1 = 2;
733 PRCNL (lookup_2bound (idx0, idx1, it, ct));
734
735 // ? p ?
736 } else PRCNL (lookup_1bound (idx0, it, ct));
737
738 // ? ? o
739 } else if (spok[2] != NULL_KEY) {
740 it->luk[0] = spok[2];
741 idx0 = 2;
742 PRCNL (lookup_1bound (idx0, it, ct));
743
744 // ? ? ? (all terms unbound)
745 } else PRCNL (lookup_0bound (it, ct));
746
747 return it;
748}
749
750
756static VOLK_rc
757mdbiter_next_key (MDBIterator *it)
758{
760 // Only advance if the previous it->rc wasn't already at the end.
761 if (it->rc == MDB_NOTFOUND) return VOLK_END;
762 RCCK (it->rc);
763
764 /* Retrieve current value and advance cursor to the next result.
765 * it->rc is set to the result of the next iteration.
766 */
767 it->iter_op_fn (it);
768 MDB_val key, data;
769 int db_rc;
770
771 key.mv_size = TRP_KLEN;
772 data.mv_data = &it->luc;
773 data.mv_size = KLEN;
774
775 VOLK_rc rc;
776 if (it->luc) {
777 rc = VOLK_NORESULT; // Flow control value, will never be returned.
778 do {
779 //log_debug ("begin ctx loop.");
780 /* If ctx is specified, look if the matching triple is associated
781 * with it. If not, move on to the next triple.
782 * The loop normally exits when a triple with matching ctx is found
783 * (VOLK_OK), if there are no more triples (VOLK_END), or if there
784 * is an error (VOLK_DB_ERR).
785 */
786 log_trace (
787 "Found spok: {%x, %x, %x}",
788 it->spok[0], it->spok[1], it->spok[2]);
789
790 key.mv_data = it->spok;
791 db_rc = mdb_cursor_get (it->ctx_cur, &key, &data, MDB_GET_BOTH);
792
793 if (db_rc == MDB_NOTFOUND) {
794 log_trace ("Triple not found in context: %x", it->luc);
795 // if the iterator is at the end, stop here.
796 if (it->rc == MDB_NOTFOUND) rc = VOLK_END;
797 // Otherwise, look up the next key.
798 else it->iter_op_fn (it);
799
800 } else {
801 RCCK (db_rc);
802 rc = VOLK_OK;
803 log_trace ("Triple found in context: %x", it->luc);
804 }
805 } while (rc == VOLK_NORESULT);
806
807 } else {
808 log_trace (
809 "Found spok in any context: {%x, %x, %x}",
810 it->spok[0], it->spok[1], it->spok[2]);
811 rc = VOLK_OK;
812 }
813
814 // Get all contexts for a triple.
815 key.mv_data = it->spok;
816 db_rc = mdb_cursor_get (it->ctx_cur, &key, &data, MDB_SET_KEY);
817 if (db_rc != MDB_SUCCESS) {
818 log_error ("No context found for triple!");
819 return VOLK_DB_ERR;
820 }
821
822 size_t ct;
823 db_rc = mdb_cursor_count (it->ctx_cur, &ct);
824 if (db_rc != MDB_SUCCESS) return VOLK_DB_ERR;
825 // 1 spare for sentinel. Always allocated even on zero matches.
826 VOLK_Key *tmp_ck = realloc (it->ck, sizeof (*it->ck) * (ct + 1));
827 NLRCCK (tmp_ck, VOLK_MEM_ERR);
828 it->ck = tmp_ck;
829
830 size_t i = 0;
831 do {
832 //log_trace ("Copying to slot #%lu @%p", i, it->ck + i);
833 memcpy (it->ck + i++, data.mv_data, sizeof (*it->ck));
834 } while (
835 mdb_cursor_get (it->ctx_cur, &key, &data, MDB_NEXT_DUP)
836 == MDB_SUCCESS);
837 //log_trace ("setting sentinel @%p", it->ck + i);
838 it->ck[i] = NULL_KEY;
839
840 return rc;
841}
842
843
844static VOLK_rc
845mdbiter_next (
846 void *h, VOLK_BufferTriple *sspo, VOLK_Buffer **ctx_p)
847{
848 MDBIterator *it = h;
849 VOLK_rc rc = mdbiter_next_key (it);
850
851 if (rc == VOLK_OK) {
852 if (sspo) {
853 RCCK (key_to_sterm (it->store, it->txn, it->spok[0], sspo->s));
854 RCCK (key_to_sterm (it->store, it->txn, it->spok[1], sspo->p));
855 RCCK (key_to_sterm (it->store, it->txn, it->spok[2], sspo->o));
856 }
857
858 // Contexts for current triple.
859 if (ctx_p) {
860 // Preallocate.
861 size_t i = 0;
862 while (it->ck[i++]); // Include sentinel in count.
863 VOLK_Buffer *ctx;
864 log_trace ("Allocating %lu context buffers + sentinel.", i - 1);
865 ctx = malloc(i * sizeof (*ctx));
866 if (!ctx) return VOLK_MEM_ERR;
867
868 for (i = 0; it->ck[i]; i++)
869 RCCK (key_to_sterm (it->store, it->txn, it->ck[i], ctx + i));
870 memset (ctx + i, 0, sizeof (*ctx)); // Sentinel
871 NLRCCK (ctx + i, VOLK_MEM_ERR);
872
873 // TODO error handling.
874 *ctx_p = ctx;
875 }
876 }
877
878 return rc;
879}
880
881
882static void
883mdbiter_free (void *h)
884{
885 if (!h) return;
886 MDBIterator *it = h;
887
888 if (it->cur) mdb_cursor_close (it->cur);
889 if (it->ctx_cur) mdb_cursor_close (it->ctx_cur);
890 if (it->flags & ITER_OPEN_TXN) mdb_txn_abort (it->txn);
891 free (it->ck);
892
893 free (it);
894}
895
896
897static VOLK_rc
898mdbstore_update_ctx (
899 void *h, const VOLK_Buffer *old_c, const VOLK_Buffer *new_c, void *th)
900{
902 MDBStore *store = h;
903 unsigned char *trp_data = NULL;
904
906 old_ck = VOLK_buffer_hash (old_c),
907 new_ck = VOLK_buffer_hash (new_c);
908 // lu_key, lu_data look up all triples with old context in c:spo, and
909 // replace old c with new c.
910 MDB_txn
911 *p_txn = th,
912 *txn;
913 CHECK (rc = txn_begin (store->env, p_txn, 0, &txn), finally);
914
915 MDB_cursor *i_cur, *d_cur;
916 CHECK (
917 rc = mdb_cursor_open (txn, store->dbi[IDX_C_SPO], &i_cur),
918 close_txn);
919
920 MDB_val key, data;
921
922 // Return error if the graph URI already exists.
923 key.mv_data = &new_ck;
924 key.mv_size = KLEN;
925 rc = mdb_cursor_get (i_cur, &key, &data, MDB_SET);
926 if (rc == MDB_SUCCESS) {
927 log_error (
928 "Context key %lu already exists. Not replacing old graph.",
929 new_ck);
930 rc = VOLK_CONFLICT;
931 goto close_i;
932 } else if (rc != MDB_NOTFOUND) CHECK (rc, close_i);
933
934 // Add new context term.
935 CHECK (rc = mdbstore_add_term (store, new_c, txn), close_i);
936
937 key.mv_data = &old_ck;
938 // Count triples in cursor.
939 rc = mdb_cursor_get (i_cur, &key, &data, MDB_SET);
940 if (rc == MDB_NOTFOUND) {
941 log_info ("No triples found associated with old context.");
942 rc = VOLK_NOACTION;
943 goto close_i;
944 } else CHECK (rc, close_i);
945
946 // From here on, it can only be VOLK_OK or error.
947 rc = VOLK_OK;
948 size_t trp_ct;
949 CHECK (rc = mdb_cursor_count (i_cur, &trp_ct), close_i);
950 trp_data = malloc (trp_ct * TRP_KLEN);
951 if (UNLIKELY (!trp_data)) {
952 rc = VOLK_MEM_ERR;
953 goto close_i;
954 }
955
956 // Copy triple data as one block to temp buffer so that entries can be
957 // deleted while cursors are active.
958 rc = mdb_cursor_get (i_cur, &key, &data, MDB_GET_MULTIPLE);
959 if (rc != MDB_SUCCESS) {
960 rc = rc == MDB_NOTFOUND ? VOLK_NOACTION : VOLK_DB_ERR;
961 goto close_i;
962 }
963 size_t loc_cur = 0;
964 do {
965 memcpy (trp_data + loc_cur, data.mv_data, data.mv_size);
966 loc_cur += data.mv_size;
967 } while (mdb_cursor_get (
968 i_cur, &key, &data, MDB_NEXT_MULTIPLE) == MDB_SUCCESS);
969
970 // Zap c:spo entries in one go.
971 key.mv_data = &old_ck;
972 key.mv_size = KLEN;
973 data.mv_size = TRP_KLEN;
974 CHECK (rc = mdb_cursor_get (i_cur, &key, NULL, MDB_SET), close_i);
975 CHECK (rc = mdb_cursor_del (i_cur, MDB_NODUPDATA), close_i);
976
977 // Re-ad c:spo data individually.
978 key.mv_data = &new_ck;
979 for (size_t i = 0; i < trp_ct; i++) {
980 data.mv_data = trp_data + i * data.mv_size;
981 CHECK (
982 rc = mdb_cursor_put (i_cur, &key, &data, MDB_APPENDDUP),
983 close_i);
984 }
985 // Re-add c:spo data in bulk from buffer with new context.
986 // FIXME this is not working. Replaced by the for loop above.
987 /*
988 MDB_val data_block[] = {
989 { .mv_data = &new_ck, .mv_size = TRP_KLEN },
990 { .mv_data = NULL, .mv_size = trp_ct },
991 };
992 db_rc = mdb_cursor_put (i_cur, &key, data_block, MDB_MULTIPLE);
993 */
994
995 // Main table.
996 // Replace spo:c values one by one.
997 CHECK (rc = mdb_cursor_open (txn, store->dbi[IDX_SPO_C], &d_cur), close_i);
998 key.mv_size = TRP_KLEN;
999 data.mv_size = KLEN;
1000 for (size_t i = 0; i < trp_ct; i++) {
1001 key.mv_data = trp_data + i * key.mv_size;
1002 data.mv_data = &old_ck;
1003 CHECK (
1004 rc = mdb_cursor_get (d_cur, &key, &data, MDB_GET_BOTH),
1005 close_d);
1006 CHECK (rc = mdb_cursor_del (d_cur, 0), close_d);
1007 data.mv_data = &new_ck;
1008 CHECK (
1009 rc = mdb_cursor_put (d_cur, &key, &data, MDB_NOOVERWRITE),
1010 close_d);
1011 }
1012
1013close_d:
1014 mdb_cursor_close (d_cur);
1015close_i:
1016 mdb_cursor_close (i_cur);
1017close_txn:
1018 if (rc == VOLK_OK) RCCK (txn_commit (txn));
1019 else mdb_txn_abort (txn);
1020
1021 if (trp_data) free (trp_data);
1022finally:
1023
1024 return rc;
1025}
1026
1027
1028static VOLK_rc
1029mdbstore_remove (
1030 void *h, const VOLK_Buffer *ss, const VOLK_Buffer *sp,
1031 const VOLK_Buffer *so, const VOLK_Buffer *sc, void *th, size_t *ct_p)
1032{
1033 MDBStore *store = h;
1035
1036 MDB_txn *txn;
1037 RCCK (txn_begin (store->env, th, 0, &txn));
1038 MDB_cursor *cur;
1039 mdb_cursor_open (txn, store->dbi[IDX_SPO_C], &cur);
1040
1041 if (sc == NULL) sc = VOLK_default_ctx_buf;
1042 VOLK_Key ck = VOLK_buffer_hash (sc);
1043 MDB_val spok_v, ck_v;
1044 spok_v.mv_size = TRP_KLEN;
1045 ck_v.mv_size = KLEN;
1046 ck_v.mv_data = &ck;
1047
1048 // Gather all the matching triples in a first pass.
1049 size_t *ct = ct_p ? ct_p : malloc (sizeof (*ct));
1050 NLRCCK (ct, VOLK_MEM_ERR);
1051 MDBIterator *it = mdbstore_lookup (store, ss, sp, so, sc, txn, ct);
1052 NLRCCK (it, VOLK_DB_ERR);
1053 log_debug ("Found %lu triples to remove.", *ct);
1054 VOLK_Key *keys = malloc (*ct * sizeof (VOLK_Key) * 3);
1055 NLRCCK (it, VOLK_MEM_ERR);
1056 size_t i = 0;
1057 while (mdbiter_next_key (it) == VOLK_OK) {
1058 log_trace ("Adding triple #%zu to remove list.", i);
1059 memcpy (keys + (3 * i++), &it->spok, TRP_KLEN);
1060 }
1061 mdbiter_free (it);
1062
1063 // Iterate over the gathered keys and delete them.
1064 for (i = 0; i < *ct; i++) {
1065 spok_v.mv_data = keys + i * 3;
1066 ck_v.mv_data = &ck;
1067 log_trace (
1068 "Removing triple #%zu: %x {%x %x %x}",
1069 i,
1070 ((VOLK_Key *)ck_v.mv_data)[0],
1071 ((VOLK_Key *)spok_v.mv_data)[0],
1072 ((VOLK_Key *)spok_v.mv_data)[1],
1073 ((VOLK_Key *)spok_v.mv_data)[2]);
1074
1075 rc = mdb_cursor_get (cur, &spok_v, &ck_v, MDB_GET_BOTH);
1076 if (rc == MDB_NOTFOUND) {
1077 log_warn ("No key found in spo:c DB.");
1078 continue; // TODO This could be a data problem.
1079 } else CHECK (rc, fail);
1080
1081 // Delete spo:c entry.
1082 CHECK (rc = mdb_cursor_del (cur, 0), fail);
1083 CHECK (rc = index_triple (
1084 store, OP_REMOVE, keys + i * 3, ck, txn
1085 ), fail);
1086 }
1087 free(keys);
1088 CHECK (txn_commit (txn), fail);
1089
1090 return rc;
1091
1092fail:
1093 mdb_txn_abort (txn);
1094 RCCK (rc);
1095
1096 return VOLK_DB_ERR;
1097}
1098
1099
1111static VOLK_rc
1112mdbstore_add_term (void *h, const VOLK_Buffer *sterm, void *th)
1113{
1114 //log_trace ("Adding term to MDB store: %s", sterm->addr);
1115 MDBStore *store = h;
1116 int db_rc;
1117 MDB_val key, data;
1118
1119 MDB_txn *txn;
1120 // If an active transaction was passed, use it, otherwise open and
1121 // close a new one.
1122 if (th) txn = th;
1123 else RCCK (txn_begin (store->env, th, 0, &txn));
1124
1125 MDB_cursor *cur;
1126 CHECK (mdb_cursor_open (txn, store->dbi[IDX_T_ST], &cur), fail);
1127
1128 VOLK_Key k = VOLK_buffer_hash (sterm);
1129 key.mv_data = &k;
1130 key.mv_size = sizeof (k);
1131
1132 data.mv_data = sterm->addr;
1133 data.mv_size = sterm->size;
1134
1135 db_rc = mdb_cursor_put (cur, &key, &data, MDB_NOOVERWRITE);
1136 if (db_rc != MDB_KEYEXIST) CHECK (db_rc, fail);
1137
1138 if (txn != th) CHECK (db_rc = txn_commit (txn), fail);
1139
1140 return VOLK_OK;
1141
1142fail:
1143 if (txn != th) mdb_txn_abort (txn);
1144 log_trace ("Aborted txn for adding term.");
1145 return VOLK_DB_ERR;
1146}
1147
1148
1149VOLK_Buffer **
1150mdbstore_ctx_list (void *h, void *th)
1151{
1152 MDBStore *store = h;
1153 VOLK_rc db_rc;
1154 MDB_txn *txn;
1155 VOLK_Buffer **tdata = NULL;
1156 if (th) txn = th;
1157 else CHECK (txn_begin (store->env, NULL, MDB_RDONLY, &txn), fail_txn);
1158
1159
1160 MDB_cursor *cur;
1161 CHECK (mdb_cursor_open (txn, store->dbi[IDX_C_SPO], &cur), fail);
1162 MDB_val key, data;
1163
1164 db_rc = mdb_cursor_get (cur, &key, &data, MDB_FIRST);
1165 size_t i = 0;
1166 while (db_rc == MDB_SUCCESS) {
1167 i++;
1168 db_rc = mdb_cursor_get (cur, &key, &data, MDB_NEXT_NODUP);
1169 }
1170 tdata = malloc ((i + 1) * sizeof (*tdata));
1171 NLCHECK (tdata, fail);
1172
1173 db_rc = mdb_cursor_get (cur, &key, &data, MDB_FIRST);
1174 i = 0;
1175 while (db_rc == MDB_SUCCESS) {
1176 tdata[i] = BUF_DUMMY;
1177 VOLK_Key tkey = *(VOLK_Key*)key.mv_data;
1178 CHECK (key_to_sterm (store, txn, tkey, tdata[i++]), fail);
1179 db_rc = mdb_cursor_get (cur, &key, &data, MDB_NEXT_NODUP);
1180 }
1181 tdata[i] = NULL; // Sentinel
1182 mdb_cursor_close (cur);
1183 if (txn != th) mdb_txn_abort (txn);
1184
1185 return tdata;
1186
1187fail:
1188 if (txn != th) mdb_txn_abort (txn);
1189 if (tdata) free (tdata);
1190fail_txn:
1191 return NULL;
1192}
1193
1194
1196 .name = "MDB Store",
1199
1200 .setup_fn = mdbstore_setup,
1201 .new_fn = mdbstore_new,
1202 .free_fn = mdbstore_free,
1203
1204 .size_fn = mdbstore_size,
1205 .id_fn = mdbstore_id,
1206
1207 .txn_begin_fn = mdbstore_txn_begin,
1208 .txn_commit_fn = mdbstore_txn_commit,
1209 .txn_abort_fn = mdbstore_txn_abort,
1210 .iter_txn_fn = mdbiter_txn,
1211
1212 .add_init_fn = mdbstore_add_init,
1213 .add_iter_fn = mdbstore_add_iter,
1214 .add_abort_fn = mdbstore_add_abort,
1215 .add_done_fn = mdbstore_add_done,
1216 .add_term_fn = mdbstore_add_term,
1217
1218 .update_ctx_fn = mdbstore_update_ctx,
1219
1220 .lookup_fn = mdbstore_lookup,
1221 .lu_next_fn = mdbiter_next,
1222 .lu_free_fn = mdbiter_free,
1223
1224 .remove_fn = mdbstore_remove,
1225
1226 .ctx_list_fn = mdbstore_ctx_list,
1227};
1228
1229
1230/* * * Static functions. * * */
1231
1241static VOLK_rc
1242index_triple(
1243 MDBStore *store, StoreOp op, VOLK_TripleKey spok, VOLK_Key ck,
1244 MDB_txn *txn)
1245{
1246 int db_rc;
1248 MDB_val v1, v2;
1249
1250 log_trace ("Indexing triple: {%x %x %x}", spok[0], spok[1], spok[2]);
1251
1252 // Index c:spo.
1253 if (op == OP_REMOVE) {
1254 log_trace ("Indexing op: REMOVE");
1255 if (ck == NULL_KEY) goto skip_remove;
1256
1257 MDB_cursor *cur;
1258 v1.mv_data = &ck;
1259 v1.mv_size = KLEN;
1260 v2.mv_data = spok;
1261 v2.mv_size = TRP_KLEN;
1262
1263 RCCK (mdb_cursor_open (txn, store->dbi[IDX_C_SPO], &cur));
1264 db_rc = mdb_cursor_get (cur, &v1, &v2, MDB_GET_BOTH);
1265 if (db_rc != MDB_NOTFOUND) {
1266 RCCK (db_rc);
1267 RCCK (mdb_cursor_del (cur, 0));
1268 v1.mv_data = &ck;
1269 v2.mv_data = spok;
1270 rc = VOLK_OK;
1271 }
1272 mdb_cursor_close (cur);
1273skip_remove:
1274
1275 } else if (op == OP_ADD) {
1276 log_trace ("Indexing op: ADD");
1277 if (ck == NULL_KEY) goto skip_add;
1278
1279 v1.mv_data = &ck;
1280 v1.mv_size = KLEN;
1281 v2.mv_data = spok;
1282 v2.mv_size = TRP_KLEN;
1283
1284 db_rc = mdb_put(
1285 txn, store->dbi[IDX_C_SPO],
1286 &v1, &v2, MDB_NODUPDATA);
1287 if (db_rc != MDB_SUCCESS) return VOLK_DB_ERR;
1288 if (db_rc != MDB_KEYEXIST) rc = VOLK_OK;
1289skip_add:
1290
1291 } else return VOLK_VALUE_ERR;
1292
1293 VOLK_DoubleKey dbl_keys[3] = {
1294 {spok[1], spok[2]}, // po
1295 {spok[0], spok[2]}, // so
1296 {spok[0], spok[1]}, // sp
1297 };
1298
1299 // Add or remove index terms.
1300 v1.mv_size = KLEN;
1301 v2.mv_size = DBL_KLEN;
1302
1303 for (int i = 0; i < 3; i++) {
1304 MDB_dbi
1305 db1 = store->dbi[lookup_indices[i]], // s:po, p:so, o:sp
1306 db2 = store->dbi[lookup_indices[i + 3]]; // po:s, so:p, sp:o
1307
1308 v1.mv_data = spok + i;
1309 v2.mv_data = dbl_keys[i];
1310
1311 if (op == OP_REMOVE) {
1312 // Remove from 1-bound index.
1313 MDB_cursor *cur1;
1314 mdb_cursor_open(txn, store->dbi[lookup_indices[i]], &cur1);
1315 RCCK (db_rc = mdb_cursor_get (cur1, &v1, &v2, MDB_GET_BOTH));
1316 mdb_cursor_del (cur1, 0);
1317 mdb_cursor_close (cur1);
1318
1319 // Restore pointers invalidated after delete.
1320 v1.mv_data = spok + i;
1321 v2.mv_data = dbl_keys[i];
1322
1323 // Remove from 2-bound index.
1324 MDB_cursor *cur2;
1325 mdb_cursor_open(txn, store->dbi[lookup_indices[i + 3]], &cur2);
1326 RCCK (db_rc = mdb_cursor_get (cur2, &v2, &v1, MDB_GET_BOTH));
1327 mdb_cursor_del (cur2, 0);
1328 mdb_cursor_close (cur2);
1329
1330 rc = VOLK_OK;
1331
1332 } else { // OP_ADD is guaranteed.
1333 // Add to 1-bound index.
1334 log_trace ("Indexing in %s: ", db_labels[lookup_indices[i]]);
1335 log_trace (
1336 "%x: %x %x", *(size_t*)(v1.mv_data),
1337 *(size_t*)(v2.mv_data), *(size_t*)(v2.mv_data) + 1);
1338
1339 db_rc = mdb_put (txn, db1, &v1, &v2, MDB_NODUPDATA);
1340
1341 if (db_rc == MDB_SUCCESS) rc = VOLK_OK;
1342 else if (db_rc != MDB_KEYEXIST) return VOLK_DB_ERR;
1343
1344 // Add to 2-bound index.
1345 log_trace ("Indexing in %s: ", db_labels[lookup_indices[i + 3]]);
1346 log_trace (
1347 "%x %x: %x", *(size_t*)(v2.mv_data),
1348 *(size_t*)(v2.mv_data) + 1, *(size_t*)(v1.mv_data));
1349
1350 db_rc = mdb_put (txn, db2, &v2, &v1, MDB_NODUPDATA);
1351
1352 if (db_rc == MDB_SUCCESS) rc = VOLK_OK;
1353 else if (db_rc != MDB_KEYEXIST) return VOLK_DB_ERR;
1354 }
1355 }
1356
1357 return rc;
1358}
1359
1360
1361/* * * Term-specific iterators. * * */
1362
1367inline static void
1368it_next_0bound (MDBIterator *it)
1369{
1370 memcpy (it->spok, it->key.mv_data, sizeof (VOLK_TripleKey));
1371 it->rc = mdb_cursor_get (it->cur, &it->key, &it->data, MDB_NEXT_NODUP);
1372}
1373
1374
1379inline static void
1380it_next_0bound_ctx (MDBIterator *it)
1381{
1382 memcpy (it->spok, it->data.mv_data, sizeof (VOLK_TripleKey));
1383 it->rc = mdb_cursor_get (it->cur, &it->key, &it->data, MDB_NEXT_DUP);
1384}
1385
1386
1393inline static void
1394it_next_1bound (MDBIterator *it)
1395{
1396 VOLK_DoubleKey *lu_dset = it->data.mv_data;
1397
1398 it->spok[it->term_order[0]] = it->luk[0];
1399 it->spok[it->term_order[1]] = lu_dset[it->i][0];
1400 it->spok[it->term_order[2]] = lu_dset[it->i][1];
1401
1402 log_trace (
1403 "Composed triple: {%x %x %x}",
1404 it->spok[0], it->spok[1], it->spok[2]);
1405
1406 // Ensure next block within the same page is not beyond the last.
1407 if (it->i < it->data.mv_size / DBL_KLEN - 1) {
1408 it->i ++;
1409 //log_debug ("Increasing page cursor to %lu.", it->i);
1410 //log_debug ("it->rc: %d", it->rc);
1411
1412 } else {
1413 // If the last block in the page is being yielded,
1414 // move cursor to beginning of next page.
1415 it->i = 0;
1416 //log_debug ("Reset page cursor to %lu.", it->i);
1417 it->rc = mdb_cursor_get (
1418 it->cur, &it->key, &it->data, MDB_NEXT_MULTIPLE);
1419 }
1420}
1421
1422
1429inline static void
1430it_next_2bound (MDBIterator *it)
1431{
1432 VOLK_Key *lu_dset = it->data.mv_data;
1433
1434 it->spok[it->term_order[0]] = it->luk[0];
1435 it->spok[it->term_order[1]] = it->luk[1];
1436 it->spok[it->term_order[2]] = lu_dset[it->i];
1437
1438 // Ensure next block within the same page is not beyond the last.
1439 if (it->i < it->data.mv_size / KLEN - 1)
1440 it->i ++;
1441 else {
1442 // If the last block in the page is being yielded,
1443 // move cursor to beginning of next page.
1444 it->i = 0;
1445 it->rc = mdb_cursor_get (it->cur, &it->key, &it->data, MDB_NEXT_MULTIPLE);
1446 }
1447}
1448
1449
1456inline static void
1457it_next_3bound (MDBIterator *it)
1458{ it->rc = MDB_NOTFOUND; }
1459
1460
1461/* * * Term-specific lookups. * * */
1462
1463inline static VOLK_rc
1464lookup_0bound (MDBIterator *it, size_t *ct)
1465{
1466 log_debug ("Looking up 0 bound terms.");
1467
1468 // Context search looks for all values in c:spo.
1469 if (it->luc != NULL_KEY) {
1470 // Look up by given context.
1471 RCCK (it->rc = mdb_cursor_open (
1472 it->txn, it->store->dbi[IDX_C_SPO], &it->cur));
1473
1474 it->key.mv_data = &it->luc;
1475 it->key.mv_size = KLEN;
1476
1477 it->rc = mdb_cursor_get (it->cur, &it->key, &it->data, MDB_SET);
1478 if (ct) {
1479 if (it->rc == MDB_NOTFOUND) *ct = 0;
1480 else {
1481 RCCK (it->rc);
1482 mdb_cursor_count (it->cur, ct);
1483 }
1484 log_debug ("Found %lu triples.", *ct);
1485 }
1486 //it->rc = mdb_cursor_get (it->cur, &it->key, &it->data, MDB_FIRST_DUP);
1487 it->iter_op_fn = it_next_0bound_ctx;
1488
1489 // No-context search looks for all keys in spo:c.
1490 } else {
1491 RCCK (it->rc = mdb_cursor_open (
1492 it->txn, it->store->dbi[IDX_SPO_C], &it->cur));
1493 it->rc = mdb_cursor_get (it->cur, &it->key, &it->data, MDB_FIRST);
1494 if (ct) {
1495 if (it->rc == MDB_NOTFOUND) *ct = 0;
1496 else {
1497 MDB_stat stat;
1498 // s:po and 1- and 2-bound indices have 1 entry per triple.
1499 mdb_stat (it->txn, it->store->dbi[IDX_S_PO], &stat);
1500
1501 *ct = stat.ms_entries;
1502 }
1503 log_debug ("Found %lu triples.", *ct);
1504 }
1505 it->iter_op_fn = it_next_0bound;
1506 }
1507 if (it->rc != MDB_NOTFOUND) RCCK (it->rc);
1508
1509 return VOLK_OK;
1510}
1511
1512
1513inline static VOLK_rc
1514lookup_1bound (uint8_t idx0, MDBIterator *it, size_t *ct)
1515{
1516 it->term_order = (const uint8_t*)lookup_ordering_1bound[idx0];
1517
1518 log_debug ("Looking up 1 bound term: %x", it->luk[0]);
1519
1520 RCCK (mdb_cursor_open (
1521 it->txn, it->store->dbi[lookup_indices[idx0]], &it->cur));
1522
1523 it->key.mv_data = it->luk;
1524 it->key.mv_size = KLEN;
1525
1526 if (ct) {
1527 // If a context is specified, the only way to count triples matching
1528 // the context is to loop over them.
1529 if (it->luc != NULL_KEY) {
1530 log_debug ("Counting in context: %x", it->luc);
1531 MDBIterator *ct_it;
1532 MALLOC_GUARD (ct_it, VOLK_MEM_ERR);
1533 /*
1534 memcpy (ct_it, it, sizeof (*ct_it));
1535 */
1536
1537 ct_it->store = it->store;
1538 ct_it->txn = it->txn;
1539 ct_it->ctx_cur = it->ctx_cur;
1540 ct_it->key = it->key;
1541 ct_it->data = it->data;
1542 ct_it->ck = NULL;
1543 ct_it->luk[0] = it->luk[0];
1544 ct_it->luc = it->luc;
1545 ct_it->i = 0;
1546
1547 PRCCK (lookup_1bound (idx0, ct_it, NULL));
1548
1549 VOLK_rc db_rc;
1550 while (VOLK_END != (db_rc = mdbiter_next_key (ct_it))) {
1551 PRCCK (db_rc);
1552 (*ct)++;
1553 }
1554
1555 // Free the counter iterator without freeing the shared txn.
1556 if (ct_it->cur) mdb_cursor_close (ct_it->cur);
1557 free (ct_it->ck);
1558 free (ct_it);
1559
1560 } else {
1561 it->rc = mdb_cursor_get (it->cur, &it->key, &it->data, MDB_SET);
1562 if (it->rc == MDB_SUCCESS) mdb_cursor_count (it->cur, ct);
1563 }
1564 log_debug ("Found %lu triples.", *ct);
1565 }
1566
1567 it->i = 0;
1568 it->iter_op_fn = it_next_1bound;
1569
1570 it->rc = mdb_cursor_get (it->cur, &it->key, &it->data, MDB_SET);
1571 if (it->rc == MDB_SUCCESS)
1572 it->rc = mdb_cursor_get (it->cur, &it->key, &it->data, MDB_GET_MULTIPLE);
1573 if (it->rc != MDB_NOTFOUND) RCCK (it->rc);
1574
1575 return VOLK_OK;
1576}
1577
1578
1579inline static VOLK_rc
1580lookup_2bound(uint8_t idx0, uint8_t idx1, MDBIterator *it, size_t *ct)
1581{
1582 uint8_t luk1_offset, luk2_offset;
1583 MDB_dbi dbi = 0;
1584
1585 // Establish lookup ordering with some awkward offset math.
1586 for (int i = 0; i < 3; i++) {
1587 if (
1588 (
1589 idx0 == lookup_ordering_2bound[i][0] &&
1590 idx1 == lookup_ordering_2bound[i][1]
1591 ) || (
1592 idx0 == lookup_ordering_2bound[i][1] &&
1593 idx1 == lookup_ordering_2bound[i][0]
1594 )
1595 ) {
1596 it->term_order = (const uint8_t*)lookup_ordering_2bound[i];
1597 if (it->term_order[0] == idx0) {
1598 luk1_offset = 0;
1599 luk2_offset = 1;
1600 } else {
1601 luk1_offset = 1;
1602 luk2_offset = 0;
1603 }
1604 dbi = it->store->dbi[lookup_indices[i + 3]];
1605 log_debug (
1606 "Looking up 2 bound in %s",
1607 db_labels[lookup_indices[i + 3]]);
1608
1609 break;
1610 }
1611 }
1612
1613 if (dbi == 0) {
1614 log_error (
1615 "Values %d and %d not found in lookup keys.",
1616 idx0, idx1);
1617 return VOLK_VALUE_ERR;
1618 }
1619
1620 // Compose term keys in lookup key.
1621 VOLK_DoubleKey luk;
1622 luk[luk1_offset] = it->luk[0];
1623 luk[luk2_offset] = it->luk[1];
1624
1625 it->key.mv_data = luk;
1626 it->key.mv_size = DBL_KLEN;
1627
1628 mdb_cursor_open (it->txn, dbi, &it->cur);
1629 it->rc = mdb_cursor_get (it->cur, &it->key, &it->data, MDB_SET);
1630
1631 if (ct) {
1632 // If a context is specified, the only way to count triples matching
1633 // the context is to loop over them.
1634 if (it->luc != NULL_KEY) {
1635 MDBIterator *ct_it;
1636 MALLOC_GUARD (ct_it, VOLK_MEM_ERR);
1637
1638 ct_it->store = it->store;
1639 ct_it->txn = it->txn;
1640 ct_it->ctx_cur = it->ctx_cur;
1641 ct_it->ck = NULL;
1642 ct_it->luk[0] = it->luk[0];
1643 ct_it->luk[1] = it->luk[1];
1644 ct_it->luc = it->luc;
1645 ct_it->i = 0;
1646
1647 lookup_2bound (idx0, idx1, ct_it, NULL);
1648
1649 while (mdbiter_next_key (ct_it) != VOLK_END) (*ct) ++;
1650
1651 // Free the counter iterator without freeing the shared txn.
1652 if (ct_it->cur) mdb_cursor_close (ct_it->cur);
1653 free (ct_it->ck);
1654 free (ct_it);
1655
1656 } else {
1657 it->rc = mdb_cursor_get (it->cur, &it->key, &it->data, MDB_SET);
1658 if (it->rc == MDB_SUCCESS) mdb_cursor_count (it->cur, ct);
1659 }
1660 log_debug ("Found %lu triples.", *ct);
1661 }
1662
1663 it->i = 0;
1664 it->iter_op_fn = it_next_2bound;
1665
1666 it->rc = mdb_cursor_get (it->cur, &it->key, &it->data, MDB_SET);
1667 if (it->rc == MDB_SUCCESS)
1668 it->rc = mdb_cursor_get (it->cur, &it->key, &it->data, MDB_GET_MULTIPLE);
1669 if (it->rc != MDB_NOTFOUND) RCCK (it->rc);
1670
1671 return VOLK_OK;
1672}
1673
1674
1675inline static VOLK_rc
1676lookup_3bound (MDBIterator *it, size_t *ct)
1677{
1678 log_debug (
1679 "Looking up 3 bound: {%x, %x, %x}",
1680 it->luk[0], it->luk[1], it->luk[2]);
1681
1682 it->key.mv_data = it->luk;
1683
1684 if (it->luc != NULL_KEY) {
1685 it->rc = mdb_cursor_open (
1686 it->txn, it->store->dbi[IDX_SPO_C], &it->cur);
1687
1688 it->key.mv_size = TRP_KLEN;
1689 it->data.mv_data = &it->luc;
1690 it->data.mv_size = KLEN;
1691
1692 } else {
1693 it->rc = mdb_cursor_open (it->txn, it->store->dbi[IDX_S_PO], &it->cur);
1694
1695 it->key.mv_size = KLEN;
1696 it->data.mv_data = it->luk + 1;
1697 it->data.mv_size = DBL_KLEN;
1698 }
1699
1700 it->rc = mdb_cursor_get (it->cur, &it->key, &it->data, MDB_GET_BOTH);
1701 if (it->rc != MDB_NOTFOUND) RCCK (it->rc);
1702
1703 mdb_cursor_close (it->cur);
1704 it->cur = NULL;
1705
1706 if (ct && it->rc == MDB_SUCCESS) *ct = 1;
1707
1708 it->iter_op_fn = it_next_3bound;
1709 memcpy (it->spok, it->luk, sizeof (VOLK_TripleKey));
1710
1711 return VOLK_OK;
1712}
1713
1714
#define UNLIKELY(x)
Definition core.h:39
#define NULL_TRP
"NULL" triple, a value that is never user-provided.
Definition core.h:66
#define DBL_KLEN
Definition core.h:58
#define KLEN
Definition core.h:57
#define TRP_KLEN
Definition core.h:59
bool VOLK_env_is_init
Whether the environment is initialized.
Definition core.c:11
#define MALLOC_GUARD(var, rc)
Allocate one pointer with malloc and return rc if it fails.
Definition core.h:409
#define RCNL(exp)
Return NULL if exp returns a nonzero value.
Definition core.h:379
#define NLCHECK(exp, marker)
Log error and jump to marker if exp is NULL.
Definition core.h:337
#define PRCNL(exp)
Return NULL if exp returns a negative value (=error).
Definition core.h:390
#define CHECK(exp, marker)
Jump to marker if exp does not return VOLK_OK.
Definition core.h:311
#define CALLOC_GUARD(var, rc)
Allocate one pointer with calloc and return rc if it fails.
Definition core.h:415
#define log_debug(...)
Definition core.h:294
#define RCCK(exp)
Return exp return value if it is of VOLK_rc type and nonzero.
Definition core.h:346
#define NLRCCK(exp, _rc)
Return rc return code if exp is NULL.
Definition core.h:370
#define LOG_RC(rc)
Log an error or warning for return codes that are not VOLK_OK.
Definition core.h:305
#define PRCCK(exp)
Return exp return value if it is of VOLK_rc type and negative (=error).
Definition core.h:358
#define log_trace(...)
Definition core.h:296
VOLK_rc rm_r(const char *path)
Remove a directory recursively (POSIX compatible).
Definition core.c:124
VOLK_rc mkdir_p(const char *_path, mode_t mode)
Make recursive directories.
Definition core.c:50
#define VOLK_VALUE_ERR
An invalid input value was provided.
Definition core.h:141
#define VOLK_MEM_ERR
Memory allocation error.
Definition core.h:156
#define VOLK_NORESULT
No result yielded.
Definition core.h:112
#define VOLK_DB_ERR
Low-level database error.
Definition core.h:147
#define VOLK_CONFLICT
Conflict warning.
Definition core.h:132
#define VOLK_END
Loop end.
Definition core.h:119
#define VOLK_OK
Generic success return code.
Definition core.h:95
#define VOLK_NOACTION
No action taken.
Definition core.h:105
int VOLK_rc
Return code.
Definition core.h:91
#define VOLK_ENV_ERR
Error while handling environment setup; or environment not initialized.
Definition core.h:170
#define VOLK_TXN_ERR
Error handling a store transaction.
Definition core.h:144
const char * VOLK_strerror(VOLK_rc rc)
Return an error message for a return code.
Definition core.c:196
#define NULL_KEY
"NULL" key, a value that is never user-provided.
Definition buffer.h:15
VOLK_Key VOLK_buffer_hash(const VOLK_Buffer *buf)
Hash a buffer.
Definition buffer.h:175
VOLK_Buffer * VOLK_btriple_pos(const VOLK_BufferTriple *trp, VOLK_TriplePos n)
Get serialized triple by term position.
Definition buffer.h:297
VOLK_Buffer * VOLK_default_ctx_buf
Serialized default context.
Definition buffer.c:5
#define BUF_DUMMY
Dummy buffer to be used with VOLK_buffer_init.
Definition buffer.h:154
@ VOLK_BUF_BORROWED
Definition buffer.h:28
size_t VOLK_Key
Term key, i.e., hash of a serialized term.
Definition core.h:244
VOLK_Key VOLK_TripleKey[3]
Array of three VOLK_Key values, representing a triple.
Definition core.h:248
VOLK_Key VOLK_DoubleKey[2]
Array of two VOLK_Key values.
Definition core.h:246
VOLK_StoreFlags
Store flags passed to various operations.
@ VOLK_STORE_TXN_RO
Start a read-only transaction.
@ VOLK_STORE_TXN
Supports transaction handling.
@ VOLK_STORE_COW
Copy on write.
@ VOLK_STORE_IDX
Store is fully SPO(C)-indexed.
@ VOLK_STORE_PERM
@ VOLK_STORE_CTX
VOLK_Buffer ** mdbstore_ctx_list(void *h, void *th)
Definition store_mdb.c:1150
StoreFlags
Store state flags.
Definition store_mdb.c:31
@ LSSTORE_OPEN
Env is open.
Definition store_mdb.c:32
#define MAIN_TABLE
Definition store_mdb.c:108
char DbLabel[8]
Definition store_mdb.c:27
#define ENV_FILE_MODE
Definition store_mdb.c:21
#define DEFAULT_MAPSIZE
Definition store_mdb.c:15
#define ENV_DIR_MODE
Definition store_mdb.c:20
const VOLK_StoreInt mdbstore_int
MDB store interface.
Definition store_mdb.c:1195
char * mdbstore_id(const void *h)
Definition store_mdb.c:437
#define LOOKUP_TABLE
Definition store_mdb.c:116
void(* iter_op_fn_t)(MDBIterator *it)
Iterator operation.
Definition store_mdb.c:69
#define N_DB
Definition store_mdb.c:6
IterFlags
Iterator state flags.
Definition store_mdb.c:36
@ ITER_OPEN_TXN
A transaction is open.
Definition store_mdb.c:37
StoreOp
Definition store_mdb.c:48
@ OP_ADD
Definition store_mdb.c:49
@ OP_REMOVE
Definition store_mdb.c:50
DBIdx
Definition store_mdb.c:140
@ IDX_SPO_C
Definition store_mdb.c:142
@ IDX_S_PO
Definition store_mdb.c:143
@ IDX_T_ST
Definition store_mdb.c:142
@ IDX_C_SPO
Definition store_mdb.c:143
LMDB graph store backend.
#define VOLK_MDB_STORE_URN
Default MDB store identifier and location.
Definition store_mdb.h:38
Triple iterator.
Definition store_mdb.c:73
MDB_cursor * ctx_cur
MDB c:spo index cursor.
Definition store_mdb.c:78
VOLK_TripleKey spok
Triple to be populated with match.
Definition store_mdb.c:81
VOLK_Key luk[3]
0รท3 lookup keys.
Definition store_mdb.c:88
MDB_cursor * cur
MDB cursor.
Definition store_mdb.c:77
const uint8_t * term_order
Term order used in 1-2bound look-ups.
Definition store_mdb.c:87
MDBStore * store
MDB store handle.
Definition store_mdb.c:74
IterFlags flags
Iterator flags.
Definition store_mdb.c:75
MDB_txn * txn
MDB transaction.
Definition store_mdb.c:76
VOLK_Key luc
Ctx key to filter by. May be NULL_KEY.
Definition store_mdb.c:89
size_t i
Internal counter for paged lookups.
Definition store_mdb.c:90
iter_op_fn_t iter_op_fn
Function used to look up next match.
Definition store_mdb.c:86
MDB_val key
Internal data handler.
Definition store_mdb.c:79
size_t ct
Definition store_mdb.c:91
int rc
MDB_* return code for the next result.
Definition store_mdb.c:93
MDB_val data
Internal data handler.
Definition store_mdb.c:80
VOLK_Key * ck
Definition store_mdb.c:82
StoreFlags flags
Store state flags.
Definition store_mdb.c:56
MDB_dbi dbi[9]
DB handles. Refer to DbIdx enum.
Definition store_mdb.c:55
MDB_env * env
Environment handle.
Definition store_mdb.c:54
Triple of byte buffers.
Definition buffer.h:60
VOLK_Buffer * o
Definition buffer.h:63
VOLK_Buffer * s
Definition buffer.h:61
VOLK_Buffer * p
Definition buffer.h:62
General-purpose data buffer.
Definition buffer.h:47
VOLK_BufferFlag flags
Definition buffer.h:50
unsigned char * addr
Definition buffer.h:48
size_t size
Definition buffer.h:49
Store interface.