Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Changes so that WAL and exclusive-locking mode work together. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
71e7b1cf9f4cd02a2a9bc8a3e58acd7a |
User & Date: | dan 2010-05-11 12:19:27.000 |
Context
2010-05-11
| ||
14:00 | When an attempt to change journal_mode fails due to locks, be sure to leave internal structures in a consistent state. (check-in: cf3d1e0b8a user: drh tags: trunk) | |
12:19 | Changes so that WAL and exclusive-locking mode work together. (check-in: 71e7b1cf9f user: dan tags: trunk) | |
02:46 | Updates to WAL TCL test scripts to support running on Windows. (check-in: 6a5630806c user: shaneh tags: trunk) | |
Changes
Changes to src/pager.c.
︙ | ︙ | |||
1400 1401 1402 1403 1404 1405 1406 | if( pPager->journalOff==0 ){ rc = SQLITE_OK; }else{ rc = sqlite3OsTruncate(pPager->jfd, 0); } pPager->journalOff = 0; pPager->journalStarted = 0; | | | | 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 | if( pPager->journalOff==0 ){ rc = SQLITE_OK; }else{ rc = sqlite3OsTruncate(pPager->jfd, 0); } pPager->journalOff = 0; pPager->journalStarted = 0; }else if( pPager->journalMode==PAGER_JOURNALMODE_PERSIST || (pPager->exclusiveMode && pPager->journalMode!=PAGER_JOURNALMODE_WAL) ){ rc = zeroJournalHdr(pPager, hasMaster); pager_error(pPager, rc); pPager->journalOff = 0; pPager->journalStarted = 0; }else{ /* This branch may be executed with Pager.journalMode==MEMORY if |
︙ | ︙ | |||
1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 | pPager->pInJournal = 0; pPager->nRec = 0; sqlite3PcacheCleanAll(pPager->pPCache); if( pagerUseWal(pPager) ){ rc2 = sqlite3WalWriteLock(pPager->pWal, 0); pPager->state = PAGER_SHARED; }else if( !pPager->exclusiveMode ){ rc2 = osUnlock(pPager->fd, SHARED_LOCK); pPager->state = PAGER_SHARED; pPager->changeCountDone = 0; }else if( pPager->state==PAGER_SYNCED ){ pPager->state = PAGER_EXCLUSIVE; } | > > > > > > > > > > > | 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 | pPager->pInJournal = 0; pPager->nRec = 0; sqlite3PcacheCleanAll(pPager->pPCache); if( pagerUseWal(pPager) ){ rc2 = sqlite3WalWriteLock(pPager->pWal, 0); pPager->state = PAGER_SHARED; /* If the connection was in locking_mode=exclusive mode but is no longer, ** drop the EXCLUSIVE lock held on the database file. */ if( rc2==SQLITE_OK && !pPager->exclusiveMode && sqlite3WalExclusiveMode(pPager->pWal, -1) ){ sqlite3WalExclusiveMode(pPager->pWal, 0); rc2 = osUnlock(pPager->fd, SHARED_LOCK); } }else if( !pPager->exclusiveMode ){ rc2 = osUnlock(pPager->fd, SHARED_LOCK); pPager->state = PAGER_SHARED; pPager->changeCountDone = 0; }else if( pPager->state==PAGER_SYNCED ){ pPager->state = PAGER_EXCLUSIVE; } |
︙ | ︙ | |||
4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 | ** sub-journal is implemented in-memory if pPager is an in-memory database, ** or using a temporary file otherwise. */ int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){ int rc = SQLITE_OK; assert( pPager->state!=PAGER_UNLOCK ); pPager->subjInMemory = (u8)subjInMemory; if( pPager->state==PAGER_SHARED ){ assert( pPager->pInJournal==0 ); assert( !MEMDB && !pPager->tempFile ); if( pagerUseWal(pPager) ){ /* Grab the write lock on the log file. If successful, upgrade to | > > > > > > > > > > > > > | > > > > > > > > > | 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 4559 4560 4561 4562 4563 4564 4565 4566 4567 | ** sub-journal is implemented in-memory if pPager is an in-memory database, ** or using a temporary file otherwise. */ int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){ int rc = SQLITE_OK; assert( pPager->state!=PAGER_UNLOCK ); pPager->subjInMemory = (u8)subjInMemory; if( pPager->state==PAGER_SHARED ){ assert( pPager->pInJournal==0 ); assert( !MEMDB && !pPager->tempFile ); 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) ){ rc = sqlite3OsLock(pPager->fd, EXCLUSIVE_LOCK); pPager->state = PAGER_SHARED; if( rc!=SQLITE_OK ){ return rc; } sqlite3WalExclusiveMode(pPager->pWal, 1); } /* Grab the write lock on the log file. If successful, upgrade to ** PAGER_RESERVED state. Otherwise, return an error code to the caller. ** The busy-handler is not invoked if another connection already ** holds the write-lock. If possible, the upper layer will call it. ** ** WAL mode sets Pager.state to PAGER_RESERVED when it has an open ** transaction, but never to PAGER_EXCLUSIVE. This is because in ** PAGER_EXCLUSIVE state the code to roll back savepoint transactions ** may copy data from the sub-journal into the database file as well ** as into the page cache. Which would be incorrect in WAL mode. */ rc = sqlite3WalWriteLock(pPager->pWal, 1); if( rc==SQLITE_OK ){ pPager->dbOrigSize = pPager->dbSize; pPager->state = PAGER_RESERVED; pPager->journalOff = 0; } assert( rc!=SQLITE_OK || pPager->state==PAGER_RESERVED ); assert( rc==SQLITE_OK || pPager->state==PAGER_SHARED ); }else{ /* Obtain a RESERVED lock on the database file. If the exFlag parameter ** is true, then immediately upgrade this to an EXCLUSIVE lock. The ** busy-handler callback can be used when upgrading to the EXCLUSIVE ** lock, but not when obtaining the RESERVED lock. */ rc = sqlite3OsLock(pPager->fd, RESERVED_LOCK); |
︙ | ︙ |
Changes to src/wal.c.
︙ | ︙ | |||
128 129 130 131 132 133 134 135 136 137 138 139 140 141 | sqlite3_file *pFd; /* File handle for WAL file */ u32 iCallback; /* Value to pass to log callback (or 0) */ sqlite3_shm *pWIndex; /* The open wal-index file */ int szWIndex; /* Size of the wal-index that is mapped in mem */ u32 *pWiData; /* Pointer to wal-index content in memory */ u8 lockState; /* SQLITE_SHM_xxxx constant showing lock state */ u8 readerType; /* SQLITE_SHM_READ or SQLITE_SHM_READ_FULL */ WalIndexHdr hdr; /* Wal-index for current snapshot */ char *zName; /* Name of underlying storage */ }; /* ** This structure is used to implement an iterator that iterates through | > | 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | sqlite3_file *pFd; /* File handle for WAL file */ u32 iCallback; /* Value to pass to log callback (or 0) */ sqlite3_shm *pWIndex; /* The open wal-index file */ int szWIndex; /* Size of the wal-index that is mapped in mem */ u32 *pWiData; /* Pointer to wal-index content in memory */ u8 lockState; /* SQLITE_SHM_xxxx constant showing lock state */ u8 readerType; /* SQLITE_SHM_READ or SQLITE_SHM_READ_FULL */ u8 exclusiveMode; /* Non-zero if connection is in exclusive mode */ WalIndexHdr hdr; /* Wal-index for current snapshot */ char *zName; /* Name of underlying storage */ }; /* ** This structure is used to implement an iterator that iterates through |
︙ | ︙ | |||
213 214 215 216 217 218 219 | ** Attempt to change the lock status. ** ** When changing the lock status to SQLITE_SHM_READ, store the ** type of reader lock (either SQLITE_SHM_READ or SQLITE_SHM_READ_FULL) ** in pWal->readerType. */ static int walSetLock(Wal *pWal, int desiredStatus){ | | | > > | | | | | | > | 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 | ** Attempt to change the lock status. ** ** When changing the lock status to SQLITE_SHM_READ, store the ** type of reader lock (either SQLITE_SHM_READ or SQLITE_SHM_READ_FULL) ** in pWal->readerType. */ static int walSetLock(Wal *pWal, int desiredStatus){ int rc = SQLITE_OK; /* Return code */ if( pWal->exclusiveMode || pWal->lockState==desiredStatus ){ pWal->lockState = desiredStatus; }else{ int got = pWal->lockState; rc = pWal->pVfs->xShmLock(pWal->pVfs, pWal->pWIndex, desiredStatus, &got); pWal->lockState = got; if( got==SQLITE_SHM_READ_FULL || got==SQLITE_SHM_READ ){ pWal->readerType = got; pWal->lockState = SQLITE_SHM_READ; } } return rc; } /* ** Update the header of the wal-index file. */ |
︙ | ︙ | |||
1227 1228 1229 1230 1231 1232 1233 | rc = SQLITE_BUSY; } walIndexUnmap(pWal); if( rc!=SQLITE_OK ){ walSetLock(pWal, SQLITE_SHM_READ); } } | | > > | < | | | | | | | | | | > | 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 | rc = SQLITE_BUSY; } walIndexUnmap(pWal); if( rc!=SQLITE_OK ){ walSetLock(pWal, SQLITE_SHM_READ); } } }else if( pWal->lockState==SQLITE_SHM_WRITE ){ rc = walSetLock(pWal, SQLITE_SHM_READ); } return rc; } /* ** If any data has been written (but not committed) to the log file, this ** function moves the write-pointer back to the start of the transaction. ** ** Additionally, the callback function is invoked for each frame written ** to the log since the start of the transaction. If the callback returns ** other than SQLITE_OK, it is not invoked again and the error code is ** returned to the caller. ** ** Otherwise, if the callback function does not return an error, this ** function returns SQLITE_OK. */ int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx){ int rc = SQLITE_OK; if( pWal->lockState==SQLITE_SHM_WRITE ){ int unused; Pgno iMax = pWal->hdr.iLastPg; Pgno iFrame; assert( pWal->pWiData==0 ); rc = walIndexReadHdr(pWal, &unused); for(iFrame=pWal->hdr.iLastPg+1; rc==SQLITE_OK && iFrame<=iMax; iFrame++){ assert( pWal->lockState==SQLITE_SHM_WRITE ); rc = xUndo(pUndoCtx, pWal->pWiData[walIndexEntry(iFrame)]); } walIndexUnmap(pWal); } return rc; } /* Return an integer that records the current (uncommitted) write ** position in the WAL */ u32 sqlite3WalSavepoint(Wal *pWal){ |
︙ | ︙ | |||
1496 1497 1498 1499 1500 1501 1502 1503 | u32 ret = 0; if( pWal ){ ret = pWal->iCallback; pWal->iCallback = 0; } return (int)ret; } #endif /* #ifndef SQLITE_OMIT_WAL */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 | u32 ret = 0; if( pWal ){ ret = pWal->iCallback; pWal->iCallback = 0; } return (int)ret; } /* ** This function is called to set or query the exclusive-mode flag ** associated with the WAL connection passed as the first argument. The ** exclusive-mode flag should be set to indicate that the caller is ** holding an EXCLUSIVE lock on the database file (it does this in ** locking_mode=exclusive mode). If the EXCLUSIVE lock is to be dropped, ** the flag set by this function should be cleared before doing so. ** ** The value of the exclusive-mode flag may only be modified when ** the WAL connection is in READ state. ** ** When the flag is set, this module does not call the VFS xShmLock() ** method to obtain any locks on the wal-index (as it assumes it ** has exclusive access to the wal and wal-index files anyhow). It ** continues to hold (and does not drop) the existing READ lock on ** the wal-index. ** ** To set or clear the flag, the "op" parameter is passed 1 or 0, ** respectively. To query the flag, pass -1. In all cases, the value ** returned is the value of the exclusive-mode flag (after its value ** has been modified, if applicable). */ int sqlite3WalExclusiveMode(Wal *pWal, int op){ if( op>=0 ){ assert( pWal->lockState==SQLITE_SHM_READ ); pWal->exclusiveMode = (u8)op; } return pWal->exclusiveMode; } #endif /* #ifndef SQLITE_OMIT_WAL */ |
Changes to src/wal.h.
︙ | ︙ | |||
91 92 93 94 95 96 97 98 99 100 | /* Return the value to pass to a sqlite3_wal_hook callback, the ** number of frames in the WAL at the point of the last commit since ** sqlite3WalCallback() was called. If no commits have occurred since ** the last call, then return 0. */ int sqlite3WalCallback(Wal *pWal); #endif /* ifndef SQLITE_OMIT_WAL */ #endif /* _WAL_H_ */ | > > > > > | 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | /* Return the value to pass to a sqlite3_wal_hook callback, the ** number of frames in the WAL at the point of the last commit since ** sqlite3WalCallback() was called. If no commits have occurred since ** the last call, then return 0. */ int sqlite3WalCallback(Wal *pWal); /* Tell the wal layer that an EXCLUSIVE lock has been obtained (or released) ** by the pager layer on the database file. */ int sqlite3WalExclusiveMode(Wal *pWal, int op); #endif /* ifndef SQLITE_OMIT_WAL */ #endif /* _WAL_H_ */ |
Changes to test/wal2.test.
︙ | ︙ | |||
358 359 360 361 362 363 364 365 366 | } incr_tvfs_hdr $::shm_file 1 1 set ::locks [list] execsql { PRAGMA wal_checkpoint } set ::locks } {CHECKPOINT UNLOCK} db close | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 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 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 | } incr_tvfs_hdr $::shm_file 1 1 set ::locks [list] execsql { PRAGMA wal_checkpoint } set ::locks } {CHECKPOINT UNLOCK} db close tvfs delete #------------------------------------------------------------------------- # This block, test cases wal2-6.*, tests the operation of WAL with # "PRAGMA locking_mode=EXCLUSIVE" set. # # wal2-6.1.*: Changing to WAL mode before setting locking_mode=exclusive. # # wal2-6.2.*: Changing to WAL mode after setting locking_mode=exclusive. # # wal2-6.3.*: Changing back to rollback mode from WAL mode after setting # locking_mode=exclusive. # # wal2-6.4.*: Check that xShmLock calls are omitted in exclusive locking # mode. # do_test wal2-6.1.1 { file delete -force test.db test.db-wal test.db-journal sqlite3 db test.db execsql { Pragma Journal_Mode = Wal; Pragma Locking_Mode = Exclusive; } } {wal exclusive} do_test wal2-6.1.2 { execsql { PRAGMA lock_status } } {main unlocked temp closed} do_test wal2-6.1.3 { execsql { BEGIN; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); COMMIT; PRAGMA lock_status; } } {main exclusive temp closed} do_test wal2-6.1.4 { execsql { PRAGMA locking_mode = normal; PRAGMA lock_status; } } {normal main exclusive temp closed} do_test wal2-6.1.5 { execsql { SELECT * FROM t1; PRAGMA lock_status; } } {1 2 main exclusive temp closed} do_test wal2-6.1.6 { execsql { INSERT INTO t1 VALUES(3, 4); PRAGMA lock_status; } } {main shared temp closed} db close do_test wal2-6.2.1 { file delete -force test.db test.db-wal test.db-journal sqlite3 db test.db execsql { Pragma Locking_Mode = Exclusive; Pragma Journal_Mode = Wal; Pragma Lock_Status; } } {exclusive wal main exclusive temp closed} do_test wal2-6.2.2 { execsql { BEGIN; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); COMMIT; Pragma loCK_STATus; } } {main exclusive temp closed} do_test wal2-6.2.3 { db close sqlite3 db test.db execsql { PRAGMA LOCKING_MODE = EXCLUSIVE } } {exclusive} do_test wal2-6.2.4 { execsql { SELECT * FROM t1; pragma lock_status; } } {1 2 main shared temp closed} do_test wal2-6.2.5 { execsql { INSERT INTO t1 VALUES(3, 4); pragma lock_status; } } {main exclusive temp closed} do_test wal2-6.2.6 { execsql { PRAGMA locking_mode = NORMAL; pragma lock_status; } } {normal main exclusive temp closed} do_test wal2-6.2.7 { execsql { BEGIN IMMEDIATE; COMMIT; pragma lock_status; } } {main shared temp closed} do_test wal2-6.2.8 { execsql { PRAGMA locking_mode = EXCLUSIVE; BEGIN IMMEDIATE; COMMIT; PRAGMA locking_mode = NORMAL; } execsql { SELECT * FROM t1; pragma lock_status; } } {1 2 3 4 main exclusive temp closed} do_test wal2-6.2.9 { execsql { INSERT INTO t1 VALUES(5, 6); SELECT * FROM t1; pragma lock_status; } } {1 2 3 4 5 6 main shared temp closed} db close do_test wal2-6.3.1 { file delete -force test.db test.db-wal test.db-journal sqlite3 db test.db execsql { PRAGMA journal_mode = WAL; PRAGMA locking_mode = exclusive; BEGIN; CREATE TABLE t1(x); INSERT INTO t1 VALUES('Chico'); INSERT INTO t1 VALUES('Harpo'); COMMIT; } list [file exists test.db-wal] [file exists test.db-journal] } {1 0} do_test wal2-6.3.2 { execsql { PRAGMA journal_mode = DELETE } file exists test.db-wal } {0} do_test wal2-6.3.3 { execsql { PRAGMA lock_status } } {main exclusive temp closed} do_test wal2-6.3.4 { execsql { BEGIN; INSERT INTO t1 VALUES('Groucho'); } list [file exists test.db-wal] [file exists test.db-journal] } {0 1} do_test wal2-6.3.5 { execsql { PRAGMA lock_status } } {main exclusive temp closed} do_test wal2-6.3.6 { execsql { COMMIT } list [file exists test.db-wal] [file exists test.db-journal] } {0 1} do_test wal2-6.3.7 { execsql { PRAGMA lock_status } } {main exclusive temp closed} db close do_test wal2-6.4.1 { file delete -force test.db test.db-wal test.db-journal proc tvfs_cb {method args} { set ::shm_file [lindex $args 0] if {$method == "xShmLock"} { lappend ::locks [lindex $args 2] } return "SQLITE_OK" } testvfs tvfs tvfs_cb sqlite3 db test.db -vfs tvfs execsql { PRAGMA journal_mode = WAL; CREATE TABLE t1(x); INSERT INTO t1 VALUES('Leonard'); INSERT INTO t1 VALUES('Arthur'); } set ::locks [list] execsql { PRAGMA locking_mode = exclusive } set ::locks } {} do_test wal2-6.4.2 { execsql { SELECT * FROM t1 } } {Leonard Arthur} do_test wal2-6.4.3 { set ::locks } {READ} do_test wal2-6.4.4 { execsql { INSERT INTO t1 VALUES('Julius Henry'); SELECT * FROM t1; } } {Leonard Arthur {Julius Henry}} do_test wal2-6.4.5 { set ::locks } {READ} do_test wal2-6.4.6 { execsql { PRAGMA locking_mode = NORMAL; DELETE FROM t1; } set ::locks } {READ UNLOCK} do_test wal2-6.4.7 { set ::locks [list] execsql { INSERT INTO t1 VALUES('Karl') } set ::locks } {READ WRITE READ UNLOCK} db close finish_test |