[libdbd-sqlite3-perl] 05/43: split dbdimp.c and move tokenizer/virtual table-related code into .inc files
Salvatore Bonaccorso
carnil at debian.org
Tue Nov 15 15:27:38 UTC 2016
This is an automated email from the git hooks/post-receive script.
carnil pushed a commit to branch master
in repository libdbd-sqlite3-perl.
commit e9ae705fb67f51a5550ede09cf443d596b869ae1
Author: Kenichi Ishigaki <ishigaki at cpan.org>
Date: Tue Feb 16 12:23:09 2016 +0900
split dbdimp.c and move tokenizer/virtual table-related code into .inc files
---
MANIFEST | 2 +
dbdimp.c | 1131 +---------------------------------------------
dbdimp_tokenizer.inc | 289 ++++++++++++
dbdimp_virtual_table.inc | 835 ++++++++++++++++++++++++++++++++++
4 files changed, 1128 insertions(+), 1129 deletions(-)
diff --git a/MANIFEST b/MANIFEST
index 145f8c9..8845088 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -3,6 +3,8 @@ Changes
constants.inc
dbdimp.c
dbdimp.h
+dbdimp_tokenizer.inc
+dbdimp_virtual_table.inc
fts3_tokenizer.h
inc/Test/NoWarnings.pm
inc/Test/NoWarnings/Warning.pm
diff --git a/dbdimp.c b/dbdimp.c
index 6a6924b..d01f728 100644
--- a/dbdimp.c
+++ b/dbdimp.c
@@ -2623,1134 +2623,7 @@ sqlite_db_backup_to_file(pTHX_ SV *dbh, char *filename)
#endif
}
-typedef struct perl_tokenizer {
- sqlite3_tokenizer base;
- SV *coderef; /* the perl tokenizer is a coderef that takes
- a string and returns a cursor coderef */
-} perl_tokenizer;
-
-typedef struct perl_tokenizer_cursor {
- sqlite3_tokenizer_cursor base;
- SV *coderef; /* ref to the closure that returns terms */
- char *pToken; /* storage for a copy of the last token */
- int nTokenAllocated; /* space allocated to pToken buffer */
-
- /* members below are only used if the input string is in utf8 */
- const char *pInput; /* input we are tokenizing */
- const char *lastByteOffset; /* offset into pInput */
- int lastCharOffset; /* char offset corresponding to lastByteOffset */
-} perl_tokenizer_cursor;
-
-/*
-** Create a new tokenizer instance.
-** Will be called whenever a FTS3 table is created with
-** CREATE .. USING fts3( ... , tokenize=perl qualified::function::name)
-** where qualified::function::name is a fully qualified perl function
-*/
-static int perl_tokenizer_Create(
- int argc, const char * const *argv,
- sqlite3_tokenizer **ppTokenizer
-){
- dTHX;
- dSP;
- int n_retval;
- SV *retval;
- perl_tokenizer *t;
-
- if (!argc) {
- return SQLITE_ERROR;
- }
-
- t = (perl_tokenizer *) sqlite3_malloc(sizeof(*t));
- if( t==NULL ) return SQLITE_NOMEM;
- memset(t, 0, sizeof(*t));
-
- ENTER;
- SAVETMPS;
-
- /* call the qualified::function::name */
- PUSHMARK(SP);
- PUTBACK;
- n_retval = call_pv(argv[0], G_SCALAR);
- SPAGAIN;
-
- /* store a copy of the returned coderef into the tokenizer structure */
- if (n_retval != 1) {
- warn("tokenizer_Create returned %d arguments", n_retval);
- }
- retval = POPs;
- t->coderef = newSVsv(retval);
- *ppTokenizer = &t->base;
-
- PUTBACK;
- FREETMPS;
- LEAVE;
-
- return SQLITE_OK;
-}
-
-/*
-** Destroy a tokenizer
-*/
-static int perl_tokenizer_Destroy(sqlite3_tokenizer *pTokenizer){
- dTHX;
- perl_tokenizer *t = (perl_tokenizer *) pTokenizer;
- sv_free(t->coderef);
- sqlite3_free(t);
- return SQLITE_OK;
-}
-
-/*
-** Prepare to begin tokenizing a particular string. The input
-** string to be tokenized is supposed to be pInput[0..nBytes-1] ..
-** except that nBytes passed by fts3 is -1 (don't know why) !
-** This is passed to the tokenizer instance, which then returns a
-** closure implementing the cursor (so the cursor is again a coderef).
-*/
-static int perl_tokenizer_Open(
- sqlite3_tokenizer *pTokenizer, /* Tokenizer object */
- const char *pInput, int nBytes, /* Input buffer */
- sqlite3_tokenizer_cursor **ppCursor /* OUT: Created tokenizer cursor */
-){
- dTHX;
- dSP;
- dMY_CXT;
- U32 flags;
- SV *perl_string;
- int n_retval;
-
- perl_tokenizer *t = (perl_tokenizer *)pTokenizer;
-
- /* allocate and initialize the cursor struct */
- perl_tokenizer_cursor *c;
- c = (perl_tokenizer_cursor *) sqlite3_malloc(sizeof(*c));
- memset(c, 0, sizeof(*c));
- *ppCursor = &c->base;
-
- /* flags for creating the Perl SV containing the input string */
- flags = SVs_TEMP; /* will call sv_2mortal */
-
- /* special handling if working with utf8 strings */
- if (MY_CXT.last_dbh_is_unicode) {
-
- /* data to keep track of byte offsets */
- c->lastByteOffset = c->pInput = pInput;
- c->lastCharOffset = 0;
-
- /* string passed to Perl needs to be flagged as utf8 */
- flags |= SVf_UTF8;
- }
-
- ENTER;
- SAVETMPS;
-
- /* build a Perl copy of the input string */
- if (nBytes < 0) { /* we get -1 from fts3. Don't know why ! */
- nBytes = strlen(pInput);
- }
- perl_string = newSVpvn_flags(pInput, nBytes, flags);
-
- /* call the tokenizer coderef */
- PUSHMARK(SP);
- XPUSHs(perl_string);
- PUTBACK;
- n_retval = call_sv(t->coderef, G_SCALAR);
- SPAGAIN;
-
- /* store the cursor coderef returned by the tokenizer */
- if (n_retval != 1) {
- warn("tokenizer returned %d arguments", n_retval);
- }
- c->coderef = newSVsv(POPs);
-
- PUTBACK;
- FREETMPS;
- LEAVE;
- return SQLITE_OK;
-}
-
-/*
-** Close a tokenization cursor previously opened by a call to
-** perl_tokenizer_Open() above.
-*/
-static int perl_tokenizer_Close(sqlite3_tokenizer_cursor *pCursor){
- perl_tokenizer_cursor *c = (perl_tokenizer_cursor *) pCursor;
-
- dTHX;
- sv_free(c->coderef);
- if (c->pToken) sqlite3_free(c->pToken);
- sqlite3_free(c);
- return SQLITE_OK;
-}
-
-/*
-** Extract the next token from a tokenization cursor. The cursor must
-** have been opened by a prior call to perl_tokenizer_Open().
-*/
-static int perl_tokenizer_Next(
- sqlite3_tokenizer_cursor *pCursor, /* Cursor returned by perl_tokenizer_Open */
- const char **ppToken, /* OUT: *ppToken is the token text */
- int *pnBytes, /* OUT: Number of bytes in token */
- int *piStartOffset, /* OUT: Starting offset of token */
- int *piEndOffset, /* OUT: Ending offset of token */
- int *piPosition /* OUT: Position integer of token */
-){
- perl_tokenizer_cursor *c = (perl_tokenizer_cursor *) pCursor;
- int result;
- int n_retval;
- char *token;
- char *byteOffset;
- STRLEN n_a; /* this is required for older perls < 5.8.8 */
- I32 hop;
-
- dTHX;
- dSP;
-
- ENTER;
- SAVETMPS;
-
- /* call the cursor */
- PUSHMARK(SP);
- PUTBACK;
- n_retval = call_sv(c->coderef, G_ARRAY);
- SPAGAIN;
-
- /* if we get back an empty list, there is no more token */
- if (n_retval == 0) {
- result = SQLITE_DONE;
- }
- /* otherwise, get token details from the return list */
- else {
- if (n_retval != 5) {
- warn("tokenizer cursor returned %d arguments", n_retval);
- }
- *piPosition = POPi;
- *piEndOffset = POPi;
- *piStartOffset = POPi;
- *pnBytes = POPi;
- token = POPpx;
-
- if (c->pInput) { /* if working with utf8 data */
-
- /* recompute *pnBytes in bytes, not in chars */
- *pnBytes = strlen(token);
-
- /* recompute start/end offsets in bytes, not in chars */
- hop = *piStartOffset - c->lastCharOffset;
- byteOffset = (char*)utf8_hop((U8*)c->lastByteOffset, hop);
- hop = *piEndOffset - *piStartOffset;
- *piStartOffset = byteOffset - c->pInput;
- byteOffset = (char*)utf8_hop((U8*)byteOffset, hop);
- *piEndOffset = byteOffset - c->pInput;
-
- /* remember where we are for next round */
- c->lastCharOffset = *piEndOffset,
- c->lastByteOffset = byteOffset;
- }
-
- /* make sure we have enough storage for copying the token */
- if (*pnBytes > c->nTokenAllocated ){
- char *pNew;
- c->nTokenAllocated = *pnBytes + 20;
- pNew = sqlite3_realloc(c->pToken, c->nTokenAllocated);
- if( !pNew ) return SQLITE_NOMEM;
- c->pToken = pNew;
- }
-
- /* need to copy the token into the C cursor before perl frees that
- memory */
- memcpy(c->pToken, token, *pnBytes);
- *ppToken = c->pToken;
-
- result = SQLITE_OK;
- }
-
- PUTBACK;
- FREETMPS;
- LEAVE;
-
- return result;
-}
-
-/*
-** The set of routines that implement the perl tokenizer
-*/
-sqlite3_tokenizer_module perl_tokenizer_Module = {
- 0,
- perl_tokenizer_Create,
- perl_tokenizer_Destroy,
- perl_tokenizer_Open,
- perl_tokenizer_Close,
- perl_tokenizer_Next
-};
-
-/*
-** Register the perl tokenizer with FTS3
-*/
-int sqlite_db_register_fts3_perl_tokenizer(pTHX_ SV *dbh)
-{
- D_imp_dbh(dbh);
-
- int rc;
- sqlite3_stmt *pStmt;
- const char zSql[] = "SELECT fts3_tokenizer(?, ?)";
- sqlite3_tokenizer_module *p = &perl_tokenizer_Module;
-
- if (!DBIc_ACTIVE(imp_dbh)) {
- sqlite_error(dbh, -2, "attempt to register fts3 tokenizer on inactive database handle");
- return FALSE;
- }
-
- rc = sqlite3_prepare_v2(imp_dbh->db, zSql, -1, &pStmt, 0);
- if( rc!=SQLITE_OK ){
- return rc;
- }
-
- sqlite3_bind_text(pStmt, 1, "perl", -1, SQLITE_STATIC);
- sqlite3_bind_blob(pStmt, 2, &p, sizeof(p), SQLITE_STATIC);
- sqlite3_step(pStmt);
-
- return sqlite3_finalize(pStmt);
-}
-
-
-
-/***********************************************************************
-** The set of routines that implement the perl "module"
-** (i.e support for virtual tables written in Perl)
-************************************************************************/
-
-typedef struct perl_vtab {
- sqlite3_vtab base;
- SV *perl_vtab_obj;
- HV *functions;
-} perl_vtab;
-
-typedef struct perl_vtab_cursor {
- sqlite3_vtab_cursor base;
- SV *perl_cursor_obj;
-} perl_vtab_cursor;
-
-typedef struct perl_vtab_init {
- SV *dbh;
- const char *perl_class;
-} perl_vtab_init;
-
-
-
-/* auxiliary routine for generalized method calls. Arg "i" may be unused */
-static int _call_perl_vtab_method(sqlite3_vtab *pVTab,
- const char *method, int i) {
- dTHX;
- dSP;
- int count;
-
- ENTER;
- SAVETMPS;
-
- PUSHMARK(SP);
- XPUSHs(((perl_vtab *) pVTab)->perl_vtab_obj);
- XPUSHs(sv_2mortal(newSViv(i)));
- PUTBACK;
- count = call_method (method, G_VOID);
- SPAGAIN;
- SP -= count;
-
- PUTBACK;
- FREETMPS;
- LEAVE;
-
- return SQLITE_OK;
-}
-
-
-
-static int perl_vt_New(const char *method,
- sqlite3 *db, void *pAux,
- int argc, const char *const *argv,
- sqlite3_vtab **ppVTab, char **pzErr){
- dTHX;
- dSP;
- perl_vtab *vt;
- perl_vtab_init *init_data = (perl_vtab_init *)pAux;
- int count, i;
- int rc = SQLITE_ERROR;
- SV *perl_vtab_obj;
- SV *sql;
-
- /* allocate a perl_vtab structure */
- vt = (perl_vtab *) sqlite3_malloc(sizeof(*vt));
- if( vt==NULL ) return SQLITE_NOMEM;
- memset(vt, 0, sizeof(*vt));
- vt->functions = newHV();
-
- ENTER;
- SAVETMPS;
-
- /* call the ->CREATE/CONNECT() method */
- PUSHMARK(SP);
- XPUSHs(sv_2mortal(newSVpv(init_data->perl_class, 0)));
- XPUSHs(init_data->dbh);
- for(i = 0; i < argc; i++) {
- XPUSHs(newSVpvn_flags(argv[i], strlen(argv[i]), SVs_TEMP|SVf_UTF8));
- }
- PUTBACK;
- count = call_method (method, G_SCALAR);
- SPAGAIN;
-
- /* check the return value */
- if ( count != 1 ) {
- *pzErr = sqlite3_mprintf("vtab->%s() should return one value, got %d",
- method, count );
- SP -= count; /* Clear the stack */
- goto cleanup;
- }
-
- /* get the VirtualTable instance */
- perl_vtab_obj = POPs;
- if ( !sv_isobject(perl_vtab_obj) ) {
- *pzErr = sqlite3_mprintf("vtab->%s() should return a blessed reference",
- method);
- goto cleanup;
- }
-
- /* call the ->VTAB_TO_DECLARE() method */
- PUSHMARK(SP);
- XPUSHs(perl_vtab_obj);
- PUTBACK;
- count = call_method ("VTAB_TO_DECLARE", G_SCALAR);
- SPAGAIN;
-
- /* check the return value */
- if (count != 1 ) {
- *pzErr = sqlite3_mprintf("vtab->VTAB_TO_DECLARE() should return one value, got %d",
- count );
- SP -= count; /* Clear the stack */
- goto cleanup;
- }
-
- /* call sqlite3_declare_vtab with the sql returned from
- method VTAB_TO_DECLARE(), converted to utf8 */
- sql = POPs;
- rc = sqlite3_declare_vtab(db, SvPVutf8_nolen(sql));
-
- cleanup:
- if (rc == SQLITE_OK) {
- /* record the VirtualTable perl instance within the vtab structure */
- vt->perl_vtab_obj = SvREFCNT_inc(perl_vtab_obj);
- *ppVTab = &vt->base;
- }
- else {
- sqlite3_free(vt);
- }
-
- PUTBACK;
- FREETMPS;
- LEAVE;
-
- return rc;
-}
-
-
-static int perl_vt_Create(sqlite3 *db, void *pAux,
- int argc, const char *const *argv,
- sqlite3_vtab **ppVTab, char **pzErr){
- return perl_vt_New("CREATE", db, pAux, argc, argv, ppVTab, pzErr);
-}
-
-static int perl_vt_Connect(sqlite3 *db, void *pAux,
- int argc, const char *const *argv,
- sqlite3_vtab **ppVTab, char **pzErr){
- return perl_vt_New("CONNECT", db, pAux, argc, argv, ppVTab, pzErr);
-}
-
-
-static int _free_perl_vtab(perl_vtab *pVTab){
- dTHX;
-
- SvREFCNT_dec(pVTab->perl_vtab_obj);
-
- /* deallocate coderefs that were declared through FindFunction() */
- hv_undef(pVTab->functions);
- SvREFCNT_dec(pVTab->functions);
-
- sqlite3_free(pVTab);
- return SQLITE_OK;
-}
-
-static int perl_vt_Disconnect(sqlite3_vtab *pVTab){
- _call_perl_vtab_method(pVTab, "DISCONNECT", 0);
- return _free_perl_vtab((perl_vtab *)pVTab);
-}
-
-static int perl_vt_Drop(sqlite3_vtab *pVTab){
- _call_perl_vtab_method(pVTab, "DROP", 0);
- return _free_perl_vtab((perl_vtab *)pVTab);
-}
-
-
-static char *
-_constraint_op_to_string(unsigned char op) {
- switch (op) {
- case SQLITE_INDEX_CONSTRAINT_EQ:
- return "=";
- case SQLITE_INDEX_CONSTRAINT_GT:
- return ">";
- case SQLITE_INDEX_CONSTRAINT_GE:
- return ">=";
- case SQLITE_INDEX_CONSTRAINT_LT:
- return "<";
- case SQLITE_INDEX_CONSTRAINT_LE:
- return "<=";
- case SQLITE_INDEX_CONSTRAINT_MATCH:
- return "MATCH";
-#if SQLITE_VERSION_NUMBER >= 3010000
- case SQLITE_INDEX_CONSTRAINT_LIKE:
- return "LIKE";
- case SQLITE_INDEX_CONSTRAINT_GLOB:
- return "GLOB";
- case SQLITE_INDEX_CONSTRAINT_REGEXP:
- return "REGEXP";
-#endif
- default:
- return "unknown";
- }
-}
-
-
-static int perl_vt_BestIndex(sqlite3_vtab *pVTab, sqlite3_index_info *pIdxInfo){
- dTHX;
- dSP;
- int i, count;
- int argvIndex;
- AV *constraints;
- AV *order_by;
- SV *hashref;
- SV **val;
- HV *hv;
- struct sqlite3_index_constraint_usage *pConsUsage;
-
- ENTER;
- SAVETMPS;
-
- /* build the "where_constraints" datastructure */
- constraints = newAV();
- for (i=0; i<pIdxInfo->nConstraint; i++){
- struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[i];
- HV *constraint = newHV();
- char *op_str = _constraint_op_to_string(pCons->op);
- hv_stores(constraint, "col", newSViv(pCons->iColumn));
- hv_stores(constraint, "op", newSVpv(op_str, 0));
- hv_stores(constraint, "usable", pCons->usable ? &PL_sv_yes : &PL_sv_no);
- av_push(constraints, newRV_noinc((SV*) constraint));
- }
-
- /* build the "order_by" datastructure */
- order_by = newAV();
- for (i=0; i<pIdxInfo->nOrderBy; i++){
- struct sqlite3_index_orderby const *pOrder = &pIdxInfo->aOrderBy[i];
- HV *order = newHV();
- hv_stores(order, "col", newSViv(pOrder->iColumn));
- hv_stores(order, "desc", pOrder->desc ? &PL_sv_yes : &PL_sv_no);
- av_push( order_by, newRV_noinc((SV*) order));
- }
-
- /* call the ->BEST_INDEX() method */
- PUSHMARK(SP);
- XPUSHs( ((perl_vtab *) pVTab)->perl_vtab_obj);
- XPUSHs( sv_2mortal( newRV_noinc((SV*) constraints)));
- XPUSHs( sv_2mortal( newRV_noinc((SV*) order_by)));
- PUTBACK;
- count = call_method ("BEST_INDEX", G_SCALAR);
- SPAGAIN;
-
- /* get values back from the returned hashref */
- if (count != 1)
- croak("BEST_INDEX() method returned %d vals instead of 1", count);
- hashref = POPs;
- if (!(hashref && SvROK(hashref) && SvTYPE(SvRV(hashref)) == SVt_PVHV))
- croak("BEST_INDEX() method did not return a hashref");
- hv = (HV*)SvRV(hashref);
- val = hv_fetch(hv, "idxNum", 6, FALSE);
- pIdxInfo->idxNum = (val && SvOK(*val)) ? SvIV(*val) : 0;
- val = hv_fetch(hv, "idxStr", 6, FALSE);
- if (val && SvOK(*val)) {
- STRLEN len;
- char *str = SvPVutf8(*val, len);
- pIdxInfo->idxStr = sqlite3_malloc(len+1);
- memcpy(pIdxInfo->idxStr, str, len);
- pIdxInfo->idxStr[len] = 0;
- pIdxInfo->needToFreeIdxStr = 1;
- }
- val = hv_fetch(hv, "orderByConsumed", 15, FALSE);
- pIdxInfo->orderByConsumed = (val && SvTRUE(*val)) ? 1 : 0;
- val = hv_fetch(hv, "estimatedCost", 13, FALSE);
- pIdxInfo->estimatedCost = (val && SvOK(*val)) ? SvNV(*val) : 0;
-#if SQLITE_VERSION_NUMBER >= 3008002
- val = hv_fetch(hv, "estimatedRows", 13, FALSE);
- pIdxInfo->estimatedRows = (val && SvOK(*val)) ? SvIV(*val) : 0;
-#endif
-
- /* loop over constraints to get back the "argvIndex" and "omit" keys
- that shoud have been added by the best_index() method call */
- for (i=0; i<pIdxInfo->nConstraint; i++){
- SV **rv = av_fetch(constraints, i, FALSE);
- if (!(rv && SvROK(*rv) && SvTYPE(SvRV(*rv)) == SVt_PVHV))
- croak("the call to BEST_INDEX() has corrupted constraint data");
- hv = (HV*)SvRV(*rv);
- val = hv_fetch(hv, "argvIndex", 9, FALSE);
- argvIndex = (val && SvOK(*val)) ? SvIV(*val) + 1: 0;
-
- pConsUsage = &pIdxInfo->aConstraintUsage[i];
- pConsUsage->argvIndex = argvIndex;
- val = hv_fetch(hv, "omit", 4, FALSE);
- pConsUsage->omit = (val && SvTRUE(*val)) ? 1 : 0;
- }
-
- PUTBACK;
- FREETMPS;
- LEAVE;
-
- return SQLITE_OK;
-}
-
-
-
-static int perl_vt_Open(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
- dTHX;
- dSP;
- int count;
- int rc = SQLITE_ERROR;
- SV *perl_cursor;
- perl_vtab_cursor *cursor;
-
- ENTER;
- SAVETMPS;
-
- /* allocate a perl_vtab_cursor structure */
- cursor = (perl_vtab_cursor *) sqlite3_malloc(sizeof(*cursor));
- if( cursor==NULL ) return SQLITE_NOMEM;
- memset(cursor, 0, sizeof(*cursor));
-
- /* call the ->OPEN() method */
- PUSHMARK(SP);
- XPUSHs( ((perl_vtab *) pVTab)->perl_vtab_obj);
- PUTBACK;
- count = call_method ("OPEN", G_SCALAR);
- SPAGAIN;
- if (count != 1) {
- warn("vtab->OPEN() method returned %d vals instead of 1", count);
- SP -= count;
- goto cleanup;
-
- }
- perl_cursor = POPs;
- if ( !sv_isobject(perl_cursor) ) {
- warn("vtab->OPEN() method did not return a blessed cursor");
- goto cleanup;
- }
-
- /* everything went OK */
- rc = SQLITE_OK;
-
- cleanup:
-
- if (rc == SQLITE_OK) {
- cursor->perl_cursor_obj = SvREFCNT_inc(perl_cursor);
- *ppCursor = &cursor->base;
- }
- else {
- sqlite3_free(cursor);
- }
-
- PUTBACK;
- FREETMPS;
- LEAVE;
-
- return rc;
-}
-
-static int perl_vt_Close(sqlite3_vtab_cursor *pVtabCursor){
- dTHX;
- dSP;
- perl_vtab_cursor *perl_pVTabCursor;
-
- ENTER;
- SAVETMPS;
-
- /* Note : there is no explicit call to a CLOSE() method; if
- needed, the Perl class can implement a DESTROY() method */
-
- perl_pVTabCursor = (perl_vtab_cursor *) pVtabCursor;
- SvREFCNT_dec(perl_pVTabCursor->perl_cursor_obj);
- sqlite3_free(perl_pVTabCursor);
-
- PUTBACK;
- FREETMPS;
- LEAVE;
-
- return SQLITE_OK;
-}
-
-static int perl_vt_Filter( sqlite3_vtab_cursor *pVtabCursor,
- int idxNum, const char *idxStr,
- int argc, sqlite3_value **argv ){
- dTHX;
- dSP;
- dMY_CXT;
- int i, count;
- int is_unicode = MY_CXT.last_dbh_is_unicode;
-
- ENTER;
- SAVETMPS;
-
- /* call the FILTER() method with ($idxNum, $idxStr, @args) */
- PUSHMARK(SP);
- XPUSHs(((perl_vtab_cursor *) pVtabCursor)->perl_cursor_obj);
- XPUSHs(sv_2mortal(newSViv(idxNum)));
- XPUSHs(sv_2mortal(newSVpv(idxStr, 0)));
- for(i = 0; i < argc; i++) {
- XPUSHs(stacked_sv_from_sqlite3_value(aTHX_ argv[i], is_unicode));
- }
- PUTBACK;
- count = call_method("FILTER", G_VOID);
- SPAGAIN;
- SP -= count;
-
- PUTBACK;
- FREETMPS;
- LEAVE;
-
- return SQLITE_OK;
-}
-
-
-static int perl_vt_Next(sqlite3_vtab_cursor *pVtabCursor){
- dTHX;
- dSP;
- int count;
-
- ENTER;
- SAVETMPS;
-
- /* call the next() method */
- PUSHMARK(SP);
- XPUSHs(((perl_vtab_cursor *) pVtabCursor)->perl_cursor_obj);
- PUTBACK;
- count = call_method ("NEXT", G_VOID);
- SPAGAIN;
- SP -= count;
-
- PUTBACK;
- FREETMPS;
- LEAVE;
-
- return SQLITE_OK;
-}
-
-static int perl_vt_Eof(sqlite3_vtab_cursor *pVtabCursor){
- dTHX;
- dSP;
- int count, eof;
-
- ENTER;
- SAVETMPS;
-
- /* call the eof() method */
- PUSHMARK(SP);
- XPUSHs(((perl_vtab_cursor *) pVtabCursor)->perl_cursor_obj);
- PUTBACK;
- count = call_method ("EOF", G_SCALAR);
- SPAGAIN;
- if (count != 1) {
- warn("cursor->EOF() method returned %d vals instead of 1", count);
- SP -= count;
- }
- else {
- SV *sv = POPs; /* need 2 lines, because this doesn't work : */
- eof = SvTRUE(sv); /* eof = SvTRUE(POPs); # I don't understand why :-( */
- }
-
- PUTBACK;
- FREETMPS;
- LEAVE;
-
- return eof;
-}
-
-
-static int perl_vt_Column(sqlite3_vtab_cursor *pVtabCursor,
- sqlite3_context* context,
- int col){
- dTHX;
- dSP;
- int count;
- int rc = SQLITE_ERROR;
-
- ENTER;
- SAVETMPS;
-
- /* call the column() method */
- PUSHMARK(SP);
- XPUSHs(((perl_vtab_cursor *) pVtabCursor)->perl_cursor_obj);
- XPUSHs(sv_2mortal(newSViv(col)));
- PUTBACK;
- count = call_method ("COLUMN", G_SCALAR);
- SPAGAIN;
- if (count != 1) {
- warn("cursor->COLUMN() method returned %d vals instead of 1", count);
- SP -= count;
- sqlite3_result_error(context, "column error", 12);
- }
- else {
- SV *result = POPs;
- sqlite_set_result(aTHX_ context, result, 0 );
- rc = SQLITE_OK;
- }
-
- PUTBACK;
- FREETMPS;
- LEAVE;
-
- return rc;
-}
-
-static int perl_vt_Rowid( sqlite3_vtab_cursor *pVtabCursor,
- sqlite3_int64 *pRowid ){
- dTHX;
- dSP;
- int count;
- int rc = SQLITE_ERROR;
-
- ENTER;
- SAVETMPS;
-
- /* call the rowid() method */
- PUSHMARK(SP);
- XPUSHs(((perl_vtab_cursor *) pVtabCursor)->perl_cursor_obj);
- PUTBACK;
- count = call_method ("ROWID", G_SCALAR);
- SPAGAIN;
- if (count != 1) {
- warn("cursor->ROWID() returned %d vals instead of 1", count);
- SP -= count;
- }
- else {
- *pRowid =POPi;
- rc = SQLITE_OK;
- }
-
- PUTBACK;
- FREETMPS;
- LEAVE;
-
- return rc;
-}
-
-static int perl_vt_Update( sqlite3_vtab *pVTab,
- int argc, sqlite3_value **argv,
- sqlite3_int64 *pRowid ){
- dTHX;
- dSP;
- dMY_CXT;
- int count, i;
- int is_unicode = MY_CXT.last_dbh_is_unicode;
- int rc = SQLITE_ERROR;
- SV *rowidsv;
-
- ENTER;
- SAVETMPS;
-
- /* call the _SQLITE_UPDATE() method */
- PUSHMARK(SP);
- XPUSHs(((perl_vtab *) pVTab)->perl_vtab_obj);
- for(i = 0; i < argc; i++) {
- XPUSHs(stacked_sv_from_sqlite3_value(aTHX_ argv[i], is_unicode));
- }
- PUTBACK;
- count = call_method ("_SQLITE_UPDATE", G_SCALAR);
- SPAGAIN;
- if (count != 1) {
- warn("cursor->_SQLITE_UPDATE() returned %d vals instead of 1", count);
- SP -= count;
- }
- else {
- if (argc > 1 && sqlite3_value_type(argv[0]) == SQLITE_NULL
- && sqlite3_value_type(argv[1]) == SQLITE_NULL) {
- /* this was an insert without any given rowid, so the result of
- the method call must be passed in *pRowid*/
- rowidsv = POPs;
- if (!SvOK(rowidsv))
- *pRowid = 0;
- else if (SvUOK(rowidsv))
- *pRowid = SvUV(rowidsv);
- else if (SvIOK(rowidsv))
- *pRowid = SvIV(rowidsv);
- else
- *pRowid = (sqlite3_int64)SvNV(rowidsv);
- }
- rc = SQLITE_OK;
- }
-
-
- PUTBACK;
- FREETMPS;
- LEAVE;
-
- return rc;
-}
-
-static int perl_vt_Begin(sqlite3_vtab *pVTab){
- return _call_perl_vtab_method(pVTab, "BEGIN_TRANSACTION", 0);
-}
-
-static int perl_vt_Sync(sqlite3_vtab *pVTab){
- return _call_perl_vtab_method(pVTab, "SYNC_TRANSACTION", 0);
-}
-
-static int perl_vt_Commit(sqlite3_vtab *pVTab){
- return _call_perl_vtab_method(pVTab, "COMMIT_TRANSACTION", 0);
-}
-
-static int perl_vt_Rollback(sqlite3_vtab *pVTab){
- return _call_perl_vtab_method(pVTab, "ROLLBACK_TRANSACTION", 0);
-}
-
-static int perl_vt_FindFunction(sqlite3_vtab *pVTab,
- int nArg, const char *zName,
- void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
- void **ppArg){
- dTHX;
- dSP;
- dMY_CXT;
- int count;
- int is_overloaded = 0;
- char *func_name = sqlite3_mprintf("%s\t%d", zName, nArg);
- STRLEN len = strlen(func_name);
- HV *functions = ((perl_vtab *) pVTab)->functions;
- SV* coderef = NULL;
- SV** val;
- SV *result;
-
- ENTER;
- SAVETMPS;
-
- /* check if that function was already in cache */
- if (hv_exists(functions, func_name, len)) {
- val = hv_fetch(functions, func_name, len, FALSE);
- if (val && SvOK(*val)) {
- coderef = *val;
- }
- }
- else {
- /* call the FIND_FUNCTION() method */
- PUSHMARK(SP);
- XPUSHs(((perl_vtab *) pVTab)->perl_vtab_obj);
- XPUSHs(sv_2mortal(newSViv(nArg)));
- XPUSHs(sv_2mortal(newSVpv(zName, 0)));
- PUTBACK;
- count = call_method ("FIND_FUNCTION", G_SCALAR);
- SPAGAIN;
- if (count != 1) {
- warn("vtab->FIND_FUNCTION() method returned %d vals instead of 1", count);
- SP -= count;
- goto cleanup;
- }
- result = POPs;
- if (SvTRUE(result)) {
- /* the coderef must be valid for the lifetime of pVTab, so
- make a copy */
- coderef = newSVsv(result);
- }
-
- /* store result in cache */
- hv_store(functions, func_name, len, coderef ? coderef : &PL_sv_undef, 0);
- }
-
- /* return function information for sqlite3 within *pxFunc and *ppArg */
- is_overloaded = coderef && SvTRUE(coderef);
- if (is_overloaded) {
- *pxFunc = MY_CXT.last_dbh_is_unicode ? sqlite_db_func_dispatcher_unicode
- : sqlite_db_func_dispatcher_no_unicode;
- *ppArg = coderef;
- }
-
- cleanup:
- PUTBACK;
- FREETMPS;
- LEAVE;
- sqlite3_free(func_name);
- return is_overloaded;
-}
-
-
-static int perl_vt_Rename(sqlite3_vtab *pVTab, const char *zNew){
- dTHX;
- dSP;
- int count;
- int rc = SQLITE_ERROR;
-
- ENTER;
- SAVETMPS;
-
- PUSHMARK(SP);
- XPUSHs(((perl_vtab *) pVTab)->perl_vtab_obj);
- XPUSHs(sv_2mortal(newSVpv(zNew, 0)));
- PUTBACK;
- count = call_method("RENAME", G_SCALAR);
- SPAGAIN;
- if (count != 1) {
- warn("vtab->RENAME() returned %d args instead of 1", count);
- SP -= count;
- }
- else {
- rc = POPi;
- }
-
- PUTBACK;
- FREETMPS;
- LEAVE;
-
- return rc;
-}
-
-static int perl_vt_Savepoint(sqlite3_vtab *pVTab, int point){
- return _call_perl_vtab_method(pVTab, "SAVEPOINT", point);
-}
-
-static int perl_vt_Release(sqlite3_vtab *pVTab, int point){
- return _call_perl_vtab_method(pVTab, "RELEASE", point);
-}
-
-static int perl_vt_RollbackTo(sqlite3_vtab *pVTab, int point){
- return _call_perl_vtab_method(pVTab, "ROLLBACK_TO", point);
-}
-
-static sqlite3_module perl_vt_Module = {
- 1, /* iVersion */
- perl_vt_Create, /* xCreate */
- perl_vt_Connect, /* xConnect */
- perl_vt_BestIndex, /* xBestIndex */
- perl_vt_Disconnect, /* xDisconnect */
- perl_vt_Drop, /* xDestroy */
- perl_vt_Open, /* xOpen - open a cursor */
- perl_vt_Close, /* xClose - close a cursor */
- perl_vt_Filter, /* xFilter - configure scan constraints */
- perl_vt_Next, /* xNext - advance a cursor */
- perl_vt_Eof, /* xEof - check for end of scan */
- perl_vt_Column, /* xColumn - read data */
- perl_vt_Rowid, /* xRowid - read data */
- perl_vt_Update, /* xUpdate (optional) */
- perl_vt_Begin, /* xBegin (optional) */
- perl_vt_Sync, /* xSync (optional) */
- perl_vt_Commit, /* xCommit (optional) */
- perl_vt_Rollback, /* xRollback (optional) */
- perl_vt_FindFunction, /* xFindFunction (optional) */
- perl_vt_Rename, /* xRename */
-#if SQLITE_VERSION_NUMBER >= 3007007
- perl_vt_Savepoint, /* xSavepoint (optional) */
- perl_vt_Release, /* xRelease (optional) */
- perl_vt_RollbackTo /* xRollbackTo (optional) */
-#endif
-};
-
-
-void
-sqlite_db_destroy_module_data(void *pAux)
-{
- dTHX;
- dSP;
- int count;
- int rc = SQLITE_ERROR;
- perl_vtab_init *init_data;
-
- ENTER;
- SAVETMPS;
-
- init_data = (perl_vtab_init *)pAux;
-
- /* call the DESTROY_MODULE() method */
- PUSHMARK(SP);
- XPUSHs(sv_2mortal(newSVpv(init_data->perl_class, 0)));
- PUTBACK;
- count = call_method("DESTROY_MODULE", G_VOID);
- SPAGAIN;
- SP -= count;
-
- /* free module memory */
- SvREFCNT_dec(init_data->dbh);
- sqlite3_free((char *)init_data->perl_class);
-
- PUTBACK;
- FREETMPS;
- LEAVE;
-}
-
-
-
-int
-sqlite_db_create_module(pTHX_ SV *dbh, const char *name, const char *perl_class)
-{
- dSP;
- D_imp_dbh(dbh);
- int count, rc, retval = TRUE;
- char *module_ISA;
- char *loading_code;
- perl_vtab_init *init_data;
-
- ENTER;
- SAVETMPS;
-
- if (!DBIc_ACTIVE(imp_dbh)) {
- sqlite_error(dbh, -2, "attempt to create module on inactive database handle");
- return FALSE;
- }
-
- /* load the module if needed */
- module_ISA = sqlite3_mprintf("%s::ISA", perl_class);
- if (!get_av(module_ISA, 0)) {
- loading_code = sqlite3_mprintf("use %s", perl_class);
- eval_pv(loading_code, TRUE);
- sqlite3_free(loading_code);
- }
- sqlite3_free(module_ISA);
-
- /* build the init datastructure that will be passed to perl_vt_New() */
- init_data = sqlite3_malloc(sizeof(*init_data));
- init_data->dbh = newRV(dbh);
- sv_rvweaken(init_data->dbh);
- init_data->perl_class = sqlite3_mprintf(perl_class);
-
- /* register within sqlite */
- rc = sqlite3_create_module_v2( imp_dbh->db,
- name,
- &perl_vt_Module,
- init_data,
- sqlite_db_destroy_module_data
- );
- if ( rc != SQLITE_OK ) {
- sqlite_error(dbh, rc, form("sqlite_create_module failed with error %s",
- sqlite3_errmsg(imp_dbh->db)));
- retval = FALSE;
- }
-
-
- /* call the CREATE_MODULE() method */
- PUSHMARK(SP);
- XPUSHs(sv_2mortal(newSVpv(perl_class, 0)));
- XPUSHs(sv_2mortal(newSVpv(name, 0)));
- PUTBACK;
- count = call_method("CREATE_MODULE", G_VOID);
- SPAGAIN;
- SP -= count;
-
- PUTBACK;
- FREETMPS;
- LEAVE;
-
- return retval;
-}
-
-
+#include "dbdimp_tokenizer.inc"
+#include "dbdimp_virtual_table.inc"
/* end */
diff --git a/dbdimp_tokenizer.inc b/dbdimp_tokenizer.inc
new file mode 100644
index 0000000..ad507fe
--- /dev/null
+++ b/dbdimp_tokenizer.inc
@@ -0,0 +1,289 @@
+typedef struct perl_tokenizer {
+ sqlite3_tokenizer base;
+ SV *coderef; /* the perl tokenizer is a coderef that takes
+ a string and returns a cursor coderef */
+} perl_tokenizer;
+
+typedef struct perl_tokenizer_cursor {
+ sqlite3_tokenizer_cursor base;
+ SV *coderef; /* ref to the closure that returns terms */
+ char *pToken; /* storage for a copy of the last token */
+ int nTokenAllocated; /* space allocated to pToken buffer */
+
+ /* members below are only used if the input string is in utf8 */
+ const char *pInput; /* input we are tokenizing */
+ const char *lastByteOffset; /* offset into pInput */
+ int lastCharOffset; /* char offset corresponding to lastByteOffset */
+} perl_tokenizer_cursor;
+
+/*
+** Create a new tokenizer instance.
+** Will be called whenever a FTS3 table is created with
+** CREATE .. USING fts3( ... , tokenize=perl qualified::function::name)
+** where qualified::function::name is a fully qualified perl function
+*/
+static int perl_tokenizer_Create(
+ int argc, const char * const *argv,
+ sqlite3_tokenizer **ppTokenizer
+){
+ dTHX;
+ dSP;
+ int n_retval;
+ SV *retval;
+ perl_tokenizer *t;
+
+ if (!argc) {
+ return SQLITE_ERROR;
+ }
+
+ t = (perl_tokenizer *) sqlite3_malloc(sizeof(*t));
+ if( t==NULL ) return SQLITE_NOMEM;
+ memset(t, 0, sizeof(*t));
+
+ ENTER;
+ SAVETMPS;
+
+ /* call the qualified::function::name */
+ PUSHMARK(SP);
+ PUTBACK;
+ n_retval = call_pv(argv[0], G_SCALAR);
+ SPAGAIN;
+
+ /* store a copy of the returned coderef into the tokenizer structure */
+ if (n_retval != 1) {
+ warn("tokenizer_Create returned %d arguments", n_retval);
+ }
+ retval = POPs;
+ t->coderef = newSVsv(retval);
+ *ppTokenizer = &t->base;
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+
+ return SQLITE_OK;
+}
+
+/*
+** Destroy a tokenizer
+*/
+static int perl_tokenizer_Destroy(sqlite3_tokenizer *pTokenizer){
+ dTHX;
+ perl_tokenizer *t = (perl_tokenizer *) pTokenizer;
+ sv_free(t->coderef);
+ sqlite3_free(t);
+ return SQLITE_OK;
+}
+
+/*
+** Prepare to begin tokenizing a particular string. The input
+** string to be tokenized is supposed to be pInput[0..nBytes-1] ..
+** except that nBytes passed by fts3 is -1 (don't know why) !
+** This is passed to the tokenizer instance, which then returns a
+** closure implementing the cursor (so the cursor is again a coderef).
+*/
+static int perl_tokenizer_Open(
+ sqlite3_tokenizer *pTokenizer, /* Tokenizer object */
+ const char *pInput, int nBytes, /* Input buffer */
+ sqlite3_tokenizer_cursor **ppCursor /* OUT: Created tokenizer cursor */
+){
+ dTHX;
+ dSP;
+ dMY_CXT;
+ U32 flags;
+ SV *perl_string;
+ int n_retval;
+
+ perl_tokenizer *t = (perl_tokenizer *)pTokenizer;
+
+ /* allocate and initialize the cursor struct */
+ perl_tokenizer_cursor *c;
+ c = (perl_tokenizer_cursor *) sqlite3_malloc(sizeof(*c));
+ memset(c, 0, sizeof(*c));
+ *ppCursor = &c->base;
+
+ /* flags for creating the Perl SV containing the input string */
+ flags = SVs_TEMP; /* will call sv_2mortal */
+
+ /* special handling if working with utf8 strings */
+ if (MY_CXT.last_dbh_is_unicode) {
+
+ /* data to keep track of byte offsets */
+ c->lastByteOffset = c->pInput = pInput;
+ c->lastCharOffset = 0;
+
+ /* string passed to Perl needs to be flagged as utf8 */
+ flags |= SVf_UTF8;
+ }
+
+ ENTER;
+ SAVETMPS;
+
+ /* build a Perl copy of the input string */
+ if (nBytes < 0) { /* we get -1 from fts3. Don't know why ! */
+ nBytes = strlen(pInput);
+ }
+ perl_string = newSVpvn_flags(pInput, nBytes, flags);
+
+ /* call the tokenizer coderef */
+ PUSHMARK(SP);
+ XPUSHs(perl_string);
+ PUTBACK;
+ n_retval = call_sv(t->coderef, G_SCALAR);
+ SPAGAIN;
+
+ /* store the cursor coderef returned by the tokenizer */
+ if (n_retval != 1) {
+ warn("tokenizer returned %d arguments", n_retval);
+ }
+ c->coderef = newSVsv(POPs);
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+ return SQLITE_OK;
+}
+
+/*
+** Close a tokenization cursor previously opened by a call to
+** perl_tokenizer_Open() above.
+*/
+static int perl_tokenizer_Close(sqlite3_tokenizer_cursor *pCursor){
+ perl_tokenizer_cursor *c = (perl_tokenizer_cursor *) pCursor;
+
+ dTHX;
+ sv_free(c->coderef);
+ if (c->pToken) sqlite3_free(c->pToken);
+ sqlite3_free(c);
+ return SQLITE_OK;
+}
+
+/*
+** Extract the next token from a tokenization cursor. The cursor must
+** have been opened by a prior call to perl_tokenizer_Open().
+*/
+static int perl_tokenizer_Next(
+ sqlite3_tokenizer_cursor *pCursor, /* Cursor returned by perl_tokenizer_Open */
+ const char **ppToken, /* OUT: *ppToken is the token text */
+ int *pnBytes, /* OUT: Number of bytes in token */
+ int *piStartOffset, /* OUT: Starting offset of token */
+ int *piEndOffset, /* OUT: Ending offset of token */
+ int *piPosition /* OUT: Position integer of token */
+){
+ perl_tokenizer_cursor *c = (perl_tokenizer_cursor *) pCursor;
+ int result;
+ int n_retval;
+ char *token;
+ char *byteOffset;
+ STRLEN n_a; /* this is required for older perls < 5.8.8 */
+ I32 hop;
+
+ dTHX;
+ dSP;
+
+ ENTER;
+ SAVETMPS;
+
+ /* call the cursor */
+ PUSHMARK(SP);
+ PUTBACK;
+ n_retval = call_sv(c->coderef, G_ARRAY);
+ SPAGAIN;
+
+ /* if we get back an empty list, there is no more token */
+ if (n_retval == 0) {
+ result = SQLITE_DONE;
+ }
+ /* otherwise, get token details from the return list */
+ else {
+ if (n_retval != 5) {
+ warn("tokenizer cursor returned %d arguments", n_retval);
+ }
+ *piPosition = POPi;
+ *piEndOffset = POPi;
+ *piStartOffset = POPi;
+ *pnBytes = POPi;
+ token = POPpx;
+
+ if (c->pInput) { /* if working with utf8 data */
+
+ /* recompute *pnBytes in bytes, not in chars */
+ *pnBytes = strlen(token);
+
+ /* recompute start/end offsets in bytes, not in chars */
+ hop = *piStartOffset - c->lastCharOffset;
+ byteOffset = (char*)utf8_hop((U8*)c->lastByteOffset, hop);
+ hop = *piEndOffset - *piStartOffset;
+ *piStartOffset = byteOffset - c->pInput;
+ byteOffset = (char*)utf8_hop((U8*)byteOffset, hop);
+ *piEndOffset = byteOffset - c->pInput;
+
+ /* remember where we are for next round */
+ c->lastCharOffset = *piEndOffset,
+ c->lastByteOffset = byteOffset;
+ }
+
+ /* make sure we have enough storage for copying the token */
+ if (*pnBytes > c->nTokenAllocated ){
+ char *pNew;
+ c->nTokenAllocated = *pnBytes + 20;
+ pNew = sqlite3_realloc(c->pToken, c->nTokenAllocated);
+ if( !pNew ) return SQLITE_NOMEM;
+ c->pToken = pNew;
+ }
+
+ /* need to copy the token into the C cursor before perl frees that
+ memory */
+ memcpy(c->pToken, token, *pnBytes);
+ *ppToken = c->pToken;
+
+ result = SQLITE_OK;
+ }
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+
+ return result;
+}
+
+/*
+** The set of routines that implement the perl tokenizer
+*/
+sqlite3_tokenizer_module perl_tokenizer_Module = {
+ 0,
+ perl_tokenizer_Create,
+ perl_tokenizer_Destroy,
+ perl_tokenizer_Open,
+ perl_tokenizer_Close,
+ perl_tokenizer_Next
+};
+
+/*
+** Register the perl tokenizer with FTS3
+*/
+int sqlite_db_register_fts3_perl_tokenizer(pTHX_ SV *dbh)
+{
+ D_imp_dbh(dbh);
+
+ int rc;
+ sqlite3_stmt *pStmt;
+ const char zSql[] = "SELECT fts3_tokenizer(?, ?)";
+ sqlite3_tokenizer_module *p = &perl_tokenizer_Module;
+
+ if (!DBIc_ACTIVE(imp_dbh)) {
+ sqlite_error(dbh, -2, "attempt to register fts3 tokenizer on inactive database handle");
+ return FALSE;
+ }
+
+ rc = sqlite3_prepare_v2(imp_dbh->db, zSql, -1, &pStmt, 0);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+
+ sqlite3_bind_text(pStmt, 1, "perl", -1, SQLITE_STATIC);
+ sqlite3_bind_blob(pStmt, 2, &p, sizeof(p), SQLITE_STATIC);
+ sqlite3_step(pStmt);
+
+ return sqlite3_finalize(pStmt);
+}
diff --git a/dbdimp_virtual_table.inc b/dbdimp_virtual_table.inc
new file mode 100644
index 0000000..3dfb3c5
--- /dev/null
+++ b/dbdimp_virtual_table.inc
@@ -0,0 +1,835 @@
+/***********************************************************************
+** The set of routines that implement the perl "module"
+** (i.e support for virtual tables written in Perl)
+************************************************************************/
+
+typedef struct perl_vtab {
+ sqlite3_vtab base;
+ SV *perl_vtab_obj;
+ HV *functions;
+} perl_vtab;
+
+typedef struct perl_vtab_cursor {
+ sqlite3_vtab_cursor base;
+ SV *perl_cursor_obj;
+} perl_vtab_cursor;
+
+typedef struct perl_vtab_init {
+ SV *dbh;
+ const char *perl_class;
+} perl_vtab_init;
+
+
+
+/* auxiliary routine for generalized method calls. Arg "i" may be unused */
+static int _call_perl_vtab_method(sqlite3_vtab *pVTab,
+ const char *method, int i) {
+ dTHX;
+ dSP;
+ int count;
+
+ ENTER;
+ SAVETMPS;
+
+ PUSHMARK(SP);
+ XPUSHs(((perl_vtab *) pVTab)->perl_vtab_obj);
+ XPUSHs(sv_2mortal(newSViv(i)));
+ PUTBACK;
+ count = call_method (method, G_VOID);
+ SPAGAIN;
+ SP -= count;
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+
+ return SQLITE_OK;
+}
+
+
+
+static int perl_vt_New(const char *method,
+ sqlite3 *db, void *pAux,
+ int argc, const char *const *argv,
+ sqlite3_vtab **ppVTab, char **pzErr){
+ dTHX;
+ dSP;
+ perl_vtab *vt;
+ perl_vtab_init *init_data = (perl_vtab_init *)pAux;
+ int count, i;
+ int rc = SQLITE_ERROR;
+ SV *perl_vtab_obj;
+ SV *sql;
+
+ /* allocate a perl_vtab structure */
+ vt = (perl_vtab *) sqlite3_malloc(sizeof(*vt));
+ if( vt==NULL ) return SQLITE_NOMEM;
+ memset(vt, 0, sizeof(*vt));
+ vt->functions = newHV();
+
+ ENTER;
+ SAVETMPS;
+
+ /* call the ->CREATE/CONNECT() method */
+ PUSHMARK(SP);
+ XPUSHs(sv_2mortal(newSVpv(init_data->perl_class, 0)));
+ XPUSHs(init_data->dbh);
+ for(i = 0; i < argc; i++) {
+ XPUSHs(newSVpvn_flags(argv[i], strlen(argv[i]), SVs_TEMP|SVf_UTF8));
+ }
+ PUTBACK;
+ count = call_method (method, G_SCALAR);
+ SPAGAIN;
+
+ /* check the return value */
+ if ( count != 1 ) {
+ *pzErr = sqlite3_mprintf("vtab->%s() should return one value, got %d",
+ method, count );
+ SP -= count; /* Clear the stack */
+ goto cleanup;
+ }
+
+ /* get the VirtualTable instance */
+ perl_vtab_obj = POPs;
+ if ( !sv_isobject(perl_vtab_obj) ) {
+ *pzErr = sqlite3_mprintf("vtab->%s() should return a blessed reference",
+ method);
+ goto cleanup;
+ }
+
+ /* call the ->VTAB_TO_DECLARE() method */
+ PUSHMARK(SP);
+ XPUSHs(perl_vtab_obj);
+ PUTBACK;
+ count = call_method ("VTAB_TO_DECLARE", G_SCALAR);
+ SPAGAIN;
+
+ /* check the return value */
+ if (count != 1 ) {
+ *pzErr = sqlite3_mprintf("vtab->VTAB_TO_DECLARE() should return one value, got %d",
+ count );
+ SP -= count; /* Clear the stack */
+ goto cleanup;
+ }
+
+ /* call sqlite3_declare_vtab with the sql returned from
+ method VTAB_TO_DECLARE(), converted to utf8 */
+ sql = POPs;
+ rc = sqlite3_declare_vtab(db, SvPVutf8_nolen(sql));
+
+ cleanup:
+ if (rc == SQLITE_OK) {
+ /* record the VirtualTable perl instance within the vtab structure */
+ vt->perl_vtab_obj = SvREFCNT_inc(perl_vtab_obj);
+ *ppVTab = &vt->base;
+ }
+ else {
+ sqlite3_free(vt);
+ }
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+
+ return rc;
+}
+
+
+static int perl_vt_Create(sqlite3 *db, void *pAux,
+ int argc, const char *const *argv,
+ sqlite3_vtab **ppVTab, char **pzErr){
+ return perl_vt_New("CREATE", db, pAux, argc, argv, ppVTab, pzErr);
+}
+
+static int perl_vt_Connect(sqlite3 *db, void *pAux,
+ int argc, const char *const *argv,
+ sqlite3_vtab **ppVTab, char **pzErr){
+ return perl_vt_New("CONNECT", db, pAux, argc, argv, ppVTab, pzErr);
+}
+
+
+static int _free_perl_vtab(perl_vtab *pVTab){
+ dTHX;
+
+ SvREFCNT_dec(pVTab->perl_vtab_obj);
+
+ /* deallocate coderefs that were declared through FindFunction() */
+ hv_undef(pVTab->functions);
+ SvREFCNT_dec(pVTab->functions);
+
+ sqlite3_free(pVTab);
+ return SQLITE_OK;
+}
+
+static int perl_vt_Disconnect(sqlite3_vtab *pVTab){
+ _call_perl_vtab_method(pVTab, "DISCONNECT", 0);
+ return _free_perl_vtab((perl_vtab *)pVTab);
+}
+
+static int perl_vt_Drop(sqlite3_vtab *pVTab){
+ _call_perl_vtab_method(pVTab, "DROP", 0);
+ return _free_perl_vtab((perl_vtab *)pVTab);
+}
+
+
+static char *
+_constraint_op_to_string(unsigned char op) {
+ switch (op) {
+ case SQLITE_INDEX_CONSTRAINT_EQ:
+ return "=";
+ case SQLITE_INDEX_CONSTRAINT_GT:
+ return ">";
+ case SQLITE_INDEX_CONSTRAINT_GE:
+ return ">=";
+ case SQLITE_INDEX_CONSTRAINT_LT:
+ return "<";
+ case SQLITE_INDEX_CONSTRAINT_LE:
+ return "<=";
+ case SQLITE_INDEX_CONSTRAINT_MATCH:
+ return "MATCH";
+#if SQLITE_VERSION_NUMBER >= 3010000
+ case SQLITE_INDEX_CONSTRAINT_LIKE:
+ return "LIKE";
+ case SQLITE_INDEX_CONSTRAINT_GLOB:
+ return "GLOB";
+ case SQLITE_INDEX_CONSTRAINT_REGEXP:
+ return "REGEXP";
+#endif
+ default:
+ return "unknown";
+ }
+}
+
+
+static int perl_vt_BestIndex(sqlite3_vtab *pVTab, sqlite3_index_info *pIdxInfo){
+ dTHX;
+ dSP;
+ int i, count;
+ int argvIndex;
+ AV *constraints;
+ AV *order_by;
+ SV *hashref;
+ SV **val;
+ HV *hv;
+ struct sqlite3_index_constraint_usage *pConsUsage;
+
+ ENTER;
+ SAVETMPS;
+
+ /* build the "where_constraints" datastructure */
+ constraints = newAV();
+ for (i=0; i<pIdxInfo->nConstraint; i++){
+ struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[i];
+ HV *constraint = newHV();
+ char *op_str = _constraint_op_to_string(pCons->op);
+ hv_stores(constraint, "col", newSViv(pCons->iColumn));
+ hv_stores(constraint, "op", newSVpv(op_str, 0));
+ hv_stores(constraint, "usable", pCons->usable ? &PL_sv_yes : &PL_sv_no);
+ av_push(constraints, newRV_noinc((SV*) constraint));
+ }
+
+ /* build the "order_by" datastructure */
+ order_by = newAV();
+ for (i=0; i<pIdxInfo->nOrderBy; i++){
+ struct sqlite3_index_orderby const *pOrder = &pIdxInfo->aOrderBy[i];
+ HV *order = newHV();
+ hv_stores(order, "col", newSViv(pOrder->iColumn));
+ hv_stores(order, "desc", pOrder->desc ? &PL_sv_yes : &PL_sv_no);
+ av_push( order_by, newRV_noinc((SV*) order));
+ }
+
+ /* call the ->BEST_INDEX() method */
+ PUSHMARK(SP);
+ XPUSHs( ((perl_vtab *) pVTab)->perl_vtab_obj);
+ XPUSHs( sv_2mortal( newRV_noinc((SV*) constraints)));
+ XPUSHs( sv_2mortal( newRV_noinc((SV*) order_by)));
+ PUTBACK;
+ count = call_method ("BEST_INDEX", G_SCALAR);
+ SPAGAIN;
+
+ /* get values back from the returned hashref */
+ if (count != 1)
+ croak("BEST_INDEX() method returned %d vals instead of 1", count);
+ hashref = POPs;
+ if (!(hashref && SvROK(hashref) && SvTYPE(SvRV(hashref)) == SVt_PVHV))
+ croak("BEST_INDEX() method did not return a hashref");
+ hv = (HV*)SvRV(hashref);
+ val = hv_fetch(hv, "idxNum", 6, FALSE);
+ pIdxInfo->idxNum = (val && SvOK(*val)) ? SvIV(*val) : 0;
+ val = hv_fetch(hv, "idxStr", 6, FALSE);
+ if (val && SvOK(*val)) {
+ STRLEN len;
+ char *str = SvPVutf8(*val, len);
+ pIdxInfo->idxStr = sqlite3_malloc(len+1);
+ memcpy(pIdxInfo->idxStr, str, len);
+ pIdxInfo->idxStr[len] = 0;
+ pIdxInfo->needToFreeIdxStr = 1;
+ }
+ val = hv_fetch(hv, "orderByConsumed", 15, FALSE);
+ pIdxInfo->orderByConsumed = (val && SvTRUE(*val)) ? 1 : 0;
+ val = hv_fetch(hv, "estimatedCost", 13, FALSE);
+ pIdxInfo->estimatedCost = (val && SvOK(*val)) ? SvNV(*val) : 0;
+#if SQLITE_VERSION_NUMBER >= 3008002
+ val = hv_fetch(hv, "estimatedRows", 13, FALSE);
+ pIdxInfo->estimatedRows = (val && SvOK(*val)) ? SvIV(*val) : 0;
+#endif
+
+ /* loop over constraints to get back the "argvIndex" and "omit" keys
+ that shoud have been added by the best_index() method call */
+ for (i=0; i<pIdxInfo->nConstraint; i++){
+ SV **rv = av_fetch(constraints, i, FALSE);
+ if (!(rv && SvROK(*rv) && SvTYPE(SvRV(*rv)) == SVt_PVHV))
+ croak("the call to BEST_INDEX() has corrupted constraint data");
+ hv = (HV*)SvRV(*rv);
+ val = hv_fetch(hv, "argvIndex", 9, FALSE);
+ argvIndex = (val && SvOK(*val)) ? SvIV(*val) + 1: 0;
+
+ pConsUsage = &pIdxInfo->aConstraintUsage[i];
+ pConsUsage->argvIndex = argvIndex;
+ val = hv_fetch(hv, "omit", 4, FALSE);
+ pConsUsage->omit = (val && SvTRUE(*val)) ? 1 : 0;
+ }
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+
+ return SQLITE_OK;
+}
+
+
+
+static int perl_vt_Open(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
+ dTHX;
+ dSP;
+ int count;
+ int rc = SQLITE_ERROR;
+ SV *perl_cursor;
+ perl_vtab_cursor *cursor;
+
+ ENTER;
+ SAVETMPS;
+
+ /* allocate a perl_vtab_cursor structure */
+ cursor = (perl_vtab_cursor *) sqlite3_malloc(sizeof(*cursor));
+ if( cursor==NULL ) return SQLITE_NOMEM;
+ memset(cursor, 0, sizeof(*cursor));
+
+ /* call the ->OPEN() method */
+ PUSHMARK(SP);
+ XPUSHs( ((perl_vtab *) pVTab)->perl_vtab_obj);
+ PUTBACK;
+ count = call_method ("OPEN", G_SCALAR);
+ SPAGAIN;
+ if (count != 1) {
+ warn("vtab->OPEN() method returned %d vals instead of 1", count);
+ SP -= count;
+ goto cleanup;
+
+ }
+ perl_cursor = POPs;
+ if ( !sv_isobject(perl_cursor) ) {
+ warn("vtab->OPEN() method did not return a blessed cursor");
+ goto cleanup;
+ }
+
+ /* everything went OK */
+ rc = SQLITE_OK;
+
+ cleanup:
+
+ if (rc == SQLITE_OK) {
+ cursor->perl_cursor_obj = SvREFCNT_inc(perl_cursor);
+ *ppCursor = &cursor->base;
+ }
+ else {
+ sqlite3_free(cursor);
+ }
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+
+ return rc;
+}
+
+static int perl_vt_Close(sqlite3_vtab_cursor *pVtabCursor){
+ dTHX;
+ dSP;
+ perl_vtab_cursor *perl_pVTabCursor;
+
+ ENTER;
+ SAVETMPS;
+
+ /* Note : there is no explicit call to a CLOSE() method; if
+ needed, the Perl class can implement a DESTROY() method */
+
+ perl_pVTabCursor = (perl_vtab_cursor *) pVtabCursor;
+ SvREFCNT_dec(perl_pVTabCursor->perl_cursor_obj);
+ sqlite3_free(perl_pVTabCursor);
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+
+ return SQLITE_OK;
+}
+
+static int perl_vt_Filter( sqlite3_vtab_cursor *pVtabCursor,
+ int idxNum, const char *idxStr,
+ int argc, sqlite3_value **argv ){
+ dTHX;
+ dSP;
+ dMY_CXT;
+ int i, count;
+ int is_unicode = MY_CXT.last_dbh_is_unicode;
+
+ ENTER;
+ SAVETMPS;
+
+ /* call the FILTER() method with ($idxNum, $idxStr, @args) */
+ PUSHMARK(SP);
+ XPUSHs(((perl_vtab_cursor *) pVtabCursor)->perl_cursor_obj);
+ XPUSHs(sv_2mortal(newSViv(idxNum)));
+ XPUSHs(sv_2mortal(newSVpv(idxStr, 0)));
+ for(i = 0; i < argc; i++) {
+ XPUSHs(stacked_sv_from_sqlite3_value(aTHX_ argv[i], is_unicode));
+ }
+ PUTBACK;
+ count = call_method("FILTER", G_VOID);
+ SPAGAIN;
+ SP -= count;
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+
+ return SQLITE_OK;
+}
+
+
+static int perl_vt_Next(sqlite3_vtab_cursor *pVtabCursor){
+ dTHX;
+ dSP;
+ int count;
+
+ ENTER;
+ SAVETMPS;
+
+ /* call the next() method */
+ PUSHMARK(SP);
+ XPUSHs(((perl_vtab_cursor *) pVtabCursor)->perl_cursor_obj);
+ PUTBACK;
+ count = call_method ("NEXT", G_VOID);
+ SPAGAIN;
+ SP -= count;
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+
+ return SQLITE_OK;
+}
+
+static int perl_vt_Eof(sqlite3_vtab_cursor *pVtabCursor){
+ dTHX;
+ dSP;
+ int count, eof;
+
+ ENTER;
+ SAVETMPS;
+
+ /* call the eof() method */
+ PUSHMARK(SP);
+ XPUSHs(((perl_vtab_cursor *) pVtabCursor)->perl_cursor_obj);
+ PUTBACK;
+ count = call_method ("EOF", G_SCALAR);
+ SPAGAIN;
+ if (count != 1) {
+ warn("cursor->EOF() method returned %d vals instead of 1", count);
+ SP -= count;
+ }
+ else {
+ SV *sv = POPs; /* need 2 lines, because this doesn't work : */
+ eof = SvTRUE(sv); /* eof = SvTRUE(POPs); # I don't understand why :-( */
+ }
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+
+ return eof;
+}
+
+
+static int perl_vt_Column(sqlite3_vtab_cursor *pVtabCursor,
+ sqlite3_context* context,
+ int col){
+ dTHX;
+ dSP;
+ int count;
+ int rc = SQLITE_ERROR;
+
+ ENTER;
+ SAVETMPS;
+
+ /* call the column() method */
+ PUSHMARK(SP);
+ XPUSHs(((perl_vtab_cursor *) pVtabCursor)->perl_cursor_obj);
+ XPUSHs(sv_2mortal(newSViv(col)));
+ PUTBACK;
+ count = call_method ("COLUMN", G_SCALAR);
+ SPAGAIN;
+ if (count != 1) {
+ warn("cursor->COLUMN() method returned %d vals instead of 1", count);
+ SP -= count;
+ sqlite3_result_error(context, "column error", 12);
+ }
+ else {
+ SV *result = POPs;
+ sqlite_set_result(aTHX_ context, result, 0 );
+ rc = SQLITE_OK;
+ }
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+
+ return rc;
+}
+
+static int perl_vt_Rowid( sqlite3_vtab_cursor *pVtabCursor,
+ sqlite3_int64 *pRowid ){
+ dTHX;
+ dSP;
+ int count;
+ int rc = SQLITE_ERROR;
+
+ ENTER;
+ SAVETMPS;
+
+ /* call the rowid() method */
+ PUSHMARK(SP);
+ XPUSHs(((perl_vtab_cursor *) pVtabCursor)->perl_cursor_obj);
+ PUTBACK;
+ count = call_method ("ROWID", G_SCALAR);
+ SPAGAIN;
+ if (count != 1) {
+ warn("cursor->ROWID() returned %d vals instead of 1", count);
+ SP -= count;
+ }
+ else {
+ *pRowid =POPi;
+ rc = SQLITE_OK;
+ }
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+
+ return rc;
+}
+
+static int perl_vt_Update( sqlite3_vtab *pVTab,
+ int argc, sqlite3_value **argv,
+ sqlite3_int64 *pRowid ){
+ dTHX;
+ dSP;
+ dMY_CXT;
+ int count, i;
+ int is_unicode = MY_CXT.last_dbh_is_unicode;
+ int rc = SQLITE_ERROR;
+ SV *rowidsv;
+
+ ENTER;
+ SAVETMPS;
+
+ /* call the _SQLITE_UPDATE() method */
+ PUSHMARK(SP);
+ XPUSHs(((perl_vtab *) pVTab)->perl_vtab_obj);
+ for(i = 0; i < argc; i++) {
+ XPUSHs(stacked_sv_from_sqlite3_value(aTHX_ argv[i], is_unicode));
+ }
+ PUTBACK;
+ count = call_method ("_SQLITE_UPDATE", G_SCALAR);
+ SPAGAIN;
+ if (count != 1) {
+ warn("cursor->_SQLITE_UPDATE() returned %d vals instead of 1", count);
+ SP -= count;
+ }
+ else {
+ if (argc > 1 && sqlite3_value_type(argv[0]) == SQLITE_NULL
+ && sqlite3_value_type(argv[1]) == SQLITE_NULL) {
+ /* this was an insert without any given rowid, so the result of
+ the method call must be passed in *pRowid*/
+ rowidsv = POPs;
+ if (!SvOK(rowidsv))
+ *pRowid = 0;
+ else if (SvUOK(rowidsv))
+ *pRowid = SvUV(rowidsv);
+ else if (SvIOK(rowidsv))
+ *pRowid = SvIV(rowidsv);
+ else
+ *pRowid = (sqlite3_int64)SvNV(rowidsv);
+ }
+ rc = SQLITE_OK;
+ }
+
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+
+ return rc;
+}
+
+static int perl_vt_Begin(sqlite3_vtab *pVTab){
+ return _call_perl_vtab_method(pVTab, "BEGIN_TRANSACTION", 0);
+}
+
+static int perl_vt_Sync(sqlite3_vtab *pVTab){
+ return _call_perl_vtab_method(pVTab, "SYNC_TRANSACTION", 0);
+}
+
+static int perl_vt_Commit(sqlite3_vtab *pVTab){
+ return _call_perl_vtab_method(pVTab, "COMMIT_TRANSACTION", 0);
+}
+
+static int perl_vt_Rollback(sqlite3_vtab *pVTab){
+ return _call_perl_vtab_method(pVTab, "ROLLBACK_TRANSACTION", 0);
+}
+
+static int perl_vt_FindFunction(sqlite3_vtab *pVTab,
+ int nArg, const char *zName,
+ void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
+ void **ppArg){
+ dTHX;
+ dSP;
+ dMY_CXT;
+ int count;
+ int is_overloaded = 0;
+ char *func_name = sqlite3_mprintf("%s\t%d", zName, nArg);
+ STRLEN len = strlen(func_name);
+ HV *functions = ((perl_vtab *) pVTab)->functions;
+ SV* coderef = NULL;
+ SV** val;
+ SV *result;
+
+ ENTER;
+ SAVETMPS;
+
+ /* check if that function was already in cache */
+ if (hv_exists(functions, func_name, len)) {
+ val = hv_fetch(functions, func_name, len, FALSE);
+ if (val && SvOK(*val)) {
+ coderef = *val;
+ }
+ }
+ else {
+ /* call the FIND_FUNCTION() method */
+ PUSHMARK(SP);
+ XPUSHs(((perl_vtab *) pVTab)->perl_vtab_obj);
+ XPUSHs(sv_2mortal(newSViv(nArg)));
+ XPUSHs(sv_2mortal(newSVpv(zName, 0)));
+ PUTBACK;
+ count = call_method ("FIND_FUNCTION", G_SCALAR);
+ SPAGAIN;
+ if (count != 1) {
+ warn("vtab->FIND_FUNCTION() method returned %d vals instead of 1", count);
+ SP -= count;
+ goto cleanup;
+ }
+ result = POPs;
+ if (SvTRUE(result)) {
+ /* the coderef must be valid for the lifetime of pVTab, so
+ make a copy */
+ coderef = newSVsv(result);
+ }
+
+ /* store result in cache */
+ hv_store(functions, func_name, len, coderef ? coderef : &PL_sv_undef, 0);
+ }
+
+ /* return function information for sqlite3 within *pxFunc and *ppArg */
+ is_overloaded = coderef && SvTRUE(coderef);
+ if (is_overloaded) {
+ *pxFunc = MY_CXT.last_dbh_is_unicode ? sqlite_db_func_dispatcher_unicode
+ : sqlite_db_func_dispatcher_no_unicode;
+ *ppArg = coderef;
+ }
+
+ cleanup:
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+ sqlite3_free(func_name);
+ return is_overloaded;
+}
+
+
+static int perl_vt_Rename(sqlite3_vtab *pVTab, const char *zNew){
+ dTHX;
+ dSP;
+ int count;
+ int rc = SQLITE_ERROR;
+
+ ENTER;
+ SAVETMPS;
+
+ PUSHMARK(SP);
+ XPUSHs(((perl_vtab *) pVTab)->perl_vtab_obj);
+ XPUSHs(sv_2mortal(newSVpv(zNew, 0)));
+ PUTBACK;
+ count = call_method("RENAME", G_SCALAR);
+ SPAGAIN;
+ if (count != 1) {
+ warn("vtab->RENAME() returned %d args instead of 1", count);
+ SP -= count;
+ }
+ else {
+ rc = POPi;
+ }
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+
+ return rc;
+}
+
+static int perl_vt_Savepoint(sqlite3_vtab *pVTab, int point){
+ return _call_perl_vtab_method(pVTab, "SAVEPOINT", point);
+}
+
+static int perl_vt_Release(sqlite3_vtab *pVTab, int point){
+ return _call_perl_vtab_method(pVTab, "RELEASE", point);
+}
+
+static int perl_vt_RollbackTo(sqlite3_vtab *pVTab, int point){
+ return _call_perl_vtab_method(pVTab, "ROLLBACK_TO", point);
+}
+
+static sqlite3_module perl_vt_Module = {
+ 1, /* iVersion */
+ perl_vt_Create, /* xCreate */
+ perl_vt_Connect, /* xConnect */
+ perl_vt_BestIndex, /* xBestIndex */
+ perl_vt_Disconnect, /* xDisconnect */
+ perl_vt_Drop, /* xDestroy */
+ perl_vt_Open, /* xOpen - open a cursor */
+ perl_vt_Close, /* xClose - close a cursor */
+ perl_vt_Filter, /* xFilter - configure scan constraints */
+ perl_vt_Next, /* xNext - advance a cursor */
+ perl_vt_Eof, /* xEof - check for end of scan */
+ perl_vt_Column, /* xColumn - read data */
+ perl_vt_Rowid, /* xRowid - read data */
+ perl_vt_Update, /* xUpdate (optional) */
+ perl_vt_Begin, /* xBegin (optional) */
+ perl_vt_Sync, /* xSync (optional) */
+ perl_vt_Commit, /* xCommit (optional) */
+ perl_vt_Rollback, /* xRollback (optional) */
+ perl_vt_FindFunction, /* xFindFunction (optional) */
+ perl_vt_Rename, /* xRename */
+#if SQLITE_VERSION_NUMBER >= 3007007
+ perl_vt_Savepoint, /* xSavepoint (optional) */
+ perl_vt_Release, /* xRelease (optional) */
+ perl_vt_RollbackTo /* xRollbackTo (optional) */
+#endif
+};
+
+
+void
+sqlite_db_destroy_module_data(void *pAux)
+{
+ dTHX;
+ dSP;
+ int count;
+ int rc = SQLITE_ERROR;
+ perl_vtab_init *init_data;
+
+ ENTER;
+ SAVETMPS;
+
+ init_data = (perl_vtab_init *)pAux;
+
+ /* call the DESTROY_MODULE() method */
+ PUSHMARK(SP);
+ XPUSHs(sv_2mortal(newSVpv(init_data->perl_class, 0)));
+ PUTBACK;
+ count = call_method("DESTROY_MODULE", G_VOID);
+ SPAGAIN;
+ SP -= count;
+
+ /* free module memory */
+ SvREFCNT_dec(init_data->dbh);
+ sqlite3_free((char *)init_data->perl_class);
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+}
+
+
+
+int
+sqlite_db_create_module(pTHX_ SV *dbh, const char *name, const char *perl_class)
+{
+ dSP;
+ D_imp_dbh(dbh);
+ int count, rc, retval = TRUE;
+ char *module_ISA;
+ char *loading_code;
+ perl_vtab_init *init_data;
+
+ ENTER;
+ SAVETMPS;
+
+ if (!DBIc_ACTIVE(imp_dbh)) {
+ sqlite_error(dbh, -2, "attempt to create module on inactive database handle");
+ return FALSE;
+ }
+
+ /* load the module if needed */
+ module_ISA = sqlite3_mprintf("%s::ISA", perl_class);
+ if (!get_av(module_ISA, 0)) {
+ loading_code = sqlite3_mprintf("use %s", perl_class);
+ eval_pv(loading_code, TRUE);
+ sqlite3_free(loading_code);
+ }
+ sqlite3_free(module_ISA);
+
+ /* build the init datastructure that will be passed to perl_vt_New() */
+ init_data = sqlite3_malloc(sizeof(*init_data));
+ init_data->dbh = newRV(dbh);
+ sv_rvweaken(init_data->dbh);
+ init_data->perl_class = sqlite3_mprintf(perl_class);
+
+ /* register within sqlite */
+ rc = sqlite3_create_module_v2( imp_dbh->db,
+ name,
+ &perl_vt_Module,
+ init_data,
+ sqlite_db_destroy_module_data
+ );
+ if ( rc != SQLITE_OK ) {
+ sqlite_error(dbh, rc, form("sqlite_create_module failed with error %s",
+ sqlite3_errmsg(imp_dbh->db)));
+ retval = FALSE;
+ }
+
+
+ /* call the CREATE_MODULE() method */
+ PUSHMARK(SP);
+ XPUSHs(sv_2mortal(newSVpv(perl_class, 0)));
+ XPUSHs(sv_2mortal(newSVpv(name, 0)));
+ PUTBACK;
+ count = call_method("CREATE_MODULE", G_VOID);
+ SPAGAIN;
+ SP -= count;
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+
+ return retval;
+}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-perl/packages/libdbd-sqlite3-perl.git
More information about the Pkg-perl-cvs-commits
mailing list