/* ** 2006 June 10 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** Code for testing the virtual table interfaces. This code ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** ** $Id: test8.c,v 1.9 2006/06/13 14:16:59 danielk1977 Exp $ */ #include "sqliteInt.h" #include "tcl.h" #include "os.h" #include #include typedef struct echo_vtab echo_vtab; typedef struct echo_cursor echo_cursor; /* ** An echo virtual-table object ** ** If it is not NULL, the aHasIndex array is allocated so that it has ** the same number of entries as there are columns in the underlying ** real table. */ struct echo_vtab { sqlite3_vtab base; Tcl_Interp *interp; sqlite3 *db; char *zStmt; /* "SELECT rowid, * FROM " */ int *aIndex; int nCol; char **aCol; }; /* An echo cursor object */ struct echo_cursor { sqlite3_vtab_cursor base; sqlite3_stmt *pStmt; int errcode; /* Error code */ }; static int getColumnNames( sqlite3 *db, const char *zTab, char ***paCol, int *pnCol ){ char **aCol = 0; char zBuf[1024]; sqlite3_stmt *pStmt = 0; int rc = SQLITE_OK; int nCol; sprintf(zBuf, "SELECT * FROM %s", zTab); rc = sqlite3_prepare(db, zBuf, -1, &pStmt, 0); if( rc==SQLITE_OK ){ int ii; nCol = sqlite3_column_count(pStmt); aCol = sqliteMalloc(sizeof(char *) * nCol); if( !aCol ){ rc = SQLITE_NOMEM; goto fail; } for(ii=0; ii=0 && cidzStmt = sqlite3MPrintf("SELECT rowid, * FROM %s ", argv[1]); if( rc==SQLITE_OK ){ rc = getIndexArray(db, argv[1], &pVtab->aIndex); } if( rc==SQLITE_OK ){ rc = getColumnNames(db, argv[1], &pVtab->aCol, &pVtab->nCol); } } return rc; } static int echoConstructor( sqlite3 *db, const sqlite3_module *pModule, int argc, char **argv, sqlite3_vtab **ppVtab ){ int i; echo_vtab *pVtab; pVtab = sqliteMalloc( sizeof(*pVtab) ); *ppVtab = &pVtab->base; pVtab->base.pModule = pModule; pVtab->interp = pModule->pAux; pVtab->db = db; for(i=0; iinterp, argv[i]); } echoDeclareVtab(pVtab, db, argc, argv); return 0; } /* Methods for the echo module */ static int echoCreate( sqlite3 *db, const sqlite3_module *pModule, int argc, char **argv, sqlite3_vtab **ppVtab ){ appendToEchoModule((Tcl_Interp *)(pModule->pAux), "xCreate"); return echoConstructor(db, pModule, argc, argv, ppVtab); } static int echoConnect( sqlite3 *db, const sqlite3_module *pModule, int argc, char **argv, sqlite3_vtab **ppVtab ){ appendToEchoModule((Tcl_Interp *)(pModule->pAux), "xConnect"); return echoConstructor(db, pModule, argc, argv, ppVtab); } static int echoDestructor(sqlite3_vtab *pVtab){ int ii; echo_vtab *p = (echo_vtab*)pVtab; sqliteFree(p->zStmt); sqliteFree(p->aIndex); for(ii=0; iinCol; ii++){ sqliteFree(p->aCol[ii]); } sqliteFree(p->aCol); sqliteFree(p); return 0; } static int echoDisconnect(sqlite3_vtab *pVtab){ appendToEchoModule(((echo_vtab *)pVtab)->interp, "xDisconnect"); return echoDestructor(pVtab); } static int echoDestroy(sqlite3_vtab *pVtab){ appendToEchoModule(((echo_vtab *)pVtab)->interp, "xDestroy"); return echoDestructor(pVtab); } static int echoOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ echo_cursor *pCur; pCur = sqliteMalloc(sizeof(echo_cursor)); *ppCursor = (sqlite3_vtab_cursor *)pCur; return SQLITE_OK; } static int echoClose(sqlite3_vtab_cursor *cur){ echo_cursor *pCur = (echo_cursor *)cur; sqlite3_finalize(pCur->pStmt); sqliteFree(pCur); return SQLITE_OK; } static int echoNext(sqlite3_vtab_cursor *cur){ int rc; echo_cursor *pCur = (echo_cursor *)cur; rc = sqlite3_step(pCur->pStmt); if( rc==SQLITE_ROW ){ rc = 1; } else { pCur->errcode = sqlite3_finalize(pCur->pStmt); pCur->pStmt = 0; rc = 0; } return rc; } static int echoColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ int iCol = i + 1; sqlite3_stmt *pStmt = ((echo_cursor *)cur)->pStmt; assert( sqlite3_data_count(pStmt)>iCol ); switch( sqlite3_column_type(pStmt, iCol) ){ case SQLITE_INTEGER: sqlite3_result_int64(ctx, sqlite3_column_int64(pStmt, iCol)); break; case SQLITE_FLOAT: sqlite3_result_double(ctx, sqlite3_column_double(pStmt, iCol)); break; case SQLITE_TEXT: sqlite3_result_text(ctx, sqlite3_column_text(pStmt, iCol), sqlite3_column_bytes(pStmt, iCol), SQLITE_TRANSIENT ); break; case SQLITE_BLOB: sqlite3_result_blob(ctx, sqlite3_column_blob(pStmt, iCol), sqlite3_column_bytes(pStmt, iCol), SQLITE_TRANSIENT ); break; } return SQLITE_OK; } static int echoRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ sqlite3_stmt *pStmt = ((echo_cursor *)cur)->pStmt; *pRowid = sqlite3_column_int64(pStmt, 0); return SQLITE_OK; } static int echoFilter( sqlite3_vtab_cursor *pVtabCursor, int idx, int argc, sqlite3_value **argv ){ int rc; char zBuf[32]; int ii; echo_cursor *pCur = (echo_cursor *)pVtabCursor; echo_vtab *pVtab = (echo_vtab *)pVtabCursor->pVtab; sqlite3 *db = pVtab->db; sprintf(zBuf, "%d", idx); appendToEchoModule(pVtab->interp, "xFilter"); appendToEchoModule(pVtab->interp, zBuf); for(ii=0; iiinterp, sqlite3_value_text(argv[ii])); } sqlite3_finalize(pCur->pStmt); pCur->pStmt = 0; rc = sqlite3_prepare(db, pVtab->zStmt, -1, &pCur->pStmt, 0); if( rc==SQLITE_OK ){ rc = echoNext(pVtabCursor); } return rc; } /* ** 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 it's left most column is considered. */ static int echoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ int ii; char *zWhere = 0; char *zOrder = 0; int nArg = 0; echo_vtab *pVtab = (echo_vtab *)tab; for(ii=0; iinConstraint; ii++){ const struct sqlite3_index_constraint *pConstraint; struct sqlite3_index_constraint_usage *pUsage; pConstraint = &pIdxInfo->aConstraint[ii]; pUsage = &pIdxInfo->aConstraintUsage[ii]; int iCol = pConstraint->iColumn; if( pVtab->aIndex[iCol] ){ char *zCol = pVtab->aCol[iCol]; char *zOp = 0; 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 = "MATCH"; break; } if( zWhere ){ char *zTmp = zWhere; zWhere = sqlite3MPrintf("%s AND %s %s ?", zWhere, zCol, zOp); sqliteFree(zTmp); } else { zWhere = sqlite3MPrintf("WHERE %s %s ?", zCol, zOp); } pUsage->argvIndex = ++nArg; pUsage->omit = 1; } } appendToEchoModule(pVtab->interp, "xBestIndex");; appendToEchoModule(pVtab->interp, zWhere); appendToEchoModule(pVtab->interp, zOrder); sqliteFree(zWhere); sqliteFree(zOrder); pIdxInfo->idxNum = 123; return SQLITE_OK; } /* ** A virtual table module that merely echos method calls into TCL ** variables. */ static sqlite3_module echoModule = { 0, /* iVersion */ "echo", /* zName */ 0, /* pAux */ echoCreate, echoConnect, echoBestIndex, echoDisconnect, echoDestroy, echoOpen, /* xOpen - open a cursor */ echoClose, /* xClose - close a cursor */ echoFilter, /* xFilter - configure scan constraints */ echoNext, /* xNext - advance a cursor */ echoColumn, /* xColumn - read data */ echoRowid /* xRowid - read data */ }; /* ** Decode a pointer to an sqlite3 object. */ static int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb){ *ppDb = (sqlite3*)sqlite3TextToPtr(zA); return TCL_OK; } /* ** Register the echo virtual table module. */ static int register_echo_module( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ sqlite3 *db; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "DB"); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; echoModule.pAux = interp; #ifndef SQLITE_OMIT_VIRTUALTABLE sqlite3_create_module(db, "echo", &echoModule); #endif return TCL_OK; } /* ** Register commands with the TCL interpreter. */ int Sqlitetest8_Init(Tcl_Interp *interp){ static struct { char *zName; Tcl_ObjCmdProc *xProc; void *clientData; } aObjCmd[] = { { "register_echo_module", register_echo_module, 0 }, }; int i; for(i=0; i