mirror of
https://github.com/DBD-SQLite/DBD-SQLite
synced 2025-06-07 14:19:10 -04:00
implementation of FIND_FUNCTION, plus a couple of cosmetic changes in various places
This commit is contained in:
parent
7af00e7edd
commit
0ec13083f1
7 changed files with 432 additions and 168 deletions
1
MANIFEST
1
MANIFEST
|
@ -112,6 +112,7 @@ t/rt_88228_sqlite_3_8_0_crash.t
|
|||
t/rt_96878_fts_contentless_table.t
|
||||
t/virtual_table/00_base.t
|
||||
t/virtual_table/01_destroy.t
|
||||
t/virtual_table/02_find_function.t
|
||||
t/virtual_table/10_filecontent.t
|
||||
t/virtual_table/11_filecontent_fulltext.t
|
||||
t/virtual_table/20_perldata.t
|
||||
|
|
337
dbdimp.c
337
dbdimp.c
|
@ -1500,7 +1500,6 @@ sqlite_db_func_dispatcher(int is_unicode, sqlite3_context *context, int argc, sq
|
|||
}
|
||||
|
||||
PUTBACK;
|
||||
|
||||
FREETMPS;
|
||||
LEAVE;
|
||||
}
|
||||
|
@ -2779,6 +2778,7 @@ int sqlite_db_register_fts3_perl_tokenizer(pTHX_ SV *dbh)
|
|||
typedef struct perl_vtab {
|
||||
sqlite3_vtab base;
|
||||
SV *perl_vtab_obj;
|
||||
HV *functions;
|
||||
} perl_vtab;
|
||||
|
||||
typedef struct perl_vtab_cursor {
|
||||
|
@ -2793,30 +2793,9 @@ typedef struct perl_vtab_init {
|
|||
|
||||
|
||||
|
||||
static int _call_perl_vtab_method(sqlite3_vtab *pVTab, const char *method) {
|
||||
dTHX;
|
||||
dSP;
|
||||
ENTER;
|
||||
SAVETMPS;
|
||||
int count;
|
||||
|
||||
PUSHMARK(SP);
|
||||
XPUSHs(((perl_vtab *) pVTab)->perl_vtab_obj);
|
||||
PUTBACK;
|
||||
count = call_method (method, G_VOID);
|
||||
SPAGAIN;
|
||||
SP -= count;
|
||||
|
||||
PUTBACK;
|
||||
FREETMPS;
|
||||
LEAVE;
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
static int _call_perl_vtab_method_int(sqlite3_vtab *pVTab,
|
||||
const char *method, int i) {
|
||||
/* 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;
|
||||
ENTER;
|
||||
|
@ -2831,7 +2810,6 @@ static int _call_perl_vtab_method(sqlite3_vtab *pVTab, const char *method) {
|
|||
SPAGAIN;
|
||||
SP -= count;
|
||||
|
||||
|
||||
PUTBACK;
|
||||
FREETMPS;
|
||||
LEAVE;
|
||||
|
@ -2841,8 +2819,6 @@ static int _call_perl_vtab_method(sqlite3_vtab *pVTab, const char *method) {
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
static int perl_vt_New(const char *method,
|
||||
sqlite3 *db, void *pAux,
|
||||
int argc, const char *const *argv,
|
||||
|
@ -2852,11 +2828,13 @@ static int perl_vt_New(const char *method,
|
|||
perl_vtab *vt;
|
||||
perl_vtab_init *init_data = (perl_vtab_init *)pAux;
|
||||
int count, i;
|
||||
int rc = SQLITE_ERROR;
|
||||
|
||||
/* 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;
|
||||
|
@ -2907,20 +2885,23 @@ static int perl_vt_New(const char *method,
|
|||
/* call sqlite3_declare_vtab with the sql returned from
|
||||
method VTAB_TO_DECLARE(), converted to utf8 */
|
||||
SV *sql = POPs;
|
||||
int rc;
|
||||
rc = sqlite3_declare_vtab(db, SvPVutf8_nolen(sql));
|
||||
|
||||
/* record the VirtualTable perl instance within the vtab structure */
|
||||
vt->perl_vtab_obj = SvREFCNT_inc(perl_vtab_obj);
|
||||
|
||||
cleanup:
|
||||
*ppVTab = &vt->base;
|
||||
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 SQLITE_OK;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
@ -2937,36 +2918,32 @@ static int perl_vt_Connect(sqlite3 *db, void *pAux,
|
|||
}
|
||||
|
||||
|
||||
static int perl_vt_Disconnect(sqlite3_vtab *pVTab){
|
||||
static int _free_perl_vtab(perl_vtab *pVTab){
|
||||
dTHX;
|
||||
|
||||
_call_perl_vtab_method(pVTab, "DISCONNECT");
|
||||
SvREFCNT_dec(pVTab->perl_vtab_obj);
|
||||
|
||||
perl_vtab *perl_pVTab = (perl_vtab *) pVTab;
|
||||
SvREFCNT_dec(perl_pVTab->perl_vtab_obj);
|
||||
|
||||
sqlite3_free(perl_pVTab);
|
||||
/* 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){
|
||||
dTHX;
|
||||
|
||||
_call_perl_vtab_method(pVTab, "DROP");
|
||||
|
||||
perl_vtab *perl_pVTab = (perl_vtab *) pVTab;
|
||||
SvREFCNT_dec(perl_pVTab->perl_vtab_obj);
|
||||
|
||||
sqlite3_free(perl_pVTab);
|
||||
|
||||
return SQLITE_OK;
|
||||
_call_perl_vtab_method(pVTab, "DROP", 0);
|
||||
return _free_perl_vtab((perl_vtab *)pVTab);
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
op2str(unsigned char op) {
|
||||
_constraint_op_to_string(unsigned char op) {
|
||||
switch (op) {
|
||||
case SQLITE_INDEX_CONSTRAINT_EQ:
|
||||
return "=";
|
||||
|
@ -2987,20 +2964,21 @@ op2str(unsigned char op) {
|
|||
|
||||
|
||||
static int perl_vt_BestIndex(sqlite3_vtab *pVTab, sqlite3_index_info *pIdxInfo){
|
||||
int i, count;
|
||||
|
||||
dTHX;
|
||||
dSP;
|
||||
ENTER;
|
||||
SAVETMPS;
|
||||
|
||||
int i, count;
|
||||
|
||||
/* build the "where_constraints" datastructure */
|
||||
AV *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(op2str(pCons->op), 0));
|
||||
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));
|
||||
}
|
||||
|
@ -3015,7 +2993,7 @@ static int perl_vt_BestIndex(sqlite3_vtab *pVTab, sqlite3_index_info *pIdxInfo){
|
|||
av_push( order_by, newRV_noinc((SV*) order));
|
||||
}
|
||||
|
||||
/* call the ->best_index() method */
|
||||
/* call the ->BEST_INDEX() method */
|
||||
PUSHMARK(SP);
|
||||
XPUSHs( ((perl_vtab *) pVTab)->perl_vtab_obj);
|
||||
XPUSHs( sv_2mortal( newRV_noinc((SV*) constraints)));
|
||||
|
@ -3038,7 +3016,7 @@ static int perl_vt_BestIndex(sqlite3_vtab *pVTab, sqlite3_index_info *pIdxInfo){
|
|||
if (val && SvOK(*val)) {
|
||||
STRLEN len;
|
||||
char *str = SvPVutf8(*val, len);
|
||||
pIdxInfo->idxStr = sqlite3_malloc(len+1);
|
||||
pIdxInfo->idxStr = sqlite3_malloc(len+1);
|
||||
memcpy(pIdxInfo->idxStr, str, len);
|
||||
pIdxInfo->idxStr[len] = 0;
|
||||
pIdxInfo->needToFreeIdxStr = 1;
|
||||
|
@ -3053,17 +3031,15 @@ static int perl_vt_BestIndex(sqlite3_vtab *pVTab, sqlite3_index_info *pIdxInfo){
|
|||
/* 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++){
|
||||
struct sqlite3_index_constraint_usage *pConsUsage
|
||||
= &pIdxInfo->aConstraintUsage[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 = (HV*)SvRV(*rv);
|
||||
SV **val;
|
||||
val = hv_fetch(hv, "argvIndex", 9, FALSE);
|
||||
|
||||
HV *hv = (HV*)SvRV(*rv);
|
||||
SV **val = hv_fetch(hv, "argvIndex", 9, FALSE);
|
||||
int argvIndex = (val && SvOK(*val)) ? SvIV(*val) + 1: 0;
|
||||
|
||||
struct sqlite3_index_constraint_usage *pConsUsage
|
||||
= &pIdxInfo->aConstraintUsage[i];
|
||||
pConsUsage->argvIndex = argvIndex;
|
||||
val = hv_fetch(hv, "omit", 4, FALSE);
|
||||
pConsUsage->omit = (val && SvTRUE(*val)) ? 1 : 0;
|
||||
|
@ -3085,6 +3061,14 @@ static int perl_vt_Open(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
|||
SAVETMPS;
|
||||
|
||||
int count;
|
||||
int rc = SQLITE_ERROR;
|
||||
SV *perl_cursor;
|
||||
|
||||
/* allocate a perl_vtab_cursor structure */
|
||||
perl_vtab_cursor *cursor;
|
||||
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);
|
||||
|
@ -3092,27 +3076,36 @@ static int perl_vt_Open(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
|||
PUTBACK;
|
||||
count = call_method ("OPEN", G_SCALAR);
|
||||
SPAGAIN;
|
||||
if (count != 1)
|
||||
croak("vtab->OPEN() method returned %d vals instead of 1", count);
|
||||
SV *perl_cursor = POPs;
|
||||
if ( !sv_isobject(perl_cursor) )
|
||||
croak("vtab->OPEN() method did not return a blessed cursor");
|
||||
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;
|
||||
}
|
||||
|
||||
/* allocate a perl_vtab_cursor structure */
|
||||
perl_vtab_cursor *cursor;
|
||||
cursor = (perl_vtab_cursor *) sqlite3_malloc(sizeof(*cursor));
|
||||
if( cursor==NULL ) return SQLITE_NOMEM;
|
||||
memset(cursor, 0, sizeof(*cursor));
|
||||
cursor->perl_cursor_obj = SvREFCNT_inc(perl_cursor);
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* return that cursor */
|
||||
*ppCursor = &cursor->base;
|
||||
PUTBACK;
|
||||
FREETMPS;
|
||||
LEAVE;
|
||||
|
||||
return SQLITE_OK;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int perl_vt_Close(sqlite3_vtab_cursor *pVtabCursor){
|
||||
|
@ -3122,8 +3115,8 @@ static int perl_vt_Close(sqlite3_vtab_cursor *pVtabCursor){
|
|||
SAVETMPS;
|
||||
int count;
|
||||
|
||||
/* Note : no call to a CLOSE() method; if needed, the Perl class
|
||||
can implement a DESTROY() method */
|
||||
/* Note : there is no explicit call to a CLOSE() method; if
|
||||
needed, the Perl class can implement a DESTROY() method */
|
||||
|
||||
perl_vtab_cursor *perl_pVTabCursor = (perl_vtab_cursor *) pVtabCursor;
|
||||
SvREFCNT_dec(perl_pVTabCursor->perl_cursor_obj);
|
||||
|
@ -3136,12 +3129,9 @@ static int perl_vt_Close(sqlite3_vtab_cursor *pVtabCursor){
|
|||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int perl_vt_Filter(
|
||||
sqlite3_vtab_cursor *pVtabCursor,
|
||||
int idxNum, const char *idxStr,
|
||||
int argc, sqlite3_value **argv
|
||||
){
|
||||
|
||||
static int perl_vt_Filter( sqlite3_vtab_cursor *pVtabCursor,
|
||||
int idxNum, const char *idxStr,
|
||||
int argc, sqlite3_value **argv ){
|
||||
dTHX;
|
||||
dSP;
|
||||
ENTER;
|
||||
|
@ -3230,6 +3220,7 @@ static int perl_vt_Column(sqlite3_vtab_cursor *pVtabCursor,
|
|||
ENTER;
|
||||
SAVETMPS;
|
||||
int count;
|
||||
int rc = SQLITE_ERROR;
|
||||
|
||||
/* call the column() method */
|
||||
PUSHMARK(SP);
|
||||
|
@ -3246,21 +3237,24 @@ static int perl_vt_Column(sqlite3_vtab_cursor *pVtabCursor,
|
|||
else {
|
||||
SV *result = POPs;
|
||||
sqlite_set_result(aTHX_ context, result, 0 );
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
|
||||
PUTBACK;
|
||||
FREETMPS;
|
||||
LEAVE;
|
||||
|
||||
return SQLITE_OK;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int perl_vt_Rowid(sqlite3_vtab_cursor *pVtabCursor, sqlite3_int64 *pRowid){
|
||||
static int perl_vt_Rowid( sqlite3_vtab_cursor *pVtabCursor,
|
||||
sqlite3_int64 *pRowid ){
|
||||
dTHX;
|
||||
dSP;
|
||||
ENTER;
|
||||
SAVETMPS;
|
||||
int count;
|
||||
int rc = SQLITE_ERROR;
|
||||
|
||||
/* call the rowid() method */
|
||||
PUSHMARK(SP);
|
||||
|
@ -3274,32 +3268,33 @@ static int perl_vt_Rowid(sqlite3_vtab_cursor *pVtabCursor, sqlite3_int64 *pRowid
|
|||
}
|
||||
else {
|
||||
*pRowid =POPi;
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
|
||||
PUTBACK;
|
||||
FREETMPS;
|
||||
LEAVE;
|
||||
|
||||
return SQLITE_OK;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int perl_vt_Update(sqlite3_vtab *pVTab,
|
||||
int argc, sqlite3_value **argv,
|
||||
sqlite3_int64 *pRowid){
|
||||
static int perl_vt_Update( sqlite3_vtab *pVTab,
|
||||
int argc, sqlite3_value **argv,
|
||||
sqlite3_int64 *pRowid ){
|
||||
dTHX;
|
||||
dSP;
|
||||
ENTER;
|
||||
SAVETMPS;
|
||||
int count, i;
|
||||
int is_unicode = _last_dbh_is_unicode();
|
||||
int rc = SQLITE_ERROR;
|
||||
|
||||
/* call the update() method */
|
||||
/* 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;
|
||||
|
@ -3307,19 +3302,22 @@ static int perl_vt_Update(sqlite3_vtab *pVTab,
|
|||
warn("cursor->_SQLITE_UPDATE() returned %d vals instead of 1", count);
|
||||
SP -= count;
|
||||
}
|
||||
else if (argc > 1 && sqlite3_value_type(argv[0]) == SQLITE_NULL
|
||||
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*/
|
||||
SV *rowidsv = POPs;
|
||||
if (!SvOK(rowidsv))
|
||||
*pRowid = 0;
|
||||
else if (SvUOK(rowidsv))
|
||||
*pRowid = SvUV(rowidsv);
|
||||
else if (SvIOK(rowidsv))
|
||||
*pRowid = SvIV(rowidsv);
|
||||
else
|
||||
*pRowid = SvNV(rowidsv);
|
||||
/* this was an insert without any given rowid, so the result of
|
||||
the method call must be passed in *pRowid*/
|
||||
SV *rowidsv = POPs;
|
||||
if (!SvOK(rowidsv))
|
||||
*pRowid = 0;
|
||||
else if (SvUOK(rowidsv))
|
||||
*pRowid = SvUV(rowidsv);
|
||||
else if (SvIOK(rowidsv))
|
||||
*pRowid = SvIV(rowidsv);
|
||||
else
|
||||
*pRowid = SvNV(rowidsv);
|
||||
}
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
|
@ -3327,36 +3325,89 @@ static int perl_vt_Update(sqlite3_vtab *pVTab,
|
|||
FREETMPS;
|
||||
LEAVE;
|
||||
|
||||
return SQLITE_OK;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static int perl_vt_Begin(sqlite3_vtab *pVTab){
|
||||
return _call_perl_vtab_method(pVTab, "BEGIN_TRANSACTION");
|
||||
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");
|
||||
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");
|
||||
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");
|
||||
return _call_perl_vtab_method(pVTab, "ROLLBACK_TRANSACTION", 0);
|
||||
}
|
||||
|
||||
static int perl_vt_FindMethod(sqlite3_vtab *pVtab, int nArg, const char *zName,
|
||||
static int perl_vt_FindFunction(sqlite3_vtab *pVTab,
|
||||
int nArg, const char *zName,
|
||||
void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||
void **ppArg){
|
||||
croak("VT_FINDMETHOD: not implemented yet"); /* TODO */
|
||||
return SQLITE_OK;
|
||||
dTHX;
|
||||
dSP;
|
||||
ENTER;
|
||||
SAVETMPS;
|
||||
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;
|
||||
|
||||
/* check if that function was already in cache */
|
||||
if (hv_exists(functions, func_name, len)) {
|
||||
SV** 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;
|
||||
}
|
||||
SV *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 = _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;
|
||||
|
@ -3379,7 +3430,6 @@ static int perl_vt_Rename(sqlite3_vtab *pVTab, const char *zNew){
|
|||
rc = POPi;
|
||||
}
|
||||
|
||||
|
||||
PUTBACK;
|
||||
FREETMPS;
|
||||
LEAVE;
|
||||
|
@ -3388,42 +3438,41 @@ static int perl_vt_Rename(sqlite3_vtab *pVTab, const char *zNew){
|
|||
}
|
||||
|
||||
static int perl_vt_Savepoint(sqlite3_vtab *pVTab, int point){
|
||||
return _call_perl_vtab_method_int(pVTab, "SAVEPOINT", point);
|
||||
return _call_perl_vtab_method(pVTab, "SAVEPOINT", point);
|
||||
}
|
||||
|
||||
static int perl_vt_Release(sqlite3_vtab *pVTab, int point){
|
||||
return _call_perl_vtab_method_int(pVTab, "RELEASE", point);
|
||||
return _call_perl_vtab_method(pVTab, "RELEASE", point);
|
||||
}
|
||||
|
||||
static int perl_vt_RollbackTo(sqlite3_vtab *pVTab, int point){
|
||||
return _call_perl_vtab_method_int(pVTab, "ROLLBACK_TO", 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_FindMethod, /\* xFindMethod (optional) *\/ */
|
||||
NULL, /* xFindMethod not implemented yet */
|
||||
perl_vt_Rename, /* xRename */
|
||||
perl_vt_Savepoint, /* xSavepoint (optional) */
|
||||
perl_vt_Release, /* xRelease (optional) */
|
||||
perl_vt_RollbackTo /* xRollbackTo (optional) */
|
||||
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 */
|
||||
perl_vt_Savepoint, /* xSavepoint (optional) */
|
||||
perl_vt_Release, /* xRelease (optional) */
|
||||
perl_vt_RollbackTo /* xRollbackTo (optional) */
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -161,7 +161,7 @@ sub ROLLBACK_TRANSACTION {return 0}
|
|||
sub SAVEPOINT {return 0}
|
||||
sub RELEASE {return 0}
|
||||
sub ROLLBACK_TO {return 0}
|
||||
sub FIND_METHOD {return 0}
|
||||
sub FIND_FUNCTION {return 0}
|
||||
sub RENAME {return 0}
|
||||
|
||||
|
||||
|
@ -196,12 +196,30 @@ sub NEW {
|
|||
}
|
||||
|
||||
|
||||
# methods to be redefined in subclasses (here are stupid implementations)
|
||||
sub FILTER { my ($self, $idxNum, $idxStr, @values) = @_; return }
|
||||
sub EOF { my ($self) = @_; return 1 }
|
||||
sub NEXT { my ($self) = @_; return }
|
||||
sub COLUMN { my ($self, $idxCol) = @_; return }
|
||||
sub ROWID { my ($self) = @_; return 1 }
|
||||
sub FILTER {
|
||||
my ($self, $idxNum, $idxStr, @values) = @_;
|
||||
die "FILTER() should be redefined in cursor subclass";
|
||||
}
|
||||
|
||||
sub EOF {
|
||||
my ($self) = @_;
|
||||
die "EOF() should be redefined in cursor subclass";
|
||||
}
|
||||
|
||||
sub NEXT {
|
||||
my ($self) = @_;
|
||||
die "NEXT() should be redefined in cursor subclass";
|
||||
}
|
||||
|
||||
sub COLUMN {
|
||||
my ($self, $idxCol) = @_;
|
||||
die "COLUMN() should be redefined in cursor subclass";
|
||||
}
|
||||
|
||||
sub ROWID {
|
||||
my ($self) = @_;
|
||||
die "ROWID() should be redefined in cursor subclass";
|
||||
}
|
||||
|
||||
|
||||
1;
|
||||
|
@ -347,11 +365,10 @@ The default implementation just calls L</NEW>.
|
|||
|
||||
$class->_PREPARE_SELF($dbh_ref, $module_name, $db_name, $vtab_name, @args);
|
||||
|
||||
Prepares the datastructure for a virtual table instance.
|
||||
C<@args> is just the collection
|
||||
of strings (comma-separated) that were given within the
|
||||
C<CREATE VIRTUAL TABLE> statement; each subclass should decide
|
||||
what to do with this information,
|
||||
Prepares the datastructure for a virtual table instance. C<@args> is
|
||||
just the collection of strings (comma-separated) that were given
|
||||
within the C<CREATE VIRTUAL TABLE> statement; each subclass should
|
||||
decide what to do with this information,
|
||||
|
||||
The method parses C<@args> to differentiate between I<options>
|
||||
(strings of shape C<$key>=C<$value> or C<$key>=C<"$value">, stored in
|
||||
|
@ -437,8 +454,11 @@ The default implementation for DISCONNECT is empty.
|
|||
This method is called automatically just after L</CREATE> or L</CONNECT>,
|
||||
to register the columns of the virtual table within the sqlite kernel.
|
||||
The method should return a string containing a SQL C<CREATE TABLE> statement;
|
||||
but only the column declaration parts will be considered (see
|
||||
L<http://sqlite.org/c3ref/declare_vtab.html>).
|
||||
but only the column declaration parts will be considered.
|
||||
Columns may be declared with the special keyword "HIDDEN", which means that
|
||||
they are used internally for the the virtual table implementation, and are
|
||||
not visible to users -- see L<http://sqlite.org/c3ref/declare_vtab.html>
|
||||
and L<http://www.sqlite.org/vtab.html#hiddencol> for detailed explanations.
|
||||
|
||||
The default implementation returns:
|
||||
|
||||
|
@ -637,6 +657,22 @@ could be changed from a SQL statement such as
|
|||
|
||||
UPDATE table SET rowid=rowid+1 WHERE ...;
|
||||
|
||||
=head3 FIND_FUNCTION
|
||||
|
||||
$vtab->FIND_FUNCTION($num_args, $func_name);
|
||||
|
||||
When a function uses a column from a virtual table as its first
|
||||
argument, this method is called to see if the virtual table would like
|
||||
to overload the function. Parameters are the number of arguments to
|
||||
the function, and the name of the function. If no overloading is
|
||||
desired, this method should return false. To overload the function,
|
||||
this method should return a coderef to the function implementation.
|
||||
|
||||
Each virtual table keeps a cache of results from L<FIND_FUNCTION> calls,
|
||||
so the method will be called only once for each pair
|
||||
C<< ($num_args, $func_name) >>.
|
||||
|
||||
|
||||
=head3 BEGIN_TRANSACTION
|
||||
|
||||
Called to begin a transaction on the virtual table.
|
||||
|
|
|
@ -73,7 +73,8 @@ sub NEW {
|
|||
$self->{columns} = [ "$self->{options}{content_col} TEXT",
|
||||
map {"$_ $src_col{$_}"} @exposed_cols ];
|
||||
|
||||
# acquire a coderef to the get_content() implementation
|
||||
# acquire a coderef to the get_content() implementation, which
|
||||
# was given as a symbolic reference in %options
|
||||
no strict 'refs';
|
||||
$self->{get_content} = \ &{$self->{options}{get_content}};
|
||||
|
||||
|
@ -179,7 +180,7 @@ sub FILTER {
|
|||
# build SQL
|
||||
local $" = ", ";
|
||||
my @cols = @{$vtable->{headers}};
|
||||
$cols[0] = 'rowid'; # replace the content column by the rowid
|
||||
$cols[0] = 'rowid'; # replace the content column by the rowid
|
||||
push @cols, $vtable->{options}{path_col}; # path col in last position
|
||||
my $sql = "SELECT @cols FROM $vtable->{options}{source}";
|
||||
$sql .= " WHERE $idxStr" if $idxStr;
|
||||
|
@ -207,7 +208,6 @@ sub NEXT {
|
|||
$self->{row} = $self->{sth}->fetchrow_arrayref;
|
||||
}
|
||||
|
||||
|
||||
sub COLUMN {
|
||||
my ($self, $idxCol) = @_;
|
||||
|
||||
|
@ -220,14 +220,14 @@ sub ROWID {
|
|||
return $self->{row}[0];
|
||||
}
|
||||
|
||||
|
||||
sub file_content {
|
||||
my ($self) = @_;
|
||||
|
||||
my $root = $self->{vtable}{options}{root};
|
||||
my $path = $self->{row}[-1];
|
||||
my $get_content_func = $self->{vtable}{get_content};
|
||||
|
||||
return $self->{vtable}{get_content}->($path, $root);
|
||||
return $get_content_func->($path, $root);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -43,10 +43,16 @@ use warnings;
|
|||
use base 'DBD::SQLite::VirtualTable';
|
||||
use YAML;
|
||||
|
||||
sub INITIALIZE {
|
||||
my $self = shift;
|
||||
sub NEW {
|
||||
my $class = shift;
|
||||
|
||||
my $self = $class->_PREPARE_SELF(@_);
|
||||
bless $self, $class;
|
||||
|
||||
# stupid pragma call, just to check that the dbh is OK
|
||||
$self->dbh->do("PRAGMA application_id=999");
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
|
||||
|
@ -118,7 +124,6 @@ sub COLUMN {
|
|||
my ($self, $idxCol) = @_;
|
||||
|
||||
return "auto_vivify:$idxCol";
|
||||
return $idxCol;
|
||||
}
|
||||
|
||||
sub ROWID {
|
||||
|
|
|
@ -51,8 +51,8 @@ ok !$DBD::SQLite::VirtualTable::T::CREATE_COUNT, "no vtab created";
|
|||
ok !$DBD::SQLite::VirtualTable::T::CONNECT_COUNT, "no vtab connected";
|
||||
|
||||
my $sth = $dbh->prepare("SELECT * FROM barfoo");
|
||||
ok !$DBD::SQLite::VirtualTable::T::CREATE_COUNT, "no vtab created";
|
||||
is $DBD::SQLite::VirtualTable::T::CONNECT_COUNT, 1, "1 vtab connected";
|
||||
ok !$DBD::SQLite::VirtualTable::T::CREATE_COUNT, "no vtab created";
|
||||
is $DBD::SQLite::VirtualTable::T::CONNECT_COUNT, 1, "1 vtab connected";
|
||||
|
||||
|
||||
package DBD::SQLite::VirtualTable::T;
|
||||
|
|
173
t/virtual_table/02_find_function.t
Normal file
173
t/virtual_table/02_find_function.t
Normal file
|
@ -0,0 +1,173 @@
|
|||
#!/usr/bin/perl
|
||||
use strict;
|
||||
BEGIN {
|
||||
$| = 1;
|
||||
$^W = 1;
|
||||
}
|
||||
|
||||
use t::lib::Test qw/connect_ok/;
|
||||
use Test::More;
|
||||
use Test::NoWarnings;
|
||||
|
||||
plan tests => 15;
|
||||
|
||||
my $dbh = connect_ok( RaiseError => 1, PrintError => 0, AutoCommit => 1 );
|
||||
|
||||
$dbh->sqlite_create_module(vtab => "DBD::SQLite::VirtualTable::T");
|
||||
|
||||
ok $dbh->do("CREATE VIRTUAL TABLE foobar USING vtab(foo INTEGER, bar INTEGER)"),
|
||||
"created foobar";
|
||||
|
||||
# overload functions "abs" and "substr"
|
||||
$DBD::SQLite::VirtualTable::T::funcs{abs}{overloaded}
|
||||
= sub {my $val = shift; return "fake_abs($val)" };
|
||||
$DBD::SQLite::VirtualTable::T::funcs{substr}{overloaded}
|
||||
= sub {my ($val, $offset, $len) = @_; return "fake_substr($val, $offset, $len)" };
|
||||
|
||||
# make a first query
|
||||
my $row = $dbh->selectrow_hashref(<<"");
|
||||
SELECT abs(foo) afoo,
|
||||
abs(bar) abar,
|
||||
substr(foo, 3, 5) sfoo,
|
||||
trim(foo) tfoo
|
||||
FROM foobar
|
||||
|
||||
is $DBD::SQLite::VirtualTable::T::funcs{abs}{calls}, 1, "abs called";
|
||||
is $DBD::SQLite::VirtualTable::T::funcs{substr}{calls}, 1, "substr called";
|
||||
is $DBD::SQLite::VirtualTable::T::funcs{trim}{calls}, 1, "trim called";
|
||||
|
||||
is_deeply $row, { 'abar' => 'fake_abs(1)',
|
||||
'afoo' => 'fake_abs(0)',
|
||||
'sfoo' => 'fake_substr(0, 3, 5)',
|
||||
'tfoo' => '0' }, "func results";
|
||||
|
||||
# new query : FIND_FUNCTION should not be called again
|
||||
$row = $dbh->selectrow_hashref(<<"");
|
||||
SELECT abs(foo) afoo,
|
||||
abs(bar) abar,
|
||||
substr(foo, 3, 5) sfoo,
|
||||
trim(foo) tfoo
|
||||
FROM foobar
|
||||
|
||||
is $DBD::SQLite::VirtualTable::T::funcs{abs}{calls}, 1, "abs still 1";
|
||||
is $DBD::SQLite::VirtualTable::T::funcs{substr}{calls}, 1, "substr still 1";
|
||||
is $DBD::SQLite::VirtualTable::T::funcs{trim}{calls}, 1, "trim still 1";
|
||||
|
||||
|
||||
# new table : should issue new calls to FIND_FUNCTION
|
||||
ok $dbh->do("CREATE VIRTUAL TABLE barfoo USING vtab(foo INTEGER, bar INTEGER)"),
|
||||
"created barfoo";
|
||||
|
||||
$row = $dbh->selectrow_hashref(<<"");
|
||||
SELECT abs(foo) afoo,
|
||||
abs(bar) abar,
|
||||
substr(foo, 3, 5) sfoo,
|
||||
trim(foo) tfoo
|
||||
FROM barfoo
|
||||
|
||||
is $DBD::SQLite::VirtualTable::T::funcs{abs}{calls}, 2, "abs now 2";
|
||||
is $DBD::SQLite::VirtualTable::T::funcs{substr}{calls}, 2, "substr now 2";
|
||||
is $DBD::SQLite::VirtualTable::T::funcs{trim}{calls}, 2, "trim now 2";
|
||||
|
||||
|
||||
# drop table : should free references to functions
|
||||
ok $dbh->do("DROP TABLE foobar");
|
||||
|
||||
# drop connection
|
||||
undef $dbh;
|
||||
|
||||
note "done";
|
||||
|
||||
package DBD::SQLite::VirtualTable::T;
|
||||
use strict;
|
||||
use warnings;
|
||||
use base 'DBD::SQLite::VirtualTable';
|
||||
use YAML;
|
||||
|
||||
|
||||
|
||||
sub BEST_INDEX {
|
||||
my ($self, $constraints, $order_by) = @_;
|
||||
|
||||
my $ix = 0;
|
||||
|
||||
foreach my $constraint (@$constraints) {
|
||||
$constraint->{argvIndex} = $ix++;
|
||||
$constraint->{omit} = 1; # to prevent sqlite core to check values
|
||||
}
|
||||
|
||||
my $outputs = {
|
||||
idxNum => 1,
|
||||
idxStr => "foobar",
|
||||
orderByConsumed => 0,
|
||||
estimatedCost => 1.0,
|
||||
estimatedRows => undef,
|
||||
};
|
||||
|
||||
return $outputs;
|
||||
}
|
||||
|
||||
our %funcs;
|
||||
|
||||
|
||||
sub FIND_FUNCTION {
|
||||
my ($self, $n_arg, $function_name) = @_;
|
||||
|
||||
$funcs{$function_name}{calls} += 1;
|
||||
my $func = $funcs{$function_name}{overloaded};
|
||||
return $func;
|
||||
}
|
||||
|
||||
|
||||
package DBD::SQLite::VirtualTable::T::Cursor;
|
||||
use strict;
|
||||
use warnings;
|
||||
use base 'DBD::SQLite::VirtualTable::Cursor';
|
||||
use YAML;
|
||||
|
||||
sub NEW {
|
||||
my $class = shift;
|
||||
|
||||
my $self = $class->DBD::SQLite::VirtualTable::Cursor::NEW(@_);
|
||||
$self->{row_count} = 5;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub FILTER {
|
||||
my ($self, $idxNum, $idxStr, @values) = @_;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
sub EOF {
|
||||
my $self = shift;
|
||||
|
||||
return !$self->{row_count};
|
||||
}
|
||||
|
||||
sub NEXT {
|
||||
my $self = shift;
|
||||
|
||||
$self->{row_count}--;
|
||||
}
|
||||
|
||||
sub COLUMN {
|
||||
my ($self, $idxCol) = @_;
|
||||
|
||||
return $idxCol;
|
||||
}
|
||||
|
||||
sub ROWID {
|
||||
my ($self) = @_;
|
||||
|
||||
return $self->{row_count};
|
||||
}
|
||||
|
||||
|
||||
1;
|
||||
|
||||
|
||||
|
Loading…
Add table
Reference in a new issue