]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Handle some of the IO error conditions that may occur in a shared-cache context....
authordanielk1977 <danielk1977@noemail.net>
Fri, 20 Jan 2006 10:55:05 +0000 (10:55 +0000)
committerdanielk1977 <danielk1977@noemail.net>
Fri, 20 Jan 2006 10:55:05 +0000 (10:55 +0000)
FossilOrigin-Name: 97491d4eb5fc24d8f5cc7605db844359ecc6a818

manifest
manifest.uuid
src/btree.c
src/pager.c
src/test1.c
src/test3.c
src/vdbeaux.c
test/malloc3.test
test/quick.test
test/shared_err.test [new file with mode: 0644]

index 73f18eaa5bbdd70c8b425367044a76521e6efbdc..49283d5ac0c267bcc8828611094957c9bd6b33c9 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Make\ssure\ssqlite3_thread_cleanup()\sdoes\snot\stry\sto\sallocate\smemory.\s(CVS\s2979)
-D 2006-01-19T17:42:51
+C Handle\ssome\sof\sthe\sIO\serror\sconditions\sthat\smay\soccur\sin\sa\sshared-cache\scontext.\s(CVS\s2980)
+D 2006-01-20T10:55:05
 F Makefile.in ab3ffd8d469cef4477257169b82810030a6bb967
 F Makefile.linux-gcc aee18d8a05546dcf1888bd4547e442008a49a092
 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
@@ -34,7 +34,7 @@ F src/alter.c 90b779cf00489535cab6490df6dc050f40e4e874
 F src/analyze.c 7d2b7ab9a9c2fd6e55700f69064dfdd3e36d7a8a
 F src/attach.c 0081040e9a5d13669b6712e947688c10f030bfc1
 F src/auth.c 9ae84d2d94eb96195e04515715e08e85963e96c2
-F src/btree.c 2ea7d4372e8ec0a8894ec13e890aedf976e4bbd1
+F src/btree.c e8ff8d76a412299cad1c9a2c4e1fd15ad48bae27
 F src/btree.h 5663c4f43e8521546ccebc8fc95acb013b8f3184
 F src/build.c 15224e2fd348ad32b9044aaa5bdc912e4067da15
 F src/callback.c 1bf497306c32229114f826707054df7ebe10abf2
@@ -59,7 +59,7 @@ F src/os_unix.c a242a9458b08f01fa11d42b23bcdb89a3fbf2a68
 F src/os_unix.h 5768d56d28240d3fe4537fac08cc85e4fb52279e
 F src/os_win.c 98e4e38db7d4a00647b2bb1c60d28b7ca5034c03
 F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b
-F src/pager.c ccdd092702decc02fd3a54380c6d46c546eea5a6
+F src/pager.c 49fab8c32de2419cb549220d285c6399bc0d899e
 F src/pager.h e0acb095b3ad0bca48f2ab00c87346665643f64f
 F src/parse.y 83df51fea35f68f7e07384d75dce83d1ed30434c
 F src/pragma.c 4496cc77dc35824e1c978c3d1413b8a5a4c777d3
@@ -73,9 +73,9 @@ F src/sqlite.h.in 492580f7e3ff71eb43193eb7bb98e2d549889ce3
 F src/sqliteInt.h 7ddd6141a57748363fe42119f165d06260996aa3
 F src/table.c 486dcfce532685b53b5a2b5da8bba0ded6fb2316
 F src/tclsqlite.c d650bea0248fc0a310ddc2cb94273a3a5021fddf
-F src/test1.c 1f1c9cd74b8127d0e29519362a4f0e6e45c9f255
+F src/test1.c 44e8f194ee7d95e25dc421b10ced2ec9d89632a0
 F src/test2.c ca74a1d8aeb7d9606e8f6b762c5daf85c1a3f92b
-F src/test3.c 9742aa146eb750cab81c1d5605286c3a0eb88054
+F src/test3.c 86e99724ee898b119ed575ef9f98618afe7e5e5d
 F src/test4.c 6633cb7b4a7429e938804a34f688d118c8b7f8e1
 F src/test5.c 7162f8526affb771c4ed256826eee7bb9eca265f
 F src/test6.c 74d91b487c68154156eded457925d96aa2a3fdbb
@@ -91,7 +91,7 @@ F src/vdbe.c 2a3d5ea76ab166bd77de9e3a7dbeb4a9354a1603
 F src/vdbe.h 8729a4ee16ff9aeab2af9667df3cf300ff978e13
 F src/vdbeInt.h 5451cf71f229e366ac543607c0a17f36e5737ea9
 F src/vdbeapi.c b5a3eacce266a657cdc0fc740b60ba480fb88649
-F src/vdbeaux.c 1b3fe25f69ec9a3616f8cd747f3b8b87a0a40f04
+F src/vdbeaux.c d9a757ed4e3eefc54408226cb781694059fe2b39
 F src/vdbefifo.c 9efb94c8c3f4c979ebd0028219483f88e57584f5
 F src/vdbemem.c 53f25c5c537e4ded24549d6c8537e65d4efc42d1
 F src/where.c 5215507b232e718606e0014f999912d53de32a70
@@ -188,7 +188,7 @@ F test/lock3.test 615111293cf32aa2ed16d01c6611737651c96fb9
 F test/main.test b12f01d49a5c805a33fa6c0ef168691f63056e79
 F test/malloc.test ce6d1e7e79f9db967b51e1975b50760af66db90d
 F test/malloc2.test e6e321db96d6c94cb18bf82ad7215070c41e624e
-F test/malloc3.test 265644c655497242f7c0a1bb5b36c8500a5fc27c
+F test/malloc3.test 1cf2376c9495973608c021efaefb25e71bd6e21f
 F test/malloc4.test 2e29d155eb4b4808019ef47eeedfcbe9e09e0f05
 F test/malloc5.test 7425272e263325fda7d32cb55706e52b5c09e7e0
 F test/manydb.test 8de36b8d33aab5ef295b11d9e95310aeded31af8
@@ -210,7 +210,7 @@ F test/pagesize.test 05c74ea49f790734ec1e9ab765d9bf1cce79b8f2
 F test/pragma.test 8759b46702f6d8ee4f5dd520216c69dbc9080b60
 F test/printf.test 9e10c74e16bf889f8495ddb3d6f5f891e75ff1b7
 F test/progress.test 16496001da445e6534afb94562c286708316d82f x
-F test/quick.test 2d2cf1b50e894b19b4029dd9a68ec59599b24f18
+F test/quick.test 38d1feefe5bfd0a61aa535775448276630ecfb92
 F test/quote.test 5891f2338980916cf7415484b4ce785294044adb
 F test/reindex.test 38b138abe36bf9a08c791ed44d9f76cd6b97b78b
 F test/rollback.test 673cd8c44c685ad54987fe7f0eeba84efa09685d
@@ -227,6 +227,7 @@ F test/select7.test 1bf795b948c133a15a2a5e99d3270e652ec58ce6
 F test/server1.test e328b8e641ba8fe9273132cfef497383185dc1f5
 F test/shared.test 9982a65ccf3f4eee844a19f3ab0bcd7a158a76e5
 F test/shared2.test 909fc0f0277684ed29cc1b36c8e159188aec7f28
+F test/shared_err.test dea32ad1ce72c1aa88a4671b5de3900961f6c687
 F test/sort.test 0e4456e729e5a92a625907c63dcdedfbe72c5dc5
 F test/subquery.test ae324ee928c5fb463a3ce08a8860d6e7f1ca5797
 F test/subselect.test 2d13fb7f450db3595adcdd24079a0dd1d2d6abc2
@@ -342,7 +343,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9
 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
 F www/version3.tcl a99cf5f6d8bd4d5537584a2b342f0fb9fa601d8b
 F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513
-P 218c6184c8edec22f9b56b3c9446d27fda9c1e00
-R 1bceb1e22a686481ca5f53186e58ea75
-U drh
-Z 93ab8d9821d0109154bcc26ca1eba6f4
+P 0208e4221a2d90b5ae0755061c345d3351a30da8
+R 1b27118630fddde079e3c9acefda5965
+U danielk1977
+Z 68c504492dfd4d0600ae3c8ff35cbb3d
index 0a79d07125c4ac545528b88812b0d6bd09cc93e3..d56f17b21eb693dc904931cfc05955a52db41997 100644 (file)
@@ -1 +1 @@
-0208e4221a2d90b5ae0755061c345d3351a30da8
\ No newline at end of file
+97491d4eb5fc24d8f5cc7605db844359ecc6a818
\ No newline at end of file
index 59b824bcd0bb4916afd56fee7f955c946bfb84c2..9471665b3c5cb9fe248534c82dc5ab7044966a1a 100644 (file)
@@ -9,7 +9,7 @@
 **    May you share freely, never taking more than you give.
 **
 *************************************************************************
-** $Id: btree.c,v 1.302 2006/01/19 08:43:31 danielk1977 Exp $
+** $Id: btree.c,v 1.303 2006/01/20 10:55:05 danielk1977 Exp $
 **
 ** This file implements a external (disk-based) database using BTrees.
 ** For a detailed discussion of BTrees, refer to
@@ -2498,7 +2498,7 @@ static int countWriteCursors(BtShared *pBt){
 }
 #endif
 
-#ifdef SQLITE_TEST
+#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
 /*
 ** Print debugging information about all cursors to standard output.
 */
@@ -4433,8 +4433,10 @@ static int balance_nonroot(MemPage *pPage){
   assert( sqlite3pager_iswriteable(pPage->aData) );
   pBt = pPage->pBt;
   pParent = pPage->pParent;
-  sqlite3pager_write(pParent->aData);
   assert( pParent );
+  if( SQLITE_OK!=(rc = sqlite3pager_write(pParent->aData)) ){
+    return rc;
+  }
   TRACE(("BALANCE: begin page %d child of %d\n", pPage->pgno, pParent->pgno));
 
 #ifndef SQLITE_OMIT_QUICKBALANCE
@@ -5873,7 +5875,7 @@ int sqlite3BtreePageDump(Btree *p, int pgno, int recursive){
 }
 #endif
 
-#ifdef SQLITE_TEST
+#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
 /*
 ** Fill aResult[] with information about the entry and page that the
 ** cursor is pointing to.
@@ -6546,7 +6548,7 @@ int sqlite3BtreeLockTable(Btree *p, int iTab, u8 isWriteLock){
 ** than in, for example, test1.c) so that it can get access to
 ** the definition of BtShared.
 */
-#if defined(SQLITE_TEST) && defined(TCLSH)
+#if defined(SQLITE_DEBUG) && defined(TCLSH)
 #include <tcl.h>
 int sqlite3_shared_cache_report(
   void * clientData,
index 27c7078b75f45562c40504daa101fcb0d9daf798..9d52df3ed37737a53f346a5ed80c69b9d61ebe58 100644 (file)
@@ -18,7 +18,7 @@
 ** file simultaneously, or one process from reading the database while
 ** another is writing.
 **
-** @(#) $Id: pager.c,v 1.245 2006/01/18 18:22:43 danielk1977 Exp $
+** @(#) $Id: pager.c,v 1.246 2006/01/20 10:55:05 danielk1977 Exp $
 */
 #ifndef SQLITE_OMIT_DISKIO
 #include "sqliteInt.h"
@@ -468,8 +468,8 @@ static u32 retrieve32bits(PgHdr *p, int offset){
 static int pager_error(Pager *pPager, int rc){
   assert( pPager->errCode==SQLITE_FULL || pPager->errCode==SQLITE_OK );
   if( 
-    rc==SQLITE_FULL || 
-    rc==SQLITE_IOERR || 
+    rc==SQLITE_FULL ||
+    rc==SQLITE_IOERR ||
     rc==SQLITE_CORRUPT ||
     rc==SQLITE_PROTOCOL
   ){
@@ -3017,19 +3017,27 @@ int sqlite3pager_write(void *pData){
           store32bits(cksum, pPg, pPager->pageSize);
           szPg = pPager->pageSize+8;
           store32bits(pPg->pgno, pPg, -4);
+
           rc = sqlite3OsWrite(pPager->jfd, &((char*)pData)[-4], szPg);
           pPager->journalOff += szPg;
           TRACE4("JOURNAL %d page %d needSync=%d\n",
                   PAGERID(pPager), pPg->pgno, pPg->needSync);
           CODEC(pPager, pData, pPg->pgno, 0);
           *(u32*)PGHDR_TO_EXTRA(pPg, pPager) = saved;
+
+         /* An error has occured writing to the journal file. The 
+          ** transaction will be rolled back by the layer above.
+          */
           if( rc!=SQLITE_OK ){
+#if 0
             sqlite3pager_rollback(pPager);
             if( !pPager->errCode ){
               pager_error(pPager, SQLITE_FULL);
             }
+#endif
             return rc;
           }
+
           pPager->nRec++;
           assert( pPager->aInJournal!=0 );
           pPager->aInJournal[pPg->pgno/8] |= 1<<(pPg->pgno&7);
@@ -3371,12 +3379,18 @@ int sqlite3pager_rollback(Pager *pPager){
   }else{
     rc = pager_playback(pPager);
   }
+#if 0
   if( rc!=SQLITE_OK ){
     rc = SQLITE_CORRUPT_BKPT;
-    pager_error(pPager, SQLITE_CORRUPT);
   }
+#endif
   pPager->dbSize = -1;
-  return rc;
+
+  /* If an error occurs during a ROLLBACK, we can no longer trust the pager
+  ** cache. So call pager_error() on the way out to make any error 
+  ** persistent.
+  */
+  return pager_error(pPager, rc);
 }
 
 /*
index 2abbcfc40f9089fb9692116ea30f71fd5174dc8d..f3444d08a7c544a05760df783cdb59fae20161ba 100644 (file)
@@ -13,7 +13,7 @@
 ** is not included in the SQLite library.  It is used for automated
 ** testing of the SQLite library.
 **
-** $Id: test1.c,v 1.199 2006/01/19 11:28:07 drh Exp $
+** $Id: test1.c,v 1.200 2006/01/20 10:55:05 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include "tcl.h"
@@ -3593,10 +3593,12 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
   set_options(interp);
 
   {
+#ifdef SQLITE_DEBUG
     extern int sqlite3_shared_cache_report(void *, Tcl_Interp *,
                                     int, Tcl_Obj *CONST[]);
     Tcl_CreateObjCommand(interp, "sqlite_shared_cache_report", 
         sqlite3_shared_cache_report, 0, 0);
+#endif
   }
   return TCL_OK;
 }
index e93e858e7ff973c693dab4145158f33c8be367be..bd0ff8a960999e710176d1426dc0ea9117af3dcc 100644 (file)
@@ -13,7 +13,7 @@
 ** is not included in the SQLite library.  It is used for automated
 ** testing of the SQLite library.
 **
-** $Id: test3.c,v 1.64 2005/12/30 16:28:02 danielk1977 Exp $
+** $Id: test3.c,v 1.65 2006/01/20 10:55:05 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include "pager.h"
@@ -597,6 +597,7 @@ static int btree_integrity_check(
 **
 ** Print information about all cursors to standard output for debugging.
 */
+#ifdef SQLITE_DEBUG
 static int btree_cursor_list(
   void *NotUsed,
   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
@@ -614,6 +615,7 @@ static int btree_cursor_list(
   sqlite3BtreeCursorList(pBt);
   return SQLITE_OK;
 }
+#endif
 
 /*
 ** Usage:   btree_cursor ID TABLENUM WRITEABLE
@@ -1184,6 +1186,7 @@ static int btree_payload_size(
 **   aResult[8] =  Local payload size
 **   aResult[9] =  Parent page number
 */
+#ifdef SQLITE_DEBUG
 static int btree_cursor_info(
   void *NotUsed,
   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
@@ -1221,6 +1224,7 @@ static int btree_cursor_info(
   Tcl_AppendResult(interp, &zBuf[1], 0);
   return SQLITE_OK;
 }
+#endif
 
 /*
 ** The command is provided for the purpose of setting breakpoints.
@@ -1428,8 +1432,6 @@ int Sqlitetest3_Init(Tcl_Interp *interp){
      { "btree_payload_size",       (Tcl_CmdProc*)btree_payload_size       },
      { "btree_first",              (Tcl_CmdProc*)btree_first              },
      { "btree_last",               (Tcl_CmdProc*)btree_last               },
-     { "btree_cursor_info",        (Tcl_CmdProc*)btree_cursor_info        },
-     { "btree_cursor_list",        (Tcl_CmdProc*)btree_cursor_list        },
      { "btree_integrity_check",    (Tcl_CmdProc*)btree_integrity_check    },
      { "btree_breakpoint",         (Tcl_CmdProc*)btree_breakpoint         },
      { "btree_varint_test",        (Tcl_CmdProc*)btree_varint_test        },
@@ -1438,6 +1440,10 @@ int Sqlitetest3_Init(Tcl_Interp *interp){
      { "btree_rollback_statement", (Tcl_CmdProc*)btree_rollback_statement },
      { "btree_from_db",            (Tcl_CmdProc*)btree_from_db            },
      { "btree_set_cache_size",     (Tcl_CmdProc*)btree_set_cache_size     },
+#ifdef SQLITE_DEBUG
+     { "btree_cursor_info",        (Tcl_CmdProc*)btree_cursor_info        },
+     { "btree_cursor_list",        (Tcl_CmdProc*)btree_cursor_list        },
+#endif
   };
   int i;
 
index 710c3e4de4202ab6d6a1effa968b673cc8b4873e..4c61e7c19240d22503995e1bbd8a340a1a4c9b7f 100644 (file)
@@ -1111,6 +1111,11 @@ static void abortOtherActiveVdbes(Vdbe *pVdbe){
   }
 }
 
+static void rollbackAll(sqlite3 *db, Vdbe *pVdbe){
+  abortOtherActiveVdbes(pVdbe);
+  sqlite3RollbackAll(db);
+}
+
 /* 
 ** This routine checks that the sqlite3.activeVdbeCnt count variable
 ** matches the number of vdbe's in the list sqlite3.pVdbe that are
@@ -1153,11 +1158,39 @@ int sqlite3VdbeHalt(Vdbe *p){
   sqlite3 *db = p->db;
   int i;
   int (*xFunc)(Btree *pBt) = 0;  /* Function to call on each btree backend */
+  int isSpecialError;            /* Set to true if SQLITE_NOMEM or IOERR */
+
+  /* This function contains the logic that determines if a statement or
+  ** transaction will be committed or rolled back as a result of the
+  ** execution of this virtual machine. 
+  **
+  ** Special errors:
+  **
+  **     If an SQLITE_NOMEM error has occured in a statement that writes to
+  **     the database, then either a statement or transaction must be rolled
+  **     back to ensure the tree-structures are in a consistent state. A
+  **     statement transaction is rolled back if one is open, otherwise the
+  **     entire transaction must be rolled back.
+  **
+  **     If an SQLITE_IOERR error has occured in a statement that writes to
+  **     the database, then the entire transaction must be rolled back. The
+  **     I/O error may have caused garbage to be written to the journal 
+  **     file. Were the transaction to continue and eventually be rolled 
+  **     back that garbage might end up in the database file.
+  **     
+  **     In both of the above cases, the Vdbe.errorAction variable is 
+  **     ignored. If the sqlite3.autoCommit flag is false and a transaction
+  **     is rolled back, it will be set to true.
+  **
+  ** Other errors:
+  **
+  ** No error:
+  **
+  */
 
   if( sqlite3MallocFailed() ){
     p->rc = SQLITE_NOMEM;
   }
-
   if( p->magic!=VDBE_MAGIC_RUN ){
     /* Already halted.  Nothing to do. */
     assert( p->magic==VDBE_MAGIC_HALT );
@@ -1165,44 +1198,25 @@ int sqlite3VdbeHalt(Vdbe *p){
   }
   closeAllCursors(p);
   checkActiveVdbeCnt(db);
-  if( p->pc<0 ){
-    /* No commit or rollback needed if the program never started */
-  }else if( db->autoCommit && db->activeVdbeCnt==1 ){
-
-    if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && p->rc!=SQLITE_NOMEM)){
-      /* The auto-commit flag is true, there are no other active queries
-      ** using this handle and the vdbe program was successful or hit an
-      ** 'OR FAIL' constraint. This means a commit is required.
-      */
-      int rc = vdbeCommit(db);
-      if( rc==SQLITE_BUSY ){
-        return SQLITE_BUSY;
-      }else if( rc!=SQLITE_OK ){
-        p->rc = rc;
-        sqlite3RollbackAll(db);
-      }else{
-        sqlite3CommitInternalChanges(db);
-      }
-    }else{
-      sqlite3RollbackAll(db);
-    }
-  }else{
 
-    if( p->rc==SQLITE_NOMEM ){
+  /* No commit or rollback needed if the program never started */
+  if( p->pc>=0 ){
+
+    /* Check for one of the special errors - SQLITE_NOMEM or SQLITE_IOERR */
+    isSpecialError = ((p->rc==SQLITE_NOMEM || p->rc==SQLITE_IOERR)?1:0);
+    if( isSpecialError ){
       /* This loop does static analysis of the query to see which of the
       ** following three categories it falls into:
       **
       **     Read-only
-      **     Query with statement journal          -> rollback statement
-      **     Query without statement journal       -> rollback transaction
+      **     Query with statement journal
+      **     Query without statement journal
       **
       ** We could do something more elegant than this static analysis (i.e.
       ** store the type of query as part of the compliation phase), but 
-      ** handling malloc() failure is a fairly obscure edge case so this is
-      ** probably easier.
-      **
-      ** Todo: This means we always override the p->errorAction value for a
-      ** malloc() failure. Is there any other choice here though?
+      ** handling malloc() or IO failure is a fairly obscure edge case so 
+      ** this is probably easier. Todo: Might be an opportunity to reduce 
+      ** code size a very small amount though...
       */
       int isReadOnly = 1;
       int isStatement = 0;
@@ -1217,56 +1231,92 @@ int sqlite3VdbeHalt(Vdbe *p){
             break;
         }
       }
-      if( (isReadOnly||isStatement) && p->errorAction!=OE_Rollback ){
-        p->errorAction = OE_Abort;
-      }else{ 
-        p->errorAction = OE_Rollback;
+  
+      /* If the query was read-only, we need do no rollback at all. Otherwise,
+      ** proceed with the special handling.
+      */
+      if( !isReadOnly ){
+        if( p->rc==SQLITE_NOMEM && isStatement ){
+          xFunc = sqlite3BtreeRollbackStmt;
+        }else{
+          /* We are forced to roll back the active transaction. Before doing
+          ** so, abort any other statements this handle currently has active.
+          */
+          db->autoCommit = 1;
+        }
       }
     }
-
-    if( p->rc==SQLITE_OK || p->errorAction==OE_Fail ){
-      xFunc = sqlite3BtreeCommitStmt;
-    }else if( p->errorAction==OE_Abort ){
-      xFunc = sqlite3BtreeRollbackStmt;
-    }else{
-      abortOtherActiveVdbes(p);
-      sqlite3RollbackAll(db);
-      db->autoCommit = 1;
+  
+    /* If the auto-commit flag is set and this is the only active vdbe, then
+    ** we do either a commit or rollback of the current transaction. 
+    **
+    ** Note: This block also runs if one of the special errors handled 
+    ** above has occured. 
+    */
+    if( db->autoCommit && db->activeVdbeCnt==1 ){
+      if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){
+       /* The auto-commit flag is true, and the vdbe program was 
+        ** successful or hit an 'OR FAIL' constraint. This means a commit 
+        ** is required.
+        */
+        int rc = vdbeCommit(db);
+        if( rc==SQLITE_BUSY ){
+          return SQLITE_BUSY;
+        }else if( rc!=SQLITE_OK ){
+          p->rc = rc;
+          rollbackAll(db, p);
+        }else{
+          sqlite3CommitInternalChanges(db);
+        }
+      }else{
+        rollbackAll(db, p);
+      }
+    }else if( !xFunc ){
+      if( p->rc==SQLITE_OK || p->errorAction==OE_Fail ){
+        xFunc = sqlite3BtreeCommitStmt;
+      }else if( p->errorAction==OE_Abort ){
+        xFunc = sqlite3BtreeRollbackStmt;
+      }else{
+        rollbackAll(db, p);
+        db->autoCommit = 1;
+      }
     }
-  }
-
-  /* If xFunc is not NULL, then it is one of 
-  ** sqlite3BtreeRollbackStmt or sqlite3BtreeCommitStmt. Call it once on
-  ** each backend. If an error occurs and the return code is still
-  ** SQLITE_OK, set the return code to the new error value.
-  */
-  assert(!xFunc ||
-    xFunc==sqlite3BtreeCommitStmt ||
-    xFunc==sqlite3BtreeRollbackStmt
-  );
-  for(i=0; xFunc && i<db->nDb; i++){ 
-    int rc;
-    Btree *pBt = db->aDb[i].pBt;
-    if( pBt ){
-      rc = xFunc(pBt);
-      if( p->rc==SQLITE_OK ) p->rc = rc;
+  
+    /* If xFunc is not NULL, then it is one of sqlite3BtreeRollbackStmt or
+    ** sqlite3BtreeCommitStmt. Call it once on each backend. If an error occurs
+    ** and the return code is still SQLITE_OK, set the return code to the new
+    ** error value.
+    */
+    assert(!xFunc ||
+      xFunc==sqlite3BtreeCommitStmt ||
+      xFunc==sqlite3BtreeRollbackStmt
+    );
+    for(i=0; xFunc && i<db->nDb; i++){ 
+      int rc;
+      Btree *pBt = db->aDb[i].pBt;
+      if( pBt ){
+        rc = xFunc(pBt);
+        if( p->rc==SQLITE_OK ) p->rc = rc;
+      }
     }
-  }
-
-  /* If this was an INSERT, UPDATE or DELETE, set the change counter. */
-  if( p->changeCntOn && p->pc>=0 ){
-    if( !xFunc || xFunc==sqlite3BtreeCommitStmt ){
-      sqlite3VdbeSetChanges(db, p->nChange);
-    }else{
-      sqlite3VdbeSetChanges(db, 0);
+  
+    /* If this was an INSERT, UPDATE or DELETE and the statement was committed, 
+    ** set the change counter. 
+    */
+    if( p->changeCntOn && p->pc>=0 ){
+      if( !xFunc || xFunc==sqlite3BtreeCommitStmt ){
+        sqlite3VdbeSetChanges(db, p->nChange);
+      }else{
+        sqlite3VdbeSetChanges(db, 0);
+      }
+      p->nChange = 0;
+    }
+  
+    /* Rollback or commit any schema changes that occurred. */
+    if( p->rc!=SQLITE_OK && db->flags&SQLITE_InternChanges ){
+      sqlite3ResetInternalSchema(db, 0);
+      db->flags = (db->flags | SQLITE_InternChanges);
     }
-    p->nChange = 0;
-  }
-
-  /* Rollback or commit any schema changes that occurred. */
-  if( p->rc!=SQLITE_OK && db->flags&SQLITE_InternChanges ){
-    sqlite3ResetInternalSchema(db, 0);
-    db->flags = (db->flags | SQLITE_InternChanges);
   }
 
   /* We have successfully halted and closed the VM.  Record this fact. */
index 0b1729a727a54cc85ff875ca1d63469811eca078..bfdf3727a59a839adaf537f8110561549bcfd90b 100644 (file)
@@ -13,7 +13,7 @@
 # correctly. The emphasis of these tests are the _prepare(), _step() and
 # _finalize() calls.
 #
-# $Id: malloc3.test,v 1.6 2006/01/10 18:27:42 danielk1977 Exp $
+# $Id: malloc3.test,v 1.7 2006/01/20 10:55:05 danielk1977 Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -482,6 +482,24 @@ TEST 31 {
   } {1 2 3 1 2 3}
 }
 
+# Test what happens when a malloc() fails while there are other active
+# statements. This changes the way sqlite3VdbeHalt() works.
+TEST 32 {
+  if {![info exists ::STMT32]} {
+    set sql "SELECT name FROM sqlite_master"
+    set ::STMT32 [sqlite3_prepare $::DB $sql -1 DUMMY]
+    do_test $testid {
+      sqlite3_step $::STMT32
+    } {SQLITE_ROW}
+  }
+puts [execsql {SELECT * FROM ghi}]
+}
+SQL {
+  BEGIN;
+  INSERT INTO ghi SELECT * FROM ghi;
+  COMMIT;
+}
+
 #
 # End of test program declaration
 #--------------------------------------------------------------------------
@@ -612,7 +630,7 @@ proc run_test {arglist {pcstart 0} {iFailStart 1}} {
 # Turn of the Tcl interface's prepared statement caching facility.
 db cache size 0
 
-run_test $::run_test_script
+run_test $::run_test_script 76 6
 # run_test [lrange $::run_test_script 0 3] 0 63
 sqlite_malloc_fail 0
 db close
index e23a986f7dae1482bcb03b38b67217b1fe5ed047..b704aa9f68c3bb5a9fe8002e2794d17fe235688f 100644 (file)
@@ -6,7 +6,7 @@
 #***********************************************************************
 # This file runs all tests.
 #
-# $Id: quick.test,v 1.41 2006/01/10 07:14:24 danielk1977 Exp $
+# $Id: quick.test,v 1.42 2006/01/20 10:55:05 danielk1977 Exp $
 
 proc lshift {lvar} {
   upvar $lvar l
@@ -45,7 +45,6 @@ set EXCLUDE {
   malloc.test
   malloc2.test
   malloc3.test
-  malloc4.test
   memleak.test
   misuse.test
   quick.test
@@ -53,6 +52,7 @@ set EXCLUDE {
   autovacuum_crash.test
   btree8.test
   utf16.test
+  shared_err.test
 }
 
 if {[sqlite3 -has-codec]} {
diff --git a/test/shared_err.test b/test/shared_err.test
new file mode 100644 (file)
index 0000000..16d629d
--- /dev/null
@@ -0,0 +1,137 @@
+# 2005 December 30
+#
+# 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.
+#
+#***********************************************************************
+#
+# The focus of the tests in this file are IO errors that occur in a shared
+# cache context. What happens to connection B if one connection A encounters
+# an IO-error whilst reading or writing the file-system?
+#
+# $Id: shared_err.test,v 1.1 2006/01/20 10:55:05 danielk1977 Exp $
+
+proc skip {args} {}
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+db close
+
+ifcapable !shared_cache||!subquery {
+  finish_test
+  return
+}
+set ::enable_shared_cache [sqlite3_enable_shared_cache 1]
+
+skip \
+do_ioerr_test shared_ioerr-1 -tclprep {
+  sqlite3 db2 test.db
+  execsql {
+    PRAGMA read_uncommitted = 1;
+    CREATE TABLE t1(a,b,c);
+    BEGIN;
+    SELECT * FROM sqlite_master;
+  } db2
+} -sqlbody {
+  SELECT * FROM sqlite_master;
+  INSERT INTO t1 VALUES(1,2,3);
+  BEGIN TRANSACTION;
+  INSERT INTO t1 VALUES(1,2,3);
+  INSERT INTO t1 VALUES(4,5,6);
+  ROLLBACK;
+  SELECT * FROM t1;
+  BEGIN TRANSACTION;
+  INSERT INTO t1 VALUES(1,2,3);
+  INSERT INTO t1 VALUES(4,5,6);
+  COMMIT;
+  SELECT * FROM t1;
+  DELETE FROM t1 WHERE a<100;
+} -cleanup {
+  do_test shared_ioerr-$n.cleanup.1 {
+    set res [catchsql {
+      SELECT * FROM t1;
+    } db2]
+    set possible_results [list            \
+      "1 {disk I/O error}"                \
+      "0 {1 2 3}"                         \
+      "0 {1 2 3 1 2 3 4 5 6}"             \
+      "0 {1 2 3 1 2 3 4 5 6 1 2 3 4 5 6}" \
+      "0 {}"                              \
+    ]
+    set rc [expr [lsearch -exact $possible_results $res] >= 0]
+    if {$rc != 1} {
+      puts ""
+      puts "Result: $res"
+    }
+    set rc
+  } {1}
+  db2 close
+}
+
+do_ioerr_test shared_ioerr-2 -tclprep {
+  sqlite3 db2 test.db
+  execsql {
+    PRAGMA read_uncommitted = 1;
+    BEGIN;
+    CREATE TABLE t1(a, b);
+    INSERT INTO t1(oid) VALUES(NULL);
+    INSERT INTO t1(oid) SELECT NULL FROM t1;
+    INSERT INTO t1(oid) SELECT NULL FROM t1;
+    INSERT INTO t1(oid) SELECT NULL FROM t1;
+    INSERT INTO t1(oid) SELECT NULL FROM t1;
+    INSERT INTO t1(oid) SELECT NULL FROM t1;
+    INSERT INTO t1(oid) SELECT NULL FROM t1;
+    INSERT INTO t1(oid) SELECT NULL FROM t1;
+    INSERT INTO t1(oid) SELECT NULL FROM t1;
+    INSERT INTO t1(oid) SELECT NULL FROM t1;
+    INSERT INTO t1(oid) SELECT NULL FROM t1;
+    UPDATE t1 set a = oid, b = 'abcdefghijklmnopqrstuvwxyz0123456789';
+    CREATE INDEX i1 ON t1(a);
+    COMMIT;
+    BEGIN;
+    SELECT * FROM sqlite_master;
+  } db2
+} -tclbody {
+  set ::residx 0
+  execsql {DELETE FROM t1 WHERE 0 = (a % 2);}
+  incr ::residx
+
+  # When this transaction begins the table contains 512 entries. The
+  # two statements together add 512+146 more if it succeeds. 
+  # (1024/7==146)
+  execsql {BEGIN;}
+  execsql {INSERT INTO t1 SELECT a+1, b FROM t1;}
+  execsql {INSERT INTO t1 SELECT 'string' || a, b FROM t1 WHERE 0 = (a%7);}
+  execsql {COMMIT;}
+
+  incr ::residx
+} -cleanup {
+  do_test shared_ioerr-2.$n.cleanup.1 {
+    set res [catchsql {
+      SELECT max(a), min(a), count(*) FROM (SELECT a FROM t1 order by a);
+    } db2]
+    set possible_results [list \
+      {0 {1024 1 1024}}        \
+      {0 {1023 1 512}}         \
+      {0 {string994 1 1170}}   \
+    ]
+    set idx [lsearch -exact $possible_results $res]
+    set success [expr {$idx==$::residx || $res=="1 {disk I/O error}"}]
+    if {!$success} {
+      puts ""
+      puts "Result: \"$res\" ($::residx)"
+    }
+    set success
+  } {1}
+  db2 close
+}
+
+catch {db close}
+sqlite3_enable_shared_cache $::enable_shared_cache
+finish_test
+