added a primitive full-text search index in libsg. This can be use for finding an abstract tree whose linearization matches given keywords

This commit is contained in:
krasimir
2015-12-20 14:04:52 +00:00
parent 0b9395fd70
commit bef9d8c5fc
6 changed files with 413 additions and 102 deletions

View File

@@ -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,20 +226,20 @@ static void
close_exprs(ExprContext* ctxt)
{
if (ctxt->n_cursors >= 4) {
sqlite3KeyInfoUnref(ctxt->crsPairs.pKeyInfo);
sqlite3BtreeCloseCursor(&ctxt->crsPairs);
}
if (ctxt->n_cursors >= 3) {
sqlite3KeyInfoUnref(ctxt->crsLiterals.pKeyInfo);
sqlite3BtreeCloseCursor(&ctxt->crsLiterals);
}
if (ctxt->n_cursors >= 3) {
if (ctxt->n_cursors >= 2) {
sqlite3KeyInfoUnref(ctxt->crsIdents.pKeyInfo);
sqlite3BtreeCloseCursor(&ctxt->crsIdents);
}
if (ctxt->n_cursors >= 2) {
sqlite3KeyInfoUnref(ctxt->crsPairs.pKeyInfo);
sqlite3BtreeCloseCursor(&ctxt->crsPairs);
}
if (ctxt->n_cursors >= 1) {
sqlite3BtreeCloseCursor(&ctxt->crsExprs);
}
@@ -241,6 +247,83 @@ close_exprs(ExprContext* ctxt)
ctxt->n_cursors = 0;
}
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;

View File

@@ -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];

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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