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/rt_96878_fts_contentless_table.t
|
||||||
t/virtual_table/00_base.t
|
t/virtual_table/00_base.t
|
||||||
t/virtual_table/01_destroy.t
|
t/virtual_table/01_destroy.t
|
||||||
|
t/virtual_table/02_find_function.t
|
||||||
t/virtual_table/10_filecontent.t
|
t/virtual_table/10_filecontent.t
|
||||||
t/virtual_table/11_filecontent_fulltext.t
|
t/virtual_table/11_filecontent_fulltext.t
|
||||||
t/virtual_table/20_perldata.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;
|
PUTBACK;
|
||||||
|
|
||||||
FREETMPS;
|
FREETMPS;
|
||||||
LEAVE;
|
LEAVE;
|
||||||
}
|
}
|
||||||
|
@ -2779,6 +2778,7 @@ int sqlite_db_register_fts3_perl_tokenizer(pTHX_ SV *dbh)
|
||||||
typedef struct perl_vtab {
|
typedef struct perl_vtab {
|
||||||
sqlite3_vtab base;
|
sqlite3_vtab base;
|
||||||
SV *perl_vtab_obj;
|
SV *perl_vtab_obj;
|
||||||
|
HV *functions;
|
||||||
} perl_vtab;
|
} perl_vtab;
|
||||||
|
|
||||||
typedef struct perl_vtab_cursor {
|
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) {
|
/* auxiliary routine for generalized method calls. Arg "i" may be unused */
|
||||||
dTHX;
|
static int _call_perl_vtab_method(sqlite3_vtab *pVTab,
|
||||||
dSP;
|
const char *method, int i) {
|
||||||
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) {
|
|
||||||
dTHX;
|
dTHX;
|
||||||
dSP;
|
dSP;
|
||||||
ENTER;
|
ENTER;
|
||||||
|
@ -2831,7 +2810,6 @@ static int _call_perl_vtab_method(sqlite3_vtab *pVTab, const char *method) {
|
||||||
SPAGAIN;
|
SPAGAIN;
|
||||||
SP -= count;
|
SP -= count;
|
||||||
|
|
||||||
|
|
||||||
PUTBACK;
|
PUTBACK;
|
||||||
FREETMPS;
|
FREETMPS;
|
||||||
LEAVE;
|
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,
|
static int perl_vt_New(const char *method,
|
||||||
sqlite3 *db, void *pAux,
|
sqlite3 *db, void *pAux,
|
||||||
int argc, const char *const *argv,
|
int argc, const char *const *argv,
|
||||||
|
@ -2852,11 +2828,13 @@ static int perl_vt_New(const char *method,
|
||||||
perl_vtab *vt;
|
perl_vtab *vt;
|
||||||
perl_vtab_init *init_data = (perl_vtab_init *)pAux;
|
perl_vtab_init *init_data = (perl_vtab_init *)pAux;
|
||||||
int count, i;
|
int count, i;
|
||||||
|
int rc = SQLITE_ERROR;
|
||||||
|
|
||||||
/* allocate a perl_vtab structure */
|
/* allocate a perl_vtab structure */
|
||||||
vt = (perl_vtab *) sqlite3_malloc(sizeof(*vt));
|
vt = (perl_vtab *) sqlite3_malloc(sizeof(*vt));
|
||||||
if( vt==NULL ) return SQLITE_NOMEM;
|
if( vt==NULL ) return SQLITE_NOMEM;
|
||||||
memset(vt, 0, sizeof(*vt));
|
memset(vt, 0, sizeof(*vt));
|
||||||
|
vt->functions = newHV();
|
||||||
|
|
||||||
ENTER;
|
ENTER;
|
||||||
SAVETMPS;
|
SAVETMPS;
|
||||||
|
@ -2907,20 +2885,23 @@ static int perl_vt_New(const char *method,
|
||||||
/* call sqlite3_declare_vtab with the sql returned from
|
/* call sqlite3_declare_vtab with the sql returned from
|
||||||
method VTAB_TO_DECLARE(), converted to utf8 */
|
method VTAB_TO_DECLARE(), converted to utf8 */
|
||||||
SV *sql = POPs;
|
SV *sql = POPs;
|
||||||
int rc;
|
|
||||||
rc = sqlite3_declare_vtab(db, SvPVutf8_nolen(sql));
|
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:
|
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;
|
PUTBACK;
|
||||||
FREETMPS;
|
FREETMPS;
|
||||||
LEAVE;
|
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;
|
dTHX;
|
||||||
|
|
||||||
_call_perl_vtab_method(pVTab, "DISCONNECT");
|
SvREFCNT_dec(pVTab->perl_vtab_obj);
|
||||||
|
|
||||||
perl_vtab *perl_pVTab = (perl_vtab *) pVTab;
|
/* deallocate coderefs that were declared through FindFunction() */
|
||||||
SvREFCNT_dec(perl_pVTab->perl_vtab_obj);
|
hv_undef(pVTab->functions);
|
||||||
|
SvREFCNT_dec(pVTab->functions);
|
||||||
sqlite3_free(perl_pVTab);
|
|
||||||
|
|
||||||
|
sqlite3_free(pVTab);
|
||||||
return SQLITE_OK;
|
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){
|
static int perl_vt_Drop(sqlite3_vtab *pVTab){
|
||||||
dTHX;
|
_call_perl_vtab_method(pVTab, "DROP", 0);
|
||||||
|
return _free_perl_vtab((perl_vtab *)pVTab);
|
||||||
_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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
op2str(unsigned char op) {
|
_constraint_op_to_string(unsigned char op) {
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case SQLITE_INDEX_CONSTRAINT_EQ:
|
case SQLITE_INDEX_CONSTRAINT_EQ:
|
||||||
return "=";
|
return "=";
|
||||||
|
@ -2987,20 +2964,21 @@ op2str(unsigned char op) {
|
||||||
|
|
||||||
|
|
||||||
static int perl_vt_BestIndex(sqlite3_vtab *pVTab, sqlite3_index_info *pIdxInfo){
|
static int perl_vt_BestIndex(sqlite3_vtab *pVTab, sqlite3_index_info *pIdxInfo){
|
||||||
int i, count;
|
|
||||||
|
|
||||||
dTHX;
|
dTHX;
|
||||||
dSP;
|
dSP;
|
||||||
ENTER;
|
ENTER;
|
||||||
SAVETMPS;
|
SAVETMPS;
|
||||||
|
|
||||||
|
int i, count;
|
||||||
|
|
||||||
/* build the "where_constraints" datastructure */
|
/* build the "where_constraints" datastructure */
|
||||||
AV *constraints = newAV();
|
AV *constraints = newAV();
|
||||||
for (i=0; i<pIdxInfo->nConstraint; i++){
|
for (i=0; i<pIdxInfo->nConstraint; i++){
|
||||||
struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[i];
|
struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[i];
|
||||||
HV *constraint = newHV();
|
HV *constraint = newHV();
|
||||||
|
char *op_str = _constraint_op_to_string(pCons->op);
|
||||||
hv_stores(constraint, "col", newSViv(pCons->iColumn));
|
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);
|
hv_stores(constraint, "usable", pCons->usable ? &PL_sv_yes : &PL_sv_no);
|
||||||
av_push(constraints, newRV_noinc((SV*) constraint));
|
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));
|
av_push( order_by, newRV_noinc((SV*) order));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* call the ->best_index() method */
|
/* call the ->BEST_INDEX() method */
|
||||||
PUSHMARK(SP);
|
PUSHMARK(SP);
|
||||||
XPUSHs( ((perl_vtab *) pVTab)->perl_vtab_obj);
|
XPUSHs( ((perl_vtab *) pVTab)->perl_vtab_obj);
|
||||||
XPUSHs( sv_2mortal( newRV_noinc((SV*) constraints)));
|
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)) {
|
if (val && SvOK(*val)) {
|
||||||
STRLEN len;
|
STRLEN len;
|
||||||
char *str = SvPVutf8(*val, len);
|
char *str = SvPVutf8(*val, len);
|
||||||
pIdxInfo->idxStr = sqlite3_malloc(len+1);
|
pIdxInfo->idxStr = sqlite3_malloc(len+1);
|
||||||
memcpy(pIdxInfo->idxStr, str, len);
|
memcpy(pIdxInfo->idxStr, str, len);
|
||||||
pIdxInfo->idxStr[len] = 0;
|
pIdxInfo->idxStr[len] = 0;
|
||||||
pIdxInfo->needToFreeIdxStr = 1;
|
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
|
/* loop over constraints to get back the "argvIndex" and "omit" keys
|
||||||
that shoud have been added by the best_index() method call */
|
that shoud have been added by the best_index() method call */
|
||||||
for (i=0; i<pIdxInfo->nConstraint; i++){
|
for (i=0; i<pIdxInfo->nConstraint; i++){
|
||||||
struct sqlite3_index_constraint_usage *pConsUsage
|
|
||||||
= &pIdxInfo->aConstraintUsage[i];
|
|
||||||
SV **rv = av_fetch(constraints, i, FALSE);
|
SV **rv = av_fetch(constraints, i, FALSE);
|
||||||
if (!(rv && SvROK(*rv) && SvTYPE(SvRV(*rv)) == SVt_PVHV))
|
if (!(rv && SvROK(*rv) && SvTYPE(SvRV(*rv)) == SVt_PVHV))
|
||||||
croak("the call to BEST_INDEX() has corrupted constraint data");
|
croak("the call to BEST_INDEX() has corrupted constraint data");
|
||||||
HV *hv = (HV*)SvRV(*rv);
|
HV *hv = (HV*)SvRV(*rv);
|
||||||
SV **val;
|
SV **val = hv_fetch(hv, "argvIndex", 9, FALSE);
|
||||||
val = hv_fetch(hv, "argvIndex", 9, FALSE);
|
|
||||||
|
|
||||||
int argvIndex = (val && SvOK(*val)) ? SvIV(*val) + 1: 0;
|
int argvIndex = (val && SvOK(*val)) ? SvIV(*val) + 1: 0;
|
||||||
|
|
||||||
|
struct sqlite3_index_constraint_usage *pConsUsage
|
||||||
|
= &pIdxInfo->aConstraintUsage[i];
|
||||||
pConsUsage->argvIndex = argvIndex;
|
pConsUsage->argvIndex = argvIndex;
|
||||||
val = hv_fetch(hv, "omit", 4, FALSE);
|
val = hv_fetch(hv, "omit", 4, FALSE);
|
||||||
pConsUsage->omit = (val && SvTRUE(*val)) ? 1 : 0;
|
pConsUsage->omit = (val && SvTRUE(*val)) ? 1 : 0;
|
||||||
|
@ -3085,6 +3061,14 @@ static int perl_vt_Open(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
||||||
SAVETMPS;
|
SAVETMPS;
|
||||||
|
|
||||||
int count;
|
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 */
|
/* call the ->OPEN() method */
|
||||||
PUSHMARK(SP);
|
PUSHMARK(SP);
|
||||||
|
@ -3092,27 +3076,36 @@ static int perl_vt_Open(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
||||||
PUTBACK;
|
PUTBACK;
|
||||||
count = call_method ("OPEN", G_SCALAR);
|
count = call_method ("OPEN", G_SCALAR);
|
||||||
SPAGAIN;
|
SPAGAIN;
|
||||||
if (count != 1)
|
if (count != 1) {
|
||||||
croak("vtab->OPEN() method returned %d vals instead of 1", count);
|
warn("vtab->OPEN() method returned %d vals instead of 1", count);
|
||||||
SV *perl_cursor = POPs;
|
SP -= count;
|
||||||
if ( !sv_isobject(perl_cursor) )
|
goto cleanup;
|
||||||
croak("vtab->OPEN() method did not return a blessed cursor");
|
|
||||||
|
|
||||||
|
}
|
||||||
|
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 */
|
/* everything went OK */
|
||||||
perl_vtab_cursor *cursor;
|
rc = SQLITE_OK;
|
||||||
cursor = (perl_vtab_cursor *) sqlite3_malloc(sizeof(*cursor));
|
|
||||||
if( cursor==NULL ) return SQLITE_NOMEM;
|
cleanup:
|
||||||
memset(cursor, 0, sizeof(*cursor));
|
|
||||||
cursor->perl_cursor_obj = SvREFCNT_inc(perl_cursor);
|
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;
|
PUTBACK;
|
||||||
FREETMPS;
|
FREETMPS;
|
||||||
LEAVE;
|
LEAVE;
|
||||||
|
|
||||||
return SQLITE_OK;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int perl_vt_Close(sqlite3_vtab_cursor *pVtabCursor){
|
static int perl_vt_Close(sqlite3_vtab_cursor *pVtabCursor){
|
||||||
|
@ -3122,8 +3115,8 @@ static int perl_vt_Close(sqlite3_vtab_cursor *pVtabCursor){
|
||||||
SAVETMPS;
|
SAVETMPS;
|
||||||
int count;
|
int count;
|
||||||
|
|
||||||
/* Note : no call to a CLOSE() method; if needed, the Perl class
|
/* Note : there is no explicit call to a CLOSE() method; if
|
||||||
can implement a DESTROY() method */
|
needed, the Perl class can implement a DESTROY() method */
|
||||||
|
|
||||||
perl_vtab_cursor *perl_pVTabCursor = (perl_vtab_cursor *) pVtabCursor;
|
perl_vtab_cursor *perl_pVTabCursor = (perl_vtab_cursor *) pVtabCursor;
|
||||||
SvREFCNT_dec(perl_pVTabCursor->perl_cursor_obj);
|
SvREFCNT_dec(perl_pVTabCursor->perl_cursor_obj);
|
||||||
|
@ -3136,12 +3129,9 @@ static int perl_vt_Close(sqlite3_vtab_cursor *pVtabCursor){
|
||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int perl_vt_Filter(
|
static int perl_vt_Filter( sqlite3_vtab_cursor *pVtabCursor,
|
||||||
sqlite3_vtab_cursor *pVtabCursor,
|
int idxNum, const char *idxStr,
|
||||||
int idxNum, const char *idxStr,
|
int argc, sqlite3_value **argv ){
|
||||||
int argc, sqlite3_value **argv
|
|
||||||
){
|
|
||||||
|
|
||||||
dTHX;
|
dTHX;
|
||||||
dSP;
|
dSP;
|
||||||
ENTER;
|
ENTER;
|
||||||
|
@ -3230,6 +3220,7 @@ static int perl_vt_Column(sqlite3_vtab_cursor *pVtabCursor,
|
||||||
ENTER;
|
ENTER;
|
||||||
SAVETMPS;
|
SAVETMPS;
|
||||||
int count;
|
int count;
|
||||||
|
int rc = SQLITE_ERROR;
|
||||||
|
|
||||||
/* call the column() method */
|
/* call the column() method */
|
||||||
PUSHMARK(SP);
|
PUSHMARK(SP);
|
||||||
|
@ -3246,21 +3237,24 @@ static int perl_vt_Column(sqlite3_vtab_cursor *pVtabCursor,
|
||||||
else {
|
else {
|
||||||
SV *result = POPs;
|
SV *result = POPs;
|
||||||
sqlite_set_result(aTHX_ context, result, 0 );
|
sqlite_set_result(aTHX_ context, result, 0 );
|
||||||
|
rc = SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
PUTBACK;
|
PUTBACK;
|
||||||
FREETMPS;
|
FREETMPS;
|
||||||
LEAVE;
|
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;
|
dTHX;
|
||||||
dSP;
|
dSP;
|
||||||
ENTER;
|
ENTER;
|
||||||
SAVETMPS;
|
SAVETMPS;
|
||||||
int count;
|
int count;
|
||||||
|
int rc = SQLITE_ERROR;
|
||||||
|
|
||||||
/* call the rowid() method */
|
/* call the rowid() method */
|
||||||
PUSHMARK(SP);
|
PUSHMARK(SP);
|
||||||
|
@ -3274,32 +3268,33 @@ static int perl_vt_Rowid(sqlite3_vtab_cursor *pVtabCursor, sqlite3_int64 *pRowid
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
*pRowid =POPi;
|
*pRowid =POPi;
|
||||||
|
rc = SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
PUTBACK;
|
PUTBACK;
|
||||||
FREETMPS;
|
FREETMPS;
|
||||||
LEAVE;
|
LEAVE;
|
||||||
|
|
||||||
return SQLITE_OK;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int perl_vt_Update(sqlite3_vtab *pVTab,
|
static int perl_vt_Update( sqlite3_vtab *pVTab,
|
||||||
int argc, sqlite3_value **argv,
|
int argc, sqlite3_value **argv,
|
||||||
sqlite3_int64 *pRowid){
|
sqlite3_int64 *pRowid ){
|
||||||
dTHX;
|
dTHX;
|
||||||
dSP;
|
dSP;
|
||||||
ENTER;
|
ENTER;
|
||||||
SAVETMPS;
|
SAVETMPS;
|
||||||
int count, i;
|
int count, i;
|
||||||
int is_unicode = _last_dbh_is_unicode();
|
int is_unicode = _last_dbh_is_unicode();
|
||||||
|
int rc = SQLITE_ERROR;
|
||||||
|
|
||||||
/* call the update() method */
|
/* call the _SQLITE_UPDATE() method */
|
||||||
PUSHMARK(SP);
|
PUSHMARK(SP);
|
||||||
XPUSHs(((perl_vtab *) pVTab)->perl_vtab_obj);
|
XPUSHs(((perl_vtab *) pVTab)->perl_vtab_obj);
|
||||||
for(i = 0; i < argc; i++) {
|
for(i = 0; i < argc; i++) {
|
||||||
XPUSHs(stacked_sv_from_sqlite3_value(aTHX_ argv[i], is_unicode));
|
XPUSHs(stacked_sv_from_sqlite3_value(aTHX_ argv[i], is_unicode));
|
||||||
}
|
}
|
||||||
|
|
||||||
PUTBACK;
|
PUTBACK;
|
||||||
count = call_method ("_SQLITE_UPDATE", G_SCALAR);
|
count = call_method ("_SQLITE_UPDATE", G_SCALAR);
|
||||||
SPAGAIN;
|
SPAGAIN;
|
||||||
|
@ -3307,19 +3302,22 @@ static int perl_vt_Update(sqlite3_vtab *pVTab,
|
||||||
warn("cursor->_SQLITE_UPDATE() returned %d vals instead of 1", count);
|
warn("cursor->_SQLITE_UPDATE() returned %d vals instead of 1", count);
|
||||||
SP -= 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) {
|
&& sqlite3_value_type(argv[1]) == SQLITE_NULL) {
|
||||||
/* this was an insert without any given rowid, so the result of
|
/* this was an insert without any given rowid, so the result of
|
||||||
the method call must be passed in *pRowid*/
|
the method call must be passed in *pRowid*/
|
||||||
SV *rowidsv = POPs;
|
SV *rowidsv = POPs;
|
||||||
if (!SvOK(rowidsv))
|
if (!SvOK(rowidsv))
|
||||||
*pRowid = 0;
|
*pRowid = 0;
|
||||||
else if (SvUOK(rowidsv))
|
else if (SvUOK(rowidsv))
|
||||||
*pRowid = SvUV(rowidsv);
|
*pRowid = SvUV(rowidsv);
|
||||||
else if (SvIOK(rowidsv))
|
else if (SvIOK(rowidsv))
|
||||||
*pRowid = SvIV(rowidsv);
|
*pRowid = SvIV(rowidsv);
|
||||||
else
|
else
|
||||||
*pRowid = SvNV(rowidsv);
|
*pRowid = SvNV(rowidsv);
|
||||||
|
}
|
||||||
|
rc = SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -3327,36 +3325,89 @@ static int perl_vt_Update(sqlite3_vtab *pVTab,
|
||||||
FREETMPS;
|
FREETMPS;
|
||||||
LEAVE;
|
LEAVE;
|
||||||
|
|
||||||
return SQLITE_OK;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static int perl_vt_Begin(sqlite3_vtab *pVTab){
|
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){
|
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){
|
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){
|
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 (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||||
void **ppArg){
|
void **ppArg){
|
||||||
croak("VT_FINDMETHOD: not implemented yet"); /* TODO */
|
dTHX;
|
||||||
return SQLITE_OK;
|
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){
|
static int perl_vt_Rename(sqlite3_vtab *pVTab, const char *zNew){
|
||||||
dTHX;
|
dTHX;
|
||||||
dSP;
|
dSP;
|
||||||
|
@ -3379,7 +3430,6 @@ static int perl_vt_Rename(sqlite3_vtab *pVTab, const char *zNew){
|
||||||
rc = POPi;
|
rc = POPi;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PUTBACK;
|
PUTBACK;
|
||||||
FREETMPS;
|
FREETMPS;
|
||||||
LEAVE;
|
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){
|
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){
|
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){
|
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 = {
|
static sqlite3_module perl_vt_Module = {
|
||||||
1, /* iVersion */
|
1, /* iVersion */
|
||||||
perl_vt_Create, /* xCreate */
|
perl_vt_Create, /* xCreate */
|
||||||
perl_vt_Connect, /* xConnect */
|
perl_vt_Connect, /* xConnect */
|
||||||
perl_vt_BestIndex, /* xBestIndex */
|
perl_vt_BestIndex, /* xBestIndex */
|
||||||
perl_vt_Disconnect, /* xDisconnect */
|
perl_vt_Disconnect, /* xDisconnect */
|
||||||
perl_vt_Drop, /* xDestroy */
|
perl_vt_Drop, /* xDestroy */
|
||||||
perl_vt_Open, /* xOpen - open a cursor */
|
perl_vt_Open, /* xOpen - open a cursor */
|
||||||
perl_vt_Close, /* xClose - close a cursor */
|
perl_vt_Close, /* xClose - close a cursor */
|
||||||
perl_vt_Filter, /* xFilter - configure scan constraints */
|
perl_vt_Filter, /* xFilter - configure scan constraints */
|
||||||
perl_vt_Next, /* xNext - advance a cursor */
|
perl_vt_Next, /* xNext - advance a cursor */
|
||||||
perl_vt_Eof, /* xEof - check for end of scan */
|
perl_vt_Eof, /* xEof - check for end of scan */
|
||||||
perl_vt_Column, /* xColumn - read data */
|
perl_vt_Column, /* xColumn - read data */
|
||||||
perl_vt_Rowid, /* xRowid - read data */
|
perl_vt_Rowid, /* xRowid - read data */
|
||||||
perl_vt_Update, /* xUpdate (optional) */
|
perl_vt_Update, /* xUpdate (optional) */
|
||||||
perl_vt_Begin, /* xBegin (optional) */
|
perl_vt_Begin, /* xBegin (optional) */
|
||||||
perl_vt_Sync, /* xSync (optional) */
|
perl_vt_Sync, /* xSync (optional) */
|
||||||
perl_vt_Commit, /* xCommit (optional) */
|
perl_vt_Commit, /* xCommit (optional) */
|
||||||
perl_vt_Rollback, /* xRollback (optional) */
|
perl_vt_Rollback, /* xRollback (optional) */
|
||||||
/* perl_vt_FindMethod, /\* xFindMethod (optional) *\/ */
|
perl_vt_FindFunction, /* xFindFunction (optional) */
|
||||||
NULL, /* xFindMethod not implemented yet */
|
perl_vt_Rename, /* xRename */
|
||||||
perl_vt_Rename, /* xRename */
|
perl_vt_Savepoint, /* xSavepoint (optional) */
|
||||||
perl_vt_Savepoint, /* xSavepoint (optional) */
|
perl_vt_Release, /* xRelease (optional) */
|
||||||
perl_vt_Release, /* xRelease (optional) */
|
perl_vt_RollbackTo /* xRollbackTo (optional) */
|
||||||
perl_vt_RollbackTo /* xRollbackTo (optional) */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -161,7 +161,7 @@ sub ROLLBACK_TRANSACTION {return 0}
|
||||||
sub SAVEPOINT {return 0}
|
sub SAVEPOINT {return 0}
|
||||||
sub RELEASE {return 0}
|
sub RELEASE {return 0}
|
||||||
sub ROLLBACK_TO {return 0}
|
sub ROLLBACK_TO {return 0}
|
||||||
sub FIND_METHOD {return 0}
|
sub FIND_FUNCTION {return 0}
|
||||||
sub RENAME {return 0}
|
sub RENAME {return 0}
|
||||||
|
|
||||||
|
|
||||||
|
@ -196,12 +196,30 @@ sub NEW {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# methods to be redefined in subclasses (here are stupid implementations)
|
sub FILTER {
|
||||||
sub FILTER { my ($self, $idxNum, $idxStr, @values) = @_; return }
|
my ($self, $idxNum, $idxStr, @values) = @_;
|
||||||
sub EOF { my ($self) = @_; return 1 }
|
die "FILTER() should be redefined in cursor subclass";
|
||||||
sub NEXT { my ($self) = @_; return }
|
}
|
||||||
sub COLUMN { my ($self, $idxCol) = @_; return }
|
|
||||||
sub ROWID { my ($self) = @_; return 1 }
|
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;
|
1;
|
||||||
|
@ -347,11 +365,10 @@ The default implementation just calls L</NEW>.
|
||||||
|
|
||||||
$class->_PREPARE_SELF($dbh_ref, $module_name, $db_name, $vtab_name, @args);
|
$class->_PREPARE_SELF($dbh_ref, $module_name, $db_name, $vtab_name, @args);
|
||||||
|
|
||||||
Prepares the datastructure for a virtual table instance.
|
Prepares the datastructure for a virtual table instance. C<@args> is
|
||||||
C<@args> is just the collection
|
just the collection of strings (comma-separated) that were given
|
||||||
of strings (comma-separated) that were given within the
|
within the C<CREATE VIRTUAL TABLE> statement; each subclass should
|
||||||
C<CREATE VIRTUAL TABLE> statement; each subclass should decide
|
decide what to do with this information,
|
||||||
what to do with this information,
|
|
||||||
|
|
||||||
The method parses C<@args> to differentiate between I<options>
|
The method parses C<@args> to differentiate between I<options>
|
||||||
(strings of shape C<$key>=C<$value> or C<$key>=C<"$value">, stored in
|
(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>,
|
This method is called automatically just after L</CREATE> or L</CONNECT>,
|
||||||
to register the columns of the virtual table within the sqlite kernel.
|
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;
|
The method should return a string containing a SQL C<CREATE TABLE> statement;
|
||||||
but only the column declaration parts will be considered (see
|
but only the column declaration parts will be considered.
|
||||||
L<http://sqlite.org/c3ref/declare_vtab.html>).
|
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:
|
The default implementation returns:
|
||||||
|
|
||||||
|
@ -637,6 +657,22 @@ could be changed from a SQL statement such as
|
||||||
|
|
||||||
UPDATE table SET rowid=rowid+1 WHERE ...;
|
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
|
=head3 BEGIN_TRANSACTION
|
||||||
|
|
||||||
Called to begin a transaction on the virtual table.
|
Called to begin a transaction on the virtual table.
|
||||||
|
|
|
@ -73,7 +73,8 @@ sub NEW {
|
||||||
$self->{columns} = [ "$self->{options}{content_col} TEXT",
|
$self->{columns} = [ "$self->{options}{content_col} TEXT",
|
||||||
map {"$_ $src_col{$_}"} @exposed_cols ];
|
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';
|
no strict 'refs';
|
||||||
$self->{get_content} = \ &{$self->{options}{get_content}};
|
$self->{get_content} = \ &{$self->{options}{get_content}};
|
||||||
|
|
||||||
|
@ -179,7 +180,7 @@ sub FILTER {
|
||||||
# build SQL
|
# build SQL
|
||||||
local $" = ", ";
|
local $" = ", ";
|
||||||
my @cols = @{$vtable->{headers}};
|
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
|
push @cols, $vtable->{options}{path_col}; # path col in last position
|
||||||
my $sql = "SELECT @cols FROM $vtable->{options}{source}";
|
my $sql = "SELECT @cols FROM $vtable->{options}{source}";
|
||||||
$sql .= " WHERE $idxStr" if $idxStr;
|
$sql .= " WHERE $idxStr" if $idxStr;
|
||||||
|
@ -207,7 +208,6 @@ sub NEXT {
|
||||||
$self->{row} = $self->{sth}->fetchrow_arrayref;
|
$self->{row} = $self->{sth}->fetchrow_arrayref;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub COLUMN {
|
sub COLUMN {
|
||||||
my ($self, $idxCol) = @_;
|
my ($self, $idxCol) = @_;
|
||||||
|
|
||||||
|
@ -220,14 +220,14 @@ sub ROWID {
|
||||||
return $self->{row}[0];
|
return $self->{row}[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub file_content {
|
sub file_content {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
|
|
||||||
my $root = $self->{vtable}{options}{root};
|
my $root = $self->{vtable}{options}{root};
|
||||||
my $path = $self->{row}[-1];
|
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 base 'DBD::SQLite::VirtualTable';
|
||||||
use YAML;
|
use YAML;
|
||||||
|
|
||||||
sub INITIALIZE {
|
sub NEW {
|
||||||
my $self = shift;
|
my $class = shift;
|
||||||
|
|
||||||
|
my $self = $class->_PREPARE_SELF(@_);
|
||||||
|
bless $self, $class;
|
||||||
|
|
||||||
# stupid pragma call, just to check that the dbh is OK
|
# stupid pragma call, just to check that the dbh is OK
|
||||||
$self->dbh->do("PRAGMA application_id=999");
|
$self->dbh->do("PRAGMA application_id=999");
|
||||||
|
|
||||||
|
return $self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -118,7 +124,6 @@ sub COLUMN {
|
||||||
my ($self, $idxCol) = @_;
|
my ($self, $idxCol) = @_;
|
||||||
|
|
||||||
return "auto_vivify:$idxCol";
|
return "auto_vivify:$idxCol";
|
||||||
return $idxCol;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub ROWID {
|
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";
|
ok !$DBD::SQLite::VirtualTable::T::CONNECT_COUNT, "no vtab connected";
|
||||||
|
|
||||||
my $sth = $dbh->prepare("SELECT * FROM barfoo");
|
my $sth = $dbh->prepare("SELECT * FROM barfoo");
|
||||||
ok !$DBD::SQLite::VirtualTable::T::CREATE_COUNT, "no vtab created";
|
ok !$DBD::SQLite::VirtualTable::T::CREATE_COUNT, "no vtab created";
|
||||||
is $DBD::SQLite::VirtualTable::T::CONNECT_COUNT, 1, "1 vtab connected";
|
is $DBD::SQLite::VirtualTable::T::CONNECT_COUNT, 1, "1 vtab connected";
|
||||||
|
|
||||||
|
|
||||||
package DBD::SQLite::VirtualTable::T;
|
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