diff --git a/manifest b/manifest index 482944ffe0..2cf6bf4a78 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sbug\sin\sicuOpen()\sin\sfts2.\s(CVS\s6038) -D 2008-12-18T05:30:26 +C Add\ssavepoint2.test,\sa\sfile\scontaining\ssavepoint\stests\ssimilar\sto\stests\sin\strans.test\sand\savtrans.test.\sAnd\sa\sfew\ssavepoint\sbug\sfixes.\s(CVS\s6039) +D 2008-12-18T15:45:07 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.in f7e4c81c347b04f7b0f1c1b081a168645d7b8af7 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 @@ -102,7 +102,7 @@ F src/attach.c 1c35f95da3c62d19de75b44cfefd12c81c1791b3 F src/auth.c c8b2ab5c8bad4bd90ed7c294694f48269162c627 F src/bitvec.c 4300d311b17fb3c1476623fd895a8feac02a0b08 F src/btmutex.c 63c5cc4ad5715690767ffcb741e185d7bc35ec1a -F src/btree.c c402a9a15fe62508b332517b162f6fdbcf1bfb47 +F src/btree.c ad51b56b1a90e3c9ad39aabd9368325aba5a6730 F src/btree.h 4f141cf748d2ee7c6d7fc175f64f87a45cd44113 F src/btreeInt.h 7ef2c872371d7508657f8d7a4efe651c741d6ee6 F src/build.c f3e8377cbc0d007b01aab1e7d4fc1d5b296c422e @@ -141,7 +141,7 @@ F src/os_common.h 24525d8b7bce66c374dfc1810a6c9043f3359b60 F src/os_os2.c bed77dc26e3a95ce4a204936b9a1ca6fe612fcc5 F src/os_unix.c 96b4a6e87335ba943455740f311b4dfb63f26756 F src/os_win.c 496e3ceb499aedc63622a89ef76f7af2dd902709 -F src/pager.c dd1aba4a1dc246b72d15fa9ffcf59902cea51d54 +F src/pager.c 9c1663c9406743f30cdad532c01c47f71bfac577 F src/pager.h 7191294438881eb4d13eedade97891e8dc993905 F src/parse.y 4d0e33a702dc3ea7b69d8ae1914b3fbd32e46057 F src/pcache.c 16dc8da6e6ba6250f8dfd9ee46036db1cbceedc6 @@ -491,6 +491,7 @@ F test/rowid.test 1c8fc43c60d273e6ea44dfb992db587f3164312c F test/rtree.test b85fd4f0861a40ca366ac195e363be2528dcfadf F test/safety.test b69e2b2dd5d52a3f78e216967086884bbc1a09c6 F test/savepoint.test fdad3b61f4a00a96cd773ca0c758cf2f53918ae3 +F test/savepoint2.test dcaf442a9eea4e91c27fce339fd74c3eaa66577e F test/schema.test a8b000723375fd42c68d310091bdbd744fde647c F test/schema2.test 35e1c9696443d6694c8980c411497c2b5190d32e F test/select1.test d0a4cad954fd41c030ec16ffbd2d08a4c0548742 @@ -679,7 +680,7 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81 F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e -P 78401b33febf678cfeec2a35514eb4172de420ab -R 09419362acc9ec794afd36b3528f0694 +P b9c722bd96b44e0fabd1564ddd982d2aabb7047c +R e8991f966fc1a93805d33d53f39d5eb2 U danielk1977 -Z 152f2d9b602bfeed8b492ec878934f0b +Z 23f7ea8ae683063fc7d5590449b66501 diff --git a/manifest.uuid b/manifest.uuid index 69052dd55b..89e756499c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b9c722bd96b44e0fabd1564ddd982d2aabb7047c \ No newline at end of file +98a53d91f6c0c2692d3b56687fdaba8eeab0959d \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index 34b4ffa442..48a7dee002 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.549 2008/12/17 17:30:26 danielk1977 Exp $ +** $Id: btree.c,v 1.550 2008/12/18 15:45:07 danielk1977 Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** See the header comment on "btreeInt.h" for additional information. @@ -2060,8 +2060,13 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){ trans_begun: if( rc==SQLITE_OK && wrflag ){ + /* This call makes sure that the pager has the correct number of + ** open savepoints. If the second parameter is greater than 0 and + ** the sub-journal is not already open, then it will be opened here. + */ rc = sqlite3PagerOpenSavepoint(pBt->pPager, p->db->nSavepoint); } + btreeIntegrity(p); sqlite3BtreeLeave(p); return rc; @@ -2740,8 +2745,7 @@ int sqlite3BtreeBeginStmt(Btree *p){ ** SQL statements. It is illegal to open, release or rollback any ** such savepoints while the statement transaction savepoint is active. */ - int iStmtpoint = p->db->nSavepoint + 1; - rc = sqlite3PagerOpenSavepoint(pBt->pPager, iStmtpoint); + rc = sqlite3PagerOpenSavepoint(pBt->pPager, p->db->nSavepoint+1); } pBt->inStmt = 1; } @@ -2797,8 +2801,14 @@ int sqlite3BtreeRollbackStmt(Btree *p){ /* ** The second argument to this function, op, is always SAVEPOINT_ROLLBACK ** or SAVEPOINT_RELEASE. This function either releases or rolls back the -** savepoint identified by parameter iSavepoint, depending on the value of -** op. +** savepoint identified by parameter iSavepoint, depending on the value +** of op. +** +** Normally, iSavepoint is greater than or equal to zero. However, if op is +** SAVEPOINT_ROLLBACK, then iSavepoint may also be -1. In this case the +** contents of the entire transaction are rolled back. This is different +** from a normal transaction rollback, as no locks are released and the +** transaction remains open. */ int sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){ int rc = SQLITE_OK; diff --git a/src/pager.c b/src/pager.c index 7743a3f929..e73b57eec8 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.515 2008/12/17 17:30:26 danielk1977 Exp $ +** @(#) $Id: pager.c,v 1.516 2008/12/18 15:45:07 danielk1977 Exp $ */ #ifndef SQLITE_OMIT_DISKIO #include "sqliteInt.h" @@ -1649,7 +1649,7 @@ static int pagerPlaybackSavepoint(Pager *pPager, PagerSavepoint *pSavepoint){ if( nJRec==0 ){ nJRec = (szJ - pPager->journalOff) / (pPager->pageSize+8); } - for(ii=0; rc==SQLITE_OK && iijournalOffjournalOff, pDone); assert( rc!=SQLITE_DONE ); } @@ -2437,6 +2437,7 @@ static int pager_write_pagelist(PgHdr *pList){ if( pList->pgno<=pPager->dbSize && 0==(pList->flags&PGHDR_DONT_WRITE) ){ i64 offset = (pList->pgno-1)*(i64)pPager->pageSize; char *pData = CODEC2(pPager, pList->pData, pList->pgno, 6); + PAGERTRACE4("STORE %d page %d hash(%08x)\n", PAGERID(pPager), pList->pgno, pager_pagehash(pList)); IOTRACE(("PGOUT %p %d\n", pPager, pList->pgno)); @@ -3548,7 +3549,7 @@ static int pager_incr_changecounter(Pager *pPager, int isDirect){ #ifndef SQLITE_ENABLE_ATOMIC_WRITE assert( isDirect==0 ); /* isDirect is only true for atomic writes */ #endif - if( !pPager->changeCountDone ){ + if( !pPager->changeCountDone && pPager->dbSize>0 ){ /* Open page 1 of the file for writing. */ rc = sqlite3PagerGet(pPager, 1, &pPgHdr); if( rc!=SQLITE_OK ) return rc; @@ -3900,7 +3901,7 @@ int sqlite3PagerIsMemdb(Pager *pPager){ int sqlite3PagerOpenSavepoint(Pager *pPager, int nSavepoint){ int rc = SQLITE_OK; - if( nSavepoint>pPager->nSavepoint ){ + if( nSavepoint>pPager->nSavepoint && pPager->useJournal ){ int ii; /* Either the sub-journal is open or there are no active savepoints. */ @@ -3925,7 +3926,7 @@ int sqlite3PagerOpenSavepoint(Pager *pPager, int nSavepoint){ /* Populate the PagerSavepoint structures just allocated. */ for(/* no-op */; iidbSize>=0 ); + assert( pPager->dbSizeValid ); aNew[ii].nOrig = pPager->dbSize; aNew[ii].iOffset = (pPager->journalOpen ? pPager->journalOff : 0); aNew[ii].iSubRec = pPager->stmtNRec; diff --git a/test/savepoint2.test b/test/savepoint2.test new file mode 100644 index 0000000000..bd259ab917 --- /dev/null +++ b/test/savepoint2.test @@ -0,0 +1,155 @@ +# 2008 December 15 +# +# 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. +# +#*********************************************************************** +# +# $Id: savepoint2.test,v 1.1 2008/12/18 15:45:07 danielk1977 Exp $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +# Tests in this file are quite similar to those run by trans.test and +# avtrans.test. +# + +proc signature {} { + return [db eval {SELECT count(*), md5sum(x) FROM t3}] +} + + + +do_test savepoint2-1 { + execsql { + PRAGMA cache_size=10; + } + db close + sqlite3 db test.db + execsql { + BEGIN; + CREATE TABLE t3(x TEXT); + INSERT INTO t3 VALUES(randstr(10,400)); + INSERT INTO t3 VALUES(randstr(10,400)); + INSERT INTO t3 SELECT randstr(10,400) FROM t3; + INSERT INTO t3 SELECT randstr(10,400) FROM t3; + INSERT INTO t3 SELECT randstr(10,400) FROM t3; + INSERT INTO t3 SELECT randstr(10,400) FROM t3; + INSERT INTO t3 SELECT randstr(10,400) FROM t3; + INSERT INTO t3 SELECT randstr(10,400) FROM t3; + INSERT INTO t3 SELECT randstr(10,400) FROM t3; + INSERT INTO t3 SELECT randstr(10,400) FROM t3; + INSERT INTO t3 SELECT randstr(10,400) FROM t3; + COMMIT; + SELECT count(*) FROM t3; + } +} {1024} + +unset -nocomplain ::sig +unset -nocomplain SQL + +set iterations 20 + +set SQL(1) { + DELETE FROM t3 WHERE random()%10!=0; + INSERT INTO t3 SELECT randstr(10,10)||x FROM t3; + INSERT INTO t3 SELECT randstr(10,10)||x FROM t3; +} +set SQL(2) { + DELETE FROM t3 WHERE random()%10!=0; + INSERT INTO t3 SELECT randstr(10,10)||x FROM t3; + DELETE FROM t3 WHERE random()%10!=0; + INSERT INTO t3 SELECT randstr(10,10)||x FROM t3; +} +set SQL(3) { + UPDATE t3 SET x = randstr(10, 400) WHERE random()%10; + INSERT INTO t3 SELECT x FROM t3 WHERE random()%10; + DELETE FROM t3 WHERE random()%10; +} +set SQL(4) { + INSERT INTO t3 SELECT randstr(10,400) FROM t3 WHERE (random()%10 == 0); +} + + + +for {set ii 2} {$ii < ($iterations+2)} {incr ii} { + + # Record the database signature. Optionally (every second run) open a + # transaction. In all cases open savepoint "one", which may or may + # not be a transaction savepoint, depending on whether or not a real + # transaction has been opened. + # + do_test savepoint2-$ii.1 { + if {$ii % 2} { execsql BEGIN } + set ::sig(one) [signature] + execsql "SAVEPOINT one" + } {} + + # Execute some SQL on the database. Then rollback to savepoint "one". + # Check that the database signature is as it was when "one" was opened. + # + do_test savepoint2-$ii.2 { + execsql $SQL(1) + execsql "ROLLBACK to one" + signature + } $::sig(one) + integrity_check savepoint2-$ii.2.1 + + # Execute some SQL. Then open savepoint "two". Savepoint "two" is therefore + # nested in savepoint "one". + # + do_test savepoint2-$ii.3 { + execsql $SQL(1) + set ::sig(two) [signature] + execsql "SAVEPOINT two" + } {} + + # More SQL changes. The rollback to savepoint "two". Check that the + # signature is as it was when savepoint "two" was opened. + # + do_test savepoint2-$ii.4 { + execsql $SQL(2) + execsql "ROLLBACK to two" + signature + } $::sig(two) + integrity_check savepoint2-$ii.4.1 + + # More SQL changes. The rollback to savepoint "two". Check that the + # signature is as it was when savepoint "two" was opened. + # + do_test savepoint2-$ii.5 { + execsql $SQL(2) + execsql "SAVEPOINT three" + execsql $SQL(3) + execsql "RELEASE three" + execsql "ROLLBACK to one" + signature + } $::sig(one) + + # By this point the database is in the same state as it was at the + # top of the for{} loop (everything having been rolled back by the + # "ROLLBACK TO one" command above). So make a few changes to the + # database and COMMIT the open transaction, so that the next iteration + # of the for{} loop works on a different dataset. + # + # The transaction being committed here may have been opened normally using + # "BEGIN", or may have been opened using a transaction savepoint created + # by the "SAVEPOINT one" statement. + # + do_test savepoint2-$ii.6 { + execsql $SQL(4) + execsql COMMIT + sqlite3_get_autocommit db + } {1} + integrity_check savepoint2-$ii.6.1 +} + +unset -nocomplain ::sig +unset -nocomplain SQL + +finish_test +