static int echoTransactionCall(sqlite3_vtab *tab, const char *zCall){ char *z; echo_vtab *pVtab = (echo_vtab *)tab; z = sqlite3_mprintf("echo(%s)", pVtab->zTableName); if( z==0 ) return SQLITE_NOMEM; appendToEchoModule(pVtab->interp, zCall); appendToEchoModule(pVtab->interp, z); sqlite3_free(z); return SQLITE_OK; }
static int echoCreate( sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVtab, char **pzErr ){ int rc = SQLITE_OK; appendToEchoModule(((EchoModule *)pAux)->interp, "xCreate"); rc = echoConstructor(db, pAux, argc, argv, ppVtab, pzErr); if( rc==SQLITE_OK && argc==5 ){ char *zSql; echo_vtab *pVtab = *(echo_vtab **)ppVtab; pVtab->zLogName = sqlite3_mprintf("%s", argv[4]); zSql = sqlite3_mprintf("CREATE TABLE %Q(logmsg)", pVtab->zLogName); rc = sqlite3_exec(db, zSql, 0, 0, 0); sqlite3_free(zSql); if( rc!=SQLITE_OK ){ *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); } } if( *ppVtab && rc!=SQLITE_OK ){ echoDestructor(*ppVtab); *ppVtab = 0; } if( rc==SQLITE_OK ){ (*(echo_vtab**)ppVtab)->inTransaction = 1; } return rc; }
/* ** Echo virtual table module xFilter method. */ static int echoFilter( sqlite3_vtab_cursor *pVtabCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv ){ int rc; int i; echo_cursor *pCur = (echo_cursor *)pVtabCursor; echo_vtab *pVtab = (echo_vtab *)pVtabCursor->pVtab; sqlite3 *db = pVtab->db; if( simulateVtabError(pVtab, "xFilter") ){ return SQLITE_ERROR; } /* Check that idxNum matches idxStr */ assert( idxNum==hashString(idxStr) ); /* Log arguments to the ::echo_module Tcl variable */ appendToEchoModule(pVtab->interp, "xFilter"); appendToEchoModule(pVtab->interp, idxStr); for(i=0; i<argc; i++){ appendToEchoModule(pVtab->interp, (const char*)sqlite3_value_text(argv[i])); } sqlite3_finalize(pCur->pStmt); pCur->pStmt = 0; /* Prepare the SQL statement created by echoBestIndex and bind the ** runtime parameters passed to this function to it. */ rc = sqlite3_prepare(db, idxStr, -1, &pCur->pStmt, 0); assert( pCur->pStmt || rc!=SQLITE_OK ); for(i=0; rc==SQLITE_OK && i<argc; i++){ rc = sqlite3_bind_value(pCur->pStmt, i+1, argv[i]); } /* If everything was successful, advance to the first row of the scan */ if( rc==SQLITE_OK ){ rc = echoNext(pVtabCursor); } return rc; }
static int echoConstructor( sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVtab, char **pzErr ){ int rc; int i; echo_vtab *pVtab; pVtab = sqlite3MallocZero( sizeof(*pVtab) ); if( !pVtab ){ return SQLITE_NOMEM; } pVtab->interp = ((EchoModule *)pAux)->interp; pVtab->db = db; pVtab->zThis = sqlite3_mprintf("%s", argv[2]); if( !pVtab->zThis ){ echoDestructor((sqlite3_vtab *)pVtab); return SQLITE_NOMEM; } if( argc>3 ){ pVtab->zTableName = sqlite3_mprintf("%s", argv[3]); dequoteString(pVtab->zTableName); if( pVtab->zTableName && pVtab->zTableName[0]=='*' ){ char *z = sqlite3_mprintf("%s%s", argv[2], &(pVtab->zTableName[1])); sqlite3_free(pVtab->zTableName); pVtab->zTableName = z; pVtab->isPattern = 1; } if( !pVtab->zTableName ){ echoDestructor((sqlite3_vtab *)pVtab); return SQLITE_NOMEM; } } for(i=0; i<argc; i++){ appendToEchoModule(pVtab->interp, argv[i]); } rc = echoDeclareVtab(pVtab, db); if( rc!=SQLITE_OK ){ echoDestructor((sqlite3_vtab *)pVtab); return rc; } *ppVtab = &pVtab->base; return SQLITE_OK; }
static int echoConnect( sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVtab, char **pzErr ){ appendToEchoModule(((EchoModule *)pAux)->interp, "xConnect"); return echoConstructor(db, pAux, argc, argv, ppVtab, pzErr); }
static int echoFilter( sqlite3_vtab_cursor *pVtabCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv ){ int rc; int i; echo_cursor *pCur = (echo_cursor *)pVtabCursor; echo_vtab *pVtab = (echo_vtab *)pVtabCursor->pVtab; sqlite3 *db = pVtab->db; if( simulateVtabError(pVtab, "xFilter") ){ return SQLITE_ERROR; } assert( idxNum==hashString(idxStr) ); appendToEchoModule(pVtab->interp, "xFilter"); appendToEchoModule(pVtab->interp, idxStr); for(i=0; i<argc; i++){ appendToEchoModule(pVtab->interp, (const char*)sqlite3_value_text(argv[i])); } sqlite3_finalize(pCur->pStmt); pCur->pStmt = 0; rc = sqlite3_prepare(db, idxStr, -1, &pCur->pStmt, 0); assert( pCur->pStmt || rc!=SQLITE_OK ); for(i=0; rc==SQLITE_OK && i<argc; i++){ rc = sqlite3_bind_value(pCur->pStmt, i+1, argv[i]); } if( rc==SQLITE_OK ){ rc = echoNext(pVtabCursor); } return rc; }
static int echoDestroy(sqlite3_vtab *pVtab){ int rc = SQLITE_OK; echo_vtab *p = (echo_vtab *)pVtab; appendToEchoModule(((echo_vtab *)pVtab)->interp, "xDestroy"); if( p && p->zLogName ){ char *zSql; zSql = sqlite3_mprintf("DROP TABLE %Q", p->zLogName); rc = sqlite3_exec(p->db, zSql, 0, 0, 0); sqlite3_free(zSql); } if( rc==SQLITE_OK ){ rc = echoDestructor(pVtab); } return rc; }
/* ** Echo virtual table module xDestroy method. */ static int echoDestroy(sqlite3_vtab *pVtab){ int rc = SQLITE_OK; echo_vtab *p = (echo_vtab *)pVtab; appendToEchoModule(((echo_vtab *)pVtab)->interp, "xDestroy"); /* Drop the "log" table, if one exists (see echoCreate() for details) */ if( p && p->zLogName ){ char *zSql; zSql = sqlite3_mprintf("DROP TABLE %Q", p->zLogName); rc = sqlite3_exec(p->db, zSql, 0, 0, 0); sqlite3_free(zSql); } if( rc==SQLITE_OK ){ rc = echoDestructor(pVtab); } return rc; }
/* ** Echo virtual table module xCreate method. */ static int echoCreate( sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVtab, char **pzErr ){ int rc = SQLITE_OK; appendToEchoModule(((EchoModule *)pAux)->interp, "xCreate"); rc = echoConstructor(db, pAux, argc, argv, ppVtab, pzErr); /* If there were two arguments passed to the module at the SQL level ** (i.e. "CREATE VIRTUAL TABLE tbl USING echo(arg1, arg2)"), then ** the second argument is used as a table name. Attempt to create ** such a table with a single column, "logmsg". This table will ** be used to log calls to the xUpdate method. It will be deleted ** when the virtual table is DROPed. ** ** Note: The main point of this is to test that we can drop tables ** from within an xDestroy method call. */ if( rc==SQLITE_OK && argc==5 ){ char *zSql; echo_vtab *pVtab = *(echo_vtab **)ppVtab; pVtab->zLogName = sqlite3_mprintf("%s", argv[4]); zSql = sqlite3_mprintf("CREATE TABLE %Q(logmsg)", pVtab->zLogName); rc = sqlite3_exec(db, zSql, 0, 0, 0); sqlite3_free(zSql); if( rc!=SQLITE_OK ){ *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); } } if( *ppVtab && rc!=SQLITE_OK ){ echoDestructor(*ppVtab); *ppVtab = 0; } if( rc==SQLITE_OK ){ (*(echo_vtab**)ppVtab)->inTransaction = 1; } return rc; }
static int echoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ int ii; char *zQuery = 0; char *zNew; int nArg = 0; const char *zSep = "WHERE"; echo_vtab *pVtab = (echo_vtab *)tab; sqlite3_stmt *pStmt = 0; Tcl_Interp *interp = pVtab->interp; int nRow; int useIdx = 0; int rc = SQLITE_OK; int useCost = 0; double cost; int isIgnoreUsable = 0; if( Tcl_GetVar(interp, "echo_module_ignore_usable", TCL_GLOBAL_ONLY) ){ isIgnoreUsable = 1; } if( simulateVtabError(pVtab, "xBestIndex") ){ return SQLITE_ERROR; } if( Tcl_GetVar(interp, "echo_module_cost", TCL_GLOBAL_ONLY) ){ cost = atof(Tcl_GetVar(interp, "echo_module_cost", TCL_GLOBAL_ONLY)); useCost = 1; } else { zQuery = sqlite3_mprintf("SELECT count(*) FROM %Q", pVtab->zTableName); if( !zQuery ){ return SQLITE_NOMEM; } rc = sqlite3_prepare(pVtab->db, zQuery, -1, &pStmt, 0); sqlite3_free(zQuery); if( rc!=SQLITE_OK ){ return rc; } sqlite3_step(pStmt); nRow = sqlite3_column_int(pStmt, 0); rc = sqlite3_finalize(pStmt); if( rc!=SQLITE_OK ){ return rc; } } zQuery = sqlite3_mprintf("SELECT rowid, * FROM %Q", pVtab->zTableName); if( !zQuery ){ return SQLITE_NOMEM; } for(ii=0; ii<pIdxInfo->nConstraint; ii++){ const struct sqlite3_index_constraint *pConstraint; struct sqlite3_index_constraint_usage *pUsage; int iCol; pConstraint = &pIdxInfo->aConstraint[ii]; pUsage = &pIdxInfo->aConstraintUsage[ii]; if( !isIgnoreUsable && !pConstraint->usable ) continue; iCol = pConstraint->iColumn; if( pVtab->aIndex[iCol] || iCol<0 ){ char *zCol = pVtab->aCol[iCol]; char *zOp = 0; useIdx = 1; if( iCol<0 ){ zCol = "rowid"; } switch( pConstraint->op ){ case SQLITE_INDEX_CONSTRAINT_EQ: zOp = "="; break; case SQLITE_INDEX_CONSTRAINT_LT: zOp = "<"; break; case SQLITE_INDEX_CONSTRAINT_GT: zOp = ">"; break; case SQLITE_INDEX_CONSTRAINT_LE: zOp = "<="; break; case SQLITE_INDEX_CONSTRAINT_GE: zOp = ">="; break; case SQLITE_INDEX_CONSTRAINT_MATCH: zOp = "LIKE"; break; } if( zOp[0]=='L' ){ zNew = sqlite3_mprintf(" %s %s LIKE (SELECT '%%'||?||'%%')", zSep, zCol); } else { zNew = sqlite3_mprintf(" %s %s %s ?", zSep, zCol, zOp); } string_concat(&zQuery, zNew, 1, &rc); zSep = "AND"; pUsage->argvIndex = ++nArg; pUsage->omit = 1; } } if( pIdxInfo->nOrderBy==1 && pVtab->aIndex[pIdxInfo->aOrderBy->iColumn] ){ int iCol = pIdxInfo->aOrderBy->iColumn; char *zCol = pVtab->aCol[iCol]; char *zDir = pIdxInfo->aOrderBy->desc?"DESC":"ASC"; if( iCol<0 ){ zCol = "rowid"; } zNew = sqlite3_mprintf(" ORDER BY %s %s", zCol, zDir); string_concat(&zQuery, zNew, 1, &rc); pIdxInfo->orderByConsumed = 1; } appendToEchoModule(pVtab->interp, "xBestIndex");; appendToEchoModule(pVtab->interp, zQuery); if( !zQuery ){ return rc; } pIdxInfo->idxNum = hashString(zQuery); pIdxInfo->idxStr = zQuery; pIdxInfo->needToFreeIdxStr = 1; if( useCost ){ pIdxInfo->estimatedCost = cost; }else if( useIdx ){ for( ii=0; ii<(sizeof(int)*8); ii++ ){ if( nRow & (1<<ii) ){ pIdxInfo->estimatedCost = (double)ii; } } }else{ pIdxInfo->estimatedCost = (double)nRow; } return rc; }
static int echoDisconnect(sqlite3_vtab *pVtab){ appendToEchoModule(((echo_vtab *)pVtab)->interp, "xDisconnect"); return echoDestructor(pVtab); }
/* ** The echo module implements the subset of query constraints and sort ** orders that may take advantage of SQLite indices on the underlying ** real table. For example, if the real table is declared as: ** ** CREATE TABLE real(a, b, c); ** CREATE INDEX real_index ON real(b); ** ** then the echo module handles WHERE or ORDER BY clauses that refer ** to the column "b", but not "a" or "c". If a multi-column index is ** present, only its left most column is considered. ** ** This xBestIndex method encodes the proposed search strategy as ** an SQL query on the real table underlying the virtual echo module ** table and stores the query in sqlite3_index_info.idxStr. The SQL ** statement is of the form: ** ** SELECT rowid, * FROM <real-table> ?<where-clause>? ?<order-by-clause>? ** ** where the <where-clause> and <order-by-clause> are determined ** by the contents of the structure pointed to by the pIdxInfo argument. */ static int echoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ int ii; char *zQuery = 0; char *zCol = 0; char *zNew; int nArg = 0; const char *zSep = "WHERE"; echo_vtab *pVtab = (echo_vtab *)tab; sqlite3_stmt *pStmt = 0; Tcl_Interp *interp = pVtab->interp; int nRow = 0; int useIdx = 0; int rc = SQLITE_OK; int useCost = 0; double cost = 0; int isIgnoreUsable = 0; if( Tcl_GetVar(interp, "echo_module_ignore_usable", TCL_GLOBAL_ONLY) ){ isIgnoreUsable = 1; } if( simulateVtabError(pVtab, "xBestIndex") ){ return SQLITE_ERROR; } /* Determine the number of rows in the table and store this value in local ** variable nRow. The 'estimated-cost' of the scan will be the number of ** rows in the table for a linear scan, or the log (base 2) of the ** number of rows if the proposed scan uses an index. */ if( Tcl_GetVar(interp, "echo_module_cost", TCL_GLOBAL_ONLY) ){ cost = atof(Tcl_GetVar(interp, "echo_module_cost", TCL_GLOBAL_ONLY)); useCost = 1; } else { zQuery = sqlite3_mprintf("SELECT count(*) FROM %Q", pVtab->zTableName); if( !zQuery ){ return SQLITE_NOMEM; } rc = sqlite3_prepare(pVtab->db, zQuery, -1, &pStmt, 0); sqlite3_free(zQuery); if( rc!=SQLITE_OK ){ return rc; } sqlite3_step(pStmt); nRow = sqlite3_column_int(pStmt, 0); rc = sqlite3_finalize(pStmt); if( rc!=SQLITE_OK ){ return rc; } } zCol = echoSelectList(pVtab, pIdxInfo); if( !zCol ) return SQLITE_NOMEM; zQuery = sqlite3_mprintf("SELECT rowid%z FROM %Q", zCol, pVtab->zTableName); if( !zQuery ) return SQLITE_NOMEM; for(ii=0; ii<pIdxInfo->nConstraint; ii++){ const struct sqlite3_index_constraint *pConstraint; struct sqlite3_index_constraint_usage *pUsage; int iCol; pConstraint = &pIdxInfo->aConstraint[ii]; pUsage = &pIdxInfo->aConstraintUsage[ii]; if( !isIgnoreUsable && !pConstraint->usable ) continue; iCol = pConstraint->iColumn; if( iCol<0 || pVtab->aIndex[iCol] ){ char *zCol = iCol>=0 ? pVtab->aCol[iCol] : "rowid"; char *zOp = 0; useIdx = 1; switch( pConstraint->op ){ case SQLITE_INDEX_CONSTRAINT_EQ: zOp = "="; break; case SQLITE_INDEX_CONSTRAINT_LT: zOp = "<"; break; case SQLITE_INDEX_CONSTRAINT_GT: zOp = ">"; break; case SQLITE_INDEX_CONSTRAINT_LE: zOp = "<="; break; case SQLITE_INDEX_CONSTRAINT_GE: zOp = ">="; break; case SQLITE_INDEX_CONSTRAINT_MATCH: /* Purposely translate the MATCH operator into a LIKE, which ** will be used by the next block of code to construct a new ** query. It should also be noted here that the next block ** of code requires the first letter of this operator to be ** in upper-case to trigger the special MATCH handling (i.e. ** wrapping the bound parameter with literal '%'s). */ zOp = "LIKE"; break; case SQLITE_INDEX_CONSTRAINT_LIKE: zOp = "like"; break; case SQLITE_INDEX_CONSTRAINT_GLOB: zOp = "glob"; break; case SQLITE_INDEX_CONSTRAINT_REGEXP: zOp = "regexp"; break; } if( zOp[0]=='L' ){ zNew = sqlite3_mprintf(" %s %s LIKE (SELECT '%%'||?||'%%')", zSep, zCol); } else { zNew = sqlite3_mprintf(" %s %s %s ?", zSep, zCol, zOp); } string_concat(&zQuery, zNew, 1, &rc); zSep = "AND"; pUsage->argvIndex = ++nArg; pUsage->omit = 1; } } /* If there is only one term in the ORDER BY clause, and it is ** on a column that this virtual table has an index for, then consume ** the ORDER BY clause. */ if( pIdxInfo->nOrderBy==1 && ( pIdxInfo->aOrderBy->iColumn<0 || pVtab->aIndex[pIdxInfo->aOrderBy->iColumn]) ){ int iCol = pIdxInfo->aOrderBy->iColumn; char *zCol = iCol>=0 ? pVtab->aCol[iCol] : "rowid"; char *zDir = pIdxInfo->aOrderBy->desc?"DESC":"ASC"; zNew = sqlite3_mprintf(" ORDER BY %s %s", zCol, zDir); string_concat(&zQuery, zNew, 1, &rc); pIdxInfo->orderByConsumed = 1; } appendToEchoModule(pVtab->interp, "xBestIndex");; appendToEchoModule(pVtab->interp, zQuery); if( !zQuery ){ return rc; } pIdxInfo->idxNum = hashString(zQuery); pIdxInfo->idxStr = zQuery; pIdxInfo->needToFreeIdxStr = 1; if( useCost ){ pIdxInfo->estimatedCost = cost; }else if( useIdx ){ /* Approximation of log2(nRow). */ for( ii=0; ii<(sizeof(int)*8)-1; ii++ ){ if( nRow & (1<<ii) ){ pIdxInfo->estimatedCost = (double)ii; } } }else{ pIdxInfo->estimatedCost = (double)nRow; } return rc; }
/* ** This function is called to do the work of the xConnect() method - ** to allocate the required in-memory structures for a newly connected ** virtual table. */ static int echoConstructor( sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVtab, char **pzErr ){ int rc; int i; echo_vtab *pVtab; /* Allocate the sqlite3_vtab/echo_vtab structure itself */ pVtab = sqlite3MallocZero( sizeof(*pVtab) ); if( !pVtab ){ return SQLITE_NOMEM; } pVtab->interp = ((EchoModule *)pAux)->interp; pVtab->db = db; /* Allocate echo_vtab.zThis */ pVtab->zThis = sqlite3_mprintf("%s", argv[2]); if( !pVtab->zThis ){ echoDestructor((sqlite3_vtab *)pVtab); return SQLITE_NOMEM; } /* Allocate echo_vtab.zTableName */ if( argc>3 ){ pVtab->zTableName = sqlite3_mprintf("%s", argv[3]); dequoteString(pVtab->zTableName); if( pVtab->zTableName && pVtab->zTableName[0]=='*' ){ char *z = sqlite3_mprintf("%s%s", argv[2], &(pVtab->zTableName[1])); sqlite3_free(pVtab->zTableName); pVtab->zTableName = z; pVtab->isPattern = 1; } if( !pVtab->zTableName ){ echoDestructor((sqlite3_vtab *)pVtab); return SQLITE_NOMEM; } } /* Log the arguments to this function to Tcl var ::echo_module */ for(i=0; i<argc; i++){ appendToEchoModule(pVtab->interp, argv[i]); } /* Invoke sqlite3_declare_vtab and set up other members of the echo_vtab ** structure. If an error occurs, delete the sqlite3_vtab structure and ** return an error code. */ rc = echoDeclareVtab(pVtab, db); if( rc!=SQLITE_OK ){ echoDestructor((sqlite3_vtab *)pVtab); return rc; } /* Success. Set *ppVtab and return */ *ppVtab = &pVtab->base; return SQLITE_OK; }