diff --git a/manifest b/manifest index 73f18eaa5b..49283d5ac0 100644 --- 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 diff --git a/manifest.uuid b/manifest.uuid index 0a79d07125..d56f17b21e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -0208e4221a2d90b5ae0755061c345d3351a30da8 \ No newline at end of file +97491d4eb5fc24d8f5cc7605db844359ecc6a818 \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index 59b824bcd0..9471665b3c 100644 --- a/src/btree.c +++ b/src/btree.c @@ -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 int sqlite3_shared_cache_report( void * clientData, diff --git a/src/pager.c b/src/pager.c index 27c7078b75..9d52df3ed3 100644 --- a/src/pager.c +++ b/src/pager.c @@ -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); } /* diff --git a/src/test1.c b/src/test1.c index 2abbcfc40f..f3444d08a7 100644 --- a/src/test1.c +++ b/src/test1.c @@ -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; } diff --git a/src/test3.c b/src/test3.c index e93e858e7f..bd0ff8a960 100644 --- a/src/test3.c +++ b/src/test3.c @@ -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; diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 710c3e4de4..4c61e7c192 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -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{ + /* No commit or rollback needed if the program never started */ + if( p->pc>=0 ){ - if( p->rc==SQLITE_NOMEM ){ + /* 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 && inDb; 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 && inDb; 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. */ diff --git a/test/malloc3.test b/test/malloc3.test index 0b1729a727..bfdf3727a5 100644 --- a/test/malloc3.test +++ b/test/malloc3.test @@ -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 diff --git a/test/quick.test b/test/quick.test index e23a986f7d..b704aa9f68 100644 --- a/test/quick.test +++ b/test/quick.test @@ -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 index 0000000000..16d629d149 --- /dev/null +++ b/test/shared_err.test @@ -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 +