Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Fix a problem causing the BtShared.isPending flag to be cleared to early. Also coverage improvements for btree.c. (CVS 6440) |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
8f1423445b29a5f52ed907de6db82128 |
User & Date: | danielk1977 2009-04-02 18:28:08.000 |
Context
2009-04-02
| ||
18:32 | Fix the sqlite3_prepare() family of interfaces so that they zero the *ppStmt value even on an SQLITE_MISUSE return. Make it clear in the documentation that the ppStmt parameter cannot be zero. (CVS 6441) (check-in: 23bf9f2665 user: drh tags: trunk) | |
18:28 | Fix a problem causing the BtShared.isPending flag to be cleared to early. Also coverage improvements for btree.c. (CVS 6440) (check-in: 8f1423445b user: danielk1977 tags: trunk) | |
17:23 | Ensure the required VerifyCookie/Transaction/TableLock opcodes are added for "x IN (SELECT c FROM t)" expressions. Ticket #3771. (CVS 6439) (check-in: 058a2f2093 user: danielk1977 tags: trunk) | |
Changes
Changes to src/btree.c.
1 2 3 4 5 6 7 8 9 10 11 | /* ** 2004 April 6 ** ** 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. ** ************************************************************************* | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | /* ** 2004 April 6 ** ** 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: btree.c,v 1.588 2009/04/02 18:28:08 danielk1977 Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** See the header comment on "btreeInt.h" for additional information. ** Including a description of file format and an overview of operation. */ #include "btreeInt.h" |
︙ | ︙ | |||
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 | } #endif /* !SQLITE_OMIT_SHARED_CACHE */ #ifndef SQLITE_OMIT_SHARED_CACHE /* ** Release all the table locks (locks obtained via calls to ** the setSharedCacheTableLock() procedure) held by Btree handle p. */ static void clearAllSharedCacheTableLocks(Btree *p){ BtShared *pBt = p->pBt; BtLock **ppIter = &pBt->pLock; assert( sqlite3BtreeHoldsMutex(p) ); assert( p->sharable || 0==*ppIter ); while( *ppIter ){ BtLock *pLock = *ppIter; assert( pBt->isExclusive==0 || pBt->pWriter==pLock->pBtree ); if( pLock->pBtree==p ){ *ppIter = pLock->pNext; sqlite3_free(pLock); }else{ ppIter = &pLock->pNext; } } | > > > > > > | 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 | } #endif /* !SQLITE_OMIT_SHARED_CACHE */ #ifndef SQLITE_OMIT_SHARED_CACHE /* ** Release all the table locks (locks obtained via calls to ** the setSharedCacheTableLock() procedure) held by Btree handle p. ** ** This function assumes that handle p has an open read or write ** transaction. If it does not, then the BtShared.isPending variable ** may be incorrectly cleared. */ static void clearAllSharedCacheTableLocks(Btree *p){ BtShared *pBt = p->pBt; BtLock **ppIter = &pBt->pLock; assert( sqlite3BtreeHoldsMutex(p) ); assert( p->sharable || 0==*ppIter ); assert( p->inTrans>0 ); while( *ppIter ){ BtLock *pLock = *ppIter; assert( pBt->isExclusive==0 || pBt->pWriter==pLock->pBtree ); assert( pLock->pBtree->inTrans>=pLock->eLock ); if( pLock->pBtree==p ){ *ppIter = pLock->pNext; sqlite3_free(pLock); }else{ ppIter = &pLock->pNext; } } |
︙ | ︙ | |||
2257 2258 2259 2260 2261 2262 2263 | set_child_ptrmaps_out: pPage->isInit = isInitOrig; return rc; } /* | | | 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 | set_child_ptrmaps_out: pPage->isInit = isInitOrig; return rc; } /* ** Somewhere on pPage, which is guaranteed to be a btree page, not an overflow ** page, is a pointer to page iFrom. Modify this pointer so that it points to ** iTo. Parameter eType describes the type of pointer to be modified, as ** follows: ** ** PTRMAP_BTREE: pPage is a btree-page. The pointer points at a child ** page of pPage. ** |
︙ | ︙ | |||
2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 | ** number of pages the database file will contain after this ** process is complete. */ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){ Pgno nFreeList; /* Number of pages still on the free-list */ assert( sqlite3_mutex_held(pBt->mutex) ); if( !PTRMAP_ISPAGE(pBt, iLastPg) && iLastPg!=PENDING_BYTE_PAGE(pBt) ){ int rc; u8 eType; Pgno iPtrPage; nFreeList = get4byte(&pBt->pPage1->aData[36]); | > | | 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 | ** number of pages the database file will contain after this ** process is complete. */ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){ Pgno nFreeList; /* Number of pages still on the free-list */ assert( sqlite3_mutex_held(pBt->mutex) ); assert( iLastPg>nFin ); if( !PTRMAP_ISPAGE(pBt, iLastPg) && iLastPg!=PENDING_BYTE_PAGE(pBt) ){ int rc; u8 eType; Pgno iPtrPage; nFreeList = get4byte(&pBt->pPage1->aData[36]); if( nFreeList==0 ){ return SQLITE_DONE; } rc = ptrmapGet(pBt, iLastPg, &eType, &iPtrPage); if( rc!=SQLITE_OK ){ return rc; } |
︙ | ︙ | |||
2684 2685 2686 2687 2688 2689 2690 | rc = sqlite3PagerCommitPhaseTwo(pBt->pPager); if( rc!=SQLITE_OK ){ sqlite3BtreeLeave(p); return rc; } pBt->inTransaction = TRANS_READ; } | < > | 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 | rc = sqlite3PagerCommitPhaseTwo(pBt->pPager); if( rc!=SQLITE_OK ){ sqlite3BtreeLeave(p); return rc; } pBt->inTransaction = TRANS_READ; } /* If the handle has any kind of transaction open, decrement the transaction ** count of the shared btree. If the transaction count reaches 0, set ** the shared state to TRANS_NONE. The unlockBtreeIfUnused() call below ** will unlock the pager. */ if( p->inTrans!=TRANS_NONE ){ clearAllSharedCacheTableLocks(p); pBt->nTransaction--; if( 0==pBt->nTransaction ){ pBt->inTransaction = TRANS_NONE; } } /* Set the handles current transaction state to TRANS_NONE and unlock |
︙ | ︙ | |||
2808 2809 2810 2811 2812 2813 2814 | ** we cannot simply return the error to the caller. Instead, abort ** all queries that may be using any of the cursors that failed to save. */ sqlite3BtreeTripAllCursors(p, rc); } #endif btreeIntegrity(p); | < | 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 | ** we cannot simply return the error to the caller. Instead, abort ** all queries that may be using any of the cursors that failed to save. */ sqlite3BtreeTripAllCursors(p, rc); } #endif btreeIntegrity(p); if( p->inTrans==TRANS_WRITE ){ int rc2; assert( TRANS_WRITE==pBt->inTransaction ); rc2 = sqlite3PagerRollback(pBt->pPager); if( rc2!=SQLITE_OK ){ |
︙ | ︙ | |||
2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 | releasePage(pPage1); } assert( countWriteCursors(pBt)==0 ); pBt->inTransaction = TRANS_READ; } if( p->inTrans!=TRANS_NONE ){ assert( pBt->nTransaction>0 ); pBt->nTransaction--; if( 0==pBt->nTransaction ){ pBt->inTransaction = TRANS_NONE; } } | > | 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 | releasePage(pPage1); } assert( countWriteCursors(pBt)==0 ); pBt->inTransaction = TRANS_READ; } if( p->inTrans!=TRANS_NONE ){ clearAllSharedCacheTableLocks(p); assert( pBt->nTransaction>0 ); pBt->nTransaction--; if( 0==pBt->nTransaction ){ pBt->inTransaction = TRANS_NONE; } } |
︙ | ︙ | |||
6784 6785 6786 6787 6788 6789 6790 | /* If autovacuumed is disabled in this build but we are trying to ** access an autovacuumed database, then make the database readonly. */ #ifdef SQLITE_OMIT_AUTOVACUUM if( idx==4 && *pMeta>0 ) pBt->readOnly = 1; #endif | > | > > > > | > | 6791 6792 6793 6794 6795 6796 6797 6798 6799 6800 6801 6802 6803 6804 6805 6806 6807 6808 6809 6810 6811 6812 | /* If autovacuumed is disabled in this build but we are trying to ** access an autovacuumed database, then make the database readonly. */ #ifdef SQLITE_OMIT_AUTOVACUUM if( idx==4 && *pMeta>0 ) pBt->readOnly = 1; #endif /* If there is currently an open transaction, grab a read-lock ** on page 1 of the database file. This is done to make sure that ** no other connection can modify the meta value just read from ** the database until the transaction is concluded. */ if( p->inTrans>0 ){ rc = setSharedCacheTableLock(p, 1, READ_LOCK); } sqlite3BtreeLeave(p); return rc; } /* ** Write meta-information back into the database. Meta[0] is ** read-only and may not be written. |
︙ | ︙ |
Changes to test/corrupt2.test.
︙ | ︙ | |||
9 10 11 12 13 14 15 | # #*********************************************************************** # This file implements regression tests for SQLite library. # # This file implements tests to make sure SQLite does not crash or # segfault if it sees a corrupt database file. # | | | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | # #*********************************************************************** # This file implements regression tests for SQLite library. # # This file implements tests to make sure SQLite does not crash or # segfault if it sees a corrupt database file. # # $Id: corrupt2.test,v 1.19 2009/04/02 18:28:08 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # The following tests - corrupt2-1.* - create some databases corrupted in # specific ways and ensure that SQLite detects them as corrupt. # |
︙ | ︙ | |||
450 451 452 453 454 455 456 457 458 | do_test corrupt2-10.1 { catchsql { SELECT * FROM t2 } } {1 {malformed database schema (t2)}} do_test corrupt2-10.2 { sqlite3_errcode db } {SQLITE_CORRUPT} } finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 | do_test corrupt2-10.1 { catchsql { SELECT * FROM t2 } } {1 {malformed database schema (t2)}} do_test corrupt2-10.2 { sqlite3_errcode db } {SQLITE_CORRUPT} } corruption_test -sqlprep { PRAGMA auto_vacuum = incremental; CREATE TABLE t1(a INTEGER PRIMARY KEY, b); CREATE TABLE t2(a INTEGER PRIMARY KEY, b); INSERT INTO t1 VALUES(1, randstr(100,100)); INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1; INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1; INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1; INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1; INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1; INSERT INTO t2 SELECT * FROM t1; DELETE FROM t1; } -corrupt { set offset [expr [file size corrupt.db] - 1024] hexio_write corrupt.db $offset FF hexio_write corrupt.db 24 12345678 } -test { do_test corrupt2-11.1 { catchsql { PRAGMA incremental_vacuum } } {1 {database disk image is malformed}} } corruption_test -sqlprep { PRAGMA auto_vacuum = incremental; CREATE TABLE t1(a INTEGER PRIMARY KEY, b); CREATE TABLE t2(a INTEGER PRIMARY KEY, b); INSERT INTO t1 VALUES(1, randstr(100,100)); INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1; INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1; INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1; INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1; INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1; INSERT INTO t2 SELECT * FROM t1; DELETE FROM t1; } -corrupt { set pgno [expr [file size corrupt.db] / 1024] hexio_write corrupt.db [expr 1024+5*($pgno-3)] 03 hexio_write corrupt.db 24 12345678 } -test { do_test corrupt2-12.1 { catchsql { PRAGMA incremental_vacuum } } {1 {database disk image is malformed}} } finish_test |
Changes to test/shared6.test.
1 2 3 4 5 6 7 8 9 10 11 | # 2009 April 01 # # 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. # #*********************************************************************** # | | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | # 2009 April 01 # # 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: shared6.test,v 1.3 2009/04/02 18:28:08 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !shared_cache { finish_test ; return } do_test shared6-1.1.1 { execsql { CREATE TABLE t1(a, b); CREATE TABLE t2(c, d); CREATE TABLE t3(e, f); } db close } {} do_test shared6-1.1.2 { set ::enable_shared_cache [sqlite3_enable_shared_cache 1] sqlite3_enable_shared_cache } {1} do_test shared6-1.1.3 { |
︙ | ︙ | |||
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | } {1 {database table is locked: t1}} do_test shared6-1.X { db1 close db2 close } {} # The following tests - shared6-2.* - test that two database connections # that connect to the same file using different VFS implementations do # not share a cache. if {$::tcl_platform(platform) eq "unix"} { do_test shared6-2.1 { sqlite3 db1 test.db -vfs unix sqlite3 db2 test.db -vfs unix sqlite3 db3 test.db -vfs unix-none sqlite3 db4 test.db -vfs unix-none } {} | > > | 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | } {1 {database table is locked: t1}} do_test shared6-1.X { db1 close db2 close } {} #------------------------------------------------------------------------- # The following tests - shared6-2.* - test that two database connections # that connect to the same file using different VFS implementations do # not share a cache. # if {$::tcl_platform(platform) eq "unix"} { do_test shared6-2.1 { sqlite3 db1 test.db -vfs unix sqlite3 db2 test.db -vfs unix sqlite3 db3 test.db -vfs unix-none sqlite3 db4 test.db -vfs unix-none } {} |
︙ | ︙ | |||
169 170 171 172 173 174 175 176 177 178 179 | do_test shared6-2.X { db1 close db2 close db3 close db4 close } {} } sqlite3_enable_shared_cache $::enable_shared_cache finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 | do_test shared6-2.X { db1 close db2 close db3 close db4 close } {} } #------------------------------------------------------------------------- # Test that it is possible to open an exclusive transaction while # already holding a read-lock on the database file. And that it is # not possible if some other connection holds such a lock. # do_test shared6-3.1 { sqlite3 db1 test.db sqlite3 db2 test.db sqlite3 db3 test.db } {} db1 eval {SELECT * FROM t1} { # Within this block [db1] is holding a read-lock on t1. Test that # this means t1 cannot be written by [db2]. # do_test shared6-3.2 { catchsql { INSERT INTO t1 VALUES(1, 2) } db2 } {1 {database table is locked: t1}} do_test shared6-3.3 { execsql { BEGIN EXCLUSIVE } db1 } {} break } do_test shared6-3.4 { catchsql { SELECT * FROM t1 } db2 } {1 {database schema is locked: main}} do_test shared6-3.5 { execsql COMMIT db1 } {} db2 eval {SELECT * FROM t1} { do_test shared6-3.6 { catchsql { BEGIN EXCLUSIVE } db1 } {1 {database table is locked}} break } do_test shared6-3.7 { execsql { BEGIN } db1 execsql { BEGIN } db2 } {} db2 eval {SELECT * FROM t1} { do_test shared6-3.8 { catchsql { INSERT INTO t1 VALUES(1, 2) } db1 } {1 {database table is locked: t1}} break } do_test shared6-3.9 { execsql { BEGIN ; ROLLBACK } db3 } {} do_test shared6-3.10 { catchsql { SELECT * FROM t1 } db3 } {1 {database table is locked}} do_test shared6-3.X { db1 close db2 close db3 close } {} do_test shared6-4.1 { #file delete -force test.db test.db-journal sqlite3 db1 test.db sqlite3 db2 test.db set ::STMT [sqlite3_prepare_v2 db1 "SELECT * FROM t1" -1 DUMMY] execsql { CREATE TABLE t5(a, b) } db2 } {} do_test shared6-4.2 { sqlite3_finalize $::STMT } {SQLITE_OK} do_test shared6-4.X { db1 close db2 close } {} sqlite3_enable_shared_cache $::enable_shared_cache finish_test |