From: dan Date: Wed, 4 May 2016 11:28:03 +0000 (+0000) Subject: Disable the PagerDontWrite() optimization for temp tables. It can cause database... X-Git-Tag: version-3.13.0~39 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=c88ae52d868d8da91931ef3863e96ca661b4356d;p=thirdparty%2Fsqlite.git Disable the PagerDontWrite() optimization for temp tables. It can cause database corruption if a page passed to PagerDontWrite() is dirty at the start of a transaction that is subsequently rolled back. FossilOrigin-Name: 6341ab2ffef298ca16b323358afbea4a4c1fb0e1 --- diff --git a/manifest b/manifest index b85ef558d9..0570c6e3af 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Minor\sstyle\stweaks\sin\sthe\sMSVC\smakefile. -D 2016-05-03T19:40:54.328 +C Disable\sthe\sPagerDontWrite()\soptimization\sfor\stemp\stables.\sIt\scan\scause\sdatabase\scorruption\sif\sa\spage\spassed\sto\sPagerDontWrite()\sis\sdirty\sat\sthe\sstart\sof\sa\stransaction\sthat\sis\ssubsequently\srolled\sback. +D 2016-05-04T11:28:03.301 F Makefile.in 9eda6e1c90d05c199c3ec8a7069b0682ad307657 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc db82b35aef27f412fef14d8534afc022138bcdfd @@ -364,7 +364,7 @@ F src/os_setup.h c9d4553b5aaa6f73391448b265b89bed0b890faa F src/os_unix.c a9443cdab41d7f3cdf0df3a5aab62fd6e1c9b234 F src/os_win.c 852fc2ff6084296348ed3739c548b2cf32df394e F src/os_win.h eb7a47aa17b26b77eb97e4823f20a00b8bda12ca -F src/pager.c a8d30c49c231e9a20d05257613db922532588963 +F src/pager.c c512873311087cd22372f2b076fc869e090e8e16 F src/pager.h 329bdf078a4e0a3b35084534d58625d21fd03681 F src/parse.y 10eb2f3fb62341291528c7984498054731f9d31e F src/pcache.c b3230ecfc7f797063fbe167f2845da363e8f07f8 @@ -1111,6 +1111,7 @@ F test/tableapi.test 2674633fa95d80da917571ebdd759a14d9819126 F test/tableopts.test dba698ba97251017b7c80d738c198d39ab747930 F test/tclsqlite.test e1306001a0ca92250b691ea6d3cecaca5b6342aa F test/tempdb.test bd92eba8f20e16a9136e434e20b280794de3cdb6 +F test/tempdb2.test 4fc92055f2a3f7626c0f2eabbb637dc021b311d5 F test/tempfault.test 0c0d349c9a99bf5f374655742577f8712c647900 F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30 F test/temptable2.test cd396beb41117a5302fff61767c35fa4270a0d5e @@ -1486,7 +1487,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P bcaa650e8796984b275eceba0ed477f360295d01 -R 962b079d2f6aa84ad32617180232ff75 -U mistachkin -Z 45ce4bc83f501da24480cd14c4b0de07 +P 1b43358f8979c4f18519e824e0d5f6ef2250107c +R ddb8134dc049c780758987466a2a1e86 +U dan +Z 527ddc60688e8fba4830106202c052a2 diff --git a/manifest.uuid b/manifest.uuid index 1744b7bee1..ae5eab176f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1b43358f8979c4f18519e824e0d5f6ef2250107c \ No newline at end of file +6341ab2ffef298ca16b323358afbea4a4c1fb0e1 \ No newline at end of file diff --git a/src/pager.c b/src/pager.c index d24337cd50..8ef0492e57 100644 --- a/src/pager.c +++ b/src/pager.c @@ -6008,10 +6008,16 @@ int sqlite3PagerIswriteable(DbPage *pPg){ ** ** Tests show that this optimization can quadruple the speed of large ** DELETE operations. +** +** This optimization cannot be used with a temp-file, as the page may +** have been dirty at the start of the transaction. In that case, if +** memory pressure forces page pPg out of the cache, the data does need +** to be written out to disk so that it may be read back in if the +** current transaction is rolled back. */ void sqlite3PagerDontWrite(PgHdr *pPg){ Pager *pPager = pPg->pPager; - if( (pPg->flags&PGHDR_DIRTY) && pPager->nSavepoint==0 ){ + if( !pPager->tempFile && (pPg->flags&PGHDR_DIRTY) && pPager->nSavepoint==0 ){ PAGERTRACE(("DONT_WRITE page %d of %d\n", pPg->pgno, PAGERID(pPager))); IOTRACE(("CLEAN %p %d\n", pPager, pPg->pgno)) pPg->flags |= PGHDR_DONT_WRITE; diff --git a/test/tempdb2.test b/test/tempdb2.test new file mode 100644 index 0000000000..bc2822e71e --- /dev/null +++ b/test/tempdb2.test @@ -0,0 +1,77 @@ +# 2016 March 3 +# +# 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. +# +#*********************************************************************** + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix tempdb2 + +db close +sqlite3 db "" + +proc int2str {i} { string range [string repeat "$i." 450] 0 899 } +db func int2str int2str + +#------------------------------------------------------------------------- +# +# 1.1: Write a big transaction to the db. One so large that it forces +# the file to be created and the cache flushed to disk on COMMIT. +# +# 1.2: Write a small transaction - one small enough that it remains in +# memory on COMMIT. All the pages of table [t1] are now dirty. +# +# 1.3: Delete the contents of [t1]. This moves all of its leaves to the +# free-list and causes the btree layer to call PagerDontWrite() on +# each of them. +# +# Then do a big update on table [t2]. So big that the former leaves +# of [t1] are forced out of the cache. Then roll back the transaction. +# If the PagerDontWrite() calls are honoured and the data is not written +# to disk, the update made in test 1.2 will be lost at this point. Or, if +# they are ignored (as they should be for temp databases), the update +# will be safely written out to disk before the cache entries are +# discarded. +# +do_execsql_test 1.1 { + PRAGMA page_size=1024; + PRAGMA cache_size=50; + + BEGIN; + CREATE TABLE t1(a INTEGER PRIMARY KEY, b); + INSERT INTO t1 VALUES(1, int2str(1)); + INSERT INTO t1 VALUES(2, int2str(1)); + INSERT INTO t1 VALUES(3, int2str(1)); + + CREATE TABLE t2(a INTEGER PRIMARY KEY, b); + WITH c(x) AS ( VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<100 ) + INSERT INTO t2 SELECT x, int2str(x) FROM c; + COMMIT; + + PRAGMA lock_status; +} {main unlocked temp closed} + +do_execsql_test 1.2 { + UPDATE t1 SET b=int2str(2); + SELECT b=int2str(2) FROM t1 +} {1 1 1} + +do_execsql_test 1.3 { + BEGIN; + DELETE FROM t1; + UPDATE t2 SET b=int2str(a+1); + ROLLBACK; +} + +do_execsql_test 1.4 { + SELECT b=int2str(2) FROM t1 +} {1 1 1} + +finish_test +