summaryrefslogtreecommitdiff
path: root/src/runtime
diff options
context:
space:
mode:
Diffstat (limited to 'src/runtime')
-rw-r--r--src/runtime/c/Makefile.am9
-rw-r--r--src/runtime/c/sg/semantic_graph.c647
-rw-r--r--src/runtime/c/sg/sg.c1007
3 files changed, 1012 insertions, 651 deletions
diff --git a/src/runtime/c/Makefile.am b/src/runtime/c/Makefile.am
index 92dace1f7..580cd6647 100644
--- a/src/runtime/c/Makefile.am
+++ b/src/runtime/c/Makefile.am
@@ -36,6 +36,10 @@ pgfinclude_HEADERS = \
pgf/graphviz.h \
pgf/pgf.h
+sgincludedir=$(includedir)/sg
+sginclude_HEADERS = \
+ sg/sg.h
+
libgu_la_SOURCES = \
gu/assert.c \
gu/bits.c \
@@ -83,11 +87,8 @@ libpgf_la_SOURCES = \
libpgf_la_LDFLAGS = "-no-undefined"
libpgf_la_LIBADD = libgu.la
-sgincludedir=$(includedir)/sg
-sginclude_HEADERS =
-
libsg_la_SOURCES = \
- sg/semantic_graph.c
+ sg/sg.c
libsg_la_LIBADD = libgu.la libpgf.la
bin_PROGRAMS = \
diff --git a/src/runtime/c/sg/semantic_graph.c b/src/runtime/c/sg/semantic_graph.c
deleted file mode 100644
index edd09eae8..000000000
--- a/src/runtime/c/sg/semantic_graph.c
+++ /dev/null
@@ -1,647 +0,0 @@
-#include "sqlite3.c"
-#include "pgf/pgf.h"
-#include "gu/file.h"
-
-#define SG_EXPRS "sg_exprs"
-#define SG_PAIRS "sg_pairs"
-#define SG_IDENTS "sg_idents"
-#define SG_TRIPLES "sg_triples"
-#define SG_TRIPLES_SPO "sg_triples_spo"
-
-typedef struct {
- sqlite3 *db;
- BtCursor* crsExprs;
- BtCursor* crsPairs;
- BtCursor* crsIdents;
- i64 key_seed;
- int file_format;
-} StoreContext;
-
-int
-sg_create_tables(sqlite3 *db)
-{
- return sqlite3_exec(db, "create table if not exists " SG_EXPRS "(fun not null, arg integer);"
- "create unique index if not exists " SG_IDENTS " on " SG_EXPRS "(fun) where arg is null;"
- "create unique index if not exists " SG_PAIRS " on " SG_EXPRS "(fun,arg) where arg is not null;"
- "create table if not exists " SG_TRIPLES "(subj integer, pred integer, obj integer);"
- "create unique index if not exists " SG_TRIPLES_SPO " on " SG_TRIPLES "(subj,pred,obj);",
- NULL, NULL, NULL);
-}
-
-static int
-store_expr(StoreContext* ctxt, PgfExpr expr, i64* pKey)
-{
- int rc = SQLITE_OK;
-
- GuVariantInfo ei = gu_variant_open(expr);
- switch (ei.tag) {
- case PGF_EXPR_ABS: {
- break;
- }
- case PGF_EXPR_APP: {
- PgfExprApp* app = ei.data;
-
- Mem mem[3];
-
- mem[0].flags = MEM_Int;
- rc = store_expr(ctxt, app->fun, &mem[0].u.i);
- if (rc != SQLITE_OK)
- return rc;
-
- mem[1].flags = MEM_Int;
- rc = store_expr(ctxt, app->arg, &mem[1].u.i);
- if (rc != SQLITE_OK)
- return rc;
-
- UnpackedRecord idxKey;
- idxKey.pKeyInfo = ctxt->crsPairs->pKeyInfo;
- idxKey.nField = 2;
- idxKey.default_rc = 0;
- idxKey.aMem = mem;
-
- int res = 0;
- rc = sqlite3BtreeMovetoUnpacked(ctxt->crsPairs,
- &idxKey, 0, 0, &res);
- if (rc != SQLITE_OK) {
- return rc;
- }
-
- if (res == 0) {
- rc = sqlite3VdbeIdxRowid(ctxt->db, ctxt->crsPairs, pKey);
- } else {
- *pKey = ++ctxt->key_seed;
-
- unsigned char buf[32]; // enough for record with three integers
- buf[1] = 3;
-
- u32 serial_type;
- unsigned char* p = buf+4;
-
- serial_type = sqlite3VdbeSerialType(&mem[0], ctxt->file_format);
- buf[2] = serial_type;
- p += sqlite3VdbeSerialPut(p, &mem[0], serial_type);
-
- serial_type = sqlite3VdbeSerialType(&mem[1], ctxt->file_format);
- buf[3] = serial_type;
- p += sqlite3VdbeSerialPut(p, &mem[1], serial_type);
-
- rc = sqlite3BtreeInsert(ctxt->crsExprs, 0, *pKey,
- buf+1, p-(buf+1), 0,
- 0, 0);
- if (rc != SQLITE_OK) {
- return rc;
- }
-
- buf[0] = 4;
- buf[1] = buf[2];
- buf[2] = buf[3];
-
- mem[2].flags = MEM_Int;
- mem[2].u.i = *pKey;
- serial_type = sqlite3VdbeSerialType(&mem[2], ctxt->file_format);
- buf[3] = serial_type;
- p += sqlite3VdbeSerialPut(p, &mem[2], serial_type);
-
- rc = sqlite3BtreeInsert(ctxt->crsPairs, buf, p-buf,
- 0, *pKey, 0,
- 0, 0);
- }
- break;
- }
- case PGF_EXPR_LIT: {
- break;
- }
- case PGF_EXPR_META: {
- }
- case PGF_EXPR_FUN: {
- PgfExprFun* fun = ei.data;
-
- Mem mem[2];
- mem[0].flags = MEM_Str;
- mem[0].n = strlen(fun->fun);
- mem[0].z = fun->fun;
-
- UnpackedRecord idxKey;
- idxKey.pKeyInfo = ctxt->crsIdents->pKeyInfo;
- idxKey.nField = 1;
- idxKey.default_rc = 0;
- idxKey.aMem = mem;
-
- int res = 0;
- rc = sqlite3BtreeMovetoUnpacked(ctxt->crsIdents,
- &idxKey, 0, 0, &res);
- if (rc != SQLITE_OK) {
- return rc;
- }
-
- if (res == 0) {
- rc = sqlite3VdbeIdxRowid(ctxt->db, ctxt->crsIdents, pKey);
- } else {
- *pKey = ++ctxt->key_seed;
-
- int serial_type_fun = sqlite3VdbeSerialType(&mem[0], ctxt->file_format);
- int serial_type_fun_hdr_len = sqlite3VarintLen(serial_type_fun);
-
- mem[1].flags = MEM_Int;
- mem[1].u.i = *pKey;
-
- int serial_type_key = sqlite3VdbeSerialType(&mem[1], ctxt->file_format);
- int serial_type_key_hdr_len = sqlite3VarintLen(serial_type_key);
-
- unsigned char* buf = malloc(1+serial_type_fun_hdr_len+MAX(1,serial_type_key_hdr_len)+mem[0].n);
- unsigned char* p = buf;
- *p++ = 1+serial_type_fun_hdr_len+1;
- p += putVarint32(p, serial_type_fun);
- *p++ = 0;
- memcpy(p, fun->fun, mem[0].n);
- p += mem[0].n;
-
- rc = sqlite3BtreeInsert(ctxt->crsExprs, 0, *pKey,
- buf, p-buf, 0,
- 0, 0);
- if (rc != SQLITE_OK) {
- goto free;
- }
-
- p = buf;
- *p++ = 1+serial_type_fun_hdr_len+serial_type_key_hdr_len;
- p += putVarint32(p, serial_type_fun);
- p += putVarint32(p, serial_type_key);
- memcpy(p, fun->fun, mem[0].n);
- p += mem[0].n;
- p += sqlite3VdbeSerialPut(p, &mem[1], serial_type_key);
- rc = sqlite3BtreeInsert(ctxt->crsIdents, buf, p-buf,
- 0, *pKey, 0,
- 0, 0);
-
-free:
- free(buf);
- }
- break;
- }
- case PGF_EXPR_VAR: {
- break;
- }
- case PGF_EXPR_TYPED: {
- break;
- }
- case PGF_EXPR_IMPL_ARG: {
- break;
- }
- default:
- gu_impossible();
- }
-
- return rc;
-}
-
-int
-sg_insert_expr(sqlite3 *db, PgfExpr expr, i64* pKey)
-{
- Table *exprsTbl =
- sqlite3HashFind(&db->aDb[0].pSchema->tblHash, SG_EXPRS);
- if (!exprsTbl) return SQLITE_ERROR;
-
- Index *pairsIdx = sqlite3HashFind(&db->aDb[0].pSchema->idxHash, SG_PAIRS);
- if (!pairsIdx) return SQLITE_ERROR;
-
- Index *identsIdx = sqlite3HashFind(&db->aDb[0].pSchema->idxHash, SG_IDENTS);
- if (!identsIdx) return SQLITE_ERROR;
-
- int rc;
- rc = sqlite3BtreeBeginTrans(db->aDb[0].pBt, 1);
- if (rc != SQLITE_OK) {
- return rc;
- }
-
- BtCursor crsExprs;
- memset(&crsExprs, 0, sizeof(crsExprs));
- rc = sqlite3BtreeCursor(db->aDb[0].pBt, exprsTbl->tnum, 1, NULL, &crsExprs);
- if (rc != SQLITE_OK) {
- goto rollback;
- }
-
- BtCursor crsPairs;
- memset(&crsPairs, 0, sizeof(crsPairs));
- KeyInfo *infPairs = sqlite3KeyInfoAlloc(db, 2, 0);
- rc = sqlite3BtreeCursor(db->aDb[0].pBt, pairsIdx->tnum, 1, infPairs, &crsPairs);
- if (rc != SQLITE_OK) {
- goto close1;
- }
-
- BtCursor crsIdents;
- memset(&crsIdents, 0, sizeof(crsIdents));
- KeyInfo *infIdents = sqlite3KeyInfoAlloc(db, 1, 1);
- rc = sqlite3BtreeCursor(db->aDb[0].pBt, identsIdx->tnum, 1, infIdents, &crsIdents);
- if (rc != SQLITE_OK) {
- goto close2;
- }
-
- int res;
- rc = sqlite3BtreeLast(&crsExprs, &res);
- if (rc != SQLITE_OK) {
- goto close;
- }
-
- i64 key = 0;
- rc = sqlite3BtreeKeySize(&crsExprs, &key);
- if (rc != SQLITE_OK) {
- goto close;
- }
-
- StoreContext ctxt;
- ctxt.db = db;
- ctxt.crsExprs = &crsExprs;
- ctxt.crsPairs = &crsPairs;
- ctxt.crsIdents = &crsIdents;
- ctxt.key_seed = key;
- ctxt.file_format = db->aDb[0].pSchema->file_format;
- rc = store_expr(&ctxt, expr, pKey);
- if (rc != SQLITE_OK) {
- goto close;
- }
-
- sqlite3KeyInfoUnref(infIdents);
- rc = sqlite3BtreeCloseCursor(&crsIdents);
- if (rc != SQLITE_OK) {
- goto close2;
- }
-
- sqlite3KeyInfoUnref(infPairs);
- rc = sqlite3BtreeCloseCursor(&crsPairs);
- if (rc != SQLITE_OK) {
- goto close1;
- }
-
- rc = sqlite3BtreeCloseCursor(&crsExprs);
- if (rc != SQLITE_OK) {
- goto rollback;
- }
-
- rc = sqlite3BtreeCommit(db->aDb[0].pBt);
- return rc;
-
-close:
- sqlite3KeyInfoUnref(infIdents);
- sqlite3BtreeCloseCursor(&crsIdents);
-
-close2:
- sqlite3KeyInfoUnref(infPairs);
- sqlite3BtreeCloseCursor(&crsPairs);
-
-close1:
- sqlite3BtreeCloseCursor(&crsExprs);
-
-rollback:
- sqlite3BtreeRollback(db->aDb[0].pBt, SQLITE_ABORT_ROLLBACK, 0);
- return rc;
-}
-
-static int
-load_expr(BtCursor* crsExprs, i64 key, PgfExpr *pExpr, GuPool* out_pool)
-{
- int res;
- int rc = sqlite3BtreeMovetoUnpacked(crsExprs, 0, key, 0, &res);
- if (rc != SQLITE_OK)
- return rc;
-
- if (res != 0) {
- *pExpr = gu_null_variant;
- return SQLITE_OK;
- }
-
- int payloadSize;
- rc = sqlite3BtreeDataSize(crsExprs, &payloadSize);
- if (rc != SQLITE_OK)
- return rc;
-
- u32 avail = 0;
- const unsigned char* row = sqlite3BtreeDataFetch(crsExprs, &avail);
- row++;
-
- int serial_type_fun, serial_type_arg;
- row += getVarint32(row, serial_type_fun);
- row += getVarint32(row, serial_type_arg);
-
- Mem mem[2];
- row += sqlite3VdbeSerialGet(row, serial_type_fun, &mem[0]);
- row += sqlite3VdbeSerialGet(row, serial_type_arg, &mem[1]);
-
- if (serial_type_arg == 0) {
- u32 len = sqlite3VdbeSerialTypeLen(serial_type_fun);
-
- PgfExprFun *efun =
- gu_new_flex_variant(PGF_EXPR_FUN,
- PgfExprFun,
- fun, len+1,
- pExpr, out_pool);
- memcpy(efun->fun, mem[0].z, len);
- efun->fun[len] = 0;
- } else {
- PgfExprApp* papp =
- gu_new_variant(PGF_EXPR_APP, PgfExprApp, pExpr, out_pool);
-
- rc = load_expr(crsExprs, mem[0].u.i, &papp->fun, out_pool);
- if (rc != SQLITE_OK)
- return rc;
-
- rc = load_expr(crsExprs, mem[1].u.i, &papp->arg, out_pool);
- if (rc != SQLITE_OK)
- return rc;
- }
-
- return SQLITE_OK;
-}
-
-int
-sg_select_expr(sqlite3 *db, i64 key, PgfExpr* pExpr, GuPool* out_pool)
-{
- Table *exprsTbl =
- sqlite3HashFind(&db->aDb[0].pSchema->tblHash, SG_EXPRS);
- if (!exprsTbl) return SQLITE_ERROR;
-
- int rc;
- rc = sqlite3BtreeBeginTrans(db->aDb[0].pBt, 0);
- if (rc != SQLITE_OK) {
- return rc;
- }
-
- BtCursor crsExprs;
- memset(&crsExprs, 0, sizeof(crsExprs));
- rc = sqlite3BtreeCursor(db->aDb[0].pBt, exprsTbl->tnum, 1, NULL, &crsExprs);
- if (rc != SQLITE_OK) {
- goto rollback;
- }
-
- rc = load_expr(&crsExprs, key, pExpr, out_pool);
- if (rc != SQLITE_OK) {
- goto close;
- }
-
- rc = sqlite3BtreeCloseCursor(&crsExprs);
- if (rc != SQLITE_OK) {
- goto rollback;
- }
-
- rc = sqlite3BtreeCommit(db->aDb[0].pBt);
- return rc;
-
-close:
- sqlite3BtreeCloseCursor(&crsExprs);
-
-rollback:
- sqlite3BtreeRollback(db->aDb[0].pBt, SQLITE_ABORT_ROLLBACK, 0);
- return rc;
-}
-
-int
-sg_insert_triple(sqlite3 *db, i64 subj, i64 pred, i64 obj, i64 *pKey)
-{
- Table *triplesTbl =
- sqlite3HashFind(&db->aDb[0].pSchema->tblHash, SG_TRIPLES);
- if (!triplesTbl) return SQLITE_ERROR;
-
- Index *spoIdx = sqlite3HashFind(&db->aDb[0].pSchema->idxHash, SG_TRIPLES_SPO);
- if (!spoIdx) return SQLITE_ERROR;
-
- int rc;
- rc = sqlite3BtreeBeginTrans(db->aDb[0].pBt, 1);
- if (rc != SQLITE_OK) {
- return rc;
- }
-
- BtCursor crsTriples;
- memset(&crsTriples, 0, sizeof(crsTriples));
- rc = sqlite3BtreeCursor(db->aDb[0].pBt, triplesTbl->tnum, 1, NULL, &crsTriples);
- if (rc != SQLITE_OK) {
- goto rollback;
- }
-
- BtCursor crsSPO;
- memset(&crsSPO, 0, sizeof(crsSPO));
- KeyInfo *infSPO = sqlite3KeyInfoAlloc(db, 3, 0);
- rc = sqlite3BtreeCursor(db->aDb[0].pBt, spoIdx->tnum, 1, infSPO, &crsSPO);
- if (rc != SQLITE_OK) {
- goto close1;
- }
-
- Mem mem[4];
- mem[0].flags = MEM_Int;
- mem[0].u.i = subj;
- mem[1].flags = MEM_Int;
- mem[1].u.i = pred;
- mem[2].flags = MEM_Int;
- mem[2].u.i = obj;
-
- UnpackedRecord idxKey;
- idxKey.pKeyInfo = crsSPO.pKeyInfo;
- idxKey.nField = 3;
- idxKey.default_rc = 0;
- idxKey.aMem = mem;
-
- int res = 0;
- rc = sqlite3BtreeMovetoUnpacked(&crsSPO,
- &idxKey, 0, 0, &res);
- if (rc != SQLITE_OK) {
- goto close;
- }
-
- if (res == 0) {
- rc = sqlite3VdbeIdxRowid(db, &crsSPO, pKey);
- } else {
- rc = sqlite3BtreeLast(&crsTriples, &res);
- if (rc != SQLITE_OK) {
- goto close;
- }
-
- rc = sqlite3BtreeKeySize(&crsTriples, pKey);
- if (rc != SQLITE_OK) {
- goto close;
- }
- (*pKey)++;
-
- int file_format = db->aDb[0].pSchema->file_format;
-
- unsigned char buf[41]; // enough for record with three integers
- buf[0] = 5;
-
- u32 serial_type;
- unsigned char* p = buf+5;
-
- serial_type = sqlite3VdbeSerialType(&mem[0], file_format);
- buf[1] = serial_type;
- p += sqlite3VdbeSerialPut(p, &mem[0], serial_type);
-
- serial_type = sqlite3VdbeSerialType(&mem[1], file_format);
- buf[2] = serial_type;
- p += sqlite3VdbeSerialPut(p, &mem[1], serial_type);
-
- serial_type = sqlite3VdbeSerialType(&mem[2], file_format);
- buf[3] = serial_type;
- p += sqlite3VdbeSerialPut(p, &mem[2], serial_type);
-
- unsigned char* tmp = p;
-
- mem[3].flags = MEM_Int;
- mem[3].u.i = 1;
- serial_type = sqlite3VdbeSerialType(&mem[3], file_format);
- buf[4] = serial_type;
- p += sqlite3VdbeSerialPut(p, &mem[3], serial_type);
-
- rc = sqlite3BtreeInsert(&crsTriples, 0, *pKey,
- buf, p-buf, 0,
- 0, 0);
- if (rc != SQLITE_OK) {
- return rc;
- }
-
- p = tmp;
-
- mem[3].flags = MEM_Int;
- mem[3].u.i = *pKey;
- serial_type = sqlite3VdbeSerialType(&mem[3], file_format);
- buf[4] = serial_type;
- p += sqlite3VdbeSerialPut(p, &mem[3], serial_type);
-
- rc = sqlite3BtreeInsert(&crsSPO, buf, p-buf,
- 0, *pKey, 0,
- 0, 0);
- if (rc != SQLITE_OK) {
- return rc;
- }
- }
-
- sqlite3KeyInfoUnref(infSPO);
- rc = sqlite3BtreeCloseCursor(&crsSPO);
- if (rc != SQLITE_OK) {
- goto close1;
- }
-
- rc = sqlite3BtreeCloseCursor(&crsTriples);
- if (rc != SQLITE_OK) {
- goto rollback;
- }
-
- rc = sqlite3BtreeCommit(db->aDb[0].pBt);
- return rc;
-
-close:
- sqlite3KeyInfoUnref(infSPO);
- sqlite3BtreeCloseCursor(&crsSPO);
-
-close1:
- sqlite3BtreeCloseCursor(&crsTriples);
-
-rollback:
- sqlite3BtreeRollback(db->aDb[0].pBt, SQLITE_ABORT_ROLLBACK, 0);
- return rc;
-}
-
-int
-sg_select_triple(sqlite3 *db, i64 key, i64 *pSubj, i64 *pPred, i64 *pObj)
-{
- Table *triplesTbl =
- sqlite3HashFind(&db->aDb[0].pSchema->tblHash, SG_TRIPLES);
- if (!triplesTbl) return SQLITE_ERROR;
-
- int rc;
- rc = sqlite3BtreeBeginTrans(db->aDb[0].pBt, 0);
- if (rc != SQLITE_OK) {
- return rc;
- }
-
- BtCursor crsTriples;
- memset(&crsTriples, 0, sizeof(crsTriples));
- rc = sqlite3BtreeCursor(db->aDb[0].pBt, triplesTbl->tnum, 1, NULL, &crsTriples);
- if (rc != SQLITE_OK) {
- goto rollback;
- }
-
- int res;
- rc = sqlite3BtreeMovetoUnpacked(&crsTriples, 0, key, 0, &res);
- if (rc != SQLITE_OK)
- goto close;
-
- if (res == 0) {
- int payloadSize;
- rc = sqlite3BtreeDataSize(&crsTriples, &payloadSize);
- if (rc != SQLITE_OK)
- goto close;
-
- u32 avail = 0;
- const unsigned char* row = sqlite3BtreeDataFetch(&crsTriples, &avail);
- row++;
-
- int serial_type_subj, serial_type_pred, serial_type_obj;
- row += getVarint32(row, serial_type_subj);
- row += getVarint32(row, serial_type_pred);
- row += getVarint32(row, serial_type_obj);
- row++;
-
- Mem mem[3];
- row += sqlite3VdbeSerialGet(row, serial_type_subj, &mem[0]);
- row += sqlite3VdbeSerialGet(row, serial_type_pred, &mem[1]);
- row += sqlite3VdbeSerialGet(row, serial_type_obj, &mem[2]);
-
- *pSubj = mem[0].u.i;
- *pPred = mem[1].u.i;
- *pObj = mem[2].u.i;
- }
-
- rc = sqlite3BtreeCloseCursor(&crsTriples);
- if (rc != SQLITE_OK) {
- goto rollback;
- }
-
- rc = sqlite3BtreeCommit(db->aDb[0].pBt);
- if (rc != SQLITE_OK) {
- return rc;
- }
-
- if (res != 0)
- rc = SQLITE_DONE;
-
- return rc;
-
-close:
- sqlite3BtreeCloseCursor(&crsTriples);
-
-rollback:
- sqlite3BtreeRollback(db->aDb[0].pBt, SQLITE_ABORT_ROLLBACK, 0);
- return rc;
-}
-
-void main()
-{
- sqlite3 *db = NULL;
- sqlite3_open("test.db", &db);
-
- sg_create_tables(db);
-
- char* str = "f x (g y)";
- GuPool* tmp_pool = gu_local_pool();
-
- GuExn* err = gu_exn(tmp_pool);
- GuIn* in = gu_data_in((uint8_t*) str, strlen(str), tmp_pool);
- GuOut* out = gu_file_out(stdout, tmp_pool);
-
- PgfExpr e = pgf_read_expr(in, tmp_pool, err);
-
- i64 key;
- sg_insert_expr(db, e, &key);
-
- sg_select_expr(db, key, &e, tmp_pool);
- pgf_print_expr(e, NULL, 0, out, err);
- printf("\n");
-
- sg_insert_triple(db, 1, 2, 3, &key);
- printf("%d\n", (int) key);
-
- i64 subj, pred, obj;
- sg_select_triple(db, key, &subj, &pred, &obj);
- printf("%d %d %d\n", (int) subj, (int) pred, (int) obj);
-
- gu_pool_free(tmp_pool);
-
- sqlite3_close(db);
-}
diff --git a/src/runtime/c/sg/sg.c b/src/runtime/c/sg/sg.c
new file mode 100644
index 000000000..27c1a8293
--- /dev/null
+++ b/src/runtime/c/sg/sg.c
@@ -0,0 +1,1007 @@
+#define SQLITE_API static
+#include "sqlite3.c"
+
+#include "sg/sg.h"
+
+#define SG_EXPRS "sg_exprs"
+#define SG_PAIRS "sg_pairs"
+#define SG_IDENTS "sg_idents"
+#define SG_TRIPLES "sg_triples"
+#define SG_TRIPLES_SPO "sg_triples_spo"
+#define SG_TRIPLES_PO "sg_triples_po"
+#define SG_TRIPLES_O "sg_triples_o"
+
+typedef struct {
+ sqlite3 *db;
+ BtCursor* crsExprs;
+ BtCursor* crsPairs;
+ BtCursor* crsIdents;
+ SgId key_seed;
+ int file_format;
+} StoreContext;
+
+void
+sg_raise_sqlite(sqlite3* db, GuExn* err)
+{
+ const char *msg = sqlite3_errmsg(db);
+
+ GuExnData* err_data = gu_raise(err, SgError);
+ if (err_data) {
+ err_data->data = gu_malloc(err_data->pool, strlen(msg+1));
+ strcpy(err_data->data, msg);
+ }
+}
+
+void
+sg_raise_err(GuString msg, GuExn* err)
+{
+ GuExnData* err_data = gu_raise(err, SgError);
+ if (err_data) {
+ err_data->data = (char*) msg;
+ }
+}
+
+SgSG*
+sg_open(const char *filename,
+ GuExn* err)
+{
+ int rc;
+
+ sqlite3* db = NULL;
+ rc = sqlite3_open(filename, &db);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ return NULL;
+ }
+
+ rc = sqlite3_exec(db, "create table if not exists " SG_EXPRS "(fun not null, arg integer);"
+ "create unique index if not exists " SG_IDENTS " on " SG_EXPRS "(fun) where arg is null;"
+ "create unique index if not exists " SG_PAIRS " on " SG_EXPRS "(fun,arg) where arg is not null;"
+ "create table if not exists " SG_TRIPLES "(subj integer, pred integer, obj integer, state integer);"
+ "create unique index if not exists " SG_TRIPLES_SPO " on " SG_TRIPLES "(subj,pred,obj);"
+ "create index if not exists " SG_TRIPLES_PO " on " SG_TRIPLES "(pred,obj);"
+ "create index if not exists " SG_TRIPLES_O " on " SG_TRIPLES "(obj);",
+ NULL, NULL, NULL);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ return NULL;
+ }
+
+ return (SgSG*) db;
+}
+
+void
+sg_close(SgSG* sg, GuExn* err)
+{
+ sqlite3 *db = (sqlite3 *) sg;
+
+ int rc;
+ rc = sqlite3_close((sqlite3*) db);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ }
+}
+
+void
+sg_shutdown()
+{
+ sqlite3_shutdown();
+}
+
+static int
+store_expr(StoreContext* ctxt, PgfExpr expr, SgId* pKey)
+{
+ int rc = SQLITE_OK;
+
+ GuVariantInfo ei = gu_variant_open(expr);
+ switch (ei.tag) {
+ case PGF_EXPR_ABS: {
+ break;
+ }
+ case PGF_EXPR_APP: {
+ PgfExprApp* app = ei.data;
+
+ Mem mem[3];
+
+ mem[0].flags = MEM_Int;
+ rc = store_expr(ctxt, app->fun, &mem[0].u.i);
+ if (rc != SQLITE_OK)
+ return rc;
+
+ mem[1].flags = MEM_Int;
+ rc = store_expr(ctxt, app->arg, &mem[1].u.i);
+ if (rc != SQLITE_OK)
+ return rc;
+
+ UnpackedRecord idxKey;
+ idxKey.pKeyInfo = ctxt->crsPairs->pKeyInfo;
+ idxKey.nField = 2;
+ idxKey.default_rc = 0;
+ idxKey.aMem = mem;
+
+ int res = 0;
+ rc = sqlite3BtreeMovetoUnpacked(ctxt->crsPairs,
+ &idxKey, 0, 0, &res);
+ if (rc != SQLITE_OK) {
+ return rc;
+ }
+
+ if (res == 0) {
+ rc = sqlite3VdbeIdxRowid(ctxt->db, ctxt->crsPairs, pKey);
+ } else {
+ *pKey = ++ctxt->key_seed;
+
+ unsigned char buf[32]; // enough for record with three integers
+ buf[1] = 3;
+
+ u32 serial_type;
+ unsigned char* p = buf+4;
+
+ serial_type = sqlite3VdbeSerialType(&mem[0], ctxt->file_format);
+ buf[2] = serial_type;
+ p += sqlite3VdbeSerialPut(p, &mem[0], serial_type);
+
+ serial_type = sqlite3VdbeSerialType(&mem[1], ctxt->file_format);
+ buf[3] = serial_type;
+ p += sqlite3VdbeSerialPut(p, &mem[1], serial_type);
+
+ rc = sqlite3BtreeInsert(ctxt->crsExprs, 0, *pKey,
+ buf+1, p-(buf+1), 0,
+ 0, 0);
+ if (rc != SQLITE_OK) {
+ return rc;
+ }
+
+ buf[0] = 4;
+ buf[1] = buf[2];
+ buf[2] = buf[3];
+
+ mem[2].flags = MEM_Int;
+ mem[2].u.i = *pKey;
+ serial_type = sqlite3VdbeSerialType(&mem[2], ctxt->file_format);
+ buf[3] = serial_type;
+ p += sqlite3VdbeSerialPut(p, &mem[2], serial_type);
+
+ rc = sqlite3BtreeInsert(ctxt->crsPairs, buf, p-buf,
+ 0, *pKey, 0,
+ 0, 0);
+ }
+ break;
+ }
+ case PGF_EXPR_LIT: {
+ break;
+ }
+ case PGF_EXPR_META: {
+ }
+ case PGF_EXPR_FUN: {
+ PgfExprFun* fun = ei.data;
+
+ Mem mem[2];
+ mem[0].flags = MEM_Str;
+ mem[0].n = strlen(fun->fun);
+ mem[0].z = fun->fun;
+
+ UnpackedRecord idxKey;
+ idxKey.pKeyInfo = ctxt->crsIdents->pKeyInfo;
+ idxKey.nField = 1;
+ idxKey.default_rc = 0;
+ idxKey.aMem = mem;
+
+ int res = 0;
+ rc = sqlite3BtreeMovetoUnpacked(ctxt->crsIdents,
+ &idxKey, 0, 0, &res);
+ if (rc != SQLITE_OK) {
+ return rc;
+ }
+
+ if (res == 0) {
+ rc = sqlite3VdbeIdxRowid(ctxt->db, ctxt->crsIdents, pKey);
+ } else {
+ *pKey = ++ctxt->key_seed;
+
+ int serial_type_fun = sqlite3VdbeSerialType(&mem[0], ctxt->file_format);
+ int serial_type_fun_hdr_len = sqlite3VarintLen(serial_type_fun);
+
+ mem[1].flags = MEM_Int;
+ mem[1].u.i = *pKey;
+
+ int serial_type_key = sqlite3VdbeSerialType(&mem[1], ctxt->file_format);
+ int serial_type_key_hdr_len = sqlite3VarintLen(serial_type_key);
+
+ unsigned char* buf = malloc(1+serial_type_fun_hdr_len+MAX(1,serial_type_key_hdr_len)+mem[0].n);
+ unsigned char* p = buf;
+ *p++ = 1+serial_type_fun_hdr_len+1;
+ p += putVarint32(p, serial_type_fun);
+ *p++ = 0;
+ memcpy(p, fun->fun, mem[0].n);
+ p += mem[0].n;
+
+ rc = sqlite3BtreeInsert(ctxt->crsExprs, 0, *pKey,
+ buf, p-buf, 0,
+ 0, 0);
+ if (rc != SQLITE_OK) {
+ goto free;
+ }
+
+ p = buf;
+ *p++ = 1+serial_type_fun_hdr_len+serial_type_key_hdr_len;
+ p += putVarint32(p, serial_type_fun);
+ p += putVarint32(p, serial_type_key);
+ memcpy(p, fun->fun, mem[0].n);
+ p += mem[0].n;
+ p += sqlite3VdbeSerialPut(p, &mem[1], serial_type_key);
+ rc = sqlite3BtreeInsert(ctxt->crsIdents, buf, p-buf,
+ 0, *pKey, 0,
+ 0, 0);
+
+free:
+ free(buf);
+ }
+ break;
+ }
+ case PGF_EXPR_VAR: {
+ break;
+ }
+ case PGF_EXPR_TYPED: {
+ break;
+ }
+ case PGF_EXPR_IMPL_ARG: {
+ break;
+ }
+ default:
+ gu_impossible();
+ }
+
+ return rc;
+}
+
+SgId
+sg_insert_expr(SgSG *sg, PgfExpr expr, GuExn* err)
+{
+ sqlite3 *db = (sqlite3 *) sg;
+
+ Table *exprsTbl =
+ sqlite3HashFind(&db->aDb[0].pSchema->tblHash, SG_EXPRS);
+ if (!exprsTbl) {
+ sg_raise_err("Table " SG_EXPRS " is missing", err);
+ return 0;
+ }
+
+ Index *pairsIdx = sqlite3HashFind(&db->aDb[0].pSchema->idxHash, SG_PAIRS);
+ if (!pairsIdx) {
+ sg_raise_err("Index " SG_PAIRS " is missing", err);
+ return 0;
+ }
+
+ Index *identsIdx = sqlite3HashFind(&db->aDb[0].pSchema->idxHash, SG_IDENTS);
+ if (!identsIdx) {
+ sg_raise_err("Index " SG_IDENTS " is missing", err);
+ return 0;
+ }
+
+ int rc;
+ rc = sqlite3BtreeBeginTrans(db->aDb[0].pBt, 1);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ return 0;
+ }
+
+ BtCursor crsExprs;
+ memset(&crsExprs, 0, sizeof(crsExprs));
+ rc = sqlite3BtreeCursor(db->aDb[0].pBt, exprsTbl->tnum, 1, NULL, &crsExprs);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ goto rollback;
+ }
+
+ BtCursor crsPairs;
+ memset(&crsPairs, 0, sizeof(crsPairs));
+ KeyInfo *infPairs = sqlite3KeyInfoAlloc(db, 2, 0);
+ rc = sqlite3BtreeCursor(db->aDb[0].pBt, pairsIdx->tnum, 1, infPairs, &crsPairs);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ goto close1;
+ }
+
+ BtCursor crsIdents;
+ memset(&crsIdents, 0, sizeof(crsIdents));
+ KeyInfo *infIdents = sqlite3KeyInfoAlloc(db, 1, 1);
+ rc = sqlite3BtreeCursor(db->aDb[0].pBt, identsIdx->tnum, 1, infIdents, &crsIdents);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ goto close2;
+ }
+
+ int res;
+ rc = sqlite3BtreeLast(&crsExprs, &res);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ goto close;
+ }
+
+ SgId key = 0;
+ rc = sqlite3BtreeKeySize(&crsExprs, &key);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ goto close;
+ }
+
+ StoreContext ctxt;
+ ctxt.db = db;
+ ctxt.crsExprs = &crsExprs;
+ ctxt.crsPairs = &crsPairs;
+ ctxt.crsIdents = &crsIdents;
+ ctxt.key_seed = key;
+ ctxt.file_format = db->aDb[0].pSchema->file_format;
+ rc = store_expr(&ctxt, expr, &key);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ goto close;
+ }
+
+ sqlite3KeyInfoUnref(infIdents);
+ rc = sqlite3BtreeCloseCursor(&crsIdents);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ goto close2;
+ }
+
+ sqlite3KeyInfoUnref(infPairs);
+ rc = sqlite3BtreeCloseCursor(&crsPairs);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ goto close1;
+ }
+
+ rc = sqlite3BtreeCloseCursor(&crsExprs);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ goto rollback;
+ }
+
+ rc = sqlite3BtreeCommit(db->aDb[0].pBt);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ return 0;
+ }
+
+ return key;
+
+close:
+ sqlite3KeyInfoUnref(infIdents);
+ sqlite3BtreeCloseCursor(&crsIdents);
+
+close2:
+ sqlite3KeyInfoUnref(infPairs);
+ sqlite3BtreeCloseCursor(&crsPairs);
+
+close1:
+ sqlite3BtreeCloseCursor(&crsExprs);
+
+rollback:
+ sqlite3BtreeRollback(db->aDb[0].pBt, SQLITE_ABORT_ROLLBACK, 0);
+ return rc;
+}
+
+static int
+load_expr(BtCursor* crsExprs, SgId key, PgfExpr *pExpr, GuPool* out_pool)
+{
+ int res;
+ int rc = sqlite3BtreeMovetoUnpacked(crsExprs, 0, key, 0, &res);
+ if (rc != SQLITE_OK)
+ return rc;
+
+ if (res != 0) {
+ *pExpr = gu_null_variant;
+ return SQLITE_OK;
+ }
+
+ u32 payloadSize;
+ rc = sqlite3BtreeDataSize(crsExprs, &payloadSize);
+ if (rc != SQLITE_OK)
+ return rc;
+
+ u32 avail = 0;
+ const unsigned char* row = sqlite3BtreeDataFetch(crsExprs, &avail);
+ row++;
+
+ int serial_type_fun, serial_type_arg;
+ row += getVarint32(row, serial_type_fun);
+ row += getVarint32(row, serial_type_arg);
+
+ Mem mem[2];
+ row += sqlite3VdbeSerialGet(row, serial_type_fun, &mem[0]);
+ row += sqlite3VdbeSerialGet(row, serial_type_arg, &mem[1]);
+
+ if (serial_type_arg == 0) {
+ u32 len = sqlite3VdbeSerialTypeLen(serial_type_fun);
+
+ PgfExprFun *efun =
+ gu_new_flex_variant(PGF_EXPR_FUN,
+ PgfExprFun,
+ fun, len+1,
+ pExpr, out_pool);
+ memcpy(efun->fun, mem[0].z, len);
+ efun->fun[len] = 0;
+ } else {
+ PgfExprApp* papp =
+ gu_new_variant(PGF_EXPR_APP, PgfExprApp, pExpr, out_pool);
+
+ rc = load_expr(crsExprs, mem[0].u.i, &papp->fun, out_pool);
+ if (rc != SQLITE_OK)
+ return rc;
+
+ rc = load_expr(crsExprs, mem[1].u.i, &papp->arg, out_pool);
+ if (rc != SQLITE_OK)
+ return rc;
+ }
+
+ return SQLITE_OK;
+}
+
+PgfExpr
+sg_select_expr(SgSG *sg, SgId key, GuPool* out_pool, GuExn* err)
+{
+ sqlite3 *db = (sqlite3 *) sg;
+
+ Table *exprsTbl =
+ sqlite3HashFind(&db->aDb[0].pSchema->tblHash, SG_EXPRS);
+ if (!exprsTbl) {
+ sg_raise_err("Table " SG_EXPRS " is missing", err);
+ return gu_null_variant;
+ }
+
+ int rc;
+ rc = sqlite3BtreeBeginTrans(db->aDb[0].pBt, 0);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ return gu_null_variant;
+ }
+
+ BtCursor crsExprs;
+ memset(&crsExprs, 0, sizeof(crsExprs));
+ rc = sqlite3BtreeCursor(db->aDb[0].pBt, exprsTbl->tnum, 0, NULL, &crsExprs);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ goto rollback;
+ }
+
+ PgfExpr expr = gu_null_variant;
+ rc = load_expr(&crsExprs, key, &expr, out_pool);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ goto close;
+ }
+
+ rc = sqlite3BtreeCloseCursor(&crsExprs);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ goto rollback;
+ }
+
+ rc = sqlite3BtreeCommit(db->aDb[0].pBt);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ goto rollback;
+ }
+
+ return expr;
+
+close:
+ sqlite3BtreeCloseCursor(&crsExprs);
+
+rollback:
+ sqlite3BtreeRollback(db->aDb[0].pBt, SQLITE_ABORT_ROLLBACK, 0);
+ return gu_null_variant;
+}
+
+static void
+open_triples(sqlite3 *db, int wrFlag, BtCursor cursor[], int *n_cursors, GuExn* err)
+{
+ Index *idx[3];
+ idx[0] = sqlite3HashFind(&db->aDb[0].pSchema->idxHash, SG_TRIPLES_SPO);
+ if (!idx[0]) {
+ sg_raise_err("Index " SG_TRIPLES_SPO " is missing", err);
+ return;
+ }
+
+ idx[1] = sqlite3HashFind(&db->aDb[0].pSchema->idxHash, SG_TRIPLES_PO);
+ if (!idx[1]) {
+ sg_raise_err("Index " SG_TRIPLES_PO " is missing", err);
+ return;
+ }
+
+ idx[2] = sqlite3HashFind(&db->aDb[0].pSchema->idxHash, SG_TRIPLES_O);
+ if (!idx[2]) {
+ sg_raise_err("Index " SG_TRIPLES_O " is missing", err);
+ return;
+ }
+
+ Table *triplesTbl =
+ sqlite3HashFind(&db->aDb[0].pSchema->tblHash, SG_TRIPLES);
+ if (!triplesTbl) {
+ sg_raise_err("Table " SG_TRIPLES " is missing", err);
+ return;
+ }
+
+ int rc;
+ rc = sqlite3BtreeBeginTrans(db->aDb[0].pBt, wrFlag);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ return;
+ }
+
+ memset(cursor, 0, sizeof(BtCursor)*4);
+
+ *n_cursors = 0;
+ while (*n_cursors < 3) {
+ KeyInfo *inf = sqlite3KeyInfoAlloc(db, 3-*n_cursors, *n_cursors);
+ rc = sqlite3BtreeCursor(db->aDb[0].pBt, idx[*n_cursors]->tnum, wrFlag, inf, &cursor[*n_cursors]);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ return;
+ }
+ (*n_cursors)++;
+ }
+
+ rc = sqlite3BtreeCursor(db->aDb[0].pBt, triplesTbl->tnum, wrFlag, NULL, &cursor[*n_cursors]);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ return;
+ }
+ (*n_cursors)++;
+}
+
+static void
+close_triples(sqlite3 *db, BtCursor cursor[], int *n_cursors)
+{
+ while (*n_cursors > 0) {
+ (*n_cursors)--;
+ if (cursor[*n_cursors].pKeyInfo != NULL) {
+ sqlite3KeyInfoUnref(cursor[*n_cursors].pKeyInfo);
+ }
+ sqlite3BtreeCloseCursor(&cursor[*n_cursors]);
+ }
+}
+
+SgId
+sg_insert_triple(SgSG *sg, SgTriple triple, GuExn* err)
+{
+ sqlite3 *db = (sqlite3 *) sg;
+
+ int rc;
+
+ BtCursor cursor[4];
+ int n_cursors = 0;
+ open_triples(db, 1, cursor, &n_cursors, err);
+ if (gu_exn_is_raised(err)) {
+ goto close;
+ }
+
+ Mem mem[4];
+ mem[0].flags = MEM_Int;
+ mem[0].u.i = triple[0];
+ mem[1].flags = MEM_Int;
+ mem[1].u.i = triple[1];
+ mem[2].flags = MEM_Int;
+ mem[2].u.i = triple[2];
+
+ UnpackedRecord idxKey;
+ idxKey.pKeyInfo = cursor[0].pKeyInfo;
+ idxKey.nField = 3;
+ idxKey.default_rc = 0;
+ idxKey.aMem = mem;
+
+ int res = 0;
+ rc = sqlite3BtreeMovetoUnpacked(&cursor[0],
+ &idxKey, 0, 0, &res);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ goto close;
+ }
+
+ SgId key;
+
+ if (res == 0) {
+ rc = sqlite3VdbeIdxRowid(db, &cursor[0], &key);
+ } else {
+ rc = sqlite3BtreeLast(&cursor[3], &res);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ goto close;
+ }
+
+ rc = sqlite3BtreeKeySize(&cursor[3], &key);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ goto close;
+ }
+ key++;
+
+ u32 serial_type;
+ unsigned char buf[41]; // enough for record with three integers
+ int file_format = db->aDb[0].pSchema->file_format;
+
+ unsigned char* p = buf+(buf[0] = 5);
+
+ serial_type = sqlite3VdbeSerialType(&mem[0], file_format);
+ buf[1] = serial_type;
+ p += sqlite3VdbeSerialPut(p, &mem[0], serial_type);
+
+ serial_type = sqlite3VdbeSerialType(&mem[1], file_format);
+ buf[2] = serial_type;
+ p += sqlite3VdbeSerialPut(p, &mem[1], serial_type);
+
+ serial_type = sqlite3VdbeSerialType(&mem[2], file_format);
+ buf[3] = serial_type;
+ p += sqlite3VdbeSerialPut(p, &mem[2], serial_type);
+
+ unsigned char* tmp = p;
+
+ mem[3].flags = MEM_Int;
+ mem[3].u.i = 1;
+ serial_type = sqlite3VdbeSerialType(&mem[3], file_format);
+ buf[4] = serial_type;
+ p += sqlite3VdbeSerialPut(p, &mem[3], serial_type);
+
+ rc = sqlite3BtreeInsert(&cursor[3], 0, key,
+ buf, p-buf, 0,
+ 0, 0);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ goto close;
+ }
+
+ mem[3].flags = MEM_Int;
+ mem[3].u.i = key;
+
+ p = tmp;
+ serial_type = sqlite3VdbeSerialType(&mem[3], file_format);
+ buf[4] = serial_type;
+ p += sqlite3VdbeSerialPut(p, &mem[3], serial_type);
+
+ rc = sqlite3BtreeInsert(&cursor[0], buf, p-buf,
+ 0, key, 0,
+ 0, 0);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ goto close;
+ }
+
+ p = buf+(buf[0] = 4);
+
+ serial_type = sqlite3VdbeSerialType(&mem[1], file_format);
+ buf[1] = serial_type;
+ p += sqlite3VdbeSerialPut(p, &mem[1], serial_type);
+
+ serial_type = sqlite3VdbeSerialType(&mem[2], file_format);
+ buf[2] = serial_type;
+ p += sqlite3VdbeSerialPut(p, &mem[2], serial_type);
+
+ serial_type = sqlite3VdbeSerialType(&mem[3], file_format);
+ buf[3] = serial_type;
+ p += sqlite3VdbeSerialPut(p, &mem[3], serial_type);
+
+ rc = sqlite3BtreeInsert(&cursor[1], buf, p-buf,
+ 0, key, 0,
+ 0, 0);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ goto close;
+ }
+
+ p = buf+(buf[0] = 3);
+
+ serial_type = sqlite3VdbeSerialType(&mem[2], file_format);
+ buf[1] = serial_type;
+ p += sqlite3VdbeSerialPut(p, &mem[2], serial_type);
+
+ serial_type = sqlite3VdbeSerialType(&mem[3], file_format);
+ buf[2] = serial_type;
+ p += sqlite3VdbeSerialPut(p, &mem[3], serial_type);
+
+ rc = sqlite3BtreeInsert(&cursor[2], buf, p-buf,
+ 0, key, 0,
+ 0, 0);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ goto close;
+ }
+ }
+
+ close_triples(db, cursor, &n_cursors);
+
+ rc = sqlite3BtreeCommit(db->aDb[0].pBt);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ goto close;
+ }
+
+ return key;
+
+close:
+ close_triples(db, cursor, &n_cursors);
+ sqlite3BtreeRollback(db->aDb[0].pBt, SQLITE_ABORT_ROLLBACK, 0);
+ return 0;
+}
+
+static int
+load_triple(BtCursor* crsTriples, SgTriple triple)
+{
+ int rc;
+
+ u32 payloadSize;
+ rc = sqlite3BtreeDataSize(crsTriples, &payloadSize);
+ if (rc != SQLITE_OK)
+ return rc;
+
+ u32 avail = 0;
+ const unsigned char* row = sqlite3BtreeDataFetch(crsTriples, &avail);
+ row++;
+
+ int serial_type_subj, serial_type_pred, serial_type_obj;
+ row += getVarint32(row, serial_type_subj);
+ row += getVarint32(row, serial_type_pred);
+ row += getVarint32(row, serial_type_obj);
+ row++;
+
+ Mem mem[3];
+ row += sqlite3VdbeSerialGet(row, serial_type_subj, &mem[0]);
+ row += sqlite3VdbeSerialGet(row, serial_type_pred, &mem[1]);
+ row += sqlite3VdbeSerialGet(row, serial_type_obj, &mem[2]);
+
+ triple[0] = mem[0].u.i;
+ triple[1] = mem[1].u.i;
+ triple[2] = mem[2].u.i;
+
+ return SQLITE_OK;
+}
+
+bool
+sg_select_triple(SgSG *sg, SgId key, SgTriple triple, GuExn* err)
+{
+ sqlite3 *db = (sqlite3 *) sg;
+
+ Table *triplesTbl =
+ sqlite3HashFind(&db->aDb[0].pSchema->tblHash, SG_TRIPLES);
+ if (!triplesTbl) {
+ sg_raise_err("Table " SG_TRIPLES " is missing", err);
+ return false;
+ }
+
+ int rc;
+ rc = sqlite3BtreeBeginTrans(db->aDb[0].pBt, 0);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ return false;
+ }
+
+ BtCursor crsTriples;
+ memset(&crsTriples, 0, sizeof(crsTriples));
+ rc = sqlite3BtreeCursor(db->aDb[0].pBt, triplesTbl->tnum, 0, NULL, &crsTriples);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ goto rollback;
+ }
+
+ int res;
+ rc = sqlite3BtreeMovetoUnpacked(&crsTriples, 0, key, 0, &res);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ goto close;
+ }
+
+ if (res == 0) {
+ rc = load_triple(&crsTriples, triple);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ goto close;
+ }
+ }
+
+ rc = sqlite3BtreeCloseCursor(&crsTriples);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ goto rollback;
+ }
+
+ rc = sqlite3BtreeCommit(db->aDb[0].pBt);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ return false;
+ }
+
+ return (res == 0);
+
+close:
+ sqlite3BtreeCloseCursor(&crsTriples);
+
+rollback:
+ sqlite3BtreeRollback(db->aDb[0].pBt, SQLITE_ABORT_ROLLBACK, 0);
+ return false;
+}
+
+struct SgTripleResult {
+ sqlite3 *db;
+
+ int n_cursors;
+ BtCursor cursors[4];
+ BtCursor* cursor;
+
+ int res;
+ Mem mem[3];
+ UnpackedRecord idxKey;
+};
+
+SgTripleResult*
+sg_query_triple(SgSG *sg, SgTriple triple, GuExn* err)
+{
+ sqlite3 *db = (sqlite3 *) sg;
+
+ SgTripleResult* tres = malloc(sizeof(SgTripleResult));
+ tres->db = db;
+
+ open_triples(db, 0, tres->cursors, &tres->n_cursors, err);
+ if (gu_exn_is_raised(err))
+ goto close;
+
+ for (int i = 0; i < 3; i++) {
+ if (triple[i] == 0)
+ tres->mem[i].flags = MEM_Null;
+ else {
+ tres->mem[i].flags = MEM_Int;
+ tres->mem[i].u.i = triple[i];
+ }
+ }
+
+ int i = 0;
+ while (i < 3) {
+ if (triple[i] != 0)
+ break;
+ i++;
+ }
+
+ tres->cursor = &tres->cursors[i];
+ tres->idxKey.pKeyInfo = tres->cursor->pKeyInfo;
+ tres->idxKey.nField = 0;
+ tres->idxKey.aMem = &tres->mem[i];
+ tres->res = 0;
+
+ while (i+tres->idxKey.nField < 3) {
+ tres->idxKey.nField++;
+
+ if (triple[i+tres->idxKey.nField] == 0)
+ break;
+ }
+
+ int rc;
+ if (tres->idxKey.nField > 0) {
+ tres->idxKey.default_rc = 1;
+ rc = sqlite3BtreeMovetoUnpacked(tres->cursor,
+ &tres->idxKey, 0, 0, &tres->res);
+ if (rc == SQLITE_OK) {
+ if (tres->res < 0) {
+ rc = sqlite3BtreeNext(tres->cursor, &tres->res);
+ }
+ tres->res = 0;
+ }
+ } else {
+ rc = sqlite3BtreeFirst(tres->cursor, &tres->res);
+ }
+
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ goto close;
+ }
+
+ return tres;
+
+close:
+ close_triples(db, tres->cursors, &tres->n_cursors);
+
+ free(tres);
+
+ if (rc == SQLITE_OK) {
+ rc = sqlite3BtreeCommit(db->aDb[0].pBt);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ return NULL;
+ }
+ } else {
+ sqlite3BtreeRollback(db->aDb[0].pBt, SQLITE_ABORT_ROLLBACK, 0);
+ }
+
+ return NULL;
+}
+
+bool
+sg_query_result_fetch(SgTripleResult* tres, SgId* pKey, SgTriple triple, GuExn* err)
+{
+ while (tres->res == 0) {
+ int rc;
+
+ if (tres->idxKey.nField > 0) {
+ i64 szData;
+ const unsigned char *zData;
+ rc = sqlite3BtreeKeySize(tres->cursor, &szData);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(tres->db, err);
+ return false;
+ }
+
+ u32 available = 0;
+ zData = sqlite3BtreeKeyFetch(tres->cursor, &available);
+ if (szData > available)
+ gu_impossible();
+
+ tres->idxKey.default_rc = 0;
+ tres->res = sqlite3VdbeRecordCompare(available, zData, &tres->idxKey);
+ if (tres->res != 0)
+ return false;
+
+ if (tres->idxKey.nField == 1 && tres->mem[2].flags != MEM_Null) {
+ int offset =
+ zData[0] +
+ sqlite3VdbeSerialTypeLen(zData[1]) +
+ sqlite3VdbeSerialTypeLen(zData[2]);
+ zData+offset;
+ Mem mem;
+ sqlite3VdbeSerialGet(zData+offset, zData[3], &mem);
+ if (mem.u.i != tres->mem[2].u.i) {
+ sqlite3BtreeNext(tres->cursor, &tres->res);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(tres->db, err);
+ return false;
+ }
+ continue;
+ }
+ }
+ }
+
+ if (tres->idxKey.nField > 0) {
+ rc = sqlite3VdbeIdxRowid(tres->db, tres->cursor, pKey);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(tres->db, err);
+ return false;
+ }
+
+ rc = sqlite3BtreeMovetoUnpacked(&tres->cursors[3], 0, *pKey, 0, &tres->res);
+ } else {
+ rc = sqlite3BtreeKeySize(tres->cursor, pKey);
+ }
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(tres->db, err);
+ return false;
+ }
+
+ rc = load_triple(&tres->cursors[3], triple);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(tres->db, err);
+ return false;
+ }
+
+ sqlite3BtreeNext(tres->cursor, &tres->res);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(tres->db, err);
+ return false;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+void
+sg_query_result_close(SgTripleResult* tres, GuExn* err)
+{
+ close_triples(tres->db, tres->cursors, &tres->n_cursors);
+
+ int rc = sqlite3BtreeCommit(tres->db->aDb[0].pBt);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(tres->db, err);
+ return;
+ }
+
+ free(tres);
+}