-C Add\sa\scouple\sof\smissing\scalls\sto\spagerLeave().\s(CVS\s4724)
-D 2008-01-18T11:33:16
+C Add\sa\stest\s(and\sfix)\sfor\spossible\scorruption\sif\smalloc()\sfails\sduring\sa\sCREATE\sINDEX\sstatement,\sthe\sapplication\scontinues\swith\sthe\stransaction,\sthen\scrashes.\s(CVS\s4725)
+D 2008-01-18T13:42:55
F Makefile.arm-wince-mingw32ce-gcc ac5f7b2cef0cd850d6f755ba6ee4ab961b1fadf7
F Makefile.in 30789bf70614bad659351660d76b8e533f3340e9
F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
F src/os_unix.h 5768d56d28240d3fe4537fac08cc85e4fb52279e
F src/os_win.c c832d528ea774c7094d887749d71884984c9034c
F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b
-F src/pager.c cf40ab8f57884ce1b5a355c0e42cdbaceaa42de0
+F src/pager.c a55923e2de46b81868da9e6e6f348e2978d477b9
F src/pager.h f504f7ae84060fee0416a853e368d3d113c3d6fa
F src/parse.y 2ae06e8d3190faace49c5b82e7cea1fc60d084a1
F src/pragma.c 155315ee3e6a861a0060ba4d184dfffd08ebbc03
F src/test3.c df62cd5c971dc1ae0be0a1842f9df3390934e7a6
F src/test4.c c2c0f5dc907f1346f5d4b65eb5799f11eb9e4071
F src/test5.c 3a6a5717a149d7ca2e6d14f5be72cf7555d54dc4
-F src/test6.c f40e41e1808d743e995a5016dc9a56702d1632bd
+F src/test6.c 8944b6482be3a54505c4f14339392448e4cabe66
F src/test7.c acec2256c7c2d279db5a8b5fa1a2a68fcc942c67
F src/test8.c 6399d2f0561f1f65785c63e94f2cdd36fb248872
F src/test9.c b46c8fe02ac7cca1a7316436d8d38d50c66f4b2f
F test/crash2.test 26d7a4c5520201e5de2c696ea51ab946b59dc0e9
F test/crash3.test 0b09687ae1a3ccbcefdfaeb4b963e26e36255d76
F test/crash4.test 02ff4f15c149ca1e88a5c299b4896c84d9450c3b
+F test/crash5.test 0a20b3c310323f1c6595e2866e238aa4c467eef3
F test/crashtest1.c 09c1c7d728ccf4feb9e481671e29dda5669bbcc2
F test/createtab.test 199cf68f44e5d9e87a0b8afc7130fdeb4def3272
F test/date.test 51734f3798f338e3f75107aff5a057ae0ff7006c
F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b
F www/whentouse.tcl fc46eae081251c3c181bd79c5faef8195d7991a5
-P 251c78a982a33194a052897c37a2a79ae9654452
-R c0ed04846e95ed3c4b78c2b81c8b73fe
+P 87534dfff9e7a37c624a83c79f4074f29ff16c9e
+R cbf1e7869193895b2025968111b8f3b5
U danielk1977
-Z a0c52f569e9f496fa4f45cbe77dad52f
+Z 9aca5a3c5a8b6ed139614576f4b62520
-87534dfff9e7a37c624a83c79f4074f29ff16c9e
\ No newline at end of file
+65245d9904db19568d5092926b27f0c193c9ef0b
\ No newline at end of file
** file simultaneously, or one process from reading the database while
** another is writing.
**
-** @(#) $Id: pager.c,v 1.401 2008/01/18 11:33:16 danielk1977 Exp $
+** @(#) $Id: pager.c,v 1.402 2008/01/18 13:42:55 danielk1977 Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"
** Pager.aInJournal bit has been set. This needs to be remedied by loading
** the page into the pager-cache and setting the PgHdr.needSync flag.
**
+ ** If the attempt to load the page into the page-cache fails, (due
+ ** to a malloc() or IO failure), clear the bit in the aInJournal[]
+ ** array. Otherwise, if the page is loaded and written again in
+ ** this transaction, it may be written to the database file before
+ ** it is synced into the journal file. This way, it may end up in
+ ** the journal file twice, but that is not a problem.
+ **
** The sqlite3PagerGet() call may cause the journal to sync. So make
** sure the Pager.needSync flag is set too.
*/
assert( pPager->needSync );
rc = sqlite3PagerGet(pPager, needSyncPgno, &pPgHdr);
if( rc!=SQLITE_OK ){
+ if( pPager->aInJournal && (int)needSyncPgno<=pPager->origDbSize ){
+ pPager->aInJournal[needSyncPgno/8] &= ~(1<<(needSyncPgno&7));
+ }
pagerLeave(pPager);
return rc;
}
*/
static int sqlite3CrashTestEnable = 0;
+static void *crash_malloc(int nByte){
+ return (void *)Tcl_Alloc((size_t)nByte);
+}
+static void crash_free(void *p){
+ Tcl_Free(p);
+}
+static void *crash_realloc(void *p, int n){
+ return (void *)Tcl_Realloc(p, (size_t)n);
+}
+
/*
** Flush the write-list as if xSync() had been called on file handle
** pFile. If isCrash is true, simulate a crash.
);
}
#endif
- sqlite3_free(pWrite);
+ crash_free(pWrite);
break;
}
case 2: { /* Do nothing */
);
#endif
- zGarbage = sqlite3_malloc(g.iSectorSize);
+ zGarbage = crash_malloc(g.iSectorSize);
if( zGarbage ){
sqlite3_int64 i;
for(i=iFirst; rc==SQLITE_OK && i<=iLast; i++){
pRealFile, zGarbage, g.iSectorSize, i*g.iSectorSize
);
}
- sqlite3_free(zGarbage);
+ crash_free(zGarbage);
}else{
rc = SQLITE_NOMEM;
}
assert((zBuf && nBuf) || (!nBuf && !zBuf));
- pNew = (WriteBuffer *)sqlite3MallocZero(sizeof(WriteBuffer) + nBuf);
+ pNew = (WriteBuffer *)crash_malloc(sizeof(WriteBuffer) + nBuf);
if( pNew==0 ){
fprintf(stderr, "out of memory in the crash simulator\n");
}
+ memset(pNew, 0, sizeof(WriteBuffer)+nBuf);
pNew->iOffset = iOffset;
pNew->nBuf = nBuf;
pNew->pFile = (CrashFile *)pFile;
while( pCrash->iSize>pCrash->nData ){
u8 *zNew;
int nNew = (pCrash->nData*2) + 4096;
- zNew = sqlite3_realloc(pCrash->zData, nNew);
+ zNew = crash_realloc(pCrash->zData, nNew);
if( !zNew ){
return SQLITE_NOMEM;
}
}
if( rc==SQLITE_OK ){
pWrapper->nData = (4096 + pWrapper->iSize);
- pWrapper->zData = sqlite3_malloc(pWrapper->nData);
+ pWrapper->zData = crash_malloc(pWrapper->nData);
if( pWrapper->zData ){
memset(pWrapper->zData, 0, pWrapper->nData);
rc = sqlite3OsRead(pReal, pWrapper->zData, pWrapper->iSize, 0);
--- /dev/null
+
+# 2007 Aug 13
+#
+# 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.
+#
+#***********************************************************************
+#
+# This file tests aspects of recovery from a malloc() failure
+# in a CREATE INDEX statement.
+#
+# $Id: crash5.test,v 1.1 2008/01/18 13:42:55 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Only run these tests if memory debugging is turned on.
+#
+ifcapable !memdebug||!crashtest||!memorymanage {
+ puts "Skipping crash5 tests: not compiled with -DSQLITE_MEMDEBUG..."
+ finish_test
+ return
+}
+
+db close
+
+for {set ii 0} {$ii < 10} {incr ii} {
+ for {set jj 50} {$jj < 100} {incr jj} {
+
+ # Set up the database so that it is an auto-vacuum database
+ # containing a single table (root page 3) with a single row.
+ # The row has an overflow page (page 4).
+ file delete -force test.db test.db-journal
+ sqlite3 db test.db
+ set c [string repeat 3 1500]
+ db eval {
+ pragma auto_vacuum = 1;
+ CREATE TABLE t1(a, b, c);
+ INSERT INTO t1 VALUES('1111111111', '2222222222', $c);
+ }
+ db close
+
+ do_test crash5-$ii.$jj.1 {
+ crashsql -delay 1 -file test.db-journal -seed $ii -tclbody [join [list \
+ [list set iFail $jj] {
+ sqlite3_crashparams 0 /Users/dan/Desktop/sqlite/bld/test.db-journal
+
+ # Begin a transaction and evaluate a "CREATE INDEX" statement
+ # with the iFail'th malloc() set to fail. This operation will
+ # have to move the current contents of page 4 (the overflow
+ # page) to make room for the new root page. The bug is that
+ # if malloc() fails at a particular point in sqlite3PagerMovepage(),
+ # sqlite mistakenly thinks that the page being moved (page 4) has
+ # been safely synced into the journal. If the page is written
+ # to later in the transaction, it may be written out to the database
+ # before the relevant part of the journal has been synced.
+ #
+ db eval BEGIN
+ sqlite3_memdebug_fail $iFail -repeat 0
+ catch {db eval { CREATE UNIQUE INDEX i1 ON t1(a); }} msg
+ # puts "$n $msg ac=[sqlite3_get_autocommit db]"
+
+ # If the transaction is still active (it may not be if the malloc()
+ # failure occured in the OS layer), write to the database. Make sure
+ # page 4 is among those written.
+ #
+ if {![sqlite3_get_autocommit db]} {
+ db eval {
+ DELETE FROM t1; -- This will put page 4 on the free list.
+ INSERT INTO t1 VALUES('111111111', '2222222222', '33333333');
+ INSERT INTO t1 SELECT * FROM t1; -- 2
+ INSERT INTO t1 SELECT * FROM t1; -- 4
+ INSERT INTO t1 SELECT * FROM t1; -- 8
+ INSERT INTO t1 SELECT * FROM t1; -- 16
+ INSERT INTO t1 SELECT * FROM t1; -- 32
+ INSERT INTO t1 SELECT * FROM t1 WHERE rowid%2; -- 48
+ }
+ }
+
+ # If the right malloc() failed during the 'CREATE INDEX' above and
+ # the transaction was not rolled back, then the sqlite cache now
+ # has a dirty page 4 that it incorrectly believes is already safely
+ # in the synced part of the journal file. When
+ # sqlite3_release_memory() is called sqlite tries to free memory
+ # by writing page 4 out to the db file. If it crashes later on,
+ # before syncing the journal... Corruption!
+ #
+ sqlite3_crashparams 1 /Users/dan/Desktop/sqlite/bld/test.db-journal
+ sqlite3_release_memory 8092
+ }]] {}
+ expr 1
+ } {1}
+
+ sqlite3 db test.db
+ do_test crash5-$ii.$jj.2 {
+ db eval {pragma integrity_check}
+ } {ok}
+ do_test crash5-$ii.$jj.3 {
+ db eval {SELECT * FROM t1}
+ } [list 1111111111 2222222222 $::c]
+ db close
+ }
+}
+
+
+finish_test
+