Handle some of the IO error conditions that may occur in a shared-cache context. (CVS 2980)

FossilOrigin-Name: 97491d4eb5fc24d8f5cc7605db844359ecc6a818
This commit is contained in:
danielk1977 2006-01-20 10:55:05 +00:00
parent f98477b846
commit 07cb560b23
10 changed files with 337 additions and 107 deletions

View File

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

View File

@ -1 +1 @@
0208e4221a2d90b5ae0755061c345d3351a30da8
97491d4eb5fc24d8f5cc7605db844359ecc6a818

View 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,

View 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);
}
/*

View 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;
}

View 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;

View 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{
/* 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 && 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. */

View 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

View 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]} {

137
test/shared_err.test Normal file
View File

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