Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Use the read and write version fields of the database header to mark a database as operating in wal-mode. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | wal |
Files: | files | file ages | folders |
SHA1: |
96bef18c1411c3e0348295886f105e16 |
User & Date: | dan 2010-04-20 18:53:15.000 |
Context
2010-04-21
| ||
06:19 | Minor changes to test cases to account for the fact that databases with read/write versions of 2 are now understood. (check-in: 278ed41e1d user: dan tags: wal) | |
2010-04-20
| ||
18:53 | Use the read and write version fields of the database header to mark a database as operating in wal-mode. (check-in: 96bef18c14 user: dan tags: wal) | |
2010-04-19
| ||
18:03 | Add the sqlite3_log_hook() interface for scheduling checkpoints. (check-in: 9bda601455 user: dan tags: wal) | |
Changes
Changes to src/btree.c.
︙ | ︙ | |||
2257 2258 2259 2260 2261 2262 2263 | int pageSize; int usableSize; u8 *page1 = pPage1->aData; rc = SQLITE_NOTADB; if( memcmp(page1, zMagicHeader, 16)!=0 ){ goto page1_init_failed; } | | | > > > > > > > > > > > > | 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 | int pageSize; int usableSize; u8 *page1 = pPage1->aData; rc = SQLITE_NOTADB; if( memcmp(page1, zMagicHeader, 16)!=0 ){ goto page1_init_failed; } if( page1[18]>2 ){ pBt->readOnly = 1; } if( page1[19]>2 ){ goto page1_init_failed; } /* If the write version is set to 2, turn on write-ahead logging mode. */ if( page1[19]==2 ){ int isOpen = 0; rc = sqlite3PagerOpenLog(pBt->pPager, &isOpen); if( rc!=SQLITE_OK ){ goto page1_init_failed; }else if( isOpen==0 ){ releasePage(pPage1); return SQLITE_OK; } } /* The maximum embedded fraction must be exactly 25%. And the minimum ** embedded fraction must be 12.5% for both leaf-data and non-leaf-data. ** The original design allowed these amounts to vary, but as of ** version 3.6.0, we require them to be fixed. */ if( memcmp(&page1[21], "\100\040\040",3)!=0 ){ |
︙ | ︙ | |||
7959 7960 7961 7962 7963 7964 7965 | assert( cursorHoldsMutex(pCur) ); assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); assert(!pCur->isIncrblobHandle); assert(!pCur->aOverflow); pCur->isIncrblobHandle = 1; } #endif | > > > > > > > > > > > > > > > > > > > > > > > | 7971 7972 7973 7974 7975 7976 7977 7978 7979 7980 7981 7982 7983 7984 7985 7986 7987 7988 7989 7990 7991 7992 7993 7994 7995 7996 7997 7998 7999 8000 | assert( cursorHoldsMutex(pCur) ); assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); assert(!pCur->isIncrblobHandle); assert(!pCur->aOverflow); pCur->isIncrblobHandle = 1; } #endif /* ** Set both the "read version" (single byte at byte offset 18) and ** "write version" (single byte at byte offset 19) fields in the database ** header to iVersion. */ int sqlite3BtreeSetVersion(Btree *pBtree, int iVersion){ BtShared *pBt = pBtree->pBt; int rc; /* Return code */ assert( pBtree->inTrans==TRANS_WRITE ); assert( pBt->pPage1 ); assert( iVersion==1 || iVersion==2 ); rc = sqlite3PagerWrite(pBt->pPage1->pDbPage); if( rc==SQLITE_OK ){ u8 *aData = pBt->pPage1->aData; aData[18] = (u8)iVersion; aData[19] = (u8)iVersion; } return rc; } |
Changes to src/btree.h.
︙ | ︙ | |||
181 182 183 184 185 186 187 188 189 190 191 192 193 194 | char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*); struct Pager *sqlite3BtreePager(Btree*); int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*); void sqlite3BtreeCacheOverflow(BtCursor *); void sqlite3BtreeClearCursor(BtCursor *); #ifndef NDEBUG int sqlite3BtreeCursorIsValid(BtCursor*); #endif #ifndef SQLITE_OMIT_BTREECOUNT int sqlite3BtreeCount(BtCursor *, i64 *); | > > | 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*); struct Pager *sqlite3BtreePager(Btree*); int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*); void sqlite3BtreeCacheOverflow(BtCursor *); void sqlite3BtreeClearCursor(BtCursor *); int sqlite3BtreeSetVersion(Btree *pBt, int iVersion); #ifndef NDEBUG int sqlite3BtreeCursorIsValid(BtCursor*); #endif #ifndef SQLITE_OMIT_BTREECOUNT int sqlite3BtreeCount(BtCursor *, i64 *); |
︙ | ︙ |
Changes to src/pager.c.
︙ | ︙ | |||
1401 1402 1403 1404 1405 1406 1407 | pager_error(pPager, rc); pPager->journalOff = 0; pPager->journalStarted = 0; }else{ /* This branch may be executed with Pager.journalMode==MEMORY if ** a hot-journal was just rolled back. In this case the journal ** file should be closed and deleted. If this connection writes to | | > > | 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 | pager_error(pPager, rc); pPager->journalOff = 0; pPager->journalStarted = 0; }else{ /* This branch may be executed with Pager.journalMode==MEMORY if ** a hot-journal was just rolled back. In this case the journal ** file should be closed and deleted. If this connection writes to ** the database file, it will do so using an in-memory journal. */ assert( pPager->journalMode==PAGER_JOURNALMODE_DELETE || pPager->journalMode==PAGER_JOURNALMODE_MEMORY || pPager->journalMode==PAGER_JOURNALMODE_WAL ); sqlite3OsClose(pPager->jfd); if( !pPager->tempFile ){ rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0); } } |
︙ | ︙ | |||
3720 3721 3722 3723 3724 3725 3726 | } } } return rc; } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 | } } } return rc; } /* ** This function is called to obtain a shared lock on the database file. ** It is illegal to call sqlite3PagerAcquire() until after this function ** has been successfully called. If a shared-lock is already held when ** this function is called, it is a no-op. ** |
︙ | ︙ | |||
3822 3823 3824 3825 3826 3827 3828 | if( isOpen(pPager->jfd) || pPager->zJournal ){ isErrorReset = 1; } pPager->errCode = SQLITE_OK; pager_reset(pPager); } | | < < < < < < < > > | 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 | if( isOpen(pPager->jfd) || pPager->zJournal ){ isErrorReset = 1; } pPager->errCode = SQLITE_OK; pager_reset(pPager); } if( pagerUseLog(pPager) ){ int changed = 0; /* True if the cache must be flushed */ /* Open a log snapshot to read from. */ rc = sqlite3LogOpenSnapshot(pPager->pLog, &changed); if( rc==SQLITE_OK ){ int dummy; if( changed ){ pager_reset(pPager); assert( pPager->errCode || pPager->dbSizeValid==0 ); } rc = sqlite3PagerPagecount(pPager, &dummy); } pPager->state = PAGER_SHARED; }else if( pPager->state==PAGER_UNLOCK || isErrorReset ){ sqlite3_vfs * const pVfs = pPager->pVfs; int isHotJournal = 0; assert( !MEMDB ); assert( sqlite3PcacheRefCount(pPager->pPCache)==0 ); if( pPager->noReadlock ){ assert( pPager->readOnly ); |
︙ | ︙ | |||
3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 | } if( memcmp(pPager->dbFileVers, dbFileVers, sizeof(dbFileVers))!=0 ){ pager_reset(pPager); } } assert( pPager->exclusiveMode || pPager->state==PAGER_SHARED ); } failed: if( rc!=SQLITE_OK ){ /* pager_unlock() is a no-op for exclusive mode and in-memory databases. */ pager_unlock(pPager); } | > > > > | 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 | } if( memcmp(pPager->dbFileVers, dbFileVers, sizeof(dbFileVers))!=0 ){ pager_reset(pPager); } } assert( pPager->exclusiveMode || pPager->state==PAGER_SHARED ); if( pPager->journalMode==PAGER_JOURNALMODE_WAL ){ pPager->journalMode = PAGER_JOURNALMODE_DELETE; } } failed: if( rc!=SQLITE_OK ){ /* pager_unlock() is a no-op for exclusive mode and in-memory databases. */ pager_unlock(pPager); } |
︙ | ︙ | |||
4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 | ** The busy-handler is not invoked if another connection already ** holds the write-lock. If possible, the upper layer will call it. */ rc = sqlite3LogWriteLock(pPager->pLog, 1); if( rc==SQLITE_OK ){ pPager->dbOrigSize = pPager->dbSize; pPager->state = PAGER_RESERVED; } }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. */ | > | 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 | ** The busy-handler is not invoked if another connection already ** holds the write-lock. If possible, the upper layer will call it. */ rc = sqlite3LogWriteLock(pPager->pLog, 1); if( rc==SQLITE_OK ){ pPager->dbOrigSize = pPager->dbSize; pPager->state = PAGER_RESERVED; pPager->journalOff = 0; } }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. */ |
︙ | ︙ | |||
4481 4482 4483 4484 4485 4486 4487 | */ rc = sqlite3PagerBegin(pPager, 0, pPager->subjInMemory); if( rc!=SQLITE_OK ){ return rc; } if( !isOpen(pPager->jfd) && pPager->journalMode!=PAGER_JOURNALMODE_OFF | | | 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 | */ rc = sqlite3PagerBegin(pPager, 0, pPager->subjInMemory); if( rc!=SQLITE_OK ){ return rc; } if( !isOpen(pPager->jfd) && pPager->journalMode!=PAGER_JOURNALMODE_OFF && !pagerUseLog(pPager) ){ assert( pPager->useJournal ); rc = pager_open_journal(pPager); if( rc!=SQLITE_OK ) return rc; } pPager->dbModified = 1; |
︙ | ︙ | |||
5617 5618 5619 5620 5621 5622 5623 | ** PAGER_JOURNALMODE_TRUNCATE ** PAGER_JOURNALMODE_PERSIST ** PAGER_JOURNALMODE_OFF ** PAGER_JOURNALMODE_MEMORY ** PAGER_JOURNALMODE_WAL ** ** If the parameter is not _QUERY, then the journal_mode is set to the | | > > > > > | < < < < < < < | 5571 5572 5573 5574 5575 5576 5577 5578 5579 5580 5581 5582 5583 5584 5585 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609 5610 5611 5612 5613 5614 5615 5616 5617 5618 5619 5620 5621 5622 5623 5624 5625 5626 5627 | ** PAGER_JOURNALMODE_TRUNCATE ** PAGER_JOURNALMODE_PERSIST ** PAGER_JOURNALMODE_OFF ** PAGER_JOURNALMODE_MEMORY ** PAGER_JOURNALMODE_WAL ** ** If the parameter is not _QUERY, then the journal_mode is set to the ** value specified if the change is allowed. The change may be disallowed ** for the following reasons: ** ** * An in-memory database can only have its journal_mode set to _OFF ** or _MEMORY. ** ** * The journal mode may not be changed while a transaction is active. ** ** The returned indicate the current (possibly updated) journal-mode. */ int sqlite3PagerJournalMode(Pager *pPager, int eMode){ assert( eMode==PAGER_JOURNALMODE_QUERY || eMode==PAGER_JOURNALMODE_DELETE || eMode==PAGER_JOURNALMODE_TRUNCATE || eMode==PAGER_JOURNALMODE_PERSIST || eMode==PAGER_JOURNALMODE_OFF || eMode==PAGER_JOURNALMODE_WAL || eMode==PAGER_JOURNALMODE_MEMORY ); assert( PAGER_JOURNALMODE_QUERY<0 ); if( eMode==PAGER_JOURNALMODE_WAL && pPager->journalMode==PAGER_JOURNALMODE_DELETE ){ pPager->journalMode = PAGER_JOURNALMODE_WAL; }else if( eMode>=0 && (pPager->tempFile==0 || eMode!=PAGER_JOURNALMODE_WAL) && (!MEMDB || eMode==PAGER_JOURNALMODE_MEMORY||eMode==PAGER_JOURNALMODE_OFF) && !pPager->dbModified && (!isOpen(pPager->jfd) || 0==pPager->journalOff) ){ if( isOpen(pPager->jfd) ){ sqlite3OsClose(pPager->jfd); } assert( (PAGER_JOURNALMODE_TRUNCATE & 1)==1 ); assert( (PAGER_JOURNALMODE_PERSIST & 1)==1 ); assert( (PAGER_JOURNALMODE_DELETE & 1)==0 ); assert( (PAGER_JOURNALMODE_MEMORY & 1)==0 ); assert( (PAGER_JOURNALMODE_OFF & 1)==0 ); if( (pPager->journalMode & 1)==1 && (eMode & 1)==0 && !pPager->exclusiveMode ){ sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0); } pPager->journalMode = (u8)eMode; } return (int)pPager->journalMode; } /* ** Get/set the size-limit used for persistent journal files. |
︙ | ︙ | |||
5708 5709 5710 5711 5712 5713 5714 5715 5716 | } return rc; } int sqlite3PagerLogCallback(Pager *pPager){ return sqlite3LogCallback(pPager->pLog); } #endif /* SQLITE_OMIT_DISKIO */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 5660 5661 5662 5663 5664 5665 5666 5667 5668 5669 5670 5671 5672 5673 5674 5675 5676 5677 5678 5679 5680 5681 5682 5683 5684 5685 5686 5687 5688 5689 5690 5691 5692 5693 5694 5695 5696 5697 5698 5699 5700 5701 5702 5703 5704 5705 5706 5707 5708 5709 5710 5711 5712 5713 5714 5715 5716 5717 5718 5719 5720 5721 5722 5723 5724 5725 5726 5727 5728 5729 5730 5731 5732 5733 5734 5735 5736 5737 5738 5739 5740 5741 | } return rc; } int sqlite3PagerLogCallback(Pager *pPager){ return sqlite3LogCallback(pPager->pLog); } /* ** Open a connection to the write-ahead log file for pager pPager. If ** the log connection is already open, this function is a no-op. ** ** The caller must be holding a SHARED lock on the database file to call ** this function. */ int sqlite3PagerOpenLog(Pager *pPager, int *pisOpen){ int rc = SQLITE_OK; /* Return code */ #ifdef SQLITE_DEBUG int locktype; sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_LOCKSTATE, &locktype); assert( locktype==SQLITE_LOCK_SHARED ); assert( pPager->state>=PAGER_SHARED ); #endif if( !pPager->pLog ){ /* Open the connection to the log file. If this operation fails, ** (e.g. due to malloc() failure), unlock the database file and ** return an error code. */ rc = sqlite3LogOpen(pPager->pVfs, pPager->zFilename, &pPager->pLog); if( rc==SQLITE_OK ){ pPager->journalMode = PAGER_JOURNALMODE_WAL; } }else{ *pisOpen = 1; } return rc; } /* ** This function is called to close the connection to the log file prior ** to switching from WAL to rollback mode. ** ** Before closing the log file, this function attempts to take an ** EXCLUSIVE lock on the database file. If this cannot be obtained, an ** error (SQLITE_BUSY) is returned and the log connection is not closed. ** If successful, the EXCLUSIVE lock is not released before returning. */ int sqlite3PagerCloseLog(Pager *pPager){ int rc = SQLITE_OK; if( pPager->pLog ){ /* Try to obtain an EXCLUSIVE lock on the database file. */ rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK); /* If the EXCLUSIVE lock was obtained, checkpoint and close the log. */ if( rc==SQLITE_OK ){ rc = sqlite3LogClose(pPager->pLog, pPager->fd, (pPager->noSync ? 0 : pPager->sync_flags), (u8*)pPager->pTmpSpace ); pPager->pLog = 0; } /* Make sure the EXCLUSIVE lock has not been lost somehow */ #ifdef SQLITE_DEBUG { int locktype; sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_LOCKSTATE, &locktype); assert( locktype==SQLITE_LOCK_EXCLUSIVE ); } #endif } return rc; } #endif /* SQLITE_OMIT_DISKIO */ |
Changes to src/pager.h.
︙ | ︙ | |||
132 133 134 135 136 137 138 139 140 141 142 143 144 145 | int sqlite3PagerRollback(Pager*); int sqlite3PagerOpenSavepoint(Pager *pPager, int n); int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint); int sqlite3PagerSharedLock(Pager *pPager); int sqlite3PagerCheckpoint(Pager *pPager); int sqlite3PagerLogCallback(Pager *pPager); /* Functions used to query pager state and configuration. */ u8 sqlite3PagerIsreadonly(Pager*); int sqlite3PagerRefcount(Pager*); int sqlite3PagerMemUsed(Pager*); const char *sqlite3PagerFilename(Pager*); const sqlite3_vfs *sqlite3PagerVfs(Pager*); | > > | 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | int sqlite3PagerRollback(Pager*); int sqlite3PagerOpenSavepoint(Pager *pPager, int n); int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint); int sqlite3PagerSharedLock(Pager *pPager); int sqlite3PagerCheckpoint(Pager *pPager); int sqlite3PagerLogCallback(Pager *pPager); int sqlite3PagerOpenLog(Pager *pPager, int *pisOpen); int sqlite3PagerCloseLog(Pager *pPager); /* Functions used to query pager state and configuration. */ u8 sqlite3PagerIsreadonly(Pager*); int sqlite3PagerRefcount(Pager*); int sqlite3PagerMemUsed(Pager*); const char *sqlite3PagerFilename(Pager*); const sqlite3_vfs *sqlite3PagerVfs(Pager*); |
︙ | ︙ |
Changes to src/pragma.c.
︙ | ︙ | |||
253 254 255 256 257 258 259 260 261 262 263 264 265 266 | default: zName = "NO ACTION"; assert( action==OE_None ); break; } return zName; } #endif /* ** Process a pragma statement. ** ** Pragmas are of this form: ** ** PRAGMA [database.]id [= value] ** | > > > > > > > > > > > > > > > > > > > > > > | 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 | default: zName = "NO ACTION"; assert( action==OE_None ); break; } return zName; } #endif /* ** Parameter eMode must be one of the PAGER_JOURNALMODE_XXX constants ** defined in pager.h. This function returns the associated lowercase ** journal-mode name. */ const char *sqlite3JournalModename(int eMode){ static char * const azModeName[] = { "delete", "persist", "off", "truncate", "memory", "wal" }; assert( PAGER_JOURNALMODE_DELETE==0 ); assert( PAGER_JOURNALMODE_PERSIST==1 ); assert( PAGER_JOURNALMODE_OFF==2 ); assert( PAGER_JOURNALMODE_TRUNCATE==3 ); assert( PAGER_JOURNALMODE_MEMORY==4 ); assert( PAGER_JOURNALMODE_WAL==5 ); assert( eMode>=0 && eMode<=ArraySize(azModeName) ); if( eMode==ArraySize(azModeName) ) return 0; return azModeName[eMode]; } /* ** Process a pragma statement. ** ** Pragmas are of this form: ** ** PRAGMA [database.]id [= value] ** |
︙ | ︙ | |||
509 510 511 512 513 514 515 | }else /* ** PRAGMA [database.]journal_mode ** PRAGMA [database.]journal_mode = (delete|persist|off|truncate|memory) */ if( sqlite3StrICmp(zLeft,"journal_mode")==0 ){ | | | | < > > < > | > | > > > > | | | > > > | < | | | | | | < | < < | < < < < < < < < < < | 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 581 582 583 584 585 586 587 588 589 590 591 592 593 | }else /* ** PRAGMA [database.]journal_mode ** PRAGMA [database.]journal_mode = (delete|persist|off|truncate|memory) */ if( sqlite3StrICmp(zLeft,"journal_mode")==0 ){ int eMode; /* One of the PAGER_JOURNALMODE_XXX symbols */ sqlite3VdbeSetNumCols(v, 1); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "journal_mode", SQLITE_STATIC); if( zRight==0 ){ eMode = PAGER_JOURNALMODE_QUERY; }else{ const char *zMode; int n = sqlite3Strlen30(zRight); for(eMode=0; (zMode = sqlite3JournalModename(eMode)); eMode++){ if( sqlite3StrNICmp(zRight, zMode, n)==0 ) break; } if( !zMode ){ eMode = PAGER_JOURNALMODE_QUERY; } } if( pId2->n==0 && eMode==PAGER_JOURNALMODE_QUERY ){ /* Simple "PRAGMA journal_mode;" statement. This is a query for ** the current default journal mode (which may be different to ** the journal-mode of the main database). */ eMode = db->dfltJournalMode; sqlite3VdbeAddOp2(v, OP_String8, 0, 1); sqlite3VdbeChangeP4(v, -1, sqlite3JournalModename(eMode), P4_STATIC); }else{ int ii; if( pId2->n==0 && eMode!=PAGER_JOURNALMODE_WAL ){ /* This indicates that no database name was specified as part ** of the PRAGMA command. In this case the journal-mode must be ** set on all attached databases, as well as the main db file. ** ** Also, the sqlite3.dfltJournalMode variable is set so that ** any subsequently attached databases also use the specified ** journal mode. Except, the default journal mode is never set ** to WAL. */ db->dfltJournalMode = (u8)eMode; } for(ii=db->nDb-1; ii>=0; ii--){ if( db->aDb[ii].pBt && (ii==iDb || pId2->n==0) ){ sqlite3VdbeUsesBtree(v, ii); sqlite3VdbeAddOp3(v, OP_JournalMode, ii, 1, eMode); } } } sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); }else /* ** PRAGMA [database.]journal_size_limit ** PRAGMA [database.]journal_size_limit=N ** |
︙ | ︙ |
Changes to src/vdbe.c.
︙ | ︙ | |||
5183 5184 5185 5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 | if( sqlite3VdbeMemTooBig(pMem) ){ goto too_big; } break; } /* Opcode: Checkpoint P1 * * * * */ case OP_Checkpoint: { Btree *pBt; /* Btree to checkpoint */ assert( pOp->p1>=0 && pOp->p1<db->nDb ); assert( (p->btreeMask & (1<<pOp->p1))!=0 ); pBt = db->aDb[pOp->p1].pBt; rc = sqlite3PagerCheckpoint(sqlite3BtreePager(pBt)); break; }; #if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH) /* Opcode: Vacuum * * * * * ** ** Vacuum the entire database. This opcode will cause other virtual ** machines to be created and run. It may not be called from within ** a transaction. | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 5183 5184 5185 5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233 5234 5235 5236 5237 5238 5239 5240 5241 5242 5243 5244 5245 5246 5247 5248 5249 5250 5251 5252 5253 5254 5255 5256 5257 5258 5259 5260 5261 5262 5263 5264 5265 5266 5267 5268 5269 5270 5271 5272 5273 5274 5275 5276 5277 5278 5279 5280 5281 5282 5283 5284 5285 5286 5287 | if( sqlite3VdbeMemTooBig(pMem) ){ goto too_big; } break; } /* Opcode: Checkpoint P1 * * * * ** ** Checkpoint database P1. This is a no-op if P1 is not currently in ** WAL mode. */ case OP_Checkpoint: { Btree *pBt; /* Btree to checkpoint */ assert( pOp->p1>=0 && pOp->p1<db->nDb ); assert( (p->btreeMask & (1<<pOp->p1))!=0 ); pBt = db->aDb[pOp->p1].pBt; rc = sqlite3PagerCheckpoint(sqlite3BtreePager(pBt)); break; }; /* Opcode: JournalMode P1 P2 P3 * * ** ** Change the journal mode of database P1 to P3. P3 must be one of the ** PAGER_JOURNALMODE_XXX values. If changing between the various rollback ** modes (delete, truncate, persist, off and memory), this is a simple ** operation. No IO is required. ** ** If changing into or out of WAL mode the procedure is more complicated. ** ** Write a string containing the final journal-mode to register P2. */ case OP_JournalMode: { Btree *pBt; /* Btree to change journal mode of */ Pager *pPager; /* Pager associated with pBt */ int eNew = pOp->p3; /* New journal mode */ assert( eNew==PAGER_JOURNALMODE_DELETE || eNew==PAGER_JOURNALMODE_TRUNCATE || eNew==PAGER_JOURNALMODE_PERSIST || eNew==PAGER_JOURNALMODE_OFF || eNew==PAGER_JOURNALMODE_MEMORY || eNew==PAGER_JOURNALMODE_WAL || eNew==PAGER_JOURNALMODE_QUERY ); assert( pOp->p1>=0 && pOp->p1<db->nDb ); assert( (p->btreeMask & (1<<pOp->p1))!=0 ); pBt = db->aDb[pOp->p1].pBt; pPager = sqlite3BtreePager(pBt); if( eNew!=PAGER_JOURNALMODE_QUERY ){ int eOld = sqlite3PagerJournalMode(pPager, PAGER_JOURNALMODE_QUERY); if( (eNew!=eOld) && (eOld==PAGER_JOURNALMODE_WAL || eNew==PAGER_JOURNALMODE_WAL) ){ if( !db->autoCommit || db->activeVdbeCnt>1 ){ rc = SQLITE_ERROR; sqlite3SetString(&p->zErrMsg, db, "cannot change %s wal mode from within a transaction", (eNew==PAGER_JOURNALMODE_WAL ? "into" : "out of") ); }else{ /* If leaving WAL mode, close the log file. If successful, the call to ** PagerCloseLog() checkpoints and deletes the write-ahead-log file. ** An EXCLUSIVE lock is still held on the database file after returning. */ if( eOld==PAGER_JOURNALMODE_WAL ){ rc = sqlite3PagerCloseLog(pPager); if( rc!=SQLITE_OK ) goto abort_due_to_error; sqlite3PagerJournalMode(pPager, eNew); }else{ sqlite3PagerJournalMode(pPager, PAGER_JOURNALMODE_DELETE); } /* Open a transaction on the database file. Regardless of the journal ** mode, this transaction always uses a rollback journal. */ assert( sqlite3BtreeIsInTrans(pBt)==0 ); rc = sqlite3BtreeBeginTrans(pBt, 2); assert( rc==SQLITE_OK || eOld!=PAGER_JOURNALMODE_WAL ); if( rc!=SQLITE_OK ) goto abort_due_to_error; rc = sqlite3BtreeSetVersion(pBt, (eNew==PAGER_JOURNALMODE_WAL ? 2 : 1)); if( rc!=SQLITE_OK ) goto abort_due_to_error; } } } eNew = sqlite3PagerJournalMode(pPager, eNew); pOut = &aMem[pOp->p2]; pOut->flags = MEM_Str|MEM_Static|MEM_Term; pOut->z = sqlite3JournalModename(eNew); pOut->n = sqlite3Strlen30(pOut->z); pOut->enc = SQLITE_UTF8; sqlite3VdbeChangeEncoding(pOut, encoding); break; }; #if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH) /* Opcode: Vacuum * * * * * ** ** Vacuum the entire database. This opcode will cause other virtual ** machines to be created and run. It may not be called from within ** a transaction. |
︙ | ︙ |
Changes to test/wal.test.
︙ | ︙ | |||
27 28 29 30 31 32 33 34 35 36 37 38 39 40 | proc blob {nByte} { incr ::blobcnt return [string range [string repeat "${::blobcnt}x" $nByte] 1 $nByte] } proc sqlite3_wal {args} { eval sqlite3 $args [lindex $args 0] eval { PRAGMA journal_mode = wal } [lindex $args 0] eval { PRAGMA synchronous = normal } [lindex $args 0] function blob blob } proc log_file_size {nFrame pgsz} { expr {12 + ($pgsz+16)*$nFrame} | > | 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | proc blob {nByte} { incr ::blobcnt return [string range [string repeat "${::blobcnt}x" $nByte] 1 $nByte] } proc sqlite3_wal {args} { eval sqlite3 $args [lindex $args 0] eval { PRAGMA page_size = 1024 } [lindex $args 0] eval { PRAGMA journal_mode = wal } [lindex $args 0] eval { PRAGMA synchronous = normal } [lindex $args 0] function blob blob } proc log_file_size {nFrame pgsz} { expr {12 + ($pgsz+16)*$nFrame} |
︙ | ︙ | |||
56 57 58 59 60 61 62 63 64 65 66 67 68 | # wal-6.*: Test creating databases with different page sizes. # do_test wal-0.1 { execsql { PRAGMA synchronous = normal } execsql { PRAGMA journal_mode = wal } } {wal} do_test wal-1.0 { execsql { BEGIN; CREATE TABLE t1(a, b); } | > > > | > > | | 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 | # wal-6.*: Test creating databases with different page sizes. # do_test wal-0.1 { execsql { PRAGMA synchronous = normal } execsql { PRAGMA journal_mode = wal } } {wal} do_test wal-0.2 { file size test.db } {1024} do_test wal-1.0 { execsql { BEGIN; CREATE TABLE t1(a, b); } list [file exists test.db-journal] \ [file exists test.db-wal] \ [file size test.db] } {0 1 1024} do_test wal-1.1 { execsql COMMIT list [file exists test.db-journal] [file exists test.db-wal] } {0 1} do_test wal-1.2 { # There are now two pages in the log. file size test.db-wal |
︙ | ︙ | |||
193 194 195 196 197 198 199 | db close foreach sector {512 4096} { sqlite3_simulate_device -sectorsize $sector foreach pgsz {512 1024 2048 4096} { file delete -force test.db test.db-wal do_test wal-6.$sector.$pgsz.1 { | | | > | 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 | db close foreach sector {512 4096} { sqlite3_simulate_device -sectorsize $sector foreach pgsz {512 1024 2048 4096} { file delete -force test.db test.db-wal do_test wal-6.$sector.$pgsz.1 { sqlite3 db test.db -vfs devsym execsql " PRAGMA page_size = $pgsz; PRAGMA journal_mode = wal; " execsql " CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); " db close file size test.db |
︙ | ︙ | |||
220 221 222 223 224 225 226 | sqlite3_wal db test.db execsql { PRAGMA page_size = 1024; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); } list [file size test.db] [file size test.db-wal] | | > > > > > > | | 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 257 258 259 260 261 262 | sqlite3_wal db test.db execsql { PRAGMA page_size = 1024; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); } list [file size test.db] [file size test.db-wal] } [list 1024 [log_file_size 3 1024]] do_test wal-7.2 { execsql { PRAGMA checkpoint } list [file size test.db] [file size test.db-wal] } [list 2048 [log_file_size 3 1024]] # Execute some transactions in auto-vacuum mode to test database file # truncation. # do_test wal-8.1 { reopen_db catch { db close } file delete -force test.db test.db-wal sqlite3 db test.db db function blob blob execsql { PRAGMA auto_vacuum = 1; PRAGMA journal_mode = wal; PRAGMA auto_vacuum; } } {wal 1} do_test wal-8.2 { execsql { PRAGMA page_size = 1024; CREATE TABLE t1(x); INSERT INTO t1 VALUES(blob(900)); INSERT INTO t1 VALUES(blob(900)); INSERT INTO t1 SELECT blob(900) FROM t1; /* 4 */ |
︙ | ︙ | |||
265 266 267 268 269 270 271 | # Run some "warm-body" tests to ensure that log-summary files with more # than 256 entries (log summaries that contain index blocks) work Ok. # do_test wal-9.1 { reopen_db execsql { | < | | 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 | # Run some "warm-body" tests to ensure that log-summary files with more # than 256 entries (log summaries that contain index blocks) work Ok. # do_test wal-9.1 { reopen_db execsql { CREATE TABLE t1(x PRIMARY KEY); INSERT INTO t1 VALUES(blob(900)); INSERT INTO t1 VALUES(blob(900)); INSERT INTO t1 SELECT blob(900) FROM t1; /* 4 */ INSERT INTO t1 SELECT blob(900) FROM t1; /* 8 */ INSERT INTO t1 SELECT blob(900) FROM t1; /* 16 */ INSERT INTO t1 SELECT blob(900) FROM t1; /* 32 */ INSERT INTO t1 SELECT blob(900) FROM t1; /* 64 */ INSERT INTO t1 SELECT blob(900) FROM t1; /* 128 */ INSERT INTO t1 SELECT blob(900) FROM t1; /* 256 */ } file size test.db } 1024 do_test wal-9.2 { sqlite3_wal db2 test.db execsql {PRAGMA integrity_check } db2 } {ok} do_test wal-9.3 { file delete -force test2.db test2.db-wal |
︙ | ︙ | |||
556 557 558 559 560 561 562 | reopen_db execsql { PRAGMA cache_size = 10; PRAGMA page_size = 1024; CREATE TABLE t1(x PRIMARY KEY); } list [expr [file size test.db]/1024] [expr [file size test.db-wal]/1044] | | | 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 | reopen_db execsql { PRAGMA cache_size = 10; PRAGMA page_size = 1024; CREATE TABLE t1(x PRIMARY KEY); } list [expr [file size test.db]/1024] [expr [file size test.db-wal]/1044] } {1 3} do_test wal-11.2 { execsql { PRAGMA checkpoint } list [expr [file size test.db]/1024] [file size test.db-wal] } [list 3 [log_file_size 3 1024]] do_test wal-11.3 { execsql { INSERT INTO t1 VALUES( blob(900) ) } list [expr [file size test.db]/1024] [file size test.db-wal] |
︙ | ︙ | |||
609 610 611 612 613 614 615 | execsql { PRAGMA cache_size = 10; BEGIN; INSERT INTO t1 SELECT blob(900) FROM t1; -- 32 SELECT count(*) FROM t1; } list [expr [file size test.db]/1024] [file size test.db-wal] | | | | | | > < | 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 | execsql { PRAGMA cache_size = 10; BEGIN; INSERT INTO t1 SELECT blob(900) FROM t1; -- 32 SELECT count(*) FROM t1; } list [expr [file size test.db]/1024] [file size test.db-wal] } [list 37 [log_file_size 37 1024]] do_test wal-11.11 { execsql { SELECT count(*) FROM t1; ROLLBACK; SELECT count(*) FROM t1; } } {32 16} do_test wal-11.12 { list [expr [file size test.db]/1024] [file size test.db-wal] } [list 37 [log_file_size 37 1024]] do_test wal-11.13 { execsql { INSERT INTO t1 VALUES( blob(900) ); SELECT count(*) FROM t1; PRAGMA integrity_check; } } {17 ok} do_test wal-11.14 { list [expr [file size test.db]/1024] [file size test.db-wal] } [list 37 [log_file_size 37 1024]] #------------------------------------------------------------------------- # This block of tests, wal-12.*, tests the fix for a problem that # could occur if a log that is a prefix of an older log is written # into a reused log file. # reopen_db do_test wal-12.1 { execsql { PRAGMA page_size = 1024; CREATE TABLE t1(x, y); CREATE TABLE t2(x, y); INSERT INTO t1 VALUES('A', 1); } list [expr [file size test.db]/1024] [file size test.db-wal] } [list 1 [log_file_size 5 1024]] do_test wal-12.2 { db close sqlite3 db test.db execsql { PRAGMA synchronous = normal; UPDATE t1 SET y = 0 WHERE x = 'A'; } list [expr [file size test.db]/1024] [expr [file size test.db-wal]/1044] } {3 1} do_test wal-12.3 { execsql { INSERT INTO t2 VALUES('B', 1) } list [expr [file size test.db]/1024] [expr [file size test.db-wal]/1044] } {3 2} do_test wal-12.4 { file copy -force test.db test2.db file copy -force test.db-wal test2.db-wal sqlite3_wal db2 test2.db execsql { SELECT * FROM t2 } db2 } {B 1} db2 close file copy -force test.db-wal A do_test wal-12.5 { execsql { |
︙ | ︙ | |||
690 691 692 693 694 695 696 | do_test wal-12.4 { file copy -force test.db test2.db file copy -force test.db-wal test2.db-wal sqlite3_wal db2 test2.db execsql { SELECT * FROM t2 } db2 } {B 2} db2 close | < | 702 703 704 705 706 707 708 709 710 711 | do_test wal-12.4 { file copy -force test.db test2.db file copy -force test.db-wal test2.db-wal sqlite3_wal db2 test2.db execsql { SELECT * FROM t2 } db2 } {B 2} db2 close finish_test |
Added test/walmode.test.
> > > > > > > > > > > > > > > > > > > > > > > > > | 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 | # 2010 April 19 # # 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 file is testing the operation of the library in # "PRAGMA journal_mode=WAL" mode. # set testdir [file dirname $argv0] source $testdir/tester.tcl do_test walmode-1.1 { execsql { PRAGMA journal_mode = wal; } } {wal} finish_test |