mirror of https://github.com/sqlite/sqlite
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:
parent
f98477b846
commit
07cb560b23
27
manifest
27
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
|
||||
|
|
|
@ -1 +1 @@
|
|||
0208e4221a2d90b5ae0755061c345d3351a30da8
|
||||
97491d4eb5fc24d8f5cc7605db844359ecc6a818
|
12
src/btree.c
12
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 <tcl.h>
|
||||
int sqlite3_shared_cache_report(
|
||||
void * clientData,
|
||||
|
|
20
src/pager.c
20
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"
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
12
src/test3.c
12
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;
|
||||
|
||||
|
|
130
src/vdbeaux.c
130
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->pc>=0 ){
|
||||
|
||||
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 ){
|
||||
/* 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,28 +1231,61 @@ int sqlite3VdbeHalt(Vdbe *p){
|
|||
break;
|
||||
}
|
||||
}
|
||||
if( (isReadOnly||isStatement) && p->errorAction!=OE_Rollback ){
|
||||
p->errorAction = OE_Abort;
|
||||
|
||||
/* 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{
|
||||
p->errorAction = OE_Rollback;
|
||||
/* 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 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{
|
||||
abortOtherActiveVdbes(p);
|
||||
sqlite3RollbackAll(db);
|
||||
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.
|
||||
/* 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 ||
|
||||
|
@ -1253,7 +1300,9 @@ int sqlite3VdbeHalt(Vdbe *p){
|
|||
}
|
||||
}
|
||||
|
||||
/* If this was an INSERT, UPDATE or DELETE, set the change counter. */
|
||||
/* 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);
|
||||
|
@ -1268,6 +1317,7 @@ int sqlite3VdbeHalt(Vdbe *p){
|
|||
sqlite3ResetInternalSchema(db, 0);
|
||||
db->flags = (db->flags | SQLITE_InternChanges);
|
||||
}
|
||||
}
|
||||
|
||||
/* We have successfully halted and closed the VM. Record this fact. */
|
||||
if( p->pc>=0 ){
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]} {
|
||||
|
|
|
@ -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
|
||||
|
Loading…
Reference in New Issue