1
0
forked from GitHub/gf-core
Files
gf-core/src/runtime/c/sg/sg.c
2017-04-12 10:05:22 +00:00

2404 lines
52 KiB
C

#include <stdlib.h>
#include "sqlite3Btree.h"
#include "sg/sg.h"
#include "gu/mem.h"
#include "pgf/data.h"
#define SG_EXPRS 1
#define SG_PAIRS 2
#define SG_IDENTS 3
#define SG_LITERALS 4
#define SG_TOKENS 5
#define SG_TRIPLES 6
#define SG_TRIPLES_SPO 7
#define SG_TRIPLES_PO 8
#define SG_TRIPLES_O 9
struct SgSG {
Btree *pBtree;
int exprsTNum; /* The page number for the table of expressions */
int identsTNum; /* The page number for the index on identifiers */
int literalsTNum; /* The page number for the index on literals */
int pairsTNum; /* The page number for the index on application pairs */
int tokensTNum; /* The page number for the index on linearization tokens */
int triplesTNum; /* The page number for the table of triples */
int triplesIdxTNum[3]; /* The page number for the three indexes on triples */
int autoCommit;
};
void
sg_raise_sqlite(int rc, GuExn* err)
{
const char *msg = sqlite3BtreeErrName(rc);
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;
}
}
static int
sg_create_table(Btree* pBtree, BtCursor* crsSchema, int tblKey, int* pTNum, int flags)
{
int rc;
int file_format = sqlite3BtreeFileFormat(pBtree);
int res = 0;
rc = sqlite3BtreeMovetoUnpacked(crsSchema, 0, tblKey, 0, &res);
if (rc != SQLITE_OK) {
return rc;
}
Mem mem;
int serial_type;
if (res == 0) {
u32 payloadSize;
rc = sqlite3BtreeDataSize(crsSchema, &payloadSize);
if (rc != SQLITE_OK)
return rc;
u32 avail = 0;
const unsigned char* row = sqlite3BtreeDataFetch(crsSchema, &avail);
row++;
row += getVarint32(row, serial_type);
row += sqlite3BtreeSerialGet(row, serial_type, &mem);
assert(mem.flags & MEM_Int);
*pTNum = mem.u.i;
} else {
rc = sqlite3BtreeCreateTable(pBtree, pTNum, flags);
if (rc != SQLITE_OK) {
return rc;
}
mem.flags = MEM_Int;
mem.u.i = *pTNum;
unsigned char buf[32];
unsigned char *p;
buf[0] = 2;
serial_type = sqlite3BtreeSerialType(&mem, file_format);
buf[1] = serial_type;
p = buf+2;
p += sqlite3BtreeSerialPut(buf+2, &mem, serial_type);
rc = sqlite3BtreeInsert(crsSchema, 0, tblKey,
buf, p-buf, 0,
0, 0);
if (rc != SQLITE_OK)
return rc;
}
return SQLITE_OK;
}
SgSG*
sg_open(const char *filename,
GuExn* err)
{
int rc;
Btree* pBtree;
rc = sqlite3BtreeOpen(0, filename, &pBtree,
0, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MAIN_DB);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
return NULL;
}
rc = sqlite3BtreeBeginTrans(pBtree, 1);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
sqlite3BtreeClose(pBtree);
return NULL;
}
BtCursor* crsSchema = NULL;
rc = sqlite3BtreeCursor(pBtree, 1, 1, 0, 0, &crsSchema);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
sqlite3BtreeClose(pBtree);
return NULL;
}
SgSG* sg = malloc(sizeof(SgSG));
sg->pBtree = pBtree;
sg->autoCommit = 1;
rc = sg_create_table(pBtree, crsSchema, SG_EXPRS, &sg->exprsTNum, BTREE_INTKEY);
if (rc != SQLITE_OK)
goto fail;
rc = sg_create_table(pBtree, crsSchema, SG_IDENTS, &sg->identsTNum, 0);
if (rc != SQLITE_OK)
goto fail;
rc = sg_create_table(pBtree, crsSchema, SG_LITERALS, &sg->literalsTNum, 0);
if (rc != SQLITE_OK)
goto fail;
rc = sg_create_table(pBtree, crsSchema, SG_PAIRS, &sg->pairsTNum, 0);
if (rc != SQLITE_OK)
goto fail;
rc = sg_create_table(pBtree, crsSchema, SG_TOKENS, &sg->tokensTNum, 0);
if (rc != SQLITE_OK)
goto fail;
rc = sg_create_table(pBtree, crsSchema, SG_TRIPLES, &sg->triplesTNum, BTREE_INTKEY);
if (rc != SQLITE_OK)
goto fail;
rc = sg_create_table(pBtree, crsSchema, SG_TRIPLES_SPO, &sg->triplesIdxTNum[0], 0);
if (rc != SQLITE_OK)
goto fail;
rc = sg_create_table(pBtree, crsSchema, SG_TRIPLES_PO, &sg->triplesIdxTNum[1], 0);
if (rc != SQLITE_OK)
goto fail;
rc = sg_create_table(pBtree, crsSchema, SG_TRIPLES_O, &sg->triplesIdxTNum[2], 0);
if (rc != SQLITE_OK)
goto fail;
sqlite3BtreeCloseCursor(crsSchema);
rc = sqlite3BtreeCommit(pBtree);
if (rc != SQLITE_OK)
goto fail;
return sg;
fail:
sg_raise_sqlite(rc, err);
if (crsSchema != NULL)
sqlite3BtreeCloseCursor(crsSchema);
sqlite3BtreeRollback(sg->pBtree, SQLITE_ABORT_ROLLBACK, 0);
sqlite3BtreeClose(pBtree);
free(sg);
return NULL;
}
void
sg_close(SgSG* sg, GuExn* err)
{
sqlite3BtreeClose(sg->pBtree);
}
void
sg_shutdown()
{
sqlite3BtreeShutdown();
}
void
sg_begin_trans(SgSG* sg, GuExn* err)
{
int rc = sqlite3BtreeBeginTrans(sg->pBtree, 1);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
return;
}
sg->autoCommit = 0;
}
void
sg_commit(SgSG* sg, GuExn* err)
{
int rc = sqlite3BtreeCommit(sg->pBtree);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
return;
}
sg->autoCommit = 1;
}
void
sg_rollback(SgSG* sg, GuExn* err)
{
int rc = sqlite3BtreeRollback(sg->pBtree, SQLITE_ABORT_ROLLBACK, 0);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
return;
}
sg->autoCommit = 1;
}
typedef struct {
int n_cursors;
BtCursor* crsExprs;
BtCursor* crsIdents;
BtCursor* crsLiterals;
BtCursor* crsPairs;
SgId key_seed;
} ExprContext;
static int
open_exprs(SgSG *sg, int wrFlag, bool identsOnly,
ExprContext* ctxt, GuExn* err)
{
ctxt->n_cursors = 0;
int rc;
rc = sqlite3BtreeCursor(sg->pBtree, sg->exprsTNum, wrFlag, 0, 0, &ctxt->crsExprs);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
return rc;
}
ctxt->n_cursors++;
rc = sqlite3BtreeCursor(sg->pBtree, sg->identsTNum, wrFlag, 1, 1, &ctxt->crsIdents);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
return rc;
}
ctxt->n_cursors++;
if (!identsOnly) {
rc = sqlite3BtreeCursor(sg->pBtree, sg->literalsTNum, wrFlag, 1, 1, &ctxt->crsLiterals);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
return rc;
}
ctxt->n_cursors++;
rc = sqlite3BtreeCursor(sg->pBtree, sg->pairsTNum, wrFlag, 2, 0, &ctxt->crsPairs);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
return rc;
}
ctxt->n_cursors++;
}
if (wrFlag) {
int res;
rc = sqlite3BtreeLast(ctxt->crsExprs, &res);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
return rc;
}
rc = sqlite3BtreeKeySize(ctxt->crsExprs, &ctxt->key_seed);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
return rc;
}
} else {
ctxt->key_seed = 0;
}
return SQLITE_OK;
}
static void
close_exprs(ExprContext* ctxt)
{
if (ctxt->n_cursors >= 4) {
sqlite3BtreeCloseCursor(ctxt->crsPairs);
}
if (ctxt->n_cursors >= 3) {
sqlite3BtreeCloseCursor(ctxt->crsLiterals);
}
if (ctxt->n_cursors >= 2) {
sqlite3BtreeCloseCursor(ctxt->crsIdents);
}
if (ctxt->n_cursors >= 1) {
sqlite3BtreeCloseCursor(ctxt->crsExprs);
}
ctxt->n_cursors = 0;
}
static int
find_function_rowid(SgSG* sg, ExprContext* ctxt,
GuString fun, SgId* pKey, int wrFlag)
{
int rc = SQLITE_OK;
int file_format = sqlite3BtreeFileFormat(sg->pBtree);
Mem mem[2];
mem[0].flags = MEM_Str;
mem[0].n = strlen(fun);
mem[0].z = (void*) fun;
UnpackedRecord idxKey;
sqlite3BtreeInitUnpackedRecord(&idxKey, ctxt->crsIdents, 1, 0, mem);
int res = 0;
rc = sqlite3BtreeMovetoUnpacked(ctxt->crsIdents,
&idxKey, 0, 0, &res);
if (rc != SQLITE_OK) {
return rc;
}
if (res == 0) {
rc = sqlite3BtreeIdxRowid(sg->pBtree, ctxt->crsIdents, pKey);
} else {
if (wrFlag == 0) {
*pKey = 0;
return SQLITE_OK;
}
*pKey = ++ctxt->key_seed;
int serial_type_fun = sqlite3BtreeSerialType(&mem[0], file_format);
int serial_type_fun_hdr_len = sqlite3BtreeVarintLen(serial_type_fun);
mem[1].flags = MEM_Int;
mem[1].u.i = *pKey;
int serial_type_key = sqlite3BtreeSerialType(&mem[1], file_format);
int serial_type_key_hdr_len = sqlite3BtreeVarintLen(serial_type_key);
unsigned char* buf = malloc(1+serial_type_fun_hdr_len+(serial_type_key_hdr_len > 1 ? serial_type_key_hdr_len : 1)+mem[0].n+8);
unsigned char* p = buf;
*p++ = 1+serial_type_fun_hdr_len+1;
p += putVarint32(p, serial_type_fun);
*p++ = 0;
memcpy(p, 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, mem[0].n);
p += mem[0].n;
p += sqlite3BtreeSerialPut(p, &mem[1], serial_type_key);
rc = sqlite3BtreeInsert(ctxt->crsIdents, buf, p-buf,
0, *pKey, 0,
0, 0);
free:
free(buf);
}
return rc;
}
static int
store_expr(SgSG* sg,
ExprContext* ctxt, PgfExpr expr, SgId* pKey, int wrFlag)
{
int rc = SQLITE_OK;
int file_format = sqlite3BtreeFileFormat(sg->pBtree);
GuVariantInfo ei = gu_variant_open(expr);
switch (ei.tag) {
case PGF_EXPR_ABS: {
gu_impossible();
break;
}
case PGF_EXPR_APP: {
PgfExprApp* app = ei.data;
Mem mem[3];
mem[0].flags = MEM_Int;
rc = store_expr(sg, ctxt, app->fun, &mem[0].u.i, wrFlag);
if (rc != SQLITE_OK)
return rc;
if (wrFlag == 0 && mem[0].u.i == 0) {
*pKey = 0;
return SQLITE_OK;
}
mem[1].flags = MEM_Int;
rc = store_expr(sg, ctxt, app->arg, &mem[1].u.i, wrFlag);
if (rc != SQLITE_OK)
return rc;
if (wrFlag == 0 && mem[1].u.i == 0) {
*pKey = 0;
return SQLITE_OK;
}
UnpackedRecord idxKey;
sqlite3BtreeInitUnpackedRecord(&idxKey, ctxt->crsPairs, 2, 0, mem);
int res = 0;
rc = sqlite3BtreeMovetoUnpacked(ctxt->crsPairs,
&idxKey, 0, 0, &res);
if (rc != SQLITE_OK) {
return rc;
}
if (res == 0) {
rc = sqlite3BtreeIdxRowid(sg->pBtree, ctxt->crsPairs, pKey);
} else {
if (wrFlag == 0) {
*pKey = 0;
return SQLITE_OK;
}
*pKey = ++ctxt->key_seed;
unsigned char buf[32]; // enough for a record with three integers
buf[1] = 3;
u32 serial_type;
unsigned char* p = buf+4;
serial_type = sqlite3BtreeSerialType(&mem[0], file_format);
buf[2] = serial_type;
p += sqlite3BtreeSerialPut(p, &mem[0], serial_type);
serial_type = sqlite3BtreeSerialType(&mem[1], file_format);
buf[3] = serial_type;
p += sqlite3BtreeSerialPut(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 = sqlite3BtreeSerialType(&mem[2], file_format);
buf[3] = serial_type;
p += sqlite3BtreeSerialPut(p, &mem[2], serial_type);
rc = sqlite3BtreeInsert(ctxt->crsPairs, buf, p-buf,
0, *pKey, 0,
0, 0);
}
break;
}
case PGF_EXPR_LIT: {
PgfExprLit* elit = ei.data;
Mem mem[2];
GuVariantInfo li = gu_variant_open(elit->lit);
switch (li.tag) {
case PGF_LITERAL_STR: {
PgfLiteralStr* lstr = li.data;
mem[0].flags = MEM_Str;
mem[0].n = strlen(lstr->val);
mem[0].z = lstr->val;
break;
}
case PGF_LITERAL_INT: {
PgfLiteralInt* lint = li.data;
mem[0].flags = MEM_Int;
mem[0].u.i = lint->val;
break;
}
case PGF_LITERAL_FLT: {
PgfLiteralFlt* lflt = li.data;
mem[0].flags = MEM_Real;
mem[0].u.r = lflt->val;
break;
}
default:
gu_impossible();
}
UnpackedRecord idxKey;
sqlite3BtreeInitUnpackedRecord(&idxKey, ctxt->crsIdents, 1, 0, mem);
int res = 0;
rc = sqlite3BtreeMovetoUnpacked(ctxt->crsLiterals,
&idxKey, 0, 0, &res);
if (rc != SQLITE_OK) {
return rc;
}
if (res == 0) {
rc = sqlite3BtreeIdxRowid(sg->pBtree, ctxt->crsLiterals, pKey);
} else {
if (wrFlag == 0) {
*pKey = 0;
return SQLITE_OK;
}
*pKey = ++ctxt->key_seed;
mem[1].flags = MEM_Int;
mem[1].u.i = 0;
int serial_type_lit = sqlite3BtreeSerialType(&mem[0], file_format);
int serial_type_lit_hdr_len = sqlite3BtreeVarintLen(serial_type_lit);
int serial_type_arg = sqlite3BtreeSerialType(&mem[1], file_format);
int serial_type_arg_hdr_len = sqlite3BtreeVarintLen(serial_type_arg);
unsigned char* buf = malloc(1+serial_type_lit_hdr_len+(serial_type_arg_hdr_len > 1 ? serial_type_arg_hdr_len : 1)+mem[0].n+8);
unsigned char* p = buf;
*p++ = 1+serial_type_lit_hdr_len+serial_type_arg_hdr_len;
p += putVarint32(p, serial_type_lit);
p += putVarint32(p, serial_type_arg);
p += sqlite3BtreeSerialPut(p, &mem[0], serial_type_lit);
p += sqlite3BtreeSerialPut(p, &mem[1], serial_type_arg);
rc = sqlite3BtreeInsert(ctxt->crsExprs, 0, *pKey,
buf, p-buf, 0,
0, 0);
if (rc == SQLITE_OK) {
mem[1].flags = MEM_Int;
mem[1].u.i = *pKey;
int serial_type_key = sqlite3BtreeSerialType(&mem[1], file_format);
int serial_type_key_hdr_len = sqlite3BtreeVarintLen(serial_type_key);
p = buf;
*p++ = 1+serial_type_lit_hdr_len+serial_type_key_hdr_len;
p += putVarint32(p, serial_type_lit);
p += putVarint32(p, serial_type_key);
p += sqlite3BtreeSerialPut(p, &mem[0], serial_type_lit);
p += sqlite3BtreeSerialPut(p, &mem[1], serial_type_key);
rc = sqlite3BtreeInsert(ctxt->crsLiterals, buf, p-buf,
0, *pKey, 0,
0, 0);
}
free(buf);
}
break;
}
case PGF_EXPR_META: {
gu_impossible();
}
case PGF_EXPR_FUN: {
PgfExprFun* fun = ei.data;
rc = find_function_rowid(sg, ctxt, fun->fun, pKey, wrFlag);
break;
}
case PGF_EXPR_VAR: {
gu_impossible();
break;
}
case PGF_EXPR_TYPED: {
PgfExprTyped* etyped = ei.data;
rc = store_expr(sg, ctxt, etyped->expr, pKey, wrFlag);
break;
}
case PGF_EXPR_IMPL_ARG: {
PgfExprImplArg* eimpl = ei.data;
rc = store_expr(sg, ctxt, eimpl->expr, pKey, wrFlag);
break;
}
default:
gu_impossible();
}
return rc;
}
SgId
sg_insert_expr(SgSG *sg, PgfExpr expr, int wrFlag, GuExn* err)
{
int rc;
if (sg->autoCommit) {
rc = sqlite3BtreeBeginTrans(sg->pBtree, wrFlag);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
return 0;
}
}
ExprContext ctxt;
rc = open_exprs(sg, wrFlag, false, &ctxt, err);
if (rc != SQLITE_OK)
goto close;
SgId key;
rc = store_expr(sg, &ctxt, expr, &key, wrFlag);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
goto close;
}
close_exprs(&ctxt);
if (sg->autoCommit) {
rc = sqlite3BtreeCommit(sg->pBtree);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
return 0;
}
}
return key;
close:
close_exprs(&ctxt);
if (sg->autoCommit) {
sqlite3BtreeRollback(sg->pBtree, SQLITE_ABORT_ROLLBACK, 0);
}
return 0;
}
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 += sqlite3BtreeSerialGet(row, serial_type_fun, &mem[0]);
row += sqlite3BtreeSerialGet(row, serial_type_arg, &mem[1]);
if (mem[1].flags & MEM_Null) {
u32 len = sqlite3BtreeSerialTypeLen(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 if (mem[1].u.i == 0) {
PgfExprLit *elit =
gu_new_variant(PGF_EXPR_LIT,
PgfExprLit,
pExpr, out_pool);
if (mem[0].flags & MEM_Str) {
u32 len = sqlite3BtreeSerialTypeLen(serial_type_fun);
PgfLiteralStr *lstr =
gu_new_flex_variant(PGF_LITERAL_STR,
PgfLiteralStr,
val, len+1,
&elit->lit, out_pool);
memcpy(lstr->val, mem[0].z, len);
lstr->val[len] = 0;
} else if (mem[0].flags & MEM_Int) {
PgfLiteralInt *lint =
gu_new_variant(PGF_LITERAL_INT,
PgfLiteralInt,
&elit->lit, out_pool);
lint->val = mem[0].u.i;
} else if (mem[0].flags & MEM_Real) {
PgfLiteralFlt *lflt =
gu_new_variant(PGF_LITERAL_FLT,
PgfLiteralFlt,
&elit->lit, out_pool);
lflt->val = mem[0].u.r;
} else {
gu_impossible();
}
} 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_get_expr(SgSG *sg, SgId key, GuPool* out_pool, GuExn* err)
{
int rc;
if (sg->autoCommit) {
rc = sqlite3BtreeBeginTrans(sg->pBtree, 0);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
return gu_null_variant;
}
}
BtCursor* crsExprs;
rc = sqlite3BtreeCursor(sg->pBtree, sg->exprsTNum, 0, 0, 0, &crsExprs);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
goto rollback;
}
PgfExpr expr = gu_null_variant;
rc = load_expr(crsExprs, key, &expr, out_pool);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
goto close;
}
rc = sqlite3BtreeCloseCursor(crsExprs);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
goto rollback;
}
if (sg->autoCommit) {
rc = sqlite3BtreeCommit(sg->pBtree);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
goto rollback;
}
}
return expr;
close:
sqlite3BtreeCloseCursor(crsExprs);
rollback:
if (sg->autoCommit) {
sqlite3BtreeRollback(sg->pBtree, SQLITE_ABORT_ROLLBACK, 0);
}
return gu_null_variant;
}
// A query is compiled into a sequence of instructions with
// the following codes:
#define QI_PUSH 1
#define QI_VAR 2
#define QI_APPLY 3
#define QI_RETURN 4
typedef struct {
int code;
SgId arg;
} QueryInstr;
struct SgQueryExprResult {
ExprContext ectxt;
GuBuf* instrs;
GuBuf* queue;
size_t iState;
PgfMetaId min_meta_id;
PgfMetaId max_meta_id;
};
typedef struct QueryArg QueryArg;
struct QueryArg {
QueryArg* prev;
SgId arg;
};
typedef struct {
QueryArg* args; // a stack of arguments
int pc; // program counter
} QueryState;
static int
build_expr_query(SgSG* sg,
SgQueryExprResult* ctxt, PgfExpr expr)
{
int rc = SQLITE_OK;
GuVariantInfo ei = gu_variant_open(expr);
switch (ei.tag) {
case PGF_EXPR_ABS: {
gu_impossible();
break;
}
case PGF_EXPR_APP: {
PgfExprApp* app = ei.data;
rc = build_expr_query(sg, ctxt, app->fun);
if (rc != SQLITE_OK)
return rc;
QueryInstr* first =
gu_buf_index_last(ctxt->instrs, QueryInstr);
rc = build_expr_query(sg, ctxt, app->arg);
if (rc != SQLITE_OK)
return rc;
QueryInstr* second =
gu_buf_index_last(ctxt->instrs, QueryInstr);
if (first->code == QI_PUSH && second->code == QI_PUSH &&
second - first == 1) {
// we could directly combine the two expressions
Mem mem[2];
mem[0].flags = MEM_Int;
mem[0].u.i = first->arg;
mem[1].flags = MEM_Int;
mem[1].u.i = second->arg;
UnpackedRecord idxKey;
sqlite3BtreeInitUnpackedRecord(&idxKey, ctxt->ectxt.crsPairs, 2, 0, mem);
int res = 0;
rc = sqlite3BtreeMovetoUnpacked(ctxt->ectxt.crsPairs,
&idxKey, 0, 0, &res);
if (rc != SQLITE_OK)
return rc;
if (res != 0)
return SQLITE_DONE;
gu_buf_pop(ctxt->instrs, QueryInstr);
rc = sqlite3BtreeIdxRowid(sg->pBtree, ctxt->ectxt.crsPairs, &first->arg);
} else if (gu_variant_tag(app->arg) != PGF_EXPR_META) {
QueryInstr* instr = gu_buf_extend(ctxt->instrs);
instr->code = QI_APPLY;
instr->arg = 0;
}
break;
}
case PGF_EXPR_LIT: {
PgfExprLit* elit = ei.data;
Mem mem;
GuVariantInfo li = gu_variant_open(elit->lit);
switch (li.tag) {
case PGF_LITERAL_STR: {
PgfLiteralStr* lstr = li.data;
mem.flags = MEM_Str;
mem.n = strlen(lstr->val);
mem.z = lstr->val;
break;
}
case PGF_LITERAL_INT: {
PgfLiteralInt* lint = li.data;
mem.flags = MEM_Int;
mem.u.i = lint->val;
break;
}
case PGF_LITERAL_FLT: {
PgfLiteralFlt* lflt = li.data;
mem.flags = MEM_Real;
mem.u.r = lflt->val;
break;
}
default:
gu_impossible();
}
UnpackedRecord idxKey;
sqlite3BtreeInitUnpackedRecord(&idxKey, ctxt->ectxt.crsIdents, 1, 0, &mem);
int res = 0;
rc = sqlite3BtreeMovetoUnpacked(ctxt->ectxt.crsLiterals,
&idxKey, 0, 0, &res);
if (rc != SQLITE_OK)
return rc;
if (res != 0)
return SQLITE_DONE;
QueryInstr* instr = gu_buf_extend(ctxt->instrs);
instr->code = QI_PUSH;
rc = sqlite3BtreeIdxRowid(sg->pBtree, ctxt->ectxt.crsLiterals, &instr->arg);
break;
}
case PGF_EXPR_META: {
PgfExprMeta* emeta = ei.data;
QueryInstr* instr = gu_buf_extend(ctxt->instrs);
instr->code = QI_VAR;
instr->arg = emeta->id;
if (ctxt->min_meta_id > emeta->id)
ctxt->min_meta_id = emeta->id;
if (ctxt->max_meta_id < emeta->id)
ctxt->max_meta_id = emeta->id;
break;
}
case PGF_EXPR_FUN: {
PgfExprFun* fun = ei.data;
QueryInstr* instr = gu_buf_extend(ctxt->instrs);
instr->code = QI_PUSH;
rc = find_function_rowid(sg, &ctxt->ectxt, fun->fun, &instr->arg, 0);
if (rc == SQLITE_OK && instr->arg == 0)
return SQLITE_DONE;
break;
}
case PGF_EXPR_VAR: {
gu_impossible();
break;
}
case PGF_EXPR_TYPED: {
PgfExprTyped* etyped = ei.data;
rc = build_expr_query(sg, ctxt, etyped->expr);
break;
}
case PGF_EXPR_IMPL_ARG: {
PgfExprImplArg* eimpl = ei.data;
rc = build_expr_query(sg, ctxt, eimpl->expr);
break;
}
default:
gu_impossible();
}
return rc;
}
static int
run_expr_query(SgSG* sg, SgQueryExprResult* ctxt, GuPool* pool)
{
int rc;
while (ctxt->iState < gu_buf_length(ctxt->queue)) {
QueryState* state =
gu_buf_index(ctxt->queue, QueryState, ctxt->iState);
QueryInstr* instr =
gu_buf_index(ctxt->instrs, QueryInstr, state->pc);
switch (instr->code) {
case QI_PUSH: {
QueryArg* arg = gu_new(QueryArg, pool);
arg->arg = instr->arg;
arg->prev = state->args;
state->args = arg;
break;
}
case QI_VAR: {
assert(state->args != NULL);
Mem mem;
mem.flags = MEM_Int;
mem.u.i = state->args->arg;
UnpackedRecord idxKey;
sqlite3BtreeInitUnpackedRecord(&idxKey, ctxt->ectxt.crsPairs, 1, 1, &mem);
int res = 0;
rc = sqlite3BtreeMovetoUnpacked(ctxt->ectxt.crsPairs,
&idxKey, 0, 0, &res);
if (rc != SQLITE_OK)
return rc;
if (res < 0) {
rc = sqlite3BtreeNext(ctxt->ectxt.crsPairs, &res);
}
res = 0;
while (res == 0) {
i64 szData;
const unsigned char *zData;
rc = sqlite3BtreeKeySize(ctxt->ectxt.crsPairs, &szData);
if (rc != SQLITE_OK)
return rc;
u32 available = 0;
zData = sqlite3BtreeKeyFetch(ctxt->ectxt.crsPairs, &available);
if (szData > available)
gu_impossible();
idxKey.default_rc = 0;
res = sqlite3BtreeRecordCompare(available, zData, &idxKey);
if (res != 0)
break;
QueryArg* arg = gu_new(QueryArg, pool);
arg->prev = state->args->prev;
QueryState* state1 = gu_buf_extend(ctxt->queue);
state1->args = arg;
state1->pc = state->pc+1;
rc = sqlite3BtreeIdxRowid(sg->pBtree, ctxt->ectxt.crsPairs, &arg->arg);
if (rc != SQLITE_OK)
return rc;
sqlite3BtreeNext(ctxt->ectxt.crsPairs, &res);
if (rc != SQLITE_OK)
return rc;
}
ctxt->iState++;
break;
}
case QI_APPLY: {
assert(state->args != NULL && state->args->prev);
Mem mem[2];
mem[0].flags = MEM_Int;
mem[0].u.i = state->args->prev->arg;
mem[1].flags = MEM_Int;
mem[1].u.i = state->args->arg;
UnpackedRecord idxKey;
sqlite3BtreeInitUnpackedRecord(&idxKey, ctxt->ectxt.crsPairs, 2, 0, mem);
int res = 0;
rc = sqlite3BtreeMovetoUnpacked(ctxt->ectxt.crsPairs,
&idxKey, 0, 0, &res);
if (rc != SQLITE_OK)
return rc;
if (res != 0) {
ctxt->iState++;
continue;
}
state->args = state->args->prev;
rc = sqlite3BtreeIdxRowid(sg->pBtree, ctxt->ectxt.crsPairs, &state->args->arg);
if (rc != SQLITE_OK)
return rc;
break;
}
case QI_RETURN:
return SQLITE_OK;
}
state->pc++;
}
return SQLITE_DONE;
}
SgQueryExprResult*
sg_query_expr(SgSG *sg, PgfExpr expr, GuPool* pool, GuExn* err)
{
int rc;
if (sg->autoCommit) {
rc = sqlite3BtreeBeginTrans(sg->pBtree, 0);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
return NULL;
}
}
SgQueryExprResult* ctxt = gu_new(SgQueryExprResult, pool);
rc = open_exprs(sg, 0, false, &ctxt->ectxt, err);
if (rc != SQLITE_OK)
goto close;
ctxt->instrs = gu_new_buf(QueryInstr, pool);
ctxt->queue = gu_new_buf(QueryState, pool);
ctxt->iState = 0;
ctxt->min_meta_id = INT_MAX;
ctxt->max_meta_id = INT_MIN;
rc = build_expr_query(sg, ctxt, expr);
if (rc == SQLITE_OK) {
QueryInstr* instr = gu_buf_extend(ctxt->instrs);
instr->code = QI_RETURN;
instr->arg = 0;
QueryState* state = gu_buf_extend(ctxt->queue);
state->args = NULL;
state->pc = 0;
} else if (rc != SQLITE_DONE) {
sg_raise_sqlite(rc, err);
goto close;
}
return ctxt;
close:
close_exprs(&ctxt->ectxt);
if (sg->autoCommit) {
sqlite3BtreeRollback(sg->pBtree, SQLITE_ABORT_ROLLBACK, 0);
}
return NULL;
}
PgfExpr
sg_query_next(SgSG *sg, SgQueryExprResult* ctxt, SgId* pKey, GuPool* pool, GuExn* err)
{
int rc;
rc = run_expr_query(sg, ctxt, pool);
if (rc == SQLITE_DONE)
return gu_null_variant;
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
return gu_null_variant;
}
QueryState* state =
gu_buf_index(ctxt->queue, QueryState, ctxt->iState);
assert(state->args != NULL);
ctxt->iState++;
PgfExpr expr;
rc = load_expr(ctxt->ectxt.crsExprs, state->args->arg, &expr, pool);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
return gu_null_variant;
}
*pKey = state->args->arg;
return expr;
}
void
sg_query_close(SgSG* sg, SgQueryExprResult* ctxt, GuExn* err)
{
int rc;
close_exprs(&ctxt->ectxt);
if (sg->autoCommit) {
rc = sqlite3BtreeCommit(sg->pBtree);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
}
}
}
static int
insert_token(SgSG *sg, BtCursor* crsTokens, GuString tok, SgId key)
{
int rc = SQLITE_OK;
int file_format = sqlite3BtreeFileFormat(sg->pBtree);
Mem mem[2];
mem[0].flags = MEM_Str;
mem[0].n = strlen(tok);
mem[0].z = (void*) tok;
int serial_type_tok = sqlite3BtreeSerialType(&mem[0], file_format);
int serial_type_tok_hdr_len = sqlite3BtreeVarintLen(serial_type_tok);
mem[1].flags = MEM_Int;
mem[1].u.i = key;
int serial_type_key = sqlite3BtreeSerialType(&mem[1], file_format);
int serial_type_key_hdr_len = sqlite3BtreeVarintLen(serial_type_key);
unsigned char* buf = malloc(1+serial_type_tok_hdr_len+serial_type_key_hdr_len+mem[0].n+8);
unsigned char* p = buf;
*p++ = 1+serial_type_tok_hdr_len+serial_type_key_hdr_len;
p += putVarint32(p, serial_type_tok);
p += putVarint32(p, serial_type_key);
memcpy(p, tok, mem[0].n);
p += mem[0].n;
p += sqlite3BtreeSerialPut(p, &mem[1], serial_type_key);
rc = sqlite3BtreeInsert(crsTokens, buf, p-buf,
0, key, 0,
0, 0);
free(buf);
return rc;
}
static int
insert_syms(SgSG *sg, BtCursor* crsTokens, PgfSymbols* syms, SgId key)
{
int rc;
size_t n_syms = gu_seq_length(syms);
for (size_t sym_idx = 0; sym_idx < n_syms; sym_idx++) {
PgfSymbol sym = gu_seq_get(syms, PgfSymbol, sym_idx);
GuVariantInfo sym_i = gu_variant_open(sym);
switch (sym_i.tag) {
case PGF_SYMBOL_KS: {
PgfSymbolKS* ks = sym_i.data;
rc = insert_token(sg, crsTokens, ks->token, key);
if (rc != SQLITE_OK)
return rc;
break;
}
case PGF_SYMBOL_KP: {
PgfSymbolKP* kp = sym_i.data;
rc = insert_syms(sg, crsTokens, kp->default_form, key);
if (rc != SQLITE_OK)
return rc;
for (size_t i = 0; i < kp->n_forms; i++) {
rc = insert_syms(sg, crsTokens, kp->forms[i].form, key);
if (rc != SQLITE_OK)
return rc;
}
break;
}
}
}
return SQLITE_OK;
}
void
sg_update_fts_index(SgSG* sg, PgfPGF* pgf, GuExn* err)
{
int rc = SQLITE_OK;
if (sg->autoCommit) {
rc = sqlite3BtreeBeginTrans(sg->pBtree, 1);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
return;
}
}
ExprContext ctxt;
rc = open_exprs(sg, 1, true, &ctxt, err);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
goto close;
}
rc = sqlite3BtreeClearTable(sg->pBtree, sg->tokensTNum, NULL);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
return;
}
BtCursor* crsTokens;
rc = sqlite3BtreeCursor(sg->pBtree, sg->tokensTNum, 1, 1, 1, &crsTokens);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
}
ctxt.n_cursors++;
size_t n_concrs = gu_seq_length(pgf->concretes);
for (size_t i = 0; i < n_concrs; i++) {
PgfConcr* concr = gu_seq_index(pgf->concretes, PgfConcr, i);
size_t n_funs = gu_seq_length(concr->cncfuns);
for (size_t funid = 0; funid < n_funs; funid++) {
PgfCncFun* cncfun = gu_seq_get(concr->cncfuns, PgfCncFun*, funid);
SgId key = 0;
rc = find_function_rowid(sg, &ctxt, cncfun->absfun->name, &key, 1);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
goto close;
}
for (size_t lin_idx = 0; lin_idx < cncfun->n_lins; lin_idx++) {
PgfSequence* seq = cncfun->lins[lin_idx];
rc = insert_syms(sg, crsTokens, seq->syms, key);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
goto close;
}
}
}
}
if (ctxt.n_cursors >= 3) {
sqlite3BtreeCloseCursor(crsTokens);
ctxt.n_cursors--;
}
close_exprs(&ctxt);
if (sg->autoCommit) {
rc = sqlite3BtreeCommit(sg->pBtree);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
}
}
return;
close:
if (ctxt.n_cursors >= 3) {
sqlite3BtreeCloseCursor(crsTokens);
ctxt.n_cursors--;
}
close_exprs(&ctxt);
if (sg->autoCommit) {
sqlite3BtreeRollback(sg->pBtree, SQLITE_ABORT_ROLLBACK, 0);
}
}
GuSeq*
sg_query_linearization(SgSG *sg, GuString tok, GuPool *pool, GuExn* err)
{
int rc;
BtCursor* crsTokens;
rc = sqlite3BtreeCursor(sg->pBtree, sg->tokensTNum, 1, 1, 1, &crsTokens);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
return NULL;
}
Mem mem[1];
mem[0].flags = MEM_Str;
mem[0].n = strlen(tok);
mem[0].z = (void*) tok;
UnpackedRecord idxKey;
sqlite3BtreeInitUnpackedRecord(&idxKey, crsTokens, 1, 0, mem);
int res = 0;
rc = sqlite3BtreeMovetoUnpacked(crsTokens,
&idxKey, 0, 0, &res);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
return NULL;
}
GuBuf* ids = gu_new_buf(SgId, pool);
while (res == 0) {
SgId key;
rc = sqlite3BtreeIdxRowid(sg->pBtree, crsTokens, &key);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
sqlite3BtreeClearCursor(crsTokens);
return NULL;
}
gu_buf_push(ids, SgId, key);
sqlite3BtreeNext(crsTokens, &res);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
sqlite3BtreeClearCursor(crsTokens);
return NULL;
}
i64 szData;
const unsigned char *zData;
rc = sqlite3BtreeKeySize(crsTokens, &szData);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
sqlite3BtreeClearCursor(crsTokens);
return NULL;
}
u32 available = 0;
zData = sqlite3BtreeKeyFetch(crsTokens, &available);
if (szData > available)
gu_impossible();
res = sqlite3BtreeRecordCompare(available, zData, &idxKey);
if (res != 0)
break;
}
sqlite3BtreeClearCursor(crsTokens);
return gu_buf_data_seq(ids);
}
typedef struct {
int n_cursors;
BtCursor* cursor[4];
} TripleContext;
static int
open_triples(SgSG *sg, int wrFlag, TripleContext* ctxt, GuExn* err)
{
int rc;
ctxt->n_cursors = 0;
while (ctxt->n_cursors < 3) {
rc = sqlite3BtreeCursor(sg->pBtree, sg->triplesIdxTNum[ctxt->n_cursors], wrFlag, 3-ctxt->n_cursors, ctxt->n_cursors, &ctxt->cursor[ctxt->n_cursors]);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
return rc;
}
ctxt->n_cursors++;
}
rc = sqlite3BtreeCursor(sg->pBtree, sg->triplesTNum, wrFlag, 0, 0, &ctxt->cursor[ctxt->n_cursors]);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
return rc;
}
ctxt->n_cursors++;
return SQLITE_OK;
}
static void
close_triples(TripleContext* ctxt)
{
while (ctxt->n_cursors > 0) {
ctxt->n_cursors--;
sqlite3BtreeCloseCursor(ctxt->cursor[ctxt->n_cursors]);
}
}
SgId
sg_insert_triple(SgSG *sg, SgTriple triple, GuExn* err)
{
int rc;
if (sg->autoCommit) {
rc = sqlite3BtreeBeginTrans(sg->pBtree, 1);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
return 0;
}
}
TripleContext tctxt;
rc = open_triples(sg, 1, &tctxt, err);
if (rc != SQLITE_OK)
goto close;
Mem mem[4];
ExprContext ectxt;
rc = open_exprs(sg, 1, false, &ectxt, err);
if (rc != SQLITE_OK)
goto close;
for (size_t i = 0; i < 3; i++) {
mem[i].flags = MEM_Int;
rc = store_expr(sg, &ectxt, triple[i], &mem[i].u.i, 1);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
goto close;
}
}
UnpackedRecord idxKey;
sqlite3BtreeInitUnpackedRecord(&idxKey, tctxt.cursor[0], 3, 0, mem);
int res = 0;
rc = sqlite3BtreeMovetoUnpacked(tctxt.cursor[0],
&idxKey, 0, 0, &res);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
goto close;
}
SgId key = 0;
if (res == 0) {
rc = sqlite3BtreeIdxRowid(sg->pBtree, tctxt.cursor[0], &key);
} else {
rc = sqlite3BtreeLast(tctxt.cursor[3], &res);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
goto close;
}
rc = sqlite3BtreeKeySize(tctxt.cursor[3], &key);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
goto close;
}
key++;
u32 serial_type;
unsigned char buf[41]; // enough for a record with three integers
int file_format = sqlite3BtreeFileFormat(sg->pBtree);
unsigned char* p = buf+(buf[0] = 5);
serial_type = sqlite3BtreeSerialType(&mem[0], file_format);
buf[1] = serial_type;
p += sqlite3BtreeSerialPut(p, &mem[0], serial_type);
serial_type = sqlite3BtreeSerialType(&mem[1], file_format);
buf[2] = serial_type;
p += sqlite3BtreeSerialPut(p, &mem[1], serial_type);
serial_type = sqlite3BtreeSerialType(&mem[2], file_format);
buf[3] = serial_type;
p += sqlite3BtreeSerialPut(p, &mem[2], serial_type);
unsigned char* tmp = p;
mem[3].flags = MEM_Int;
mem[3].u.i = 1;
serial_type = sqlite3BtreeSerialType(&mem[3], file_format);
buf[4] = serial_type;
p += sqlite3BtreeSerialPut(p, &mem[3], serial_type);
rc = sqlite3BtreeInsert(tctxt.cursor[3], 0, key,
buf, p-buf, 0,
0, 0);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
goto close;
}
mem[3].flags = MEM_Int;
mem[3].u.i = key;
p = tmp;
serial_type = sqlite3BtreeSerialType(&mem[3], file_format);
buf[4] = serial_type;
p += sqlite3BtreeSerialPut(p, &mem[3], serial_type);
rc = sqlite3BtreeInsert(tctxt.cursor[0], buf, p-buf,
0, key, 0,
0, 0);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
goto close;
}
p = buf+(buf[0] = 4);
serial_type = sqlite3BtreeSerialType(&mem[1], file_format);
buf[1] = serial_type;
p += sqlite3BtreeSerialPut(p, &mem[1], serial_type);
serial_type = sqlite3BtreeSerialType(&mem[2], file_format);
buf[2] = serial_type;
p += sqlite3BtreeSerialPut(p, &mem[2], serial_type);
serial_type = sqlite3BtreeSerialType(&mem[3], file_format);
buf[3] = serial_type;
p += sqlite3BtreeSerialPut(p, &mem[3], serial_type);
rc = sqlite3BtreeInsert(tctxt.cursor[1], buf, p-buf,
0, key, 0,
0, 0);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
goto close;
}
p = buf+(buf[0] = 3);
serial_type = sqlite3BtreeSerialType(&mem[2], file_format);
buf[1] = serial_type;
p += sqlite3BtreeSerialPut(p, &mem[2], serial_type);
serial_type = sqlite3BtreeSerialType(&mem[3], file_format);
buf[2] = serial_type;
p += sqlite3BtreeSerialPut(p, &mem[3], serial_type);
rc = sqlite3BtreeInsert(tctxt.cursor[2], buf, p-buf,
0, key, 0,
0, 0);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
goto close;
}
}
close:
close_exprs(&ectxt);
close_triples(&tctxt);
if (sg->autoCommit) {
if (rc == SQLITE_OK || rc == SQLITE_DONE) {
rc = sqlite3BtreeCommit(sg->pBtree);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
return 0;
}
} else {
sqlite3BtreeRollback(sg->pBtree, SQLITE_ABORT_ROLLBACK, 0);
}
}
return key;
}
static int
load_triple(BtCursor* crsTriples, BtCursor* crsExprs, SgTriple triple,
GuPool* out_pool)
{
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 += sqlite3BtreeSerialGet(row, serial_type_subj, &mem[0]);
row += sqlite3BtreeSerialGet(row, serial_type_pred, &mem[1]);
row += sqlite3BtreeSerialGet(row, serial_type_obj, &mem[2]);
for (int i = 0; i < 3; i++) {
if (gu_variant_is_null(triple[i])) {
rc = load_expr(crsExprs, mem[i].u.i, &triple[i], out_pool);
if (rc != SQLITE_OK)
return rc;
}
}
return SQLITE_OK;
}
int
sg_get_triple(SgSG *sg, SgId key, SgTriple triple,
GuPool* out_pool, GuExn* err)
{
triple[0] = gu_null_variant;
triple[1] = gu_null_variant;
triple[2] = gu_null_variant;
int rc;
if (sg->autoCommit) {
rc = sqlite3BtreeBeginTrans(sg->pBtree, 0);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
return false;
}
}
BtCursor* crsTriples;
rc = sqlite3BtreeCursor(sg->pBtree, sg->triplesTNum, 0, 0, 0, &crsTriples);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
goto rollback;
}
BtCursor* crsExprs;
rc = sqlite3BtreeCursor(sg->pBtree, sg->exprsTNum, 0, 0, 0, &crsExprs);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
goto close1;
}
int res;
rc = sqlite3BtreeMovetoUnpacked(crsTriples, 0, key, 0, &res);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
goto close;
}
if (res == 0) {
rc = load_triple(crsTriples, crsExprs, triple, out_pool);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
goto close;
}
}
rc = sqlite3BtreeCloseCursor(crsExprs);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
goto close1;
}
rc = sqlite3BtreeCloseCursor(crsTriples);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
goto rollback;
}
if (sg->autoCommit) {
rc = sqlite3BtreeCommit(sg->pBtree);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
return false;
}
}
return (res == 0);
close:
sqlite3BtreeCloseCursor(crsExprs);
close1:
sqlite3BtreeCloseCursor(crsTriples);
rollback:
if (sg->autoCommit) {
sqlite3BtreeRollback(sg->pBtree, SQLITE_ABORT_ROLLBACK, 0);
}
return false;
}
typedef struct {
TripleContext tctxt;
BtCursor* cursor;
int res;
Mem mem[3];
UnpackedRecord idxKey;
} SgTripleResultInt;
struct SgTripleResult {
SgSG *sg;
SgTriple triple;
ExprContext ectxt;
SgTripleResultInt i;
};
static int
triple_result_init(SgSG *sg, SgTripleResultInt* tresi, GuExn* err)
{
int rc;
rc = open_triples(sg, 0, &tresi->tctxt, err);
if (rc != SQLITE_OK)
return rc;
int i = 0;
while (i < 3) {
if (tresi->mem[i].flags != MEM_Null)
break;
i++;
}
tresi->cursor = tresi->tctxt.cursor[i];
sqlite3BtreeInitUnpackedRecord(&tresi->idxKey, tresi->cursor, 0, 0, &tresi->mem[i]);
tresi->res = 0;
while (i+tresi->idxKey.nField < 3) {
tresi->idxKey.nField++;
if (tresi->mem[i+tresi->idxKey.nField].flags == MEM_Null)
break;
}
if (tresi->idxKey.nField > 0) {
tresi->idxKey.default_rc = 1;
rc = sqlite3BtreeMovetoUnpacked(tresi->cursor,
&tresi->idxKey, 0, 0, &tresi->res);
if (rc == SQLITE_OK) {
if (tresi->res < 0) {
rc = sqlite3BtreeNext(tresi->cursor, &tresi->res);
}
tresi->res = 0;
}
} else {
rc = sqlite3BtreeFirst(tresi->cursor, &tresi->res);
}
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
}
return rc;
}
static bool
triple_result_fetch(SgSG* sg,
SgTripleResultInt* tresi,
SgId* pKey, GuExn* err)
{
while (tresi->res == 0) {
int rc;
if (tresi->idxKey.nField > 0) {
i64 szData;
const unsigned char *zData;
rc = sqlite3BtreeKeySize(tresi->cursor, &szData);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
return false;
}
u32 available = 0;
zData = sqlite3BtreeKeyFetch(tresi->cursor, &available);
if (szData > available)
gu_impossible();
tresi->idxKey.default_rc = 0;
tresi->res = sqlite3BtreeRecordCompare(available, zData, &tresi->idxKey);
if (tresi->res != 0)
return false;
if (tresi->idxKey.aMem == &tresi->mem[0] &&
tresi->idxKey.nField == 1 &&
tresi->mem[2].flags != MEM_Null) {
int offset =
zData[0] +
sqlite3BtreeSerialTypeLen(zData[1]) +
sqlite3BtreeSerialTypeLen(zData[2]);
zData+offset;
Mem mem;
sqlite3BtreeSerialGet(zData+offset, zData[3], &mem);
if (mem.u.i != tresi->mem[2].u.i) {
sqlite3BtreeNext(tresi->cursor, &tresi->res);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
return false;
}
continue;
}
}
rc = sqlite3BtreeIdxRowid(sg->pBtree, tresi->cursor, pKey);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
return false;
}
rc = sqlite3BtreeMovetoUnpacked(tresi->tctxt.cursor[3], 0, *pKey, 0, &tresi->res);
} else {
rc = sqlite3BtreeKeySize(tresi->cursor, pKey);
}
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
return false;
}
return true;
}
return false;
}
SgTripleResult*
sg_query_triple(SgSG *sg, SgTriple triple, GuExn* err)
{
int rc;
if (sg->autoCommit) {
rc = sqlite3BtreeBeginTrans(sg->pBtree, 0);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
return NULL;
}
}
SgTripleResult* tres = malloc(sizeof(SgTripleResult));
tres->sg = sg;
tres->triple[0] = triple[0];
tres->triple[1] = triple[1];
tres->triple[2] = triple[2];
rc = open_exprs(sg, 0, false, &tres->ectxt, err);
if (rc != SQLITE_OK)
goto close;
for (int i = 0; i < 3; i++) {
if (gu_variant_is_null(triple[i]))
tres->i.mem[i].flags = MEM_Null;
else {
tres->i.mem[i].flags = MEM_Int;
rc = store_expr(sg, &tres->ectxt, triple[i], &tres->i.mem[i].u.i, 0);
if (rc != SQLITE_OK)
goto close;
if (tres->i.mem[i].u.i == 0) {
tres->i.res = 1;
tres->i.tctxt.n_cursors = 0; // this is important since the triples are not initialized yet
return tres;
}
}
}
rc = triple_result_init(sg, &tres->i, err);
if (rc != SQLITE_OK) {
return NULL;
}
return tres;
close:
close_exprs(&tres->ectxt);
if (sg->autoCommit) {
sqlite3BtreeRollback(sg->pBtree, SQLITE_ABORT_ROLLBACK, 0);
}
free(tres);
return NULL;
}
int
sg_triple_result_fetch(SgTripleResult* tres, SgId* pKey, SgTriple triple,
GuPool* out_pool, GuExn* err)
{
int rc;
triple[0] = tres->triple[0];
triple[1] = tres->triple[1];
triple[2] = tres->triple[2];
bool found =
triple_result_fetch(tres->sg, &tres->i, pKey, err);
if (!found)
return 0;
rc = load_triple(tres->i.tctxt.cursor[3], tres->ectxt.crsExprs,
triple, out_pool);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
return 0;
}
sqlite3BtreeNext(tres->i.cursor, &tres->i.res);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
return 0;
}
return 1;
}
void
sg_triple_result_get_query(SgTripleResult* tres, SgTriple triple)
{
triple[0] = tres->triple[0];
triple[1] = tres->triple[1];
triple[2] = tres->triple[2];
}
void
sg_triple_result_close(SgTripleResult* tres, GuExn* err)
{
close_exprs(&tres->ectxt);
close_triples(&tres->i.tctxt);
if (tres->sg->autoCommit) {
int rc = sqlite3BtreeCommit(tres->sg->pBtree);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
return;
}
}
free(tres);
}
typedef int SgPattern[3];
struct SgQueryResult {
SgSG* sg;
size_t n_vars;
struct {
SgId id;
PgfExpr expr;
}* vars;
size_t n_sels;
int* sels;
ExprContext ectxt;
bool is_empty;
size_t n_results;
size_t n_patterns;
struct {
SgPattern pattern;
SgTripleResultInt result;
} triples[];
};
SgQueryResult*
sg_query(SgSG *sg, size_t n_triples, SgTriple* triples, GuExn* err)
{
int rc;
if (sg->autoCommit) {
rc = sqlite3BtreeBeginTrans(sg->pBtree, 0);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
return NULL;
}
}
SgQueryResult* qres = malloc(GU_FLEX_SIZE(SgQueryResult, triples, n_triples));
qres->sg = sg;
qres->is_empty = false;
qres->n_results = 0;
qres->n_vars = 0;
qres->vars = malloc(sizeof(qres->vars[0])*n_triples*3);
qres->n_sels = 0;
qres->sels = malloc(sizeof(int)*n_triples*3);
rc = open_exprs(sg, 0, false, &qres->ectxt, err);
if (rc != SQLITE_OK)
goto close;
qres->n_patterns = n_triples;
for (size_t i = 0; i < n_triples; i++) {
for (int k = 0; k < 3; k++) {
PgfExpr expr = triples[i][k];
size_t j = 0;
while (j < qres->n_vars) {
if (pgf_expr_eq(expr, qres->vars[j].expr))
break;
j++;
}
if (j >= qres->n_vars) {
qres->vars[j].expr = expr;
if (gu_variant_tag(expr) == PGF_EXPR_META) {
qres->vars[j].id = 0;
qres->sels[qres->n_sels++] = j;
qres->triples[i].result.mem[k].flags = MEM_Null;
} else {
rc = store_expr(sg, &qres->ectxt, expr, &qres->vars[j].id, 0);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
goto close;
}
if (qres->vars[j].id == 0)
goto close;
qres->triples[i].result.mem[k].flags = MEM_Int;
}
qres->n_vars++;
} else {
qres->triples[i].result.mem[k].flags = MEM_Int;
}
qres->triples[i].pattern[k] = j;
}
}
return qres;
close:
qres->is_empty = true;
close_exprs(&qres->ectxt);
if (sg->autoCommit) {
sqlite3BtreeRollback(sg->pBtree, SQLITE_ABORT_ROLLBACK, 0);
}
return qres;
}
size_t
sg_query_result_columns(SgQueryResult* qres) {
return qres->n_sels;
}
static int
load_vars(SgQueryResult* qres, BtCursor* crsTriples, SgPattern pattern)
{
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 += sqlite3BtreeSerialGet(row, serial_type_subj, &mem[0]);
row += sqlite3BtreeSerialGet(row, serial_type_pred, &mem[1]);
row += sqlite3BtreeSerialGet(row, serial_type_obj, &mem[2]);
for (int i = 0; i < 3; i++) {
qres->vars[pattern[i]].id = mem[i].u.i;
}
return SQLITE_OK;
}
int
query_result_next(SgQueryResult* qres, GuExn* err)
{
if (qres->is_empty)
return 0;
int rc;
SgId key;
size_t i = (qres->n_results == 0) ? 0 : qres->n_results-1;
while (i < qres->n_patterns) {
if (i >= qres->n_results) {
for (int k = 0; k < 3; k++) {
qres->triples[i].result.mem[k].u.i = qres->vars[qres->triples[i].pattern[k]].id;
}
rc = triple_result_init(qres->sg, &qres->triples[i].result, err);
if (rc != SQLITE_OK) {
goto close;
}
qres->n_results++;
}
bool found =
triple_result_fetch(qres->sg,
&qres->triples[i].result,
&key, err);
if (gu_exn_is_raised(err)) {
goto close;
}
if (found) {
rc = sqlite3BtreeNext(qres->triples[i].result.cursor, &qres->triples[i].result.res);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
goto close;
}
rc = load_vars(qres, qres->triples[i].result.tctxt.cursor[3], qres->triples[i].pattern);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
goto close;
}
i++;
} else {
close_triples(&qres->triples[i].result.tctxt);
if (i == 0)
goto close;
i--;
qres->n_results--;
}
}
close:
qres->is_empty = true;
return 0;
}
int
sg_query_result_fetch(SgQueryResult* qres, PgfExpr* res,
GuPool* out_pool, GuExn* err)
{
if (!query_result_next(qres, err))
return 0;
for (size_t i = 0; i < qres->n_sels; i++) {
int rc = load_expr(qres->ectxt.crsExprs,
qres->vars[qres->sels[i]].id,
&res[i], out_pool);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
qres->is_empty = true;
return 0;
}
}
return 1;
}
static PgfExpr
instantiate_expr(SgQueryResult* qres, PgfExpr expr,
GuPool* out_pool, GuExn* err)
{
PgfExpr new_expr = gu_null_variant;
GuVariantInfo ei = gu_variant_open(expr);
switch (ei.tag) {
case PGF_EXPR_ABS: {
PgfExprAbs* abs = ei.data;
PgfExprAbs* new_abs =
gu_new_variant(PGF_EXPR_ABS,
PgfExprAbs,
&new_expr, out_pool);
new_abs->bind_type = abs->bind_type;
new_abs->id = gu_string_copy(abs->id, out_pool);
new_abs->body = instantiate_expr(qres, abs->body, out_pool, err);
break;
}
case PGF_EXPR_APP: {
PgfExprApp* app = ei.data;
PgfExprApp* new_app =
gu_new_variant(PGF_EXPR_APP,
PgfExprApp,
&new_expr, out_pool);
new_app->fun = instantiate_expr(qres, app->fun, out_pool, err);
new_app->arg = instantiate_expr(qres, app->arg, out_pool, err);
break;
}
case PGF_EXPR_LIT: {
PgfExprLit* lit = ei.data;
PgfExprLit* new_lit =
gu_new_variant(PGF_EXPR_LIT,
PgfExprLit,
&new_expr, out_pool);
GuVariantInfo i = gu_variant_open(lit->lit);
switch (i.tag) {
case PGF_LITERAL_STR: {
PgfLiteralStr* lstr = i.data;
PgfLiteralStr* new_lstr =
gu_new_flex_variant(PGF_LITERAL_STR,
PgfLiteralStr,
val, strlen(lstr->val)+1,
&new_lit->lit, out_pool);
strcpy(new_lstr->val, lstr->val);
break;
}
case PGF_LITERAL_INT: {
PgfLiteralInt* lint = i.data;
PgfLiteralInt* new_lint =
gu_new_variant(PGF_LITERAL_INT,
PgfLiteralInt,
&new_lit->lit, out_pool);
new_lint->val = lint->val;
break;
}
case PGF_LITERAL_FLT: {
PgfLiteralFlt* lflt = i.data;
PgfLiteralFlt* new_lflt =
gu_new_variant(PGF_LITERAL_FLT,
PgfLiteralFlt,
&new_lit->lit, out_pool);
new_lflt->val = lflt->val;
break;
}
default:
gu_impossible();
}
break;
}
case PGF_EXPR_META: {
new_expr = expr;
size_t j = 0;
while (j < qres->n_vars) {
if (pgf_expr_eq(expr, qres->vars[j].expr)) {
int rc = load_expr(qres->ectxt.crsExprs,
qres->vars[j].id,
&new_expr, out_pool);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
qres->is_empty = true;
return new_expr;
}
break;
}
j++;
}
break;
}
case PGF_EXPR_FUN: {
PgfExprFun* fun = ei.data;
PgfExprFun *new_fun =
gu_new_flex_variant(PGF_EXPR_FUN,
PgfExprFun,
fun, strlen(fun->fun)+1,
&new_expr, out_pool);
strcpy(new_fun->fun, fun->fun);
break;
}
case PGF_EXPR_VAR: {
PgfExprVar* var = ei.data;
PgfExprVar* new_var =
gu_new_variant(PGF_EXPR_VAR,
PgfExprVar,
&new_expr, out_pool);
new_var->var = var->var;
break;
}
case PGF_EXPR_TYPED: {
break;
}
case PGF_EXPR_IMPL_ARG: {
PgfExprImplArg* impl = ei.data;
PgfExprImplArg* new_impl =
gu_new_variant(PGF_EXPR_IMPL_ARG,
PgfExprImplArg,
&new_expr, out_pool);
new_impl->expr = instantiate_expr(qres, impl->expr, out_pool, err);
break;
}
default:
gu_impossible();
}
return new_expr;
}
PgfExpr
sg_query_result_fetch_expr(SgQueryResult* qres, PgfExpr expr,
GuPool* out_pool, GuExn* err)
{
if (!query_result_next(qres, err))
return gu_null_variant;
return instantiate_expr(qres, expr, out_pool, err);
}
void
sg_query_result_close(SgQueryResult* qres, GuExn* err)
{
while (qres->n_results > 0) {
close_triples(&qres->triples[qres->n_results-1].result.tctxt);
qres->n_results--;
}
close_exprs(&qres->ectxt);
if (qres->sg->autoCommit) {
int rc = sqlite3BtreeCommit(qres->sg->pBtree);
if (rc != SQLITE_OK) {
sg_raise_sqlite(rc, err);
return;
}
}
free(qres->vars);
free(qres->sels);
free(qres);
}