From cc013f891ce1490d2fa7e173391ca155b05b673e Mon Sep 17 00:00:00 2001 From: danielk1977 Date: Sat, 24 Jun 2006 06:36:11 +0000 Subject: [PATCH] Clean up and clarify code in test8.c. (CVS 3289) FossilOrigin-Name: 4acf7594a6c47142e7112d2cd9766a563401879b --- manifest | 14 +- manifest.uuid | 2 +- src/test8.c | 351 +++++++++++++++++++++++++++++++++----------------- src/vdbeInt.h | 2 +- 4 files changed, 244 insertions(+), 125 deletions(-) diff --git a/manifest b/manifest index 74005e2320..3b92deab88 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Modify\sthe\stest\scases\sin\stkt1444.test\sthat\swere\sfailing.\sI\sam\sconvinced\sthat\sthe\stest\scases\swere\sincorrect.\s(CVS\s3288) -D 2006-06-23T14:43:30 +C Clean\sup\sand\sclarify\scode\sin\stest8.c.\s(CVS\s3289) +D 2006-06-24T06:36:11 F Makefile.in f839b470345d3cb4b0644068474623fe2464b5d3 F Makefile.linux-gcc 2d8574d1ba75f129aba2019f0b959db380a90935 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028 @@ -84,7 +84,7 @@ F src/test4.c 8b784cd82de158a2317cb4ac4bc86f91ad315e25 F src/test5.c 7162f8526affb771c4ed256826eee7bb9eca265f F src/test6.c 60a02961ceb7b3edc25f5dc5c1ac2556622a76de F src/test7.c 03fa8d787f6aebc6d1f72504d52f33013ad2c8e3 -F src/test8.c 054989bf4b6f10ffac090e24ce6843662ebb9b91 +F src/test8.c c7aa1d069087935f3d4eecd685c80a8d4426ece0 F src/test_async.c e3deaedd4d86a56391b81808fde9e44fbd92f1d3 F src/test_loadext.c 22065d601a18878e5542191001f0eaa5d77c0ed8 F src/test_md5.c 6c42bc0a3c0b54be34623ff77a0eec32b2fa96e3 @@ -99,7 +99,7 @@ F src/util.c ca6ee72772c0f5dc04d2e0ab1973fd3b6a9bf79d F src/vacuum.c 5b37d0f436f8e1ffacd17934e44720b38d2247f9 F src/vdbe.c 294e2f35fbd81f5b1e4bd918d2d0bb38313d4b11 F src/vdbe.h 258b5d1c0aaa72192f09ff0568ce42b383f156fa -F src/vdbeInt.h be3396aa33e7587c96f8c8c264100623a071a3d0 +F src/vdbeInt.h 37d74cc5651547d76c11682c67286bdf4099b54b F src/vdbeapi.c 6af0e7160af260052a7a4500464221a03dada75f F src/vdbeaux.c 1144cee0a5644c26f63e7fa34574dcd9349ac799 F src/vdbefifo.c 9efb94c8c3f4c979ebd0028219483f88e57584f5 @@ -373,7 +373,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0 F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513 -P a56bfa560425a5dc9273229f8838471dfc402024 -R e8bb3e94e8692c0623fd2ff8d33a84bc +P 0534f6e15b84560124c3f1abd05f2967d10261c4 +R 28bdb392366e183c1a405e5b76b10720 U danielk1977 -Z d9a57a4940bb8ae48fc84966242bf92b +Z baad93a642a5a5dec8ca0ab524eb163c diff --git a/manifest.uuid b/manifest.uuid index efd49bbe12..0d80e580d2 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -0534f6e15b84560124c3f1abd05f2967d10261c4 \ No newline at end of file +4acf7594a6c47142e7112d2cd9766a563401879b \ No newline at end of file diff --git a/src/test8.c b/src/test8.c index 6bcf8a94ac..be4327b435 100644 --- a/src/test8.c +++ b/src/test8.c @@ -13,7 +13,7 @@ ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** -** $Id: test8.c,v 1.34 2006/06/23 14:32:08 danielk1977 Exp $ +** $Id: test8.c,v 1.35 2006/06/24 06:36:11 danielk1977 Exp $ */ #include "sqliteInt.h" #include "tcl.h" @@ -21,6 +21,8 @@ #include #include +#ifndef SQLITE_OMIT_VIRTUALTABLE + typedef struct echo_vtab echo_vtab; typedef struct echo_cursor echo_cursor; @@ -30,6 +32,15 @@ typedef struct echo_cursor echo_cursor; ** ** $::echo_module ** $::echo_module_sync_fail +** +** The variable ::echo_module is a list. Each time one of the following +** methods is called, one or more elements are appended to the list. +** This is used for automated testing of virtual table modules. +** +** The ::echo_module_sync_fail variable is set by test scripts and read +** by code in this file. If it is set to the name of a real table in the +** the database, then all xSync operations on echo virtual tables that +** use the named table as a backing store will fail. */ /* @@ -45,8 +56,8 @@ typedef struct echo_cursor echo_cursor; */ struct echo_vtab { sqlite3_vtab base; - Tcl_Interp *interp; - sqlite3 *db; + Tcl_Interp *interp; /* Tcl interpreter containing debug variables */ + sqlite3 *db; /* Database connection */ char *zTableName; /* Name of the real table */ char *zLogName; /* Name of the log table */ @@ -59,9 +70,18 @@ struct echo_vtab { struct echo_cursor { sqlite3_vtab_cursor base; sqlite3_stmt *pStmt; - int errcode; /* Error code */ }; +/* +** Retrieve the column names for the table named zTab via database +** connection db. SQLITE_OK is returned on success, or an sqlite error +** code otherwise. +** +** If successful, the number of columns is written to *pnCol. *paCol is +** set to point at sqliteMalloc()'d space containing the array of +** nCol column names. The caller is responsible for calling sqliteFree +** on *paCol. +*/ static int getColumnNames( sqlite3 *db, const char *zTab, @@ -69,75 +89,114 @@ static int getColumnNames( int *pnCol ){ char **aCol = 0; - char zBuf[1024]; + char *zSql; sqlite3_stmt *pStmt = 0; int rc = SQLITE_OK; int nCol = 0; - sprintf(zBuf, "SELECT * FROM %s", zTab); - rc = sqlite3_prepare(db, zBuf, -1, &pStmt, 0); + /* Prepare the statement "SELECT * FROM ". The column names + ** of the result set of the compiled SELECT will be the same as + ** the column names of table . + */ + zSql = sqlite3MPrintf("SELECT * FROM %Q", zTab); + if( !zSql ){ + rc = SQLITE_NOMEM; + goto out; + } + rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0); + sqliteFree(zSql); + if( rc==SQLITE_OK ){ int ii; + int nBytes; + char *zSpace; nCol = sqlite3_column_count(pStmt); - aCol = sqliteMalloc(sizeof(char *) * nCol); + + /* Figure out how much space to allocate for the array of column names + ** (including space for the strings themselves). Then allocate it. + */ + nBytes = sizeof(char *) * nCol; + for(ii=0; ii=0 && cidaIndex); + rc = getColumnNames(db, argv[3], &pVtab->aCol, &pVtab->nCol); } if( rc==SQLITE_OK ){ - rc = getColumnNames(db, argv[3], &pVtab->aCol, &pVtab->nCol); + rc = getIndexArray(db, argv[3], pVtab->nCol, &pVtab->aIndex); } } return rc; } +/* +** This function frees all runtime structures associated with the virtual +** table pVtab. +*/ static int echoDestructor(sqlite3_vtab *pVtab){ - int ii; echo_vtab *p = (echo_vtab*)pVtab; sqliteFree(p->aIndex); - for(ii=0; iinCol; ii++){ - sqliteFree(p->aCol[ii]); - } sqliteFree(p->aCol); sqliteFree(p->zTableName); sqliteFree(p->zLogName); @@ -243,6 +302,11 @@ static int echoDestructor(sqlite3_vtab *pVtab){ return 0; } +/* +** 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, @@ -252,32 +316,43 @@ static int echoConstructor( int i; echo_vtab *pVtab; + /* Allocate the sqlite3_vtab/echo_vtab structure itself */ pVtab = sqliteMalloc( sizeof(*pVtab) ); if( !pVtab ){ return SQLITE_NOMEM; } pVtab->interp = (Tcl_Interp *)pAux; pVtab->db = db; + + /* Allocate echo_vtab.zTableName */ pVtab->zTableName = sqlite3MPrintf("%s", argv[3]); if( !pVtab->zTableName ){ echoDestructor((sqlite3_vtab *)pVtab); return SQLITE_NOMEM; } + /* Log the arguments to this function to Tcl var ::echo_module */ for(i=0; iinterp, 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. + */ if( echoDeclareVtab(pVtab, db, argc, argv) ){ echoDestructor((sqlite3_vtab *)pVtab); return SQLITE_ERROR; } + /* Success. Set *ppVtab and return */ *ppVtab = &pVtab->base; return SQLITE_OK; } -/* Methods for the echo module */ +/* +** Echo virtual table module xCreate method. +*/ static int echoCreate( sqlite3 *db, void *pAux, @@ -287,7 +362,17 @@ static int echoCreate( int rc = SQLITE_OK; appendToEchoModule((Tcl_Interp *)(pAux), "xCreate"); rc = echoConstructor(db, pAux, argc, argv, ppVtab); -#if 1 + + /* 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; @@ -296,9 +381,13 @@ static int echoCreate( rc = sqlite3_exec(db, zSql, 0, 0, 0); sqliteFree(zSql); } -#endif + return rc; } + +/* +** Echo virtual table module xConnect method. +*/ static int echoConnect( sqlite3 *db, void *pAux, @@ -309,28 +398,39 @@ static int echoConnect( return echoConstructor(db, pAux, argc, argv, ppVtab); } +/* +** Echo virtual table module xDisconnect method. +*/ static int echoDisconnect(sqlite3_vtab *pVtab){ appendToEchoModule(((echo_vtab *)pVtab)->interp, "xDisconnect"); return echoDestructor(pVtab); } + +/* +** 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"); -#if 1 + + /* Drop the "log" table, if one exists (see echoCreate() for details) */ if( p && p->zLogName ){ char *zSql; zSql = sqlite3MPrintf("DROP TABLE %Q", p->zLogName); rc = sqlite3_exec(p->db, zSql, 0, 0, 0); sqliteFree(zSql); } -#endif + if( rc==SQLITE_OK ){ rc = echoDestructor(pVtab); } return rc; } +/* +** Echo virtual table module xOpen method. +*/ static int echoOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ echo_cursor *pCur; pCur = sqliteMalloc(sizeof(echo_cursor)); @@ -338,6 +438,9 @@ static int echoOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ return (pCur ? SQLITE_OK : SQLITE_NOMEM); } +/* +** Echo virtual table module xClose method. +*/ static int echoClose(sqlite3_vtab_cursor *cur){ int rc; echo_cursor *pCur = (echo_cursor *)cur; @@ -356,10 +459,12 @@ static int echoEof(sqlite3_vtab_cursor *cur){ return (((echo_cursor *)cur)->pStmt ? 0 : 1); } +/* +** Echo virtual table module xNext method. +*/ static int echoNext(sqlite3_vtab_cursor *cur){ int rc; echo_cursor *pCur = (echo_cursor *)cur; - sqlite3_stmt *pStmt = pCur->pStmt; rc = sqlite3_step(pCur->pStmt); if( rc==SQLITE_ROW ){ @@ -372,12 +477,12 @@ static int echoNext(sqlite3_vtab_cursor *cur){ return rc; } +/* +** Echo virtual table module xColumn method. +*/ static int echoColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ int iCol = i + 1; sqlite3_stmt *pStmt = ((echo_cursor *)cur)->pStmt; - if( ((echo_cursor *)cur)->errcode ){ - return ((echo_cursor *)cur)->errcode; - } if( !pStmt ){ sqlite3_result_null(ctx); }else{ @@ -387,6 +492,9 @@ static int echoColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ return SQLITE_OK; } +/* +** Echo virtual table module xRowid method. +*/ static int echoRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ sqlite3_stmt *pStmt = ((echo_cursor *)cur)->pStmt; *pRowid = sqlite3_column_int64(pStmt, 0); @@ -411,7 +519,9 @@ static int hashString(const char *zString){ return val; } - +/* +** Echo virtual table module xFilter method. +*/ static int echoFilter( sqlite3_vtab_cursor *pVtabCursor, int idxNum, const char *idxStr, @@ -424,52 +534,65 @@ static int echoFilter( echo_vtab *pVtab = (echo_vtab *)pVtabCursor->pVtab; sqlite3 *db = pVtab->db; + /* 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; iinterp, sqlite3_value_text(argv[i])); } - assert( idxNum==hashString(idxStr) ); 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 && ipStmt, i+1, sqlite3_value_int64(argv[i])); - break; - } - case SQLITE_FLOAT: { - sqlite3_bind_double(pCur->pStmt, i+1, sqlite3_value_double(argv[i])); - break; - } - case SQLITE_NULL: { - sqlite3_bind_null(pCur->pStmt, i+1); - break; - } - case SQLITE_TEXT: { - sqlite3_bind_text(pCur->pStmt, i+1, sqlite3_value_text(argv[i]), - sqlite3_value_bytes(argv[i]), SQLITE_TRANSIENT); - break; - } - case SQLITE_BLOB: { - sqlite3_bind_blob(pCur->pStmt, i+1, sqlite3_value_blob(argv[i]), - sqlite3_value_bytes(argv[i]), SQLITE_TRANSIENT); - break; - } - } + 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); - }else{ - assert( !pCur->pStmt ); } return rc; } + +/* +** A helper function used by echoUpdate() and echoBestIndex() for +** manipulating strings in concert with the sqlite3_mprintf() function. +** +** Parameter pzStr points to a pointer to a string allocated with +** sqlite3_mprintf. The second parameter, zAppend, points to another +** string. The two strings are concatenated together and *pzStr +** set to point at the result. The initial buffer pointed to by *pzStr +** is deallocated via sqlite3_free(). +** +** If the third argument, doFree, is true, then sqlite3_free() is +** also called to free the buffer pointed to by zAppend. +*/ +static void string_concat(char **pzStr, char *zAppend, int doFree){ + char *zIn = *pzStr; + if( zIn ){ + char *zTemp = zIn; + zIn = sqlite3_mprintf("%s%s", zIn, zAppend); + sqlite3_free(zTemp); + }else{ + zIn = sqlite3_mprintf("%s", zAppend); + } + *pzStr = zIn; + if( doFree ){ + sqlite3_free(zAppend); + } +} + /* ** The echo module implements the subset of query constraints and sort ** orders that may take advantage of SQLite indices on the underlying @@ -481,6 +604,16 @@ static int echoFilter( ** 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. +** +** 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 ?? ?? +** +** where the and 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; @@ -543,13 +676,13 @@ static int echoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ zOp = "LIKE"; break; } if( zOp[0]=='L' ){ - zNew = sqlite3_mprintf("%s %s %s LIKE (SELECT '%%'||?||'%%')", - zQuery, zSep, zCol); + zNew = sqlite3_mprintf(" %s %s LIKE (SELECT '%%'||?||'%%')", + zSep, zCol); } else { - zNew = sqlite3_mprintf("%s %s %s %s ?", zQuery, zSep, zCol, zOp); + zNew = sqlite3_mprintf(" %s %s %s ?", zSep, zCol, zOp); } - sqlite3_free(zQuery); - zQuery = zNew; + string_concat(&zQuery, zNew, 1); + zSep = "AND"; pUsage->argvIndex = ++nArg; pUsage->omit = 1; @@ -563,9 +696,8 @@ static int echoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ if( pIdxInfo->nOrderBy==1 && pVtab->aIndex[pIdxInfo->aOrderBy->iColumn] ){ char *zCol = pVtab->aCol[pIdxInfo->aOrderBy->iColumn]; char *zDir = pIdxInfo->aOrderBy->desc?"DESC":"ASC"; - zNew = sqlite3_mprintf("%s ORDER BY %s %s", zQuery, zCol, zDir); - sqlite3_free(zQuery); - zQuery = zNew; + zNew = sqlite3_mprintf(" ORDER BY %s %s", zCol, zDir); + string_concat(&zQuery, zNew, 1); pIdxInfo->orderByConsumed = 1; } @@ -588,22 +720,9 @@ static int echoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ return rc; } -static void string_concat(char **pzStr, char *zAppend, int doFree){ - char *zIn = *pzStr; - if( zIn ){ - char *zTemp = zIn; - zIn = sqlite3_mprintf("%s%s", zIn, zAppend); - sqlite3_free(zTemp); - }else{ - zIn = sqlite3_mprintf("%s", zAppend); - } - *pzStr = zIn; - if( doFree ){ - sqlite3_free(zAppend); - } -} - /* +** The xUpdate method for echo module virtual tables. +** ** apData[0] apData[1] apData[2..] ** ** INTEGER DELETE @@ -759,8 +878,8 @@ static int echoRollback(sqlite3_vtab *tab){ } /* -** A virtual table module that merely echos method calls into TCL -** variables. +** A virtual table module that merely "echos" the contents of another +** table (like an SQL VIEW). */ static sqlite3_module echoModule = { 0, /* iVersion */ @@ -792,7 +911,6 @@ static int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb){ return TCL_OK; } - /* ** Register the echo virtual table module. */ @@ -808,12 +926,11 @@ static int register_echo_module( return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; -#ifndef SQLITE_OMIT_VIRTUALTABLE sqlite3_create_module(db, "echo", &echoModule, (void *)interp); -#endif return TCL_OK; } +#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ /* ** Register commands with the TCL interpreter. @@ -824,7 +941,9 @@ int Sqlitetest8_Init(Tcl_Interp *interp){ Tcl_ObjCmdProc *xProc; void *clientData; } aObjCmd[] = { +#ifndef SQLITE_OMIT_VIRTUALTABLE { "register_echo_module", register_echo_module, 0 }, +#endif }; int i; for(i=0; i