forked from GitHub/gf-core
2404 lines
52 KiB
C
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);
|
|
}
|