From 3e3a84d32a9efa40b4843606568d3c61eaa20fcc Mon Sep 17 00:00:00 2001 From: danielk1977 Date: Fri, 1 Aug 2008 17:37:40 +0000 Subject: [PATCH] Test that virtual table methods xBestIndex, xOpen, xFilter, xNext, xColumn, xRowid, xUpdate, xSync and xBegin can all return error messages using the sqlite3_vtab.zErrMsg variable. (CVS 5519) FossilOrigin-Name: 007359b770f225877880b11f4c5d97bb548e38ca --- manifest | 26 +++++++++--------- manifest.uuid | 2 +- src/sqliteInt.h | 4 +-- src/test8.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++-- src/vdbe.c | 42 ++++++++++++++++++++++++----- src/vdbeaux.c | 8 +++--- src/vtab.c | 25 ++++++++--------- src/where.c | 27 +++++++++++-------- test/vtab1.test | 47 +++++++++++++++++++++++++++++++- 9 files changed, 200 insertions(+), 53 deletions(-) diff --git a/manifest b/manifest index ee67f1a2df..d7974593d3 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Test\scases\sfor\ssqlite3_db_config()\sand\ssqlite3_db_status().\s(CVS\s5518) -D 2008-08-01T16:31:14 +C Test\sthat\svirtual\stable\smethods\sxBestIndex,\sxOpen,\sxFilter,\sxNext,\sxColumn,\sxRowid,\sxUpdate,\sxSync\sand\sxBegin\scan\sall\sreturn\serror\smessages\susing\sthe\ssqlite3_vtab.zErrMsg\svariable.\s(CVS\s5519) +D 2008-08-01T17:37:41 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.in bbb62eecc851379aef5a48a1bf8787eb13e6ec06 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 @@ -146,7 +146,7 @@ F src/select.c ef18af5624fc3189014e6b617a36562394740f91 F src/shell.c 4b835fe734304ac22a3385868cd3790c1e4f7aa1 F src/sqlite.h.in 30af3a002a0b672aaae8f4a5deb0a2e9a4b699af F src/sqlite3ext.h 1e3887c9bd3ae66cb599e922824b04cd0d0f2c3e -F src/sqliteInt.h 99a49b2d85b7648fbb2f7933b145dfb5b11d8941 +F src/sqliteInt.h 80fa4a46024dee8cb6c66a6f04a716c253d98165 F src/sqliteLimit.h f435e728c6b620ef7312814d660a81f9356eb5c8 F src/status.c 8ad1f215934c5f5afb91df86e44dccff7ef3c1d0 F src/table.c 22744786199c9195720c15a7a42cb97b2e2728d8 @@ -158,7 +158,7 @@ F src/test4.c 41056378671e7b00e6305fa9ac6fa27e6f96f406 F src/test5.c 847eb5cfe89c197b6b494858ccf60be981235bdf F src/test6.c 0a0304a69cfa4962a429d084c6d451ff9e4fb572 F src/test7.c 475b1fa7e3275408b40a3cbdc9508cbdc41ffa02 -F src/test8.c ae09a70c009097561d0d21e0291d383ad059d3f8 +F src/test8.c f869497d3f6e8a7566e9b7116dce7ada49ee1183 F src/test9.c 904ebe0ed1472d6bad17a81e2ecbfc20017dc237 F src/test_async.c da9f58f49faccd3a26ba89f58de125862351b6e2 F src/test_autoext.c f53b0cdf7bf5f08100009572a5d65cdb540bd0ad @@ -183,16 +183,16 @@ F src/update.c 79b77a3cc8ed5f8903a7f37055fcedd69388dcae F src/utf.c a7004436a6ef2aee012ace93de274dd0f3c7624e F src/util.c afe659ccc05d1f8af9e8631dabfec3ee3a7144af F src/vacuum.c ef342828002debc97514617af3424aea8ef8522c -F src/vdbe.c caa169475973e56a87eb90477928d84f3daa14b8 +F src/vdbe.c 90296a45d28939fd631c05c235dab7e123f94f57 F src/vdbe.h c46155c221418bea29ee3a749d5950fcf85a70e2 F src/vdbeInt.h ab27f964458fd070c6660f80694ab85d56d5f4c5 F src/vdbeapi.c 25dd01c8b12978c14ec30e9a50666b23da767b27 -F src/vdbeaux.c dbd2756c203d8c45281dd2f33a9f4741b5f81047 +F src/vdbeaux.c 78c9d6413b8720edfe5b2a40a14850d21b0079b7 F src/vdbeblob.c f93110888ddc246215e9ba1f831d3d375bfd8355 F src/vdbefifo.c 20fda2a7c4c0bcee1b90eb7e545fefcdbf2e1de7 F src/vdbemem.c bdf92746583b0187655d736c4a20a2622eb9ab69 -F src/vtab.c 691813795f1bba5df57389c1cec62f9e73eebd9d -F src/where.c ee4878e42ac97e2b3389b44cee34b35458321488 +F src/vtab.c 914db4c9435c61a522e3cdaf103dac163d38aa30 +F src/where.c a800184a2d023b15d6f2758b7a6c7ab011258fee F tclinstaller.tcl 4356d9d94d2b5ed5e68f9f0c80c4df3048dd7617 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/all.test 89e09ed0074083ac6f208dc3243429e8f89efb69 @@ -569,7 +569,7 @@ F test/vacuum3.test 75dee6ffd1baa60308dcad93f2c689126500dcff F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102 F test/veryquick.test e265401afefa994cdf2fe4b6f286b1e87c2f9b9d F test/view.test 5799906511d6c77cfe3516d3d1189224350ef732 -F test/vtab1.test 905a1aedad6e7307aea29f00d87b255b04994777 +F test/vtab1.test 2108cfb3ff72cb0f46363589d3c4746c04723368 F test/vtab2.test 1da49b015582965a8fc386aa23d051a5a622b08e F test/vtab3.test baad99fd27217f5d6db10660522e0b7192446de1 F test/vtab4.test 942f8b8280b3ea8a41dae20e7822d065ca1cb275 @@ -615,7 +615,7 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81 F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 1dbced29de5f59ba2ebf877edcadf171540374d1 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e -P 3f70e03ae83d374f5f69770bd7a4b279584f7090 -R 21a8914e4d9d6622f109fe97eb26a591 -U drh -Z 0e954aa8108084642ae7fe8343f84356 +P 6a6b94302acdfe6404b04bff1cc8d16c1ef69df9 +R 5672f7054494561346fae0939e873dda +U danielk1977 +Z bf74e8d5f26a63b2617162677c14612e diff --git a/manifest.uuid b/manifest.uuid index 46966a19cc..c38e0fd1e8 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6a6b94302acdfe6404b04bff1cc8d16c1ef69df9 \ No newline at end of file +007359b770f225877880b11f4c5d97bb548e38ca \ No newline at end of file diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 1551183f4c..caf04211cc 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.748 2008/07/31 17:35:45 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.749 2008/08/01 17:37:41 danielk1977 Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ @@ -2267,7 +2267,7 @@ int sqlite3AutoLoadExtensions(sqlite3*); # define sqlite3VtabTransferError(A,B,C) #else void sqlite3VtabClear(Table*); - int sqlite3VtabSync(sqlite3 *db, int rc); + int sqlite3VtabSync(sqlite3 *db, char **); int sqlite3VtabRollback(sqlite3 *db); int sqlite3VtabCommit(sqlite3 *db); void sqlite3VtabTransferError(sqlite3 *db, int, sqlite3_vtab*); diff --git a/src/test8.c b/src/test8.c index effd768b8f..c6e1aa481b 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.69 2008/07/28 19:34:54 drh Exp $ +** $Id: test8.c,v 1.70 2008/08/01 17:37:41 danielk1977 Exp $ */ #include "sqliteInt.h" #include "tcl.h" @@ -44,6 +44,22 @@ typedef struct echo_cursor echo_cursor; ** use the named table as a backing store will fail. */ +/* +** Errors can be provoked within the following echo virtual table methods: +** +** xBestIndex xOpen xFilter xNext +** xColumn xRowid xUpdate xSync +** xBegin +** +** This is done by setting the global tcl variable: +** +** echo_module_fail($method,$tbl) +** +** where $method is set to the name of the virtual table method to fail +** (i.e. "xBestIndex") and $tbl is the name of the table being echoed (not +** the name of the virtual table, the name of the underlying real table). +*/ + /* ** An echo virtual-table object. ** @@ -76,6 +92,18 @@ struct echo_cursor { sqlite3_stmt *pStmt; }; +static int simulateVtabError(echo_vtab *p, const char *zMethod){ + const char *zErr; + char zVarname[128]; + zVarname[127] = '\0'; + snprintf(zVarname, 127, "echo_module_fail(%s,%s)", zMethod, p->zTableName); + zErr = Tcl_GetVar(p->interp, zVarname, TCL_GLOBAL_ONLY); + if( zErr ){ + p->base.zErrMsg = sqlite3_mprintf("echo-vtab-error: %s", zErr); + } + return (zErr!=0); +} + /* ** Convert an SQL-style quoted string into a normal string by removing ** the quote characters. The conversion is done in-place. If the @@ -524,6 +552,9 @@ static int echoDestroy(sqlite3_vtab *pVtab){ */ static int echoOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ echo_cursor *pCur; + if( simulateVtabError((echo_vtab *)pVTab, "xOpen") ){ + return SQLITE_ERROR; + } pCur = sqlite3MallocZero(sizeof(echo_cursor)); *ppCursor = (sqlite3_vtab_cursor *)pCur; return (pCur ? SQLITE_OK : SQLITE_NOMEM); @@ -557,6 +588,10 @@ static int echoNext(sqlite3_vtab_cursor *cur){ int rc = SQLITE_OK; echo_cursor *pCur = (echo_cursor *)cur; + if( simulateVtabError((echo_vtab *)(cur->pVtab), "xNext") ){ + return SQLITE_ERROR; + } + if( pCur->pStmt ){ rc = sqlite3_step(pCur->pStmt); if( rc==SQLITE_ROW ){ @@ -576,6 +611,11 @@ static int echoNext(sqlite3_vtab_cursor *cur){ static int echoColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ int iCol = i + 1; sqlite3_stmt *pStmt = ((echo_cursor *)cur)->pStmt; + + if( simulateVtabError((echo_vtab *)(cur->pVtab), "xColumn") ){ + return SQLITE_ERROR; + } + if( !pStmt ){ sqlite3_result_null(ctx); }else{ @@ -590,6 +630,11 @@ static int echoColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ */ static int echoRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ sqlite3_stmt *pStmt = ((echo_cursor *)cur)->pStmt; + + if( simulateVtabError((echo_vtab *)(cur->pVtab), "xRowid") ){ + return SQLITE_ERROR; + } + *pRowid = sqlite3_column_int64(pStmt, 0); return SQLITE_OK; } @@ -627,6 +672,10 @@ static int echoFilter( 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) ); @@ -734,12 +783,15 @@ static int echoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ 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; + } + /* 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 @@ -892,6 +944,10 @@ int echoUpdate( ** making any changes to a virtual table */ assert( pVtab->inTransaction ); + if( simulateVtabError(pVtab, "xUpdate") ){ + return SQLITE_ERROR; + } + /* If apData[0] is an integer and nData>1 then do an UPDATE */ if( nData>1 && sqlite3_value_type(apData[0])==SQLITE_INTEGER ){ char *zSep = " SET"; @@ -1019,6 +1075,10 @@ static int echoBegin(sqlite3_vtab *tab){ ** a transaction */ assert( !pVtab->inTransaction ); + if( simulateVtabError(pVtab, "xBegin") ){ + return SQLITE_ERROR; + } + rc = echoTransactionCall(tab, "xBegin"); if( rc==SQLITE_OK ){ @@ -1046,6 +1106,10 @@ static int echoSync(sqlite3_vtab *tab){ ** transaction */ assert( pVtab->inTransaction ); + if( simulateVtabError(pVtab, "xSync") ){ + return SQLITE_ERROR; + } + rc = echoTransactionCall(tab, "xSync"); if( rc==SQLITE_OK ){ @@ -1068,6 +1132,10 @@ static int echoCommit(sqlite3_vtab *tab){ ** a transaction */ assert( pVtab->inTransaction ); + if( simulateVtabError(pVtab, "xCommit") ){ + return SQLITE_ERROR; + } + sqlite3BeginBenignMalloc(); rc = echoTransactionCall(tab, "xCommit"); sqlite3EndBenignMalloc(); diff --git a/src/vdbe.c b/src/vdbe.c index 744bc75591..878d34ce72 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -43,7 +43,7 @@ ** in this file for details. If in doubt, do not deviate from existing ** commenting and indentation practices when changing or adding code. ** -** $Id: vdbe.c,v 1.769 2008/07/30 13:27:11 drh Exp $ +** $Id: vdbe.c,v 1.770 2008/08/01 17:37:41 danielk1977 Exp $ */ #include "sqliteInt.h" #include @@ -4570,11 +4570,21 @@ case OP_TableLock: { #ifndef SQLITE_OMIT_VIRTUALTABLE /* Opcode: VBegin * * * P4 * ** -** P4 a pointer to an sqlite3_vtab structure. Call the xBegin method -** for that table. +** P4 may be a pointer to an sqlite3_vtab structure. If so, call the +** xBegin method for that table. +** +** Also, whether or not P4 is set, check that this is not being called from +** within a callback to a virtual table xSync() method. If it is, set the +** error code to SQLITE_LOCKED. */ case OP_VBegin: { - rc = sqlite3VtabBegin(db, pOp->p4.pVtab); + sqlite3_vtab *pVtab = pOp->p4.pVtab; + rc = sqlite3VtabBegin(db, pVtab); + if( pVtab ){ + sqlite3DbFree(db, p->zErrMsg); + p->zErrMsg = pVtab->zErrMsg; + pVtab->zErrMsg = 0; + } break; } #endif /* SQLITE_OMIT_VIRTUALTABLE */ @@ -4699,6 +4709,9 @@ case OP_VFilter: { /* jump */ p->inVtabMethod = 1; rc = pModule->xFilter(pVtabCursor, iQuery, pOp->p4.z, nArg, apArg); p->inVtabMethod = 0; + sqlite3DbFree(db, p->zErrMsg); + p->zErrMsg = pVtab->zErrMsg; + pVtab->zErrMsg = 0; if( rc==SQLITE_OK ){ res = pModule->xEof(pVtabCursor); } @@ -4721,6 +4734,7 @@ case OP_VFilter: { /* jump */ ** the virtual-table that the P1 cursor is pointing to. */ case OP_VRowid: { /* out2-prerelease */ + sqlite3_vtab *pVtab; const sqlite3_module *pModule; sqlite_int64 iRow; Cursor *pCur = p->apCsr[pOp->p1]; @@ -4729,10 +4743,14 @@ case OP_VRowid: { /* out2-prerelease */ if( pCur->nullRow ){ break; } - pModule = pCur->pVtabCursor->pVtab->pModule; + pVtab = pCur->pVtabCursor->pVtab; + pModule = pVtab->pModule; assert( pModule->xRowid ); if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse; rc = pModule->xRowid(pCur->pVtabCursor, &iRow); + sqlite3DbFree(db, p->zErrMsg); + p->zErrMsg = pVtab->zErrMsg; + pVtab->zErrMsg = 0; if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse; MemSetTypeFlag(pOut, MEM_Int); pOut->u.i = iRow; @@ -4748,6 +4766,7 @@ case OP_VRowid: { /* out2-prerelease */ ** P1 cursor is pointing to into register P3. */ case OP_VColumn: { + sqlite3_vtab *pVtab; const sqlite3_module *pModule; Mem *pDest; sqlite3_context sContext; @@ -4760,7 +4779,8 @@ case OP_VColumn: { sqlite3VdbeMemSetNull(pDest); break; } - pModule = pCur->pVtabCursor->pVtab->pModule; + pVtab = pCur->pVtabCursor->pVtab; + pModule = pVtab->pModule; assert( pModule->xColumn ); memset(&sContext, 0, sizeof(sContext)); @@ -4774,6 +4794,9 @@ case OP_VColumn: { if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse; rc = pModule->xColumn(pCur->pVtabCursor, &sContext, pOp->p2); + sqlite3DbFree(db, p->zErrMsg); + p->zErrMsg = pVtab->zErrMsg; + pVtab->zErrMsg = 0; /* Copy the result of the function to the P3 register. We ** do this regardless of whether or not an error occured to ensure any @@ -4802,6 +4825,7 @@ case OP_VColumn: { ** the end of its result set, then fall through to the next instruction. */ case OP_VNext: { /* jump */ + sqlite3_vtab *pVtab; const sqlite3_module *pModule; int res = 0; @@ -4810,7 +4834,8 @@ case OP_VNext: { /* jump */ if( pCur->nullRow ){ break; } - pModule = pCur->pVtabCursor->pVtab->pModule; + pVtab = pCur->pVtabCursor->pVtab; + pModule = pVtab->pModule; assert( pModule->xNext ); /* Invoke the xNext() method of the module. There is no way for the @@ -4823,6 +4848,9 @@ case OP_VNext: { /* jump */ p->inVtabMethod = 1; rc = pModule->xNext(pCur->pVtabCursor); p->inVtabMethod = 0; + sqlite3DbFree(db, p->zErrMsg); + p->zErrMsg = pVtab->zErrMsg; + pVtab->zErrMsg = 0; if( rc==SQLITE_OK ){ res = pModule->xEof(pCur->pVtabCursor); } diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 8dc9c58efd..22ff34a9e8 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -14,7 +14,7 @@ ** to version 2.8.7, all this code was combined into the vdbe.c source file. ** But that file was getting too big so this subroutines were split out. ** -** $Id: vdbeaux.c,v 1.402 2008/07/31 01:43:14 shane Exp $ +** $Id: vdbeaux.c,v 1.403 2008/08/01 17:37:41 danielk1977 Exp $ */ #include "sqliteInt.h" #include @@ -1216,7 +1216,7 @@ int sqlite3VdbeSetColName(Vdbe *p, int idx, int var, const char *zName, int N){ ** write-transaction spanning more than one database file, this routine ** takes care of the master journal trickery. */ -static int vdbeCommit(sqlite3 *db){ +static int vdbeCommit(sqlite3 *db, Vdbe *p){ int i; int nTrans = 0; /* Number of databases with an active write-transaction */ int rc = SQLITE_OK; @@ -1228,7 +1228,7 @@ static int vdbeCommit(sqlite3 *db){ ** required, as an xSync() callback may add an attached database ** to the transaction. */ - rc = sqlite3VtabSync(db, rc); + rc = sqlite3VtabSync(db, &p->zErrMsg); if( rc!=SQLITE_OK ){ return rc; } @@ -1592,7 +1592,7 @@ int sqlite3VdbeHalt(Vdbe *p){ ** successful or hit an 'OR FAIL' constraint. This means a commit ** is required. */ - int rc = vdbeCommit(db); + int rc = vdbeCommit(db, p); if( rc==SQLITE_BUSY ){ sqlite3BtreeMutexArrayLeave(&p->aMutex); return SQLITE_BUSY; diff --git a/src/vtab.c b/src/vtab.c index c9ff4e0bdd..62636104aa 100644 --- a/src/vtab.c +++ b/src/vtab.c @@ -11,7 +11,7 @@ ************************************************************************* ** This file contains code used to help implement virtual tables. ** -** $Id: vtab.c,v 1.72 2008/07/28 19:34:54 drh Exp $ +** $Id: vtab.c,v 1.73 2008/08/01 17:37:41 danielk1977 Exp $ */ #ifndef SQLITE_OMIT_VIRTUALTABLE #include "sqliteInt.h" @@ -640,17 +640,18 @@ static void callFinaliser(sqlite3 *db, int offset){ } /* -** If argument rc2 is not SQLITE_OK, then return it and do nothing. -** Otherwise, invoke the xSync method of all virtual tables in the -** sqlite3.aVTrans array. Return the error code for the first error -** that occurs, or SQLITE_OK if all xSync operations are successful. +** Invoke the xSync method of all virtual tables in the sqlite3.aVTrans +** array. Return the error code for the first error that occurs, or +** SQLITE_OK if all xSync operations are successful. +** +** Set *pzErrmsg to point to a buffer that should be released using +** sqlite3DbFree() containing an error message, if one is available. */ -int sqlite3VtabSync(sqlite3 *db, int rc2){ +int sqlite3VtabSync(sqlite3 *db, char **pzErrmsg){ int i; int rc = SQLITE_OK; int rcsafety; sqlite3_vtab **aVTrans = db->aVTrans; - if( rc2!=SQLITE_OK ) return rc2; rc = sqlite3SafetyOff(db); db->aVTrans = 0; @@ -660,6 +661,9 @@ int sqlite3VtabSync(sqlite3 *db, int rc2){ x = pVtab->pModule->xSync; if( x ){ rc = x(pVtab); + sqlite3DbFree(db, *pzErrmsg); + *pzErrmsg = pVtab->zErrMsg; + pVtab->zErrMsg = 0; } } db->aVTrans = aVTrans; @@ -727,12 +731,9 @@ int sqlite3VtabBegin(sqlite3 *db, sqlite3_vtab *pVtab){ /* Invoke the xBegin method */ rc = pModule->xBegin(pVtab); - sqlite3VtabTransferError(db, rc, pVtab); - if( rc!=SQLITE_OK ){ - return rc; + if( rc==SQLITE_OK ){ + rc = addToVTrans(db, pVtab); } - - rc = addToVTrans(db, pVtab); } return rc; } diff --git a/src/where.c b/src/where.c index dac5161b83..9c70c31abd 100644 --- a/src/where.c +++ b/src/where.c @@ -16,7 +16,7 @@ ** so is applicable. Because this module is responsible for selecting ** indices, you might also think of this module as the "query optimizer". ** -** $Id: where.c,v 1.318 2008/07/28 19:34:54 drh Exp $ +** $Id: where.c,v 1.319 2008/08/01 17:37:41 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -1259,6 +1259,7 @@ static double bestVirtualIndex( sqlite3_index_info **ppIdxInfo /* Index information passed to xBestIndex */ ){ Table *pTab = pSrc->pTab; + sqlite3_vtab *pVtab = pTab->pVtab; sqlite3_index_info *pIdxInfo; struct sqlite3_index_constraint *pIdxCons; struct sqlite3_index_orderby *pIdxOrderBy; @@ -1370,7 +1371,7 @@ static double bestVirtualIndex( ** sqlite3ViewGetColumnNames() would have picked up the error. */ assert( pTab->azModuleArg && pTab->azModuleArg[0] ); - assert( pTab->pVtab ); + assert( pVtab ); #if 0 if( pTab->pVtab==0 ){ sqlite3ErrorMsg(pParse, "undefined module %s for table %s", @@ -1423,10 +1424,22 @@ static double bestVirtualIndex( (void)sqlite3SafetyOff(pParse->db); WHERETRACE(("xBestIndex for %s\n", pTab->zName)); TRACE_IDX_INPUTS(pIdxInfo); - rc = pTab->pVtab->pModule->xBestIndex(pTab->pVtab, pIdxInfo); + rc = pVtab->pModule->xBestIndex(pVtab, pIdxInfo); TRACE_IDX_OUTPUTS(pIdxInfo); (void)sqlite3SafetyOn(pParse->db); + if( rc!=SQLITE_OK ){ + if( rc==SQLITE_NOMEM ){ + pParse->db->mallocFailed = 1; + }else if( !pVtab->zErrMsg ){ + sqlite3ErrorMsg(pParse, "%s", sqlite3ErrStr(rc)); + }else{ + sqlite3ErrorMsg(pParse, "%s", pVtab->zErrMsg); + } + } + sqlite3DbFree(pParse->db, pVtab->zErrMsg); + pVtab->zErrMsg = 0; + for(i=0; inConstraint; i++){ if( !pIdxInfo->aConstraint[i].usable && pUsage[i].argvIndex>0 ){ sqlite3ErrorMsg(pParse, @@ -1435,15 +1448,7 @@ static double bestVirtualIndex( } } - if( rc!=SQLITE_OK ){ - if( rc==SQLITE_NOMEM ){ - pParse->db->mallocFailed = 1; - }else { - sqlite3ErrorMsg(pParse, "%s", sqlite3ErrStr(rc)); - } - } *(int*)&pIdxInfo->nOrderBy = nOrderBy; - return pIdxInfo->estimatedCost; } #endif /* SQLITE_OMIT_VIRTUALTABLE */ diff --git a/test/vtab1.test b/test/vtab1.test index 3a568bd986..c46b9ada5a 100644 --- a/test/vtab1.test +++ b/test/vtab1.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is creating and dropping virtual tables. # -# $Id: vtab1.test,v 1.55 2008/07/23 21:07:25 drh Exp $ +# $Id: vtab1.test,v 1.56 2008/08/01 17:37:41 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -52,6 +52,9 @@ ifcapable !vtab||!schema_pragmas { # We cannot create a virtual table if the module has not been registered. # do_test vtab1-1.1 { + explain { + CREATE VIRTUAL TABLE t1 USING echo; + } catchsql { CREATE VIRTUAL TABLE t1 USING echo; } @@ -1108,6 +1111,48 @@ do_test vtab1-15.4 { } } {1 {datatype mismatch}} +# The following tests - vtab1-16.* - are designed to test that setting +# sqlite3_vtab.zErrMsg variable can be used by the vtab interface to +# return an error message to the user. +# +do_test vtab1-16.1 { + execsql { + CREATE TABLE t2(a PRIMARY KEY, b, c); + INSERT INTO t2 VALUES(1, 2, 3); + INSERT INTO t2 VALUES(4, 5, 6); + CREATE VIRTUAL TABLE echo_t2 USING echo(t2); + } +} {} + +set tn 2 +foreach method [list \ + xBestIndex \ + xOpen \ + xFilter \ + xNext \ + xColumn \ + xRowid \ +] { + do_test vtab1-16.$tn { + set echo_module_fail($method,t2) "the $method method has failed" + catchsql { SELECT rowid, * FROM echo_t2 WHERE a >= 1 } + } "1 {echo-vtab-error: the $method method has failed}" + unset echo_module_fail($method,t2) + incr tn +} + +foreach method [list \ + xUpdate \ + xBegin \ + xSync \ +] { + do_test vtab1-16.$tn { + set echo_module_fail($method,t2) "the $method method has failed" + catchsql { INSERT INTO echo_t2 VALUES(7, 8, 9) } + } "1 {echo-vtab-error: the $method method has failed}" + unset echo_module_fail($method,t2) + incr tn +} unset -nocomplain echo_module_begin_fail finish_test