From 029ead645d5f5675ea0534a1d23ac1382f926e5e Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 27 Oct 2011 15:19:58 +0000 Subject: [PATCH] If an error occurs within sqlite3_step() on a statement prepared using sqlite3_prepare_v2(), transfer both the error code and error message to the database handle before sqlite3_step() returns (so that they are available via sqlite3_errcode() and sqlite3_errmsg(). Prior to this commit, only the error code was transfered. The error message was not available until after either sqlite3_reset() or sqlite3_finalize() had been called on the statement handle. FossilOrigin-Name: 8f88cc4e616b4b30ed349f89e148f782da5cb6c4 --- manifest | 19 ++++---- manifest.uuid | 2 +- src/vdbeInt.h | 1 + src/vdbeapi.c | 2 +- src/vdbeaux.c | 37 ++++++++++----- test/errmsg.test | 116 +++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 154 insertions(+), 23 deletions(-) create mode 100644 test/errmsg.test diff --git a/manifest b/manifest index c430b84374..4afcf632ed 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Avoid\sa\sharmless\sreference\sto\san\suninitialized\svariable\sfollowing\san\nerror\sin\sFTS3.\s\sThis\sis\snot\sa\sbug.\s\sThe\schange\sis\sto\ssilence\sa\svalgrind\nwarning. -D 2011-10-22T21:00:46.835 +C If\san\serror\soccurs\swithin\ssqlite3_step()\son\sa\sstatement\sprepared\susing\ssqlite3_prepare_v2(),\stransfer\sboth\sthe\serror\scode\sand\serror\smessage\sto\sthe\sdatabase\shandle\sbefore\ssqlite3_step()\sreturns\s(so\sthat\sthey\sare\savailable\svia\ssqlite3_errcode()\sand\ssqlite3_errmsg().\sPrior\sto\sthis\scommit,\sonly\sthe\serror\scode\swas\stransfered.\sThe\serror\smessage\swas\snot\savailable\suntil\safter\seither\ssqlite3_reset()\sor\ssqlite3_finalize()\shad\sbeen\scalled\son\sthe\sstatement\shandle. +D 2011-10-27T15:19:58.880 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in a162fe39e249b8ed4a65ee947c30152786cfe897 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -240,9 +240,9 @@ F src/util.c df83983bd57057df4951516880066b42b7055269 F src/vacuum.c 0c0ba2242355c6048d65e2b333abe0f7c06348fa F src/vdbe.c 326994a64a9a08853122200dc9f62cb96b8f0831 F src/vdbe.h f0725ee997db869ecae5bb70a71612aabeca7755 -F src/vdbeInt.h 693d6ac6810298fc6b4c503cfbe3f99a240f40af -F src/vdbeapi.c 11dc47987abacb76ad016dcf5abc0dc422482a98 -F src/vdbeaux.c 5bd4886b444051b779eb4a2e27e46a5deb65c0fd +F src/vdbeInt.h 9498fc98a2c9e349a4ef13455ff5a3e898f40176 +F src/vdbeapi.c 4dbba7f94f127f6ea8d2d0505ee1f98e5ffbf546 +F src/vdbeaux.c 9cb5c1a8f6b592059372cb8a5f774bdbf5b85853 F src/vdbeblob.c 32f2a4899d67f69634ea4dd93e3f651936d732cb F src/vdbemem.c 2fc78b3e0fabcc1eaa23cd79dd2e30e6dcfe1e56 F src/vdbesort.c 468d43c057063e54da4f1988b38b4f46d60e7790 @@ -392,6 +392,7 @@ F test/enc2.test 796c59832e2b9a52842f382ffda8f3e989db03ad F test/enc3.test 90683ad0e6ea587b9d5542ca93568af9a9858c40 F test/enc4.test c8f1ce3618508fd0909945beb8b8831feef2c020 F test/eqp.test f14fadd76da53405e9885e2431cacf7191d83cdb +F test/errmsg.test 3bb606db9d040cc6854459f8f5e5a2bcd9b7fd2a F test/eval.test bc269c365ba877554948441e91ad5373f9f91be3 F test/exclusive.test a1b324cb21834a490cd052d409d34789cfef57cb F test/exclusive2.test 372be98f6de44dd78734e364b7b626ea211761a6 @@ -971,7 +972,7 @@ F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f F tool/warnings-clang.sh 9f406d66e750e8ac031c63a9ef3248aaa347ef2a F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 -P aed2bf7a3c828a7191389b3f8235a9387977b476 -R 581537dd5ceacce0228b1e700fb5fa3e -U drh -Z b9c4b650240d28ae14b5d71fc3a026c4 +P d980c5b22fd700afb3cac5de5501c9350f2f589e +R ae51b75095c5b5f8c67203e309153d5d +U dan +Z bb68bef02a5a1fffaf7c3c37d61664aa diff --git a/manifest.uuid b/manifest.uuid index deedc7ab0b..0f87fa2b4d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d980c5b22fd700afb3cac5de5501c9350f2f589e \ No newline at end of file +8f88cc4e616b4b30ed349f89e148f782da5cb6c4 \ No newline at end of file diff --git a/src/vdbeInt.h b/src/vdbeInt.h index 7fbd5693fb..803ae1630e 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -395,6 +395,7 @@ int sqlite3VdbeCloseStatement(Vdbe *, int); void sqlite3VdbeFrameDelete(VdbeFrame*); int sqlite3VdbeFrameRestore(VdbeFrame *); void sqlite3VdbeMemStoreType(Mem *pMem); +int sqlite3VdbeTransferError(Vdbe *p); #ifdef SQLITE_OMIT_MERGE_SORT # define sqlite3VdbeSorterInit(Y,Z) SQLITE_OK diff --git a/src/vdbeapi.c b/src/vdbeapi.c index 678f0d91e7..adc9dba2f1 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -454,7 +454,7 @@ end_of_step: ** error has occured, then return the error code in p->rc to the ** caller. Set the error code in the database handle to the same value. */ - rc = db->errCode = p->rc; + rc = sqlite3VdbeTransferError(p); } return (rc&db->errMask); } diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 15b28ab90a..931114e62c 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -2310,6 +2310,28 @@ void sqlite3VdbeResetStepResult(Vdbe *p){ p->rc = SQLITE_OK; } +/* +** Copy the error code and error message belonging to the VDBE passed +** as the first argument to its database handle (so that they will be +** returned by calls to sqlite3_errcode() and sqlite3_errmsg()). +** +** This function does not clear the VDBE error code or message, just +** copies them to the database handle. +*/ +int sqlite3VdbeTransferError(Vdbe *p){ + sqlite3 *db = p->db; + int rc = p->rc; + if( p->zErrMsg ){ + sqlite3BeginBenignMalloc(); + sqlite3ValueSetStr(db->pErr, -1, p->zErrMsg, SQLITE_UTF8, SQLITE_TRANSIENT); + sqlite3EndBenignMalloc(); + db->errCode = rc; + }else{ + sqlite3Error(db, rc, 0); + } + return rc; +} + /* ** Clean up a VDBE after execution but do not delete the VDBE just yet. ** Write any error messages into *pzErrMsg. Return the result code. @@ -2337,18 +2359,9 @@ int sqlite3VdbeReset(Vdbe *p){ ** instructions yet, leave the main database error information unchanged. */ if( p->pc>=0 ){ - if( p->zErrMsg ){ - sqlite3BeginBenignMalloc(); - sqlite3ValueSetStr(db->pErr,-1,p->zErrMsg,SQLITE_UTF8,SQLITE_TRANSIENT); - sqlite3EndBenignMalloc(); - db->errCode = p->rc; - sqlite3DbFree(db, p->zErrMsg); - p->zErrMsg = 0; - }else if( p->rc ){ - sqlite3Error(db, p->rc, 0); - }else{ - sqlite3Error(db, SQLITE_OK, 0); - } + sqlite3VdbeTransferError(p); + sqlite3DbFree(db, p->zErrMsg); + p->zErrMsg = 0; if( p->runOnlyOnce ) p->expired = 1; }else if( p->rc && p->expired ){ /* The expired flag was set on the VDBE before the first call diff --git a/test/errmsg.test b/test/errmsg.test new file mode 100644 index 0000000000..9f8409b147 --- /dev/null +++ b/test/errmsg.test @@ -0,0 +1,116 @@ +# 2001 September 15 +# +# 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. +# +#*********************************************************************** +# Test that if sqlite3_prepare_v2() is used to prepare a query, the +# error-message associated with an sqlite3_step() error is available +# immediately. Whereas if sqlite3_prepare() is used, it is not available +# until sqlite3_finalize() or sqlite3_reset() has been called. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +set testprefix errmsg + +# Test organization: +# +# errmsg-1.* User-defined SQL function errors +# errmsg-2.* Errors generated by the VDBE (constraint failures etc.) +# errmsg-3.* SQLITE_SCHEMA and statement recompilation errors. +# + +proc error_messages_worker {prepare sql schema} { + set ret [list] + + set stmt [$prepare db $sql -1 dummy] + execsql $schema + lappend ret [sqlite3_step $stmt] + lappend ret [sqlite3_errmsg db] + lappend ret [sqlite3_finalize $stmt] + lappend ret [sqlite3_errmsg db] + + set ret +} + +proc error_messages_v2 {sql {schema {}}} { + error_messages_worker sqlite3_prepare_v2 $sql $schema +} + +proc error_messages {sql {schema {}}} { + error_messages_worker sqlite3_prepare $sql $schema +} + +proc sql_error {msg} { error $msg } +db func sql_error sql_error + +#------------------------------------------------------------------------- +# Test error messages returned by user-defined SQL functions. +# +do_test 1.1 { + error_messages "SELECT sql_error('custom message')" +} [list {*}{ + SQLITE_ERROR {SQL logic error or missing database} + SQLITE_ERROR {custom message} +}] +do_test 1.2 { + error_messages_v2 "SELECT sql_error('custom message')" +} [list {*}{ + SQLITE_ERROR {custom message} + SQLITE_ERROR {custom message} +}] + +#------------------------------------------------------------------------- +# Test error messages generated directly by VDBE code (e.g. constraint +# failures). +# +do_execsql_test 2.1 { + CREATE TABLE t1(a PRIMARY KEY, b UNIQUE); + INSERT INTO t1 VALUES('abc', 'def'); +} +do_test 2.2 { + error_messages "INSERT INTO t1 VALUES('ghi', 'def')" +} [list {*}{ + SQLITE_ERROR {SQL logic error or missing database} + SQLITE_CONSTRAINT {column b is not unique} +}] +do_test 2.3 { + error_messages_v2 "INSERT INTO t1 VALUES('ghi', 'def')" +} [list {*}{ + SQLITE_CONSTRAINT {column b is not unique} + SQLITE_CONSTRAINT {column b is not unique} +}] + +#------------------------------------------------------------------------- +# Test SQLITE_SCHEMA errors. And, for _v2(), test that if the schema +# change invalidates the SQL statement itself the error message is returned +# correctly. +# +do_execsql_test 3.1.1 { + CREATE TABLE t2(a PRIMARY KEY, b UNIQUE); + INSERT INTO t2 VALUES('abc', 'def'); +} +do_test 3.1.2 { + error_messages "SELECT a FROM t2" "DROP TABLE t2" +} [list {*}{ + SQLITE_ERROR {SQL logic error or missing database} + SQLITE_SCHEMA {database schema has changed} +}] +do_execsql_test 3.2.1 { + CREATE TABLE t2(a PRIMARY KEY, b UNIQUE); + INSERT INTO t2 VALUES('abc', 'def'); +} +do_test 3.2.2 { + error_messages_v2 "SELECT a FROM t2" "DROP TABLE t2" +} [list {*}{ + SQLITE_ERROR {no such table: t2} + SQLITE_ERROR {no such table: t2} +}] + +finish_test