Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Detect database file changes using a 128-bit segment of the file header that includes the change counter. Ticket #2303. (CVS 3844) |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
e44995debf2456e55b502783849e93a0 |
User & Date: | drh 2007-04-16 15:02:19.000 |
Context
2007-04-16
| ||
15:06 | Ensure sqlite3_finalize() can be called from within the xDisconnect() method of virtual tables. (CVS 3845) (check-in: 8d6c3bfc4d user: danielk1977 tags: trunk) | |
15:02 | Detect database file changes using a 128-bit segment of the file header that includes the change counter. Ticket #2303. (CVS 3844) (check-in: e44995debf user: drh tags: trunk) | |
2007-04-14
| ||
12:04 | Update the whentouse.html document to mention that less bitmap memory is used for larger page sizes. (CVS 3843) (check-in: 2c8e2a5be3 user: drh tags: trunk) | |
Changes
Changes to src/pager.c.
︙ | ︙ | |||
14 15 16 17 18 19 20 | ** The pager is used to access a database disk file. It implements ** atomic commit and rollback through the use of a journal file that ** is separate from the database file. The pager also implements file ** locking to prevent two processes from writing the same database ** file simultaneously, or one process from reading the database while ** another is writing. ** | | | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | ** The pager is used to access a database disk file. It implements ** atomic commit and rollback through the use of a journal file that ** is separate from the database file. The pager also implements file ** locking to prevent two processes from writing the same database ** file simultaneously, or one process from reading the database while ** another is writing. ** ** @(#) $Id: pager.c,v 1.329 2007/04/16 15:02:19 drh Exp $ */ #ifndef SQLITE_OMIT_DISKIO #include "sqliteInt.h" #include "os.h" #include "pager.h" #include <assert.h> #include <string.h> |
︙ | ︙ | |||
290 291 292 293 294 295 296 | #endif int nHash; /* Size of the pager hash table */ PgHdr **aHash; /* Hash table to map page number to PgHdr */ #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT Pager *pNext; /* Linked list of pagers in this thread */ #endif char *pTmpSpace; /* Pager.pageSize bytes of space for tmp use */ | | | 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 | #endif int nHash; /* Size of the pager hash table */ PgHdr **aHash; /* Hash table to map page number to PgHdr */ #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT Pager *pNext; /* Linked list of pagers in this thread */ #endif char *pTmpSpace; /* Pager.pageSize bytes of space for tmp use */ char dbFileVers[16]; /* Changes whenever database file changes */ }; /* ** The following global variables hold counters used for ** testing purposes only. These variables do not exist in ** a non-testing build. These variables are not thread-safe. */ |
︙ | ︙ | |||
1127 1128 1129 1130 1131 1132 1133 | memcpy(pData, aData, pPager->pageSize); if( pPager->xReiniter ){ pPager->xReiniter(pPg, pPager->pageSize); } #ifdef SQLITE_CHECK_PAGES pPg->pageHash = pager_pagehash(pPg); #endif | < < | > | > > > | 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 | memcpy(pData, aData, pPager->pageSize); if( pPager->xReiniter ){ pPager->xReiniter(pPg, pPager->pageSize); } #ifdef SQLITE_CHECK_PAGES pPg->pageHash = pager_pagehash(pPg); #endif /* If this was page 1, then restore the value of Pager.dbFileVers. ** Do this before any decoding. */ if( pgno==1 ){ memcpy(&pPager->dbFileVers, &((u8*)pData)[24],sizeof(pPager->dbFileVers)); } /* Decode the page just read from disk */ CODEC1(pPager, pData, pPg->pgno, 3); } return rc; } /* ** Parameter zMaster is the name of a master journal file. A single journal ** file that referred to the master journal file has just been rolled back. |
︙ | ︙ | |||
2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 | if( pList->pgno<=pPager->dbSize ){ char *pData = CODEC2(pPager, PGHDR_TO_DATA(pList), pList->pgno, 6); PAGERTRACE3("STORE %d page %d\n", PAGERID(pPager), pList->pgno); IOTRACE(("PGOUT %p %d\n", pPager, pList->pgno)); rc = sqlite3OsWrite(pPager->fd, pData, pPager->pageSize); PAGER_INCR(sqlite3_pager_writedb_count); PAGER_INCR(pPager->nWrite); } #ifndef NDEBUG else{ PAGERTRACE3("NOSTORE %d page %d\n", PAGERID(pPager), pList->pgno); } #endif if( rc ) return rc; | > > > | 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 | if( pList->pgno<=pPager->dbSize ){ char *pData = CODEC2(pPager, PGHDR_TO_DATA(pList), pList->pgno, 6); PAGERTRACE3("STORE %d page %d\n", PAGERID(pPager), pList->pgno); IOTRACE(("PGOUT %p %d\n", pPager, pList->pgno)); 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{ PAGERTRACE3("NOSTORE %d page %d\n", PAGERID(pPager), pList->pgno); } #endif if( rc ) return rc; |
︙ | ︙ | |||
2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 | rc = sqlite3OsRead(pPager->fd, PGHDR_TO_DATA(pPg), pPager->pageSize); } PAGER_INCR(sqlite3_pager_readdb_count); PAGER_INCR(pPager->nRead); IOTRACE(("PGIN %p %d\n", pPager, pgno)); PAGERTRACE3("FETCH %d page %d\n", PAGERID(pPager), pPg->pgno); CODEC1(pPager, PGHDR_TO_DATA(pPg), pPg->pgno, 3); return rc; } /* ** This function is called to obtain the shared lock required before | > > > > | 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 | rc = sqlite3OsRead(pPager->fd, PGHDR_TO_DATA(pPg), pPager->pageSize); } PAGER_INCR(sqlite3_pager_readdb_count); 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; } /* ** This function is called to obtain the shared lock required before |
︙ | ︙ | |||
2776 2777 2778 2779 2780 2781 2782 | (pPager->exclusiveMode && pPager->state>PAGER_SHARED) ); } 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 | | < | | > | > | > > | > > > < < > < | | > | < | 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 | (pPager->exclusiveMode && pPager->state>PAGER_SHARED) ); } 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). 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. */ char dbFileVers[sizeof(pPager->dbFileVers)]; sqlite3PagerPagecount(pPager); if( pPager->errCode ){ return pPager->errCode; } if( pPager->dbSize>0 ){ rc = sqlite3OsSeek(pPager->fd, 24); if( rc!=SQLITE_OK ){ return rc; } rc = sqlite3OsRead(pPager->fd, &dbFileVers, sizeof(dbFileVers)); if( rc!=SQLITE_OK ){ return rc; } }else{ memset(dbFileVers, 0, sizeof(dbFileVers)); } if( memcmp(pPager->dbFileVers, dbFileVers, sizeof(dbFileVers))!=0 ){ pager_reset(pPager); } } } assert( pPager->exclusiveMode || pPager->state<=PAGER_SHARED ); if( pPager->state==PAGER_UNLOCK ){ pPager->state = PAGER_SHARED; } |
︙ | ︙ | |||
3025 3026 3027 3028 3029 3030 3031 | rc = readDbPage(pPager, pPg, pgno); if( rc!=SQLITE_OK && rc!=SQLITE_IOERR_SHORT_READ ){ pPg->pgno = 0; sqlite3PagerUnref(pPg); return rc; } } | < < < < | 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 | rc = readDbPage(pPager, pPg, pgno); if( rc!=SQLITE_OK && rc!=SQLITE_IOERR_SHORT_READ ){ pPg->pgno = 0; sqlite3PagerUnref(pPg); return rc; } } /* Link the page into the page hash table */ h = pgno & (pPager->nHash-1); assert( pgno!=0 ); pPg->pNextHash = pPager->aHash[h]; pPager->aHash[h] = pPg; if( pPg->pNextHash ){ |
︙ | ︙ | |||
3718 3719 3720 3721 3722 3723 3724 | /* Read the current value at byte 24. */ change_counter = retrieve32bits(pPgHdr, 24); /* Increment the value just read and write it back to byte 24. */ change_counter++; put32bits(((char*)PGHDR_TO_DATA(pPgHdr))+24, change_counter); | < | 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 | /* Read the current value at byte 24. */ change_counter = retrieve32bits(pPgHdr, 24); /* Increment the value just read and write it back to byte 24. */ change_counter++; put32bits(((char*)PGHDR_TO_DATA(pPgHdr))+24, change_counter); /* Release the page reference. */ sqlite3PagerUnref(pPgHdr); pPager->changeCountDone = 1; } return SQLITE_OK; } |
︙ | ︙ |
Changes to test/exclusive2.test.
1 2 3 4 5 6 7 8 9 10 11 12 | # 2007 March 24 # # 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 implements regression tests for SQLite library. # | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | # 2007 March 24 # # 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 implements regression tests for SQLite library. # # $Id: exclusive2.test,v 1.4 2007/04/16 15:02:20 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable {!pager_pragmas} { finish_test return |
︙ | ︙ | |||
52 53 54 55 56 57 58 | pagerChangeCounter test.db } {0} #----------------------------------------------------------------------- # The following tests - exclusive2-1.X - check that: # # 1-3: Build a database with connection 1, calculate a signature. | | > | | | | | | | | | | | 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | pagerChangeCounter test.db } {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 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. # 9: Increment the change-counter. # 10: Ensure that the first connection now sees the updated database. It # sees the change-counter has been incremented and discards the # invalid in-memory cache. # do_test exclusive2-1.1 { execsql { BEGIN; 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; } } {64} do_test exclusive2-1.2 { set ::sig [t1sig] pagerChangeCounter test.db } {1} do_test exclusive2-1.3 { t1sig } $::sig do_test exclusive2-1.4 { sqlite3 db2 test.db t1sig db2 } $::sig do_test exclusive2-1.5 { execsql { UPDATE t1 SET b=a, a=NULL; } db2 expr {[t1sig db2] eq $::sig} } 0 do_test exclusive2-1.6 { pagerChangeCounter test.db } {2} do_test exclusive2-1.7 { |
︙ | ︙ | |||
131 132 133 134 135 136 137 | # SQLite detects the modified change-counter and discards the # in-memory cache. Then it finds the corruption caused in step 4.... # do_test exclusive2-2.1 { execsql {PRAGMA locking_mode = exclusive;} execsql { BEGIN; | > | | | | | | | | 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | # SQLite detects the modified change-counter and discards the # in-memory cache. Then it finds the corruption caused in step 4.... # do_test exclusive2-2.1 { execsql {PRAGMA locking_mode = exclusive;} execsql { BEGIN; 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; } } {64} do_test exclusive2-2.2 { set ::sig [t1sig] pagerChangeCounter test.db |
︙ | ︙ |
Changes to test/speed2.test.
1 2 3 4 5 6 7 8 9 10 11 12 13 | # 2006 November 23 # # 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 implements regression tests for SQLite library. The # focus of this script is measuring executing speed. # | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | # 2006 November 23 # # 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 implements regression tests for SQLite library. The # focus of this script is measuring executing speed. # # $Id: speed2.test,v 1.7 2007/04/16 15:02:20 drh Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl speed_trial_init speed2 # Set a uniform random seed |
︙ | ︙ | |||
63 64 65 66 67 68 69 | # Create a database schema. # do_test speed2-1.0 { execsql { PRAGMA page_size=1024; PRAGMA cache_size=8192; | | | 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | # Create a database schema. # do_test speed2-1.0 { execsql { PRAGMA page_size=1024; PRAGMA cache_size=8192; 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); CREATE INDEX i2b ON t2(b); } execsql { SELECT name FROM sqlite_master ORDER BY 1; |
︙ | ︙ |