From 86a88114fa4a7c298836e4ef391416f9cf149f81 Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 16 Apr 2007 15:02:19 +0000 Subject: [PATCH] Detect database file changes using a 128-bit segment of the file header that includes the change counter. Ticket #2303. (CVS 3844) FossilOrigin-Name: e44995debf2456e55b502783849e93a045a527c8 --- manifest | 16 ++++++------ manifest.uuid | 2 +- src/pager.c | 58 +++++++++++++++++++++++++------------------- test/exclusive2.test | 40 +++++++++++++++--------------- test/speed2.test | 4 +-- 5 files changed, 65 insertions(+), 55 deletions(-) diff --git a/manifest b/manifest index 59ec2cac17..32d99e17a2 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Update\sthe\swhentouse.html\sdocument\sto\smention\sthat\sless\sbitmap\smemory\nis\sused\sfor\slarger\spage\ssizes.\s(CVS\s3843) -D 2007-04-14T12:04:39 +C Detect\sdatabase\sfile\schanges\susing\sa\s128-bit\ssegment\sof\sthe\sfile\sheader\nthat\sincludes\sthe\schange\scounter.\s\sTicket\s#2303.\s(CVS\s3844) +D 2007-04-16T15:02:19 F Makefile.in 8cab54f7c9f5af8f22fd97ddf1ecfd1e1860de62 F Makefile.linux-gcc 2d8574d1ba75f129aba2019f0b959db380a90935 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028 @@ -86,7 +86,7 @@ F src/os_unix.c 426b4c03c304ad78746d65d9ba101e0b72e18e23 F src/os_unix.h 5768d56d28240d3fe4537fac08cc85e4fb52279e F src/os_win.c e94903c7dc1c0599c8ddce42efa0b6928068ddc5 F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b -F src/pager.c 4fb7253edc2377b12f16fa33adffda79f070c1b4 +F src/pager.c 33c632ce9c228d87f14879a139fa123d43e4bf25 F src/pager.h d652ddf092d2318d00e41f8539760fe8e57c157c F src/parse.y b6cfbadb6d5b21b5087d30698ee5af0ebb098767 F src/pragma.c 3b992b5b2640d6ae25cef05aa6a42cd1d6c43234 @@ -204,7 +204,7 @@ F test/enc.test 7a03417a1051fe8bc6c7641cf4c8c3f7e0066d52 F test/enc2.test 45710bacfa9df29720bc84c067dfdf8c8ddfb797 F test/enc3.test 890508efff6677345e93bf2a8adb0489b30df030 F test/exclusive.test 5bc520ba366ae3d242420af025ab64d465b04706 -F test/exclusive2.test dcb10d527722eb066ef7d060a0d47d7e59070d2e +F test/exclusive2.test 6ef76efd3b442c95819446f8d15e6a63a1e95a4e F test/exclusive3.test 0e49c35b7e7cb8e7280b4ce3f0359d30b207d2ff F test/expr.test ab21e2fc3613595131efd7d8bbca4b95ed5cc608 F test/filefmt.test 053b622009fbbb74dd37921ffad374d852c13cd8 @@ -316,7 +316,7 @@ F test/shared3.test 01e3e124dbb3859788aabc7cfb82f7ea04421749 F test/shared_err.test cc528f6e78665787e93d9ce3a782a2ce5179d821 F test/sort.test 0e4456e729e5a92a625907c63dcdedfbe72c5dc5 F test/speed1.test 22e1b27af0683ed44dcd2f93ed817a9c3e65084a -F test/speed2.test 9b93b93681f82f320caa4b2c9f15c0de4f3a3d33 +F test/speed2.test 53177056baf6556dcbdcf032bbdfc41c1aa74ded F test/subquery.test ae324ee928c5fb463a3ce08a8860d6e7f1ca5797 F test/subselect.test 974e87f8fc91c5f00dd565316d396a5a6c3106c4 F test/sync.test d05397b8f89f423dd6dba528692019ab036bc1c3 @@ -458,7 +458,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0 F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b F www/whentouse.tcl fc46eae081251c3c181bd79c5faef8195d7991a5 -P cfc6f933dc60ca88ae848f7f0c402e820437c2ff -R 28caddcdc5058b3369c1c52b75efe638 +P 2c8e2a5be34cdfe11ef22bd6f78ec0519f497392 +R 814512695981b3392256843a965db823 U drh -Z 5805ea2bb38918a004e8fa50c9627a61 +Z 2bdc95de39af6db85ea861709e0ec935 diff --git a/manifest.uuid b/manifest.uuid index a0c9a728d9..b7da5c7fd8 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -2c8e2a5be34cdfe11ef22bd6f78ec0519f497392 \ No newline at end of file +e44995debf2456e55b502783849e93a045a527c8 \ No newline at end of file diff --git a/src/pager.c b/src/pager.c index 57cf9a6373..b7fae8ed87 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.328 2007/04/13 04:01:59 drh Exp $ +** @(#) $Id: pager.c,v 1.329 2007/04/16 15:02:19 drh Exp $ */ #ifndef SQLITE_OMIT_DISKIO #include "sqliteInt.h" @@ -294,7 +294,7 @@ struct Pager { Pager *pNext; /* Linked list of pagers in this thread */ #endif char *pTmpSpace; /* Pager.pageSize bytes of space for tmp use */ - u32 iChangeCount; /* Db change-counter for which cache is valid */ + char dbFileVers[16]; /* Changes whenever database file changes */ }; /* @@ -1131,12 +1131,14 @@ static int pager_playback_one_page(Pager *pPager, OsFile *jfd, int useCksum){ #ifdef SQLITE_CHECK_PAGES pPg->pageHash = pager_pagehash(pPg); #endif - CODEC1(pPager, pData, pPg->pgno, 3); - - /* If this was page 1, then restore the value of Pager.iChangeCount */ + /* If this was page 1, then restore the value of Pager.dbFileVers. + ** Do this before any decoding. */ if( pgno==1 ){ - pPager->iChangeCount = retrieve32bits(pPg, 24); + memcpy(&pPager->dbFileVers, &((u8*)pData)[24],sizeof(pPager->dbFileVers)); } + + /* Decode the page just read from disk */ + CODEC1(pPager, pData, pPg->pgno, 3); } return rc; } @@ -2441,6 +2443,9 @@ static int pager_write_pagelist(PgHdr *pList){ rc = sqlite3OsWrite(pPager->fd, pData, pPager->pageSize); PAGER_INCR(sqlite3_pager_writedb_count); PAGER_INCR(pPager->nWrite); + if( pList->pgno==1 ){ + memcpy(&pPager->dbFileVers, &pData[24], sizeof(pPager->dbFileVers)); + } } #ifndef NDEBUG else{ @@ -2680,6 +2685,10 @@ static int readDbPage(Pager *pPager, PgHdr *pPg, Pgno pgno){ PAGER_INCR(pPager->nRead); IOTRACE(("PGIN %p %d\n", pPager, pgno)); PAGERTRACE3("FETCH %d page %d\n", PAGERID(pPager), pPg->pgno); + if( pgno==1 ){ + memcpy(&pPager->dbFileVers, &((u8*)PGHDR_TO_DATA(pPg))[24], + sizeof(pPager->dbFileVers)); + } CODEC1(pPager, PGHDR_TO_DATA(pPg), pPg->pgno, 3); return rc; } @@ -2780,16 +2789,21 @@ static int pagerSharedLock(Pager *pPager){ if( pPager->pAll ){ /* The shared-lock has just been acquired on the database file ** and there are already pages in the cache (from a previous - ** read or write transaction). If the value of the change-counter - ** stored in Pager.iChangeCount matches that found on page 1 of - ** the database file, then no database changes have occured since - ** the cache was last valid and it is safe to retain the cached - ** pages. Otherwise, if Pager.iChangeCount does not match the - ** change-counter on page 1 of the file, the current cache contents - ** must be discarded. + ** read or write transaction). Check to see if the database + ** has been modified. If the database has changed, flush the + ** cache. + ** + ** Database changes is detected by looking at 15 bytes beginning + ** at offset 24 into the file. The first 4 of these 16 bytes are + ** a 32-bit counter that is incremented with each change. The + ** other bytes change randomly with each file change when + ** a codec is in use. + ** + ** There is a vanishingly small chance that a change will not be + ** deteched. The chance of an undetected change is so small that + ** it can be neglected. */ - u8 zC[4]; - u32 iChangeCounter = 0; + char dbFileVers[sizeof(pPager->dbFileVers)]; sqlite3PagerPagecount(pPager); if( pPager->errCode ){ @@ -2797,21 +2811,20 @@ static int pagerSharedLock(Pager *pPager){ } if( pPager->dbSize>0 ){ - /* Read the 4-byte change counter directly from the file. */ rc = sqlite3OsSeek(pPager->fd, 24); if( rc!=SQLITE_OK ){ return rc; } - rc = sqlite3OsRead(pPager->fd, zC, 4); + rc = sqlite3OsRead(pPager->fd, &dbFileVers, sizeof(dbFileVers)); if( rc!=SQLITE_OK ){ return rc; } - iChangeCounter = (zC[0]<<24) + (zC[1]<<16) + (zC[2]<<8) + zC[3]; + }else{ + memset(dbFileVers, 0, sizeof(dbFileVers)); } - if( iChangeCounter!=pPager->iChangeCount ){ + if( memcmp(pPager->dbFileVers, dbFileVers, sizeof(dbFileVers))!=0 ){ pager_reset(pPager); - pPager->iChangeCount = iChangeCounter; } } } @@ -3029,10 +3042,6 @@ int sqlite3PagerAcquire( return rc; } } - /* If this was page 1, then restore the value of Pager.iChangeCount */ - if( pgno==1 ){ - pPager->iChangeCount = retrieve32bits(pPg, 24); - } /* Link the page into the page hash table */ h = pgno & (pPager->nHash-1); @@ -3722,7 +3731,6 @@ static int pager_incr_changecounter(Pager *pPager){ /* Increment the value just read and write it back to byte 24. */ change_counter++; put32bits(((char*)PGHDR_TO_DATA(pPgHdr))+24, change_counter); - pPager->iChangeCount = change_counter; /* Release the page reference. */ sqlite3PagerUnref(pPgHdr); diff --git a/test/exclusive2.test b/test/exclusive2.test index 17f42b5f08..a9c9dd0467 100644 --- a/test/exclusive2.test +++ b/test/exclusive2.test @@ -10,7 +10,7 @@ #*********************************************************************** # This file implements regression tests for SQLite library. # -# $Id: exclusive2.test,v 1.3 2007/04/08 16:52:22 drh Exp $ +# $Id: exclusive2.test,v 1.4 2007/04/16 15:02:20 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -56,8 +56,9 @@ do_test exclusive2-1.0 { # The following tests - exclusive2-1.X - check that: # # 1-3: Build a database with connection 1, calculate a signature. -# 4-9: Modify the database using a second connection, then reset -# the pager change-counter to the value it had before the modifications. +# 4-9: Modify the database using a second connection in a way that +# does not modify the freelist, then reset the pager change-counter +# to the value it had before the modifications. # 8: Check that using the first connection, the database signature # is still the same. This is because it uses the in-memory cache. # It can't tell the db has changed because we reset the change-counter. @@ -69,14 +70,14 @@ do_test exclusive2-1.0 { do_test exclusive2-1.1 { execsql { BEGIN; - CREATE TABLE t1(a UNIQUE); - INSERT INTO t1 VALUES(randstr(10, 400)); - INSERT INTO t1 VALUES(randstr(10, 400)); - INSERT INTO t1 SELECT randstr(10, 400) FROM t1; - INSERT INTO t1 SELECT randstr(10, 400) FROM t1; - INSERT INTO t1 SELECT randstr(10, 400) FROM t1; - INSERT INTO t1 SELECT randstr(10, 400) FROM t1; - INSERT INTO t1 SELECT randstr(10, 400) FROM t1; + CREATE TABLE t1(a, b); + INSERT INTO t1(a) VALUES(randstr(10, 400)); + INSERT INTO t1(a) VALUES(randstr(10, 400)); + INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; + INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; + INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; + INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; + INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; COMMIT; SELECT count(*) FROM t1; } @@ -94,7 +95,7 @@ do_test exclusive2-1.4 { } $::sig do_test exclusive2-1.5 { execsql { - DELETE FROM t1; + UPDATE t1 SET b=a, a=NULL; } db2 expr {[t1sig db2] eq $::sig} } 0 @@ -135,13 +136,14 @@ do_test exclusive2-2.1 { execsql {PRAGMA locking_mode = exclusive;} execsql { BEGIN; - INSERT INTO t1 VALUES(randstr(10, 400)); - INSERT INTO t1 VALUES(randstr(10, 400)); - INSERT INTO t1 SELECT randstr(10, 400) FROM t1; - INSERT INTO t1 SELECT randstr(10, 400) FROM t1; - INSERT INTO t1 SELECT randstr(10, 400) FROM t1; - INSERT INTO t1 SELECT randstr(10, 400) FROM t1; - INSERT INTO t1 SELECT randstr(10, 400) FROM t1; + DELETE FROM t1; + INSERT INTO t1(a) VALUES(randstr(10, 400)); + INSERT INTO t1(a) VALUES(randstr(10, 400)); + INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; + INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; + INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; + INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; + INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1; COMMIT; SELECT count(*) FROM t1; } diff --git a/test/speed2.test b/test/speed2.test index 08ddb15f46..f6d1a4c8ab 100644 --- a/test/speed2.test +++ b/test/speed2.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this script is measuring executing speed. # -# $Id: speed2.test,v 1.6 2007/03/31 22:34:16 drh Exp $ +# $Id: speed2.test,v 1.7 2007/04/16 15:02:20 drh Exp $ # set testdir [file dirname $argv0] @@ -67,7 +67,7 @@ do_test speed2-1.0 { execsql { PRAGMA page_size=1024; PRAGMA cache_size=8192; --- PRAGMA locking_mode=EXCLUSIVE; + PRAGMA locking_mode=EXCLUSIVE; CREATE TABLE t1(a INTEGER, b INTEGER, c TEXT); CREATE TABLE t2(a INTEGER, b INTEGER, c TEXT); CREATE INDEX i2a ON t2(a);