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
This commit is contained in:
dan 2011-10-27 15:19:58 +00:00
parent 7abc54021c
commit 029ead645d
6 changed files with 154 additions and 23 deletions

View File

@ -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

View File

@ -1 +1 @@
d980c5b22fd700afb3cac5de5501c9350f2f589e
8f88cc4e616b4b30ed349f89e148f782da5cb6c4

View File

@ -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

View File

@ -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);
}

View File

@ -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

116
test/errmsg.test Normal file
View File

@ -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