/ Check-in [8f88cc4e]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment: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.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 8f88cc4e616b4b30ed349f89e148f782da5cb6c4
User & Date: dan 2011-10-27 15:19:58
References
2011-10-31
06:52
Update fts3fault.test to account for the sqlite3_errmsg() related changes in [8f88cc4e61] and [dcb7879347]. check-in: 3f2d49c6 user: dan tags: trunk
2011-10-29
19:25
Update fkey_malloc.test to account for the sqlite3_errmsg() related changes in [8f88cc4e61] and [dcb7879347]. check-in: 5b82ec6f user: dan tags: trunk
Context
2011-10-29
01:33
Avoid reporting a NOMEM error if a memory allocation fails while copying the error message from a prepared statement into the database connection. check-in: dcb78793 user: drh tags: trunk
2011-10-27
15:19
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. check-in: 8f88cc4e user: dan tags: trunk
2011-10-22
21:00
Avoid a harmless reference to an uninitialized variable following an error in FTS3. This is not a bug. The change is to silence a valgrind warning. check-in: d980c5b2 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/vdbeInt.h.

   391    391   int sqlite3VdbeMemFinalize(Mem*, FuncDef*);
   392    392   const char *sqlite3OpcodeName(int);
   393    393   int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve);
   394    394   int sqlite3VdbeCloseStatement(Vdbe *, int);
   395    395   void sqlite3VdbeFrameDelete(VdbeFrame*);
   396    396   int sqlite3VdbeFrameRestore(VdbeFrame *);
   397    397   void sqlite3VdbeMemStoreType(Mem *pMem);
          398  +int sqlite3VdbeTransferError(Vdbe *p);
   398    399   
   399    400   #ifdef SQLITE_OMIT_MERGE_SORT
   400    401   # define sqlite3VdbeSorterInit(Y,Z)      SQLITE_OK
   401    402   # define sqlite3VdbeSorterWrite(X,Y,Z)   SQLITE_OK
   402    403   # define sqlite3VdbeSorterClose(Y,Z)
   403    404   # define sqlite3VdbeSorterRowkey(Y,Z)    SQLITE_OK
   404    405   # define sqlite3VdbeSorterRewind(X,Y,Z)  SQLITE_OK

Changes to src/vdbeapi.c.

   450    450     );
   451    451     assert( p->rc!=SQLITE_ROW && p->rc!=SQLITE_DONE );
   452    452     if( p->isPrepareV2 && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){
   453    453       /* If this statement was prepared using sqlite3_prepare_v2(), and an
   454    454       ** error has occured, then return the error code in p->rc to the
   455    455       ** caller. Set the error code in the database handle to the same value.
   456    456       */ 
   457         -    rc = db->errCode = p->rc;
          457  +    rc = sqlite3VdbeTransferError(p);
   458    458     }
   459    459     return (rc&db->errMask);
   460    460   }
   461    461   
   462    462   /*
   463    463   ** The maximum number of times that a statement will try to reparse
   464    464   ** itself before giving up and returning SQLITE_SCHEMA.

Changes to src/vdbeaux.c.

  2305   2305   /*
  2306   2306   ** Each VDBE holds the result of the most recent sqlite3_step() call
  2307   2307   ** in p->rc.  This routine sets that result back to SQLITE_OK.
  2308   2308   */
  2309   2309   void sqlite3VdbeResetStepResult(Vdbe *p){
  2310   2310     p->rc = SQLITE_OK;
  2311   2311   }
         2312  +
         2313  +/*
         2314  +** Copy the error code and error message belonging to the VDBE passed
         2315  +** as the first argument to its database handle (so that they will be 
         2316  +** returned by calls to sqlite3_errcode() and sqlite3_errmsg()).
         2317  +**
         2318  +** This function does not clear the VDBE error code or message, just
         2319  +** copies them to the database handle.
         2320  +*/
         2321  +int sqlite3VdbeTransferError(Vdbe *p){
         2322  +  sqlite3 *db = p->db;
         2323  +  int rc = p->rc;
         2324  +  if( p->zErrMsg ){
         2325  +    sqlite3BeginBenignMalloc();
         2326  +    sqlite3ValueSetStr(db->pErr, -1, p->zErrMsg, SQLITE_UTF8, SQLITE_TRANSIENT);
         2327  +    sqlite3EndBenignMalloc();
         2328  +    db->errCode = rc;
         2329  +  }else{
         2330  +    sqlite3Error(db, rc, 0);
         2331  +  }
         2332  +  return rc;
         2333  +}
  2312   2334   
  2313   2335   /*
  2314   2336   ** Clean up a VDBE after execution but do not delete the VDBE just yet.
  2315   2337   ** Write any error messages into *pzErrMsg.  Return the result code.
  2316   2338   **
  2317   2339   ** After this routine is run, the VDBE should be ready to be executed
  2318   2340   ** again.
................................................................................
  2333   2355   
  2334   2356     /* If the VDBE has be run even partially, then transfer the error code
  2335   2357     ** and error message from the VDBE into the main database structure.  But
  2336   2358     ** if the VDBE has just been set to run but has not actually executed any
  2337   2359     ** instructions yet, leave the main database error information unchanged.
  2338   2360     */
  2339   2361     if( p->pc>=0 ){
  2340         -    if( p->zErrMsg ){
  2341         -      sqlite3BeginBenignMalloc();
  2342         -      sqlite3ValueSetStr(db->pErr,-1,p->zErrMsg,SQLITE_UTF8,SQLITE_TRANSIENT);
  2343         -      sqlite3EndBenignMalloc();
  2344         -      db->errCode = p->rc;
  2345         -      sqlite3DbFree(db, p->zErrMsg);
  2346         -      p->zErrMsg = 0;
  2347         -    }else if( p->rc ){
  2348         -      sqlite3Error(db, p->rc, 0);
  2349         -    }else{
  2350         -      sqlite3Error(db, SQLITE_OK, 0);
  2351         -    }
         2362  +    sqlite3VdbeTransferError(p);
         2363  +    sqlite3DbFree(db, p->zErrMsg);
         2364  +    p->zErrMsg = 0;
  2352   2365       if( p->runOnlyOnce ) p->expired = 1;
  2353   2366     }else if( p->rc && p->expired ){
  2354   2367       /* The expired flag was set on the VDBE before the first call
  2355   2368       ** to sqlite3_step(). For consistency (since sqlite3_step() was
  2356   2369       ** called), set the database error in this case as well.
  2357   2370       */
  2358   2371       sqlite3Error(db, p->rc, 0);

Added test/errmsg.test.

            1  +# 2001 September 15
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#***********************************************************************
           11  +# Test that if sqlite3_prepare_v2() is used to prepare a query, the
           12  +# error-message associated with an sqlite3_step() error is available
           13  +# immediately. Whereas if sqlite3_prepare() is used, it is not available
           14  +# until sqlite3_finalize() or sqlite3_reset() has been called.
           15  +#
           16  +
           17  +set testdir [file dirname $argv0]
           18  +source $testdir/tester.tcl
           19  +
           20  +set testprefix errmsg
           21  +
           22  +# Test organization:
           23  +#
           24  +#   errmsg-1.*         User-defined SQL function errors
           25  +#   errmsg-2.*         Errors generated by the VDBE (constraint failures etc.)
           26  +#   errmsg-3.*         SQLITE_SCHEMA and statement recompilation errors.
           27  +#
           28  +
           29  +proc error_messages_worker {prepare sql schema} {
           30  +  set ret [list]
           31  +
           32  +  set stmt [$prepare db $sql -1 dummy]
           33  +  execsql $schema
           34  +  lappend ret [sqlite3_step $stmt]
           35  +  lappend ret [sqlite3_errmsg db]
           36  +  lappend ret [sqlite3_finalize $stmt]
           37  +  lappend ret [sqlite3_errmsg db]
           38  +
           39  +  set ret
           40  +}
           41  +
           42  +proc error_messages_v2 {sql {schema {}}} {
           43  +  error_messages_worker sqlite3_prepare_v2 $sql $schema
           44  +}
           45  +
           46  +proc error_messages {sql {schema {}}} {
           47  +  error_messages_worker sqlite3_prepare $sql $schema
           48  +}
           49  +
           50  +proc sql_error {msg} { error $msg }
           51  +db func sql_error sql_error
           52  +
           53  +#-------------------------------------------------------------------------
           54  +# Test error messages returned by user-defined SQL functions.
           55  +#
           56  +do_test 1.1 {
           57  +  error_messages "SELECT sql_error('custom message')"
           58  +} [list {*}{
           59  +    SQLITE_ERROR {SQL logic error or missing database} 
           60  +    SQLITE_ERROR {custom message}
           61  +}]
           62  +do_test 1.2 {
           63  +  error_messages_v2 "SELECT sql_error('custom message')"
           64  +} [list {*}{
           65  +    SQLITE_ERROR {custom message}
           66  +    SQLITE_ERROR {custom message}
           67  +}]
           68  +
           69  +#-------------------------------------------------------------------------
           70  +# Test error messages generated directly by VDBE code (e.g. constraint
           71  +# failures).
           72  +#
           73  +do_execsql_test 2.1 {
           74  +  CREATE TABLE t1(a PRIMARY KEY, b UNIQUE);
           75  +  INSERT INTO t1 VALUES('abc', 'def');
           76  +}
           77  +do_test 2.2 {
           78  +  error_messages "INSERT INTO t1 VALUES('ghi', 'def')"
           79  +} [list {*}{
           80  +    SQLITE_ERROR      {SQL logic error or missing database} 
           81  +    SQLITE_CONSTRAINT {column b is not unique}
           82  +}]
           83  +do_test 2.3 {
           84  +  error_messages_v2 "INSERT INTO t1 VALUES('ghi', 'def')"
           85  +} [list {*}{
           86  +    SQLITE_CONSTRAINT {column b is not unique}
           87  +    SQLITE_CONSTRAINT {column b is not unique}
           88  +}]
           89  +
           90  +#-------------------------------------------------------------------------
           91  +# Test SQLITE_SCHEMA errors. And, for _v2(), test that if the schema
           92  +# change invalidates the SQL statement itself the error message is returned
           93  +# correctly.
           94  +#
           95  +do_execsql_test 3.1.1 {
           96  +  CREATE TABLE t2(a PRIMARY KEY, b UNIQUE);
           97  +  INSERT INTO t2 VALUES('abc', 'def');
           98  +}
           99  +do_test 3.1.2 {
          100  +  error_messages "SELECT a FROM t2" "DROP TABLE t2"
          101  +} [list {*}{
          102  +    SQLITE_ERROR {SQL logic error or missing database} 
          103  +    SQLITE_SCHEMA {database schema has changed}
          104  +}]
          105  +do_execsql_test 3.2.1 {
          106  +  CREATE TABLE t2(a PRIMARY KEY, b UNIQUE);
          107  +  INSERT INTO t2 VALUES('abc', 'def');
          108  +}
          109  +do_test 3.2.2 {
          110  +  error_messages_v2 "SELECT a FROM t2" "DROP TABLE t2"
          111  +} [list {*}{
          112  +    SQLITE_ERROR {no such table: t2} 
          113  +    SQLITE_ERROR {no such table: t2}
          114  +}]
          115  +
          116  +finish_test