summaryrefslogtreecommitdiff
path: root/src/runtime
diff options
context:
space:
mode:
Diffstat (limited to 'src/runtime')
-rw-r--r--src/runtime/c/sg/sg.c466
-rw-r--r--src/runtime/c/sg/sg.h6
-rw-r--r--src/runtime/haskell-bind/PGF2.hsc2
-rw-r--r--src/runtime/haskell-bind/PGF2/FFI.hs3
-rw-r--r--src/runtime/haskell-bind/SG.hsc32
-rw-r--r--src/runtime/haskell-bind/SG/FFI.hs6
6 files changed, 413 insertions, 102 deletions
diff --git a/src/runtime/c/sg/sg.c b/src/runtime/c/sg/sg.c
index ea9199518..355033dba 100644
--- a/src/runtime/c/sg/sg.c
+++ b/src/runtime/c/sg/sg.c
@@ -2,11 +2,13 @@
#include "sqlite3.c"
#include "sg/sg.h"
+#include "pgf/data.h"
#define SG_EXPRS "sg_exprs"
#define SG_PAIRS "sg_pairs"
#define SG_IDENTS "sg_idents"
#define SG_LITERALS "sg_literals"
+#define SG_TOKENS "sg_tokens"
#define SG_TRIPLES "sg_triples"
#define SG_TRIPLES_SPO "sg_triples_spo"
#define SG_TRIPLES_PO "sg_triples_po"
@@ -48,6 +50,7 @@ sg_open(const char *filename,
rc = sqlite3_exec(db, "create table if not exists " SG_EXPRS "(fun not null, arg integer);"
"create unique index if not exists " SG_IDENTS " on " SG_EXPRS "(fun) where arg is null;"
+ "create index if not exists " SG_TOKENS " on " SG_EXPRS "(fun) where arg <> arg;" // actually used for full text search
"create unique index if not exists " SG_LITERALS " on " SG_EXPRS "(fun) where arg = 0;"
"create unique index if not exists " SG_PAIRS " on " SG_EXPRS "(fun,arg) where arg > 0;"
"create table if not exists " SG_TRIPLES "(subj integer, pred integer, obj integer, state integer);"
@@ -123,14 +126,15 @@ sg_rollback(SgSG* sg, GuExn* err)
typedef struct {
int n_cursors;
BtCursor crsExprs;
- BtCursor crsPairs;
BtCursor crsIdents;
BtCursor crsLiterals;
+ BtCursor crsPairs;
SgId key_seed;
} ExprContext;
static int
-open_exprs(sqlite3 *db, int wrFlag, ExprContext* ctxt, GuExn* err)
+open_exprs(sqlite3 *db, int wrFlag, bool identsOnly,
+ ExprContext* ctxt, GuExn* err)
{
ctxt->n_cursors = 0;
@@ -140,12 +144,6 @@ open_exprs(sqlite3 *db, int wrFlag, ExprContext* ctxt, GuExn* err)
sg_raise_err("Table " SG_EXPRS " is missing", err);
return SQLITE_ERROR;
}
-
- Index *pairsIdx = sqlite3HashFind(&db->aDb[0].pSchema->idxHash, SG_PAIRS);
- if (!pairsIdx) {
- sg_raise_err("Index " SG_PAIRS " is missing", err);
- return SQLITE_ERROR;
- }
Index *identsIdx = sqlite3HashFind(&db->aDb[0].pSchema->idxHash, SG_IDENTS);
if (!identsIdx) {
@@ -159,6 +157,12 @@ open_exprs(sqlite3 *db, int wrFlag, ExprContext* ctxt, GuExn* err)
return SQLITE_ERROR;
}
+ Index *pairsIdx = sqlite3HashFind(&db->aDb[0].pSchema->idxHash, SG_PAIRS);
+ if (!pairsIdx) {
+ sg_raise_err("Index " SG_PAIRS " is missing", err);
+ return SQLITE_ERROR;
+ }
+
int rc;
memset(&ctxt->crsExprs, 0, sizeof(ctxt->crsExprs));
@@ -169,15 +173,6 @@ open_exprs(sqlite3 *db, int wrFlag, ExprContext* ctxt, GuExn* err)
}
ctxt->n_cursors++;
- memset(&ctxt->crsPairs, 0, sizeof(ctxt->crsPairs));
- KeyInfo *infPairs = sqlite3KeyInfoAlloc(db, 2, 0);
- rc = sqlite3BtreeCursor(db->aDb[0].pBt, pairsIdx->tnum, wrFlag, infPairs, &ctxt->crsPairs);
- if (rc != SQLITE_OK) {
- sg_raise_sqlite(db, err);
- return rc;
- }
- ctxt->n_cursors++;
-
memset(&ctxt->crsIdents, 0, sizeof(ctxt->crsIdents));
KeyInfo *infIdents = sqlite3KeyInfoAlloc(db, 1, 1);
rc = sqlite3BtreeCursor(db->aDb[0].pBt, identsIdx->tnum, wrFlag, infIdents, &ctxt->crsIdents);
@@ -187,14 +182,25 @@ open_exprs(sqlite3 *db, int wrFlag, ExprContext* ctxt, GuExn* err)
}
ctxt->n_cursors++;
- memset(&ctxt->crsLiterals, 0, sizeof(ctxt->crsLiterals));
- KeyInfo *infLiterals = sqlite3KeyInfoAlloc(db, 1, 1);
- rc = sqlite3BtreeCursor(db->aDb[0].pBt, literalsIdx->tnum, wrFlag, infLiterals, &ctxt->crsLiterals);
- if (rc != SQLITE_OK) {
- sg_raise_sqlite(db, err);
- return rc;
+ if (!identsOnly) {
+ memset(&ctxt->crsLiterals, 0, sizeof(ctxt->crsLiterals));
+ KeyInfo *infLiterals = sqlite3KeyInfoAlloc(db, 1, 1);
+ rc = sqlite3BtreeCursor(db->aDb[0].pBt, literalsIdx->tnum, wrFlag, infLiterals, &ctxt->crsLiterals);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ return rc;
+ }
+ ctxt->n_cursors++;
+
+ memset(&ctxt->crsPairs, 0, sizeof(ctxt->crsPairs));
+ KeyInfo *infPairs = sqlite3KeyInfoAlloc(db, 2, 0);
+ rc = sqlite3BtreeCursor(db->aDb[0].pBt, pairsIdx->tnum, wrFlag, infPairs, &ctxt->crsPairs);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ return rc;
+ }
+ ctxt->n_cursors++;
}
- ctxt->n_cursors++;
if (wrFlag) {
int res;
@@ -220,18 +226,18 @@ static void
close_exprs(ExprContext* ctxt)
{
if (ctxt->n_cursors >= 4) {
- sqlite3KeyInfoUnref(ctxt->crsLiterals.pKeyInfo);
- sqlite3BtreeCloseCursor(&ctxt->crsLiterals);
+ sqlite3KeyInfoUnref(ctxt->crsPairs.pKeyInfo);
+ sqlite3BtreeCloseCursor(&ctxt->crsPairs);
}
if (ctxt->n_cursors >= 3) {
- sqlite3KeyInfoUnref(ctxt->crsIdents.pKeyInfo);
- sqlite3BtreeCloseCursor(&ctxt->crsIdents);
+ sqlite3KeyInfoUnref(ctxt->crsLiterals.pKeyInfo);
+ sqlite3BtreeCloseCursor(&ctxt->crsLiterals);
}
if (ctxt->n_cursors >= 2) {
- sqlite3KeyInfoUnref(ctxt->crsPairs.pKeyInfo);
- sqlite3BtreeCloseCursor(&ctxt->crsPairs);
+ sqlite3KeyInfoUnref(ctxt->crsIdents.pKeyInfo);
+ sqlite3BtreeCloseCursor(&ctxt->crsIdents);
}
if (ctxt->n_cursors >= 1) {
@@ -242,6 +248,83 @@ close_exprs(ExprContext* ctxt)
}
static int
+find_function_rowid(sqlite3* db, ExprContext* ctxt,
+ GuString fun, SgId* pKey, int wrFlag)
+{
+ int rc = SQLITE_OK;
+ int file_format = db->aDb[0].pSchema->file_format;
+
+ Mem mem[2];
+ mem[0].flags = MEM_Str;
+ mem[0].n = strlen(fun);
+ mem[0].z = (void*) fun;
+
+ UnpackedRecord idxKey;
+ idxKey.pKeyInfo = ctxt->crsIdents.pKeyInfo;
+ idxKey.nField = 1;
+ idxKey.default_rc = 0;
+ idxKey.aMem = mem;
+
+ int res = 0;
+ rc = sqlite3BtreeMovetoUnpacked(&ctxt->crsIdents,
+ &idxKey, 0, 0, &res);
+ if (rc != SQLITE_OK) {
+ return rc;
+ }
+
+ if (res == 0) {
+ rc = sqlite3VdbeIdxRowid(db, &ctxt->crsIdents, pKey);
+ } else {
+ if (wrFlag == 0) {
+ *pKey = 0;
+ return SQLITE_OK;
+ }
+
+ *pKey = ++ctxt->key_seed;
+
+ int serial_type_fun = sqlite3VdbeSerialType(&mem[0], file_format);
+ int serial_type_fun_hdr_len = sqlite3VarintLen(serial_type_fun);
+
+ mem[1].flags = MEM_Int;
+ mem[1].u.i = *pKey;
+
+ int serial_type_key = sqlite3VdbeSerialType(&mem[1], file_format);
+ int serial_type_key_hdr_len = sqlite3VarintLen(serial_type_key);
+
+ unsigned char* buf = malloc(1+serial_type_fun_hdr_len+MAX(1,serial_type_key_hdr_len)+mem[0].n+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 += sqlite3VdbeSerialPut(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(sqlite3* db,
ExprContext* ctxt, PgfExpr expr, SgId* pKey, int wrFlag)
{
@@ -439,73 +522,7 @@ store_expr(sqlite3* db,
}
case PGF_EXPR_FUN: {
PgfExprFun* fun = ei.data;
-
- Mem mem[2];
- mem[0].flags = MEM_Str;
- mem[0].n = strlen(fun->fun);
- mem[0].z = fun->fun;
-
- UnpackedRecord idxKey;
- idxKey.pKeyInfo = ctxt->crsIdents.pKeyInfo;
- idxKey.nField = 1;
- idxKey.default_rc = 0;
- idxKey.aMem = mem;
-
- int res = 0;
- rc = sqlite3BtreeMovetoUnpacked(&ctxt->crsIdents,
- &idxKey, 0, 0, &res);
- if (rc != SQLITE_OK) {
- return rc;
- }
-
- if (res == 0) {
- rc = sqlite3VdbeIdxRowid(db, &ctxt->crsIdents, pKey);
- } else {
- if (wrFlag == 0) {
- *pKey = 0;
- return SQLITE_OK;
- }
-
- *pKey = ++ctxt->key_seed;
-
- int serial_type_fun = sqlite3VdbeSerialType(&mem[0], file_format);
- int serial_type_fun_hdr_len = sqlite3VarintLen(serial_type_fun);
-
- mem[1].flags = MEM_Int;
- mem[1].u.i = *pKey;
-
- int serial_type_key = sqlite3VdbeSerialType(&mem[1], file_format);
- int serial_type_key_hdr_len = sqlite3VarintLen(serial_type_key);
-
- unsigned char* buf = malloc(1+serial_type_fun_hdr_len+MAX(1,serial_type_key_hdr_len)+mem[0].n+8);
- unsigned char* p = buf;
- *p++ = 1+serial_type_fun_hdr_len+1;
- p += putVarint32(p, serial_type_fun);
- *p++ = 0;
- memcpy(p, fun->fun, mem[0].n);
- p += mem[0].n;
-
- rc = sqlite3BtreeInsert(&ctxt->crsExprs, 0, *pKey,
- buf, p-buf, 0,
- 0, 0);
- if (rc != SQLITE_OK) {
- goto free;
- }
-
- p = buf;
- *p++ = 1+serial_type_fun_hdr_len+serial_type_key_hdr_len;
- p += putVarint32(p, serial_type_fun);
- p += putVarint32(p, serial_type_key);
- memcpy(p, fun->fun, mem[0].n);
- p += mem[0].n;
- p += sqlite3VdbeSerialPut(p, &mem[1], serial_type_key);
- rc = sqlite3BtreeInsert(&ctxt->crsIdents, buf, p-buf,
- 0, *pKey, 0,
- 0, 0);
-
-free:
- free(buf);
- }
+ rc = find_function_rowid(db, ctxt, fun->fun, pKey, wrFlag);
break;
}
case PGF_EXPR_VAR: {
@@ -545,7 +562,7 @@ sg_insert_expr(SgSG *sg, PgfExpr expr, GuExn* err)
}
ExprContext ctxt;
- rc = open_exprs(db, 1, &ctxt, err);
+ rc = open_exprs(db, 1, false, &ctxt, err);
if (rc != SQLITE_OK)
goto close;
@@ -726,6 +743,255 @@ rollback:
return gu_null_variant;
}
+static int
+insert_token(sqlite3 *db, BtCursor* crsTokens, GuString tok, SgId key)
+{
+ int rc = SQLITE_OK;
+ int file_format = db->aDb[0].pSchema->file_format;
+
+ Mem mem[2];
+ mem[0].flags = MEM_Str;
+ mem[0].n = strlen(tok);
+ mem[0].z = (void*) tok;
+
+ int serial_type_tok = sqlite3VdbeSerialType(&mem[0], file_format);
+ int serial_type_tok_hdr_len = sqlite3VarintLen(serial_type_tok);
+
+ mem[1].flags = MEM_Int;
+ mem[1].u.i = key;
+
+ int serial_type_key = sqlite3VdbeSerialType(&mem[1], file_format);
+ int serial_type_key_hdr_len = sqlite3VarintLen(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 += sqlite3VdbeSerialPut(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(sqlite3 *db, 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(db, crsTokens, ks->token, key);
+ if (rc != SQLITE_OK)
+ return rc;
+ break;
+ }
+ case PGF_SYMBOL_KP: {
+ PgfSymbolKP* kp = sym_i.data;
+ rc = insert_syms(db, crsTokens, kp->default_form, key);
+ if (rc != SQLITE_OK)
+ return rc;
+
+ for (size_t i = 0; i < kp->n_forms; i++) {
+ rc = insert_syms(db, 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;
+ sqlite3 *db = (sqlite3 *) sg;
+
+ if (db->autoCommit) {
+ rc = sqlite3BtreeBeginTrans(db->aDb[0].pBt, 1);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ return;
+ }
+ }
+
+ ExprContext ctxt;
+ rc = open_exprs(db, 1, true, &ctxt, err);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ goto close;
+ }
+
+ Index *tokensIdx = sqlite3HashFind(&db->aDb[0].pSchema->idxHash, SG_TOKENS);
+ if (!tokensIdx) {
+ sg_raise_err("Index " SG_TOKENS " is missing", err);
+ return;
+ }
+
+ rc = sqlite3BtreeClearTable(db->aDb[0].pBt, tokensIdx->tnum, NULL);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ return;
+ }
+
+ BtCursor crsTokens;
+ memset(&crsTokens, 0, sizeof(crsTokens));
+ KeyInfo *infTokens = sqlite3KeyInfoAlloc(db, 1, 1);
+ rc = sqlite3BtreeCursor(db->aDb[0].pBt, tokensIdx->tnum, 1, infTokens, &crsTokens);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, 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(db, &ctxt, cncfun->absfun->name, &key, 1);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, 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(db, &crsTokens, seq->syms, key);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ goto close;
+ }
+ }
+ }
+ }
+
+ if (ctxt.n_cursors >= 3) {
+ sqlite3KeyInfoUnref(crsTokens.pKeyInfo);
+ sqlite3BtreeCloseCursor(&crsTokens);
+ ctxt.n_cursors--;
+ }
+
+ close_exprs(&ctxt);
+
+ if (db->autoCommit) {
+ rc = sqlite3BtreeCommit(db->aDb[0].pBt);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ }
+ }
+
+ return;
+
+close:
+ if (ctxt.n_cursors >= 3) {
+ sqlite3KeyInfoUnref(crsTokens.pKeyInfo);
+ sqlite3BtreeCloseCursor(&crsTokens);
+ ctxt.n_cursors--;
+ }
+
+ close_exprs(&ctxt);
+
+ if (db->autoCommit) {
+ sqlite3BtreeRollback(db->aDb[0].pBt, SQLITE_ABORT_ROLLBACK, 0);
+ }
+}
+
+GuSeq*
+sg_query_linearization(SgSG *sg, GuString tok, GuPool *pool, GuExn* err)
+{
+ int rc;
+ sqlite3 *db = (sqlite3 *) sg;
+
+ Index *tokensIdx = sqlite3HashFind(&db->aDb[0].pSchema->idxHash, SG_TOKENS);
+ if (!tokensIdx) {
+ sg_raise_err("Index " SG_TOKENS " is missing", err);
+ return NULL;
+ }
+
+ BtCursor crsTokens;
+ memset(&crsTokens, 0, sizeof(crsTokens));
+ KeyInfo *infTokens = sqlite3KeyInfoAlloc(db, 1, 1);
+ rc = sqlite3BtreeCursor(db->aDb[0].pBt, tokensIdx->tnum, 1, infTokens, &crsTokens);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ return NULL;
+ }
+
+ Mem mem[1];
+ mem[0].flags = MEM_Str;
+ mem[0].n = strlen(tok);
+ mem[0].z = (void*) tok;
+
+ UnpackedRecord idxKey;
+ idxKey.pKeyInfo = crsTokens.pKeyInfo;
+ idxKey.nField = 1;
+ idxKey.default_rc = 0;
+ idxKey.aMem = mem;
+
+ int res = 0;
+ rc = sqlite3BtreeMovetoUnpacked(&crsTokens,
+ &idxKey, 0, 0, &res);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ return NULL;
+ }
+
+ GuBuf* ids = gu_new_buf(SgId, pool);
+
+ while (res == 0) {
+ SgId key;
+ rc = sqlite3VdbeIdxRowid(db, &crsTokens, &key);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ return NULL;
+ }
+
+ gu_buf_push(ids, SgId, key);
+
+ sqlite3BtreeNext(&crsTokens, &res);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ return NULL;
+ }
+
+ i64 szData;
+ const unsigned char *zData;
+ rc = sqlite3BtreeKeySize(&crsTokens, &szData);
+ if (rc != SQLITE_OK) {
+ sg_raise_sqlite(db, err);
+ return NULL;
+ }
+
+ u32 available = 0;
+ zData = sqlite3BtreeKeyFetch(&crsTokens, &available);
+ if (szData > available)
+ gu_impossible();
+
+ res = sqlite3VdbeRecordCompare(available, zData, &idxKey);
+ if (res != 0)
+ break;
+ }
+
+ return gu_buf_data_seq(ids);
+}
+
typedef struct {
int n_cursors;
BtCursor cursor[4];
@@ -820,7 +1086,7 @@ sg_insert_triple(SgSG *sg, SgTriple triple, GuExn* err)
Mem mem[4];
ExprContext ectxt;
- rc = open_exprs(db, 1, &ectxt, err);
+ rc = open_exprs(db, 1, false, &ectxt, err);
if (rc != SQLITE_OK)
goto close;
@@ -1151,7 +1417,7 @@ sg_query_triple(SgSG *sg, SgTriple triple, GuExn* err)
if (rc != SQLITE_OK)
goto close;
- rc = open_exprs(db, 0, &tres->ectxt, err);
+ rc = open_exprs(db, 0, false, &tres->ectxt, err);
if (rc != SQLITE_OK)
goto close;
diff --git a/src/runtime/c/sg/sg.h b/src/runtime/c/sg/sg.h
index 3bf0cb8c5..6ef57584d 100644
--- a/src/runtime/c/sg/sg.h
+++ b/src/runtime/c/sg/sg.h
@@ -30,6 +30,12 @@ sg_insert_expr(SgSG *sg, PgfExpr expr, GuExn* err);
PgfExpr
sg_get_expr(SgSG *sg, SgId key, GuPool* out_pool, GuExn* err);
+void
+sg_update_fts_index(SgSG* sg, PgfPGF* pgf, GuExn* err);
+
+GuSeq*
+sg_query_linearization(SgSG *sg, GuString tok, GuPool* pool, GuExn* err);
+
typedef PgfExpr SgTriple[3];
diff --git a/src/runtime/haskell-bind/PGF2.hsc b/src/runtime/haskell-bind/PGF2.hsc
index c7b24d680..e88dcc9ce 100644
--- a/src/runtime/haskell-bind/PGF2.hsc
+++ b/src/runtime/haskell-bind/PGF2.hsc
@@ -59,8 +59,6 @@ import Data.Function(on)
-- the foreign pointer in case if the application still has a reference
-- to Concr but has lost its reference to PGF.
-data PGF = PGF {pgf :: Ptr PgfPGF, pgfMaster :: ForeignPtr GuPool}
-data Concr = Concr {concr :: Ptr PgfConcr, concrMaster :: PGF}
type AbsName = String -- ^ Name of abstract syntax
type ConcName = String -- ^ Name of concrete syntax
diff --git a/src/runtime/haskell-bind/PGF2/FFI.hs b/src/runtime/haskell-bind/PGF2/FFI.hs
index c6fc2e2e2..ee26214c7 100644
--- a/src/runtime/haskell-bind/PGF2/FFI.hs
+++ b/src/runtime/haskell-bind/PGF2/FFI.hs
@@ -9,6 +9,9 @@ import Foreign.ForeignPtr
import Control.Exception
import GHC.Ptr
+data PGF = PGF {pgf :: Ptr PgfPGF, pgfMaster :: ForeignPtr GuPool}
+data Concr = Concr {concr :: Ptr PgfConcr, concrMaster :: PGF}
+
------------------------------------------------------------------
-- libgu API
diff --git a/src/runtime/haskell-bind/SG.hsc b/src/runtime/haskell-bind/SG.hsc
index 3f7baa5fd..5ee02b8b2 100644
--- a/src/runtime/haskell-bind/SG.hsc
+++ b/src/runtime/haskell-bind/SG.hsc
@@ -8,6 +8,8 @@ module SG( SG, openSG, closeSG
, beginTrans, commit, rollback, inTransaction
, SgId
, insertExpr, getExpr
+ , updateFtsIndex
+ , queryLinearization
, readTriple, showTriple
, insertTriple, getTriple
, queryTriple
@@ -106,6 +108,36 @@ getExpr (SG sg) id = do
return Nothing
else do return $ Just (Expr c_expr exprFPl)
+updateFtsIndex :: SG -> PGF -> IO ()
+updateFtsIndex (SG sg) p = do
+ withGuPool $ \tmpPl -> do
+ exn <- gu_new_exn tmpPl
+ sg_update_fts_index sg (pgf p) exn
+ handle_sg_exn exn
+
+queryLinearization :: SG -> String -> IO [Expr]
+queryLinearization (SG sg) query = do
+ exprPl <- gu_new_pool
+ exprFPl <- newForeignPtr gu_pool_finalizer exprPl
+ (withCString query $ \c_query ->
+ withGuPool $ \tmpPl -> do
+ exn <- gu_new_exn tmpPl
+ seq <- sg_query_linearization sg c_query tmpPl exn
+ handle_sg_exn exn
+ len <- (#peek GuSeq, len) seq
+ ids <- peekArray (fromIntegral (len :: CInt)) (seq `plusPtr` (#offset GuSeq, data))
+ getExprs exprFPl exprPl exn ids)
+ where
+ getExprs exprFPl exprPl exn [] = return []
+ getExprs exprFPl exprPl exn (id:ids) = do
+ c_expr <- sg_get_expr sg id exprPl exn
+ handle_sg_exn exn
+ if c_expr == nullPtr
+ then getExprs exprFPl exprPl exn ids
+ else do let e = Expr c_expr exprFPl
+ es <- getExprs exprFPl exprPl exn ids
+ return (e:es)
+
-----------------------------------------------------------------------
-- Triples
diff --git a/src/runtime/haskell-bind/SG/FFI.hs b/src/runtime/haskell-bind/SG/FFI.hs
index 37c7f8c3a..69e3efe3f 100644
--- a/src/runtime/haskell-bind/SG/FFI.hs
+++ b/src/runtime/haskell-bind/SG/FFI.hs
@@ -32,6 +32,12 @@ foreign import ccall "sg/sg.h sg_insert_expr"
foreign import ccall "sg/sg.h sg_get_expr"
sg_get_expr :: Ptr SgSG -> SgId -> Ptr GuPool -> Ptr GuExn -> IO PgfExpr
+foreign import ccall "sg/sg.h sg_update_fts_index"
+ sg_update_fts_index :: Ptr SgSG -> Ptr PgfPGF -> Ptr GuExn -> IO ()
+
+foreign import ccall "sg/sg.h sg_query_linearization"
+ sg_query_linearization :: Ptr SgSG -> CString -> Ptr GuPool -> Ptr GuExn -> IO (Ptr GuSeq)
+
foreign import ccall "sg/sg.h sg_insert_triple"
sg_insert_triple :: Ptr SgSG -> SgTriple -> Ptr GuExn -> IO SgId