1
0
Fork 0
mirror of https://github.com/DBD-SQLite/DBD-SQLite synced 2025-06-07 06:08:38 -04:00

implementation of FIND_FUNCTION, plus a couple of cosmetic changes in various places

This commit is contained in:
Laurent Dami 2014-07-20 18:57:19 +02:00
parent 7af00e7edd
commit 0ec13083f1
7 changed files with 432 additions and 168 deletions

View file

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

@ -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) */
};

View file

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

View file

@ -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);
}

View file

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

View file

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

View 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;