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
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 1551183..caf0421 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 @@
# 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 effd768..c6e1aa4 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 @@
** 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 @@
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 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 @@
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 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 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 @@
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 @@
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 @@
** 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 @@
** a transaction */
assert( !pVtab->inTransaction );
+ if( simulateVtabError(pVtab, "xBegin") ){
+ return SQLITE_ERROR;
+ }
+
rc = echoTransactionCall(tab, "xBegin");
if( rc==SQLITE_OK ){
@@ -1046,6 +1106,10 @@
** transaction */
assert( pVtab->inTransaction );
+ if( simulateVtabError(pVtab, "xSync") ){
+ return SQLITE_ERROR;
+ }
+
rc = echoTransactionCall(tab, "xSync");
if( rc==SQLITE_OK ){
@@ -1068,6 +1132,10 @@
** 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 744bc75..878d34c 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 <ctype.h>
@@ -4570,11 +4570,21 @@
#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 @@
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 @@
** 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 @@
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 @@
** 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 @@
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 @@
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 @@
** 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 @@
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 @@
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 8dc9c58..22ff34a 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 <ctype.h>
@@ -1216,7 +1216,7 @@
** 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 @@
** 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 @@
** 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 c9ff4e0..6263610 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 @@
}
/*
-** 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 @@
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 @@
/* 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 dac5161..9c70c31 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 @@
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 @@
** 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 @@
(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; i<pIdxInfo->nConstraint; i++){
if( !pIdxInfo->aConstraint[i].usable && pUsage[i].argvIndex>0 ){
sqlite3ErrorMsg(pParse,
@@ -1435,15 +1448,7 @@
}
}
- 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 */