Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Improve coverage of pager.c. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
2fa05d01b6b11788a5b73d203fcac9d4 |
User & Date: | dan 2010-08-11 18:56:46.000 |
Context
2010-08-12
| ||
02:41 | Increase the maximum page size from 32k to 64k. (check-in: 45362437d4 user: drh tags: trunk) | |
2010-08-11
| ||
18:56 | Improve coverage of pager.c. (check-in: 2fa05d01b6 user: dan tags: trunk) | |
12:26 | Update an r-tree extension test case to account for recent changes to the query planner. Also fix a comment in rtree.c. (check-in: eaaca669a4 user: dan tags: trunk) | |
Changes
Changes to src/pager.c.
︙ | ︙ | |||
269 270 271 272 273 274 275 | ** * All writing and syncing of journal and database data has finished. ** If no error occured, all that remains is to finalize the journal to ** commit the transaction. If an error did occur, the caller will need ** to rollback the transaction. ** ** ERROR: ** | | | | | > > > | 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 | ** * All writing and syncing of journal and database data has finished. ** If no error occured, all that remains is to finalize the journal to ** commit the transaction. If an error did occur, the caller will need ** to rollback the transaction. ** ** ERROR: ** ** The ERROR state is entered when an IO or disk-full error (including ** SQLITE_IOERR_NOMEM) occurs at a point in the code that makes it ** difficult to be sure that the in-memory pager state (cache contents, ** db size etc.) are consistent with the contents of the file-system. ** ** Temporary pager files may enter the ERROR state, but in-memory pagers ** cannot. ** ** For example, if an IO error occurs while performing a rollback, ** the contents of the page-cache may be left in an inconsistent state. ** At this point it would be dangerous to change back to READER state ** (as usually happens after a rollback). Any subsequent readers might ** report database corruption (due to the inconsistent cache), and if ** they upgrade to writers, they may inadvertently corrupt the database |
︙ | ︙ | |||
319 320 321 322 323 324 325 326 327 328 329 330 331 332 | ** automatically attempt a rollback, as it assumes that an error in a ** read-only statement cannot leave the pager in an internally inconsistent ** state. ** ** * The Pager.errCode variable is set to something other than SQLITE_OK. ** * There are one or more outstanding references to pages (after the ** last reference is dropped the pager should move back to OPEN state). ** ** ** Notes: ** ** * A pager is never in WRITER_DBMOD or WRITER_FINISHED state if the ** connection is open in WAL mode. A WAL connection is always in one ** of the first four states. | > | 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 | ** automatically attempt a rollback, as it assumes that an error in a ** read-only statement cannot leave the pager in an internally inconsistent ** state. ** ** * The Pager.errCode variable is set to something other than SQLITE_OK. ** * There are one or more outstanding references to pages (after the ** last reference is dropped the pager should move back to OPEN state). ** * The pager is not an in-memory pager. ** ** ** Notes: ** ** * A pager is never in WRITER_DBMOD or WRITER_FINISHED state if the ** connection is open in WAL mode. A WAL connection is always in one ** of the first four states. |
︙ | ︙ | |||
821 822 823 824 825 826 827 | /* If the useJournal flag is clear, the journal-mode must be "OFF". ** And if the journal-mode is "OFF", the journal file must not be open. */ assert( p->journalMode==PAGER_JOURNALMODE_OFF || p->useJournal ); assert( p->journalMode!=PAGER_JOURNALMODE_OFF || !isOpen(p->jfd) ); | | > > > > > > > > | > > > > > > | 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 | /* If the useJournal flag is clear, the journal-mode must be "OFF". ** And if the journal-mode is "OFF", the journal file must not be open. */ assert( p->journalMode==PAGER_JOURNALMODE_OFF || p->useJournal ); assert( p->journalMode!=PAGER_JOURNALMODE_OFF || !isOpen(p->jfd) ); /* Check that MEMDB implies noSync. And an in-memory journal. Since ** this means an in-memory pager performs no IO at all, it cannot encounter ** either SQLITE_IOERR or SQLITE_FULL during rollback or while finalizing ** a journal file. (although the in-memory journal implementation may ** return SQLITE_IOERR_NOMEM while the journal file is being written). It ** is therefore not possible for an in-memory pager to enter the ERROR ** state. */ if( MEMDB ){ assert( p->noSync ); assert( p->journalMode==PAGER_JOURNALMODE_OFF || p->journalMode==PAGER_JOURNALMODE_MEMORY ); assert( p->eState!=PAGER_ERROR && p->eState!=PAGER_OPEN ); assert( pagerUseWal(p)==0 ); } /* If changeCountDone is set, a RESERVED lock or greater must be held ** on the file. */ assert( pPager->changeCountDone==0 || pPager->eLock>=RESERVED_LOCK ); assert( p->eLock!=PENDING_LOCK ); |
︙ | ︙ | |||
1755 1756 1757 1758 1759 1760 1761 | } /* If Pager.errCode is set, the contents of the pager cache cannot be ** trusted. Now that there are no outstanding references to the pager, ** it can safely move back to PAGER_OPEN state. This happens in both ** normal and exclusive-locking mode. */ | | > | 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 | } /* If Pager.errCode is set, the contents of the pager cache cannot be ** trusted. Now that there are no outstanding references to the pager, ** it can safely move back to PAGER_OPEN state. This happens in both ** normal and exclusive-locking mode. */ if( pPager->errCode ){ assert( !MEMDB ); pager_reset(pPager); pPager->changeCountDone = pPager->tempFile; pPager->eState = PAGER_OPEN; pPager->errCode = SQLITE_OK; } pPager->journalOff = 0; |
︙ | ︙ | |||
2563 2564 2565 2566 2567 2568 2569 | u32 u; /* Unsigned loop counter */ Pgno mxPg = 0; /* Size of the original file in pages */ int rc; /* Result code of a subroutine */ int res = 1; /* Value returned by sqlite3OsAccess() */ char *zMaster = 0; /* Name of master journal file if any */ int needPagerReset; /* True to reset page prior to first page rollback */ | < < < < > | 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 | u32 u; /* Unsigned loop counter */ Pgno mxPg = 0; /* Size of the original file in pages */ int rc; /* Result code of a subroutine */ int res = 1; /* Value returned by sqlite3OsAccess() */ char *zMaster = 0; /* Name of master journal file if any */ int needPagerReset; /* True to reset page prior to first page rollback */ /* Figure out how many records are in the journal. Abort early if ** the journal is empty. */ assert( isOpen(pPager->jfd) ); rc = sqlite3OsFileSize(pPager->jfd, &szJ); if( rc!=SQLITE_OK || szJ==0 ){ goto end_playback; } /* Read the master journal name from the journal, if it is present. ** If a master journal file name is specified, but the file is not |
︙ | ︙ | |||
3337 3338 3339 3340 3341 3342 3343 | ** ** If the page size is not changed, either because one of the enumerated ** conditions above is not true, the pager was in error state when this ** function was called, or because the memory allocation attempt failed, ** then *pPageSize is set to the old, retained page size before returning. */ int sqlite3PagerSetPagesize(Pager *pPager, u16 *pPageSize, int nReserve){ | < < > > > > > < < | | | | | | | | | | | | | | | | | | | | | | | | > | | | | | < | | 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 | ** ** If the page size is not changed, either because one of the enumerated ** conditions above is not true, the pager was in error state when this ** function was called, or because the memory allocation attempt failed, ** then *pPageSize is set to the old, retained page size before returning. */ int sqlite3PagerSetPagesize(Pager *pPager, u16 *pPageSize, int nReserve){ /* It is not possible to do a full assert_pager_state() here, as this ** function may be called from within PagerOpen(), before the state ** of the Pager object is internally consistent. ** ** At one point this function returned an error if the pager was in ** PAGER_ERROR state. But since PAGER_ERROR state guarantees that ** there is at least one outstanding page reference, this function ** is a no-op for that case anyhow. */ u16 pageSize = *pPageSize; assert( pageSize==0 || (pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE) ); if( (pPager->memDb==0 || pPager->dbSize==0) && sqlite3PcacheRefCount(pPager->pPCache)==0 && pageSize && pageSize!=pPager->pageSize ){ char *pNew; /* New temp space */ i64 nByte = 0; if( pPager->eState>PAGER_OPEN && isOpen(pPager->fd) ){ int rc = sqlite3OsFileSize(pPager->fd, &nByte); if( rc!=SQLITE_OK ) return rc; } pNew = (char *)sqlite3PageMalloc(pageSize); if( !pNew ){ return SQLITE_NOMEM; }else{ pager_reset(pPager); pPager->dbSize = nByte/pageSize; pPager->pageSize = pageSize; sqlite3PageFree(pPager->pTmpSpace); pPager->pTmpSpace = pNew; sqlite3PcacheSetPageSize(pPager->pPCache, pageSize); } } *pPageSize = (u16)pPager->pageSize; if( nReserve<0 ) nReserve = pPager->nReserve; assert( nReserve>=0 && nReserve<1000 ); pPager->nReserve = (i16)nReserve; pagerReportSize(pPager); return SQLITE_OK; } /* ** Return a pointer to the "temporary page" buffer held internally ** by the pager. This is a buffer that is big enough to hold the ** entire content of a database page. This buffer is used internally ** during rollback and will be overwritten whenever a rollback |
︙ | ︙ | |||
4739 4740 4741 4742 4743 4744 4745 4746 4747 | } } /* If there is a WAL file in the file-system, open this database in WAL ** mode. Otherwise, the following function call is a no-op. */ rc = pagerOpenWalIfPresent(pPager); } | > | > > | 4756 4757 4758 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 4769 4770 4771 4772 4773 4774 4775 4776 4777 4778 4779 4780 4781 4782 4783 4784 | } } /* If there is a WAL file in the file-system, open this database in WAL ** mode. Otherwise, the following function call is a no-op. */ rc = pagerOpenWalIfPresent(pPager); assert( pPager->pWal==0 || rc==SQLITE_OK ); } if( pagerUseWal(pPager) ){ assert( rc==SQLITE_OK ); rc = pagerBeginReadTransaction(pPager); } if( pPager->eState==PAGER_OPEN && rc==SQLITE_OK ){ rc = pagerPagecount(pPager, &pPager->dbSize); } failed: if( rc!=SQLITE_OK ){ assert( !MEMDB ); pager_unlock(pPager); assert( pPager->eState==PAGER_OPEN ); }else{ pPager->eState = PAGER_READER; } return rc; } |
︙ | ︙ | |||
5076 5077 5078 5079 5080 5081 5082 | ** or using a temporary file otherwise. */ int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){ int rc = SQLITE_OK; assert( pPager->eState>=PAGER_READER ); pPager->subjInMemory = (u8)subjInMemory; | | | 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105 5106 5107 5108 5109 5110 | ** or using a temporary file otherwise. */ int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){ int rc = SQLITE_OK; assert( pPager->eState>=PAGER_READER ); pPager->subjInMemory = (u8)subjInMemory; if( ALWAYS(pPager->eState==PAGER_READER) ){ assert( pPager->pInJournal==0 ); if( pagerUseWal(pPager) ){ /* If the pager is configured to use locking_mode=exclusive, and an ** exclusive lock on the database is not already held, obtain it now. */ if( pPager->exclusiveMode && sqlite3WalExclusiveMode(pPager->pWal, -1) ){ |
︙ | ︙ |
Changes to test/pager1.test.
︙ | ︙ | |||
1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 | } {13} do_execsql_test pager1-6.8 { INSERT INTO t11 VALUES(3, 4); PRAGMA max_page_count = 10; } {11} do_execsql_test pager1-6.9 { COMMIT } {} #------------------------------------------------------------------------- # The following tests work with "PRAGMA journal_mode=TRUNCATE" and # "PRAGMA locking_mode=EXCLUSIVE". # # Each test is specified with 5 variables. As follows: # | > > > > | 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 | } {13} do_execsql_test pager1-6.8 { INSERT INTO t11 VALUES(3, 4); PRAGMA max_page_count = 10; } {11} do_execsql_test pager1-6.9 { COMMIT } {} do_execsql_test pager1-6.10 { PRAGMA max_page_count = 10 } {10} do_execsql_test pager1-6.11 { SELECT * FROM t11 } {1 2 3 4} do_execsql_test pager1-6.12 { PRAGMA max_page_count } {11} #------------------------------------------------------------------------- # The following tests work with "PRAGMA journal_mode=TRUNCATE" and # "PRAGMA locking_mode=EXCLUSIVE". # # Each test is specified with 5 variables. As follows: # |
︙ | ︙ | |||
1738 1739 1740 1741 1742 1743 1744 | PRAGMA omit_readlock = 1; ATTACH 'test.db' AS two; BEGIN; SELECT * FROM t1; } } {1 2 3 4 5 6} do_test pager1-17.$tn.3.2 { | | | 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 | PRAGMA omit_readlock = 1; ATTACH 'test.db' AS two; BEGIN; SELECT * FROM t1; } } {1 2 3 4 5 6} do_test pager1-17.$tn.3.2 { csql1 { INSERT INTO t1 VALUES(3, 4) } } {1 {database is locked}} do_test pager1-17.$tn.3.3 { sql2 COMMIT } {} } #------------------------------------------------------------------------- # Test the pagers response to the b-tree layer requesting illegal page # numbers: |
︙ | ︙ | |||
2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 | do_execsql_test pager1-26.1 { UPDATE tbl SET b = a_string(550); } {} db close tv delete #------------------------------------------------------------------------- do_test pager1.27.1 { faultsim_delete_and_reopen sqlite3_pager_refcounts db execsql { BEGIN; CREATE TABLE t1(a, b); } sqlite3_pager_refcounts db execsql COMMIT } {} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > | 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 | do_execsql_test pager1-26.1 { UPDATE tbl SET b = a_string(550); } {} db close tv delete #------------------------------------------------------------------------- # do_test pager1.27.1 { faultsim_delete_and_reopen sqlite3_pager_refcounts db execsql { BEGIN; CREATE TABLE t1(a, b); } sqlite3_pager_refcounts db execsql COMMIT } {} #------------------------------------------------------------------------- # Test that attempting to open a write-transaction with # locking_mode=exclusive in WAL mode fails if there are other clients on # the same database. # catch { db close } do_multiclient_test tn { do_test pager1-28.$tn.1 { sql1 { PRAGMA journal_mode = WAL; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES('a', 'b'); } } {wal} do_test pager1-28.$tn.2 { sql2 { SELECT * FROM t1 } } {a b} do_test pager1-28.$tn.3 { sql1 { PRAGMA locking_mode=exclusive } } {exclusive} do_test pager1-28.$tn.4 { csql1 { BEGIN; INSERT INTO t1 VALUES('c', 'd'); } } {1 {database is locked}} code2 { db2 close ; sqlite3 db2 test.db } do_test pager1-28.$tn.4 { sql1 { INSERT INTO t1 VALUES('c', 'd'); COMMIT } } {} } finish_test |
Changes to test/pager2.test.
︙ | ︙ | |||
112 113 114 115 116 117 118 119 | } [list $x ok] } } } db close tv delete finish_test | > > > > > > > > > > > > > > > > > | 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | } [list $x ok] } } } db close tv delete #------------------------------------------------------------------------- # Test a ROLLBACK with journal_mode=off. # breakpoint do_test pager2-2.1 { faultsim_delete_and_reopen execsql { CREATE TABLE t1(a, b); PRAGMA journal_mode = off; BEGIN; INSERT INTO t1 VALUES(1, 2); ROLLBACK; SELECT * FROM t1; } } {off 1 2} finish_test |
Changes to test/pagerfault.test.
︙ | ︙ | |||
48 49 50 51 52 53 54 | faultsim_save_and_close } {} do_faultsim_test pagerfault-1 -prep { faultsim_restore_and_reopen } -body { execsql { SELECT count(*) FROM t1 } } -test { | | | 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | faultsim_save_and_close } {} do_faultsim_test pagerfault-1 -prep { faultsim_restore_and_reopen } -body { execsql { SELECT count(*) FROM t1 } } -test { faultsim_test_result {0 4} faultsim_integrity_check if {[db one { SELECT count(*) FROM t1 }] != 4} { error "Database content appears incorrect" } } #------------------------------------------------------------------------- |
︙ | ︙ | |||
1073 1074 1075 1076 1077 1078 1079 1080 1081 | } -body { execsql { INSERT INTO t2 VALUES(2) } execsql { SELECT * FROM t2 } } -test { faultsim_test_result {0 {1 2}} faultsim_integrity_check } finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 | } -body { execsql { INSERT INTO t2 VALUES(2) } execsql { SELECT * FROM t2 } } -test { faultsim_test_result {0 {1 2}} faultsim_integrity_check } #------------------------------------------------------------------------- # Provoke an OOM error during a commit of multi-file transaction. One of # the databases written during the transaction is an in-memory database. # This test causes rollback of the in-memory database after CommitPhaseOne() # has successfully returned. i.e. the series of calls for the aborted commit # is: # # PagerCommitPhaseOne(<in-memory-db>) -> SQLITE_OK # PagerCommitPhaseOne(<file-db>) -> SQLITE_IOERR # PagerRollback(<in-memory-db>) # PagerRollback(<file-db>) # do_faultsim_test pagerfault-23 -prep { foreach f [glob -nocomplain test.db*] { file delete -force $f } sqlite3 db :memory: db eval { ATTACH 'test.db2' AS aux; CREATE TABLE t1(a, b); CREATE TABLE aux.t2(a, b); } } -body { execsql { BEGIN; INSERT INTO t1 VALUES(1,2); INSERT INTO t2 VALUES(3,4); COMMIT; } } -test { faultsim_test_result {0 {}} faultsim_integrity_check } do_faultsim_test pagerfault-24 -prep { faultsim_delete_and_reopen db eval { PRAGMA temp_store = file } execsql { CREATE TABLE x(a, b) } } -body { execsql { CREATE TEMP TABLE t1(a, b) } } -test { faultsim_test_result {0 {}} {1 {unable to open a temporary database file for storing temporary tables}} set ic [db eval { PRAGMA temp.integrity_check }] if {$ic != "ok"} { error "Integrity check: $ic" } } finish_test |