Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | If a database file with the WAL flag set is opened in exclusive-locking mode, use heap memory to store the wal-index instead of shared-memory. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
8dd5c69198619866923c6053b71899c1 |
User & Date: | dan 2010-11-01 17:38:25.000 |
Context
2010-11-01
| ||
18:45 | Add test cases to restore coverage of pager.c and wal.c. (check-in: 6cae552927 user: dan tags: trunk) | |
17:38 | If a database file with the WAL flag set is opened in exclusive-locking mode, use heap memory to store the wal-index instead of shared-memory. (check-in: 8dd5c69198 user: dan tags: trunk) | |
14:34 | Change the version number to 3.7.4. (check-in: db64843b54 user: drh tags: trunk) | |
Changes
Changes to src/pager.c.
︙ | ︙ | |||
1046 1047 1048 1049 1050 1051 1052 | ** Except, if Pager.eLock is set to UNKNOWN_LOCK when this function is ** called, do not modify it. See the comment above the #define of ** UNKNOWN_LOCK for an explanation of this. */ static int pagerUnlockDb(Pager *pPager, int eLock){ int rc = SQLITE_OK; | | | 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 | ** Except, if Pager.eLock is set to UNKNOWN_LOCK when this function is ** called, do not modify it. See the comment above the #define of ** UNKNOWN_LOCK for an explanation of this. */ static int pagerUnlockDb(Pager *pPager, int eLock){ int rc = SQLITE_OK; assert( !pPager->exclusiveMode || pPager->eLock==eLock ); assert( eLock==NO_LOCK || eLock==SHARED_LOCK ); assert( eLock!=NO_LOCK || pagerUseWal(pPager)==0 ); if( isOpen(pPager->fd) ){ assert( pPager->eLock>=eLock ); rc = sqlite3OsUnlock(pPager->fd, eLock); if( pPager->eLock!=UNKNOWN_LOCK ){ pPager->eLock = (u8)eLock; |
︙ | ︙ | |||
6346 6347 6348 6349 6350 6351 6352 | */ int sqlite3PagerLockingMode(Pager *pPager, int eMode){ assert( eMode==PAGER_LOCKINGMODE_QUERY || eMode==PAGER_LOCKINGMODE_NORMAL || eMode==PAGER_LOCKINGMODE_EXCLUSIVE ); assert( PAGER_LOCKINGMODE_QUERY<0 ); assert( PAGER_LOCKINGMODE_NORMAL>=0 && PAGER_LOCKINGMODE_EXCLUSIVE>=0 ); | > | | 6346 6347 6348 6349 6350 6351 6352 6353 6354 6355 6356 6357 6358 6359 6360 6361 | */ int sqlite3PagerLockingMode(Pager *pPager, int eMode){ assert( eMode==PAGER_LOCKINGMODE_QUERY || eMode==PAGER_LOCKINGMODE_NORMAL || eMode==PAGER_LOCKINGMODE_EXCLUSIVE ); assert( PAGER_LOCKINGMODE_QUERY<0 ); assert( PAGER_LOCKINGMODE_NORMAL>=0 && PAGER_LOCKINGMODE_EXCLUSIVE>=0 ); assert( pPager->exclusiveMode || 0==sqlite3WalHeapMemory(pPager->pWal) ); if( eMode>=0 && !pPager->tempFile && !sqlite3WalHeapMemory(pPager->pWal) ){ pPager->exclusiveMode = (u8)eMode; } return (int)pPager->exclusiveMode; } /* ** Set the journal-mode for this pager. Parameter eMode must be one of: |
︙ | ︙ | |||
6533 6534 6535 6536 6537 6538 6539 | /* ** Return true if the underlying VFS for the given pager supports the ** primitives necessary for write-ahead logging. */ int sqlite3PagerWalSupported(Pager *pPager){ const sqlite3_io_methods *pMethods = pPager->fd->pMethods; | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 6534 6535 6536 6537 6538 6539 6540 6541 6542 6543 6544 6545 6546 6547 6548 6549 6550 6551 6552 6553 6554 6555 6556 6557 6558 6559 6560 6561 6562 6563 6564 6565 6566 6567 6568 6569 6570 6571 6572 6573 6574 6575 6576 6577 6578 6579 6580 6581 6582 6583 6584 6585 6586 6587 6588 6589 6590 6591 6592 6593 6594 6595 6596 6597 6598 6599 6600 6601 | /* ** Return true if the underlying VFS for the given pager supports the ** primitives necessary for write-ahead logging. */ int sqlite3PagerWalSupported(Pager *pPager){ const sqlite3_io_methods *pMethods = pPager->fd->pMethods; return pPager->exclusiveMode || (pMethods->iVersion>=2 && pMethods->xShmMap); } /* ** Attempt to take an exclusive lock on the database file. If a PENDING lock ** is obtained instead, immediately release it. */ static int pagerExclusiveLock(Pager *pPager){ int rc; /* Return code */ assert( pPager->eLock==SHARED_LOCK || pPager->eLock==EXCLUSIVE_LOCK ); rc = pagerLockDb(pPager, EXCLUSIVE_LOCK); if( rc!=SQLITE_OK ){ /* If the attempt to grab the pending lock failed, release the ** exclusive lock that may have been obtained instead. */ pagerUnlockDb(pPager, SHARED_LOCK); } return rc; } /* ** Call sqlite3WalOpen() to open the WAL handle. If the pager is in ** exclusive-locking mode when this function is called, take an EXCLUSIVE ** lock on the database file and use heap-memory to store the wal-index ** in. Otherwise, use the normal shared-memory. */ static int pagerOpenWal(Pager *pPager){ int rc = SQLITE_OK; assert( pPager->pWal==0 && pPager->tempFile==0 ); assert( pPager->eLock==SHARED_LOCK || pPager->eLock==EXCLUSIVE_LOCK ); /* If the pager is already in exclusive-mode, the WAL module will use ** heap-memory for the wal-index instead of the VFS shared-memory ** implementation. Take the exclusive lock now, before opening the WAL ** file, to make sure this is safe. */ if( pPager->exclusiveMode ){ rc = pagerExclusiveLock(pPager); } /* Open the connection to the log file. If this operation fails, ** (e.g. due to malloc() failure), return an error code. */ if( rc==SQLITE_OK ){ rc = sqlite3WalOpen(pPager->pVfs, pPager->fd, pPager->zWal, pPager->exclusiveMode, &pPager->pWal ); } return rc; } /* ** The caller must be holding a SHARED lock on the database file to call ** this function. ** ** If the pager passed as the first argument is open on a real database ** file (not a temp file or an in-memory database), and the WAL file |
︙ | ︙ | |||
6569 6570 6571 6572 6573 6574 6575 | if( !pPager->tempFile && !pPager->pWal ){ if( !sqlite3PagerWalSupported(pPager) ) return SQLITE_CANTOPEN; /* Close any rollback journal previously open */ sqlite3OsClose(pPager->jfd); | < < < < | | 6622 6623 6624 6625 6626 6627 6628 6629 6630 6631 6632 6633 6634 6635 6636 | if( !pPager->tempFile && !pPager->pWal ){ if( !sqlite3PagerWalSupported(pPager) ) return SQLITE_CANTOPEN; /* Close any rollback journal previously open */ sqlite3OsClose(pPager->jfd); rc = pagerOpenWal(pPager); if( rc==SQLITE_OK ){ pPager->journalMode = PAGER_JOURNALMODE_WAL; pPager->eState = PAGER_OPEN; } }else{ *pbOpen = 1; } |
︙ | ︙ | |||
6612 6613 6614 6615 6616 6617 6618 | rc = pagerLockDb(pPager, SHARED_LOCK); if( rc==SQLITE_OK ){ rc = sqlite3OsAccess( pPager->pVfs, pPager->zWal, SQLITE_ACCESS_EXISTS, &logexists ); } if( rc==SQLITE_OK && logexists ){ | < | | | | < < < < | 6661 6662 6663 6664 6665 6666 6667 6668 6669 6670 6671 6672 6673 6674 6675 6676 6677 6678 6679 6680 6681 6682 6683 6684 6685 6686 6687 6688 6689 | rc = pagerLockDb(pPager, SHARED_LOCK); if( rc==SQLITE_OK ){ rc = sqlite3OsAccess( pPager->pVfs, pPager->zWal, SQLITE_ACCESS_EXISTS, &logexists ); } if( rc==SQLITE_OK && logexists ){ rc = pagerOpenWal(pPager); } } /* Checkpoint and close the log. Because an EXCLUSIVE lock is held on ** the database file, the log and log-summary files will be deleted. */ if( rc==SQLITE_OK && pPager->pWal ){ rc = pagerExclusiveLock(pPager); if( rc==SQLITE_OK ){ rc = sqlite3WalClose(pPager->pWal, (pPager->noSync ? 0 : pPager->sync_flags), pPager->pageSize, (u8*)pPager->pTmpSpace ); pPager->pWal = 0; } } return rc; } #ifdef SQLITE_HAS_CODEC /* |
︙ | ︙ |
Changes to src/test_vfs.c.
︙ | ︙ | |||
22 23 24 25 26 27 28 29 30 31 32 33 34 35 | ** ** Available options are: ** ** -noshm BOOLEAN (True to omit shm methods. Default false) ** -default BOOLEAN (True to make the vfs default. Default false) ** -szosfile INTEGER (Value for sqlite3_vfs.szOsFile) ** -mxpathname INTEGER (Value for sqlite3_vfs.mxPathname) */ #include "sqlite3.h" #include "sqliteInt.h" typedef struct Testvfs Testvfs; typedef struct TestvfsShm TestvfsShm; | > | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | ** ** Available options are: ** ** -noshm BOOLEAN (True to omit shm methods. Default false) ** -default BOOLEAN (True to make the vfs default. Default false) ** -szosfile INTEGER (Value for sqlite3_vfs.szOsFile) ** -mxpathname INTEGER (Value for sqlite3_vfs.mxPathname) ** -iversion INTEGER (Value for sqlite3_vfs.iVersion) */ #include "sqlite3.h" #include "sqliteInt.h" typedef struct Testvfs Testvfs; typedef struct TestvfsShm TestvfsShm; |
︙ | ︙ |
Changes to src/wal.c.
︙ | ︙ | |||
424 425 426 427 428 429 430 431 432 433 434 435 436 437 | const char *zWalName; /* Name of WAL file */ u32 nCkpt; /* Checkpoint sequence counter in the wal-header */ #ifdef SQLITE_DEBUG u8 lockError; /* True if a locking error has occurred */ #endif }; /* ** Each page of the wal-index mapping contains a hash-table made up of ** an array of HASHTABLE_NSLOT elements of the following type. */ typedef u16 ht_slot; /* | > > > > > > > | 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 | const char *zWalName; /* Name of WAL file */ u32 nCkpt; /* Checkpoint sequence counter in the wal-header */ #ifdef SQLITE_DEBUG u8 lockError; /* True if a locking error has occurred */ #endif }; /* ** Candidate values for Wal.exclusiveMode. */ #define WAL_NORMAL_MODE 0 #define WAL_EXCLUSIVE_MODE 1 #define WAL_HEAPMEMORY_MODE 2 /* ** Each page of the wal-index mapping contains a hash-table made up of ** an array of HASHTABLE_NSLOT elements of the following type. */ typedef u16 ht_slot; /* |
︙ | ︙ | |||
510 511 512 513 514 515 516 | sizeof(u32*)*(iPage+1-pWal->nWiData)); pWal->apWiData = apNew; pWal->nWiData = iPage+1; } /* Request a pointer to the required page from the VFS */ if( pWal->apWiData[iPage]==0 ){ | > > > > | | | > | 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 | sizeof(u32*)*(iPage+1-pWal->nWiData)); pWal->apWiData = apNew; pWal->nWiData = iPage+1; } /* Request a pointer to the required page from the VFS */ if( pWal->apWiData[iPage]==0 ){ if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ){ pWal->apWiData[iPage] = (u32 volatile *)sqlite3MallocZero(WALINDEX_PGSZ); if( !pWal->apWiData[iPage] ) rc = SQLITE_NOMEM; }else{ rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ, pWal->writeLock, (void volatile **)&pWal->apWiData[iPage] ); } } *ppPage = pWal->apWiData[iPage]; assert( iPage==0 || *ppPage || rc!=SQLITE_OK ); return rc; } |
︙ | ︙ | |||
594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 | aData += 2; }while( aData<aEnd ); } aOut[0] = s1; aOut[1] = s2; } /* ** Write the header information in pWal->hdr into the wal-index. ** ** The checksum on pWal->hdr is updated before it is written. */ static void walIndexWriteHdr(Wal *pWal){ volatile WalIndexHdr *aHdr = walIndexHdr(pWal); const int nCksum = offsetof(WalIndexHdr, aCksum); assert( pWal->writeLock ); pWal->hdr.isInit = 1; pWal->hdr.iVersion = WALINDEX_MAX_VERSION; walChecksumBytes(1, (u8*)&pWal->hdr, nCksum, 0, pWal->hdr.aCksum); memcpy((void *)&aHdr[1], (void *)&pWal->hdr, sizeof(WalIndexHdr)); | > > > > > > | | 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 | aData += 2; }while( aData<aEnd ); } aOut[0] = s1; aOut[1] = s2; } static void walShmBarrier(Wal *pWal){ if( pWal->exclusiveMode!=WAL_HEAPMEMORY_MODE ){ sqlite3OsShmBarrier(pWal->pDbFd); } } /* ** Write the header information in pWal->hdr into the wal-index. ** ** The checksum on pWal->hdr is updated before it is written. */ static void walIndexWriteHdr(Wal *pWal){ volatile WalIndexHdr *aHdr = walIndexHdr(pWal); const int nCksum = offsetof(WalIndexHdr, aCksum); assert( pWal->writeLock ); pWal->hdr.isInit = 1; pWal->hdr.iVersion = WALINDEX_MAX_VERSION; walChecksumBytes(1, (u8*)&pWal->hdr, nCksum, 0, pWal->hdr.aCksum); memcpy((void *)&aHdr[1], (void *)&pWal->hdr, sizeof(WalIndexHdr)); walShmBarrier(pWal); memcpy((void *)&aHdr[0], (void *)&pWal->hdr, sizeof(WalIndexHdr)); } /* ** This function encodes a single frame header and writes it to a buffer ** supplied by the caller. A frame-header is made up of a series of ** 4-byte big-endian integers, as follows: |
︙ | ︙ | |||
1181 1182 1183 1184 1185 1186 1187 | return rc; } /* ** Close an open wal-index. */ static void walIndexClose(Wal *pWal, int isDelete){ | > > > > > > > | > | 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 | return rc; } /* ** Close an open wal-index. */ static void walIndexClose(Wal *pWal, int isDelete){ if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ){ int i; for(i=0; i<pWal->nWiData; i++){ sqlite3_free((void *)pWal->apWiData[i]); pWal->apWiData[i] = 0; } }else{ sqlite3OsShmUnmap(pWal->pDbFd, isDelete); } } /* ** Open a connection to the WAL file zWalName. The database file must ** already be opened on connection pDbFd. The buffer that zWalName points ** to must remain valid for the lifetime of the returned Wal* handle. ** |
︙ | ︙ | |||
1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 | ** *ppWal is set to point to a new WAL handle. If an error occurs, ** an SQLite error code is returned and *ppWal is left unmodified. */ int sqlite3WalOpen( sqlite3_vfs *pVfs, /* vfs module to open wal and wal-index */ sqlite3_file *pDbFd, /* The open database file */ const char *zWalName, /* Name of the WAL file */ Wal **ppWal /* OUT: Allocated Wal handle */ ){ int rc; /* Return Code */ Wal *pRet; /* Object to allocate and return */ int flags; /* Flags passed to OsOpen() */ assert( zWalName && zWalName[0] ); | > | 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 | ** *ppWal is set to point to a new WAL handle. If an error occurs, ** an SQLite error code is returned and *ppWal is left unmodified. */ int sqlite3WalOpen( sqlite3_vfs *pVfs, /* vfs module to open wal and wal-index */ sqlite3_file *pDbFd, /* The open database file */ const char *zWalName, /* Name of the WAL file */ int bNoShm, /* True to run in heap-memory mode */ Wal **ppWal /* OUT: Allocated Wal handle */ ){ int rc; /* Return Code */ Wal *pRet; /* Object to allocate and return */ int flags; /* Flags passed to OsOpen() */ assert( zWalName && zWalName[0] ); |
︙ | ︙ | |||
1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 | } pRet->pVfs = pVfs; pRet->pWalFd = (sqlite3_file *)&pRet[1]; pRet->pDbFd = pDbFd; pRet->readLock = -1; pRet->zWalName = zWalName; /* Open file handle on the write-ahead log file. */ flags = (SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_WAL); rc = sqlite3OsOpen(pVfs, zWalName, pRet->pWalFd, flags, &flags); if( rc==SQLITE_OK && flags&SQLITE_OPEN_READONLY ){ pRet->readOnly = 1; } | > | 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 | } pRet->pVfs = pVfs; pRet->pWalFd = (sqlite3_file *)&pRet[1]; pRet->pDbFd = pDbFd; pRet->readLock = -1; pRet->zWalName = zWalName; pRet->exclusiveMode = (bNoShm ? WAL_HEAPMEMORY_MODE: WAL_NORMAL_MODE); /* Open file handle on the write-ahead log file. */ flags = (SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_WAL); rc = sqlite3OsOpen(pVfs, zWalName, pRet->pWalFd, flags, &flags); if( rc==SQLITE_OK && flags&SQLITE_OPEN_READONLY ){ pRet->readOnly = 1; } |
︙ | ︙ | |||
1669 1670 1671 1672 1673 1674 1675 | ** the database. In this case checkpoint the database and unlink both ** the wal and wal-index files. ** ** The EXCLUSIVE lock is not released before returning. */ rc = sqlite3OsLock(pWal->pDbFd, SQLITE_LOCK_EXCLUSIVE); if( rc==SQLITE_OK ){ | > | > | 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 | ** the database. In this case checkpoint the database and unlink both ** the wal and wal-index files. ** ** The EXCLUSIVE lock is not released before returning. */ rc = sqlite3OsLock(pWal->pDbFd, SQLITE_LOCK_EXCLUSIVE); if( rc==SQLITE_OK ){ if( pWal->exclusiveMode==WAL_NORMAL_MODE ){ pWal->exclusiveMode = WAL_EXCLUSIVE_MODE; } rc = sqlite3WalCheckpoint(pWal, sync_flags, nBuf, zBuf); if( rc==SQLITE_OK ){ isDelete = 1; } } walIndexClose(pWal, isDelete); |
︙ | ︙ | |||
1725 1726 1727 1728 1729 1730 1731 | ** There are two copies of the header at the beginning of the wal-index. ** When reading, read [0] first then [1]. Writes are in the reverse order. ** Memory barriers are used to prevent the compiler or the hardware from ** reordering the reads and writes. */ aHdr = walIndexHdr(pWal); memcpy(&h1, (void *)&aHdr[0], sizeof(h1)); | | | 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 | ** There are two copies of the header at the beginning of the wal-index. ** When reading, read [0] first then [1]. Writes are in the reverse order. ** Memory barriers are used to prevent the compiler or the hardware from ** reordering the reads and writes. */ aHdr = walIndexHdr(pWal); memcpy(&h1, (void *)&aHdr[0], sizeof(h1)); walShmBarrier(pWal); memcpy(&h2, (void *)&aHdr[1], sizeof(h2)); if( memcmp(&h1, &h2, sizeof(h1))!=0 ){ return 1; /* Dirty read */ } if( h1.isInit==0 ){ return 1; /* Malformed header - probably all zeros */ |
︙ | ︙ | |||
1926 1927 1928 1929 1930 1931 1932 | pInfo = walCkptInfo(pWal); if( !useWal && pInfo->nBackfill==pWal->hdr.mxFrame ){ /* The WAL has been completely backfilled (or it is empty). ** and can be safely ignored. */ rc = walLockShared(pWal, WAL_READ_LOCK(0)); | | | 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 | pInfo = walCkptInfo(pWal); if( !useWal && pInfo->nBackfill==pWal->hdr.mxFrame ){ /* The WAL has been completely backfilled (or it is empty). ** and can be safely ignored. */ rc = walLockShared(pWal, WAL_READ_LOCK(0)); walShmBarrier(pWal); if( rc==SQLITE_OK ){ if( memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) ){ /* It is not safe to allow the reader to continue here if frames ** may have been appended to the log before READ_LOCK(0) was obtained. ** When holding READ_LOCK(0), the reader ignores the entire log file, ** which implies that the database file contains a trustworthy ** snapshoT. Since holding READ_LOCK(0) prevents a checkpoint from |
︙ | ︙ | |||
2020 2021 2022 2023 2024 2025 2026 | ** ** This does not guarantee that the copy of the wal-index header is up to ** date before proceeding. That would not be possible without somehow ** blocking writers. It only guarantees that a dangerous checkpoint or ** log-wrap (either of which would require an exclusive lock on ** WAL_READ_LOCK(mxI)) has not occurred since the snapshot was valid. */ | | | 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 | ** ** This does not guarantee that the copy of the wal-index header is up to ** date before proceeding. That would not be possible without somehow ** blocking writers. It only guarantees that a dangerous checkpoint or ** log-wrap (either of which would require an exclusive lock on ** WAL_READ_LOCK(mxI)) has not occurred since the snapshot was valid. */ walShmBarrier(pWal); if( pInfo->aReadMark[mxI]!=mxReadMark || memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) ){ walUnlockShared(pWal, WAL_READ_LOCK(mxI)); return WAL_RETRY; }else{ assert( mxReadMark<=pWal->hdr.mxFrame ); |
︙ | ︙ | |||
2663 2664 2665 2666 2667 2668 2669 | ** locking_mode=EXCLUSIVE. This means that the pWal->readLock must ** be released. Return 1 if the transition is made and 0 if the ** WAL is already in exclusive-locking mode - meaning that this ** routine is a no-op. The pager must already hold the exclusive lock ** on the main database file before invoking this operation. ** ** If op is negative, then do a dry-run of the op==1 case but do | | > | 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 | ** locking_mode=EXCLUSIVE. This means that the pWal->readLock must ** be released. Return 1 if the transition is made and 0 if the ** WAL is already in exclusive-locking mode - meaning that this ** routine is a no-op. The pager must already hold the exclusive lock ** on the main database file before invoking this operation. ** ** If op is negative, then do a dry-run of the op==1 case but do ** not actually change anything. The pager uses this to see if it ** should acquire the database exclusive lock prior to invoking ** the op==1 case. */ int sqlite3WalExclusiveMode(Wal *pWal, int op){ int rc; assert( pWal->writeLock==0 ); assert( pWal->exclusiveMode!=WAL_HEAPMEMORY_MODE || op==-1 ); /* pWal->readLock is usually set, but might be -1 if there was a ** prior error while attempting to acquire are read-lock. This cannot ** happen if the connection is actually in exclusive mode (as no xShmLock ** locks are taken in this case). Nor should the pager attempt to ** upgrade to exclusive-mode following such an error. */ |
︙ | ︙ | |||
2702 2703 2704 2705 2706 2707 2708 2709 2710 | pWal->exclusiveMode = 1; rc = 1; }else{ rc = pWal->exclusiveMode==0; } return rc; } #endif /* #ifndef SQLITE_OMIT_WAL */ | > > > > > > > > > | 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 | pWal->exclusiveMode = 1; rc = 1; }else{ rc = pWal->exclusiveMode==0; } return rc; } /* ** Return true if the argument is non-NULL and the WAL module is using ** heap-memory for the wal-index. Otherwise, if the argument is NULL or the ** WAL module is using shared-memory, return false. */ int sqlite3WalHeapMemory(Wal *pWal){ return (pWal && pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ); } #endif /* #ifndef SQLITE_OMIT_WAL */ |
Changes to src/wal.h.
︙ | ︙ | |||
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | # define sqlite3WalUndo(x,y,z) 0 # define sqlite3WalSavepoint(y,z) # define sqlite3WalSavepointUndo(y,z) 0 # define sqlite3WalFrames(u,v,w,x,y,z) 0 # define sqlite3WalCheckpoint(u,v,w,x) 0 # define sqlite3WalCallback(z) 0 # define sqlite3WalExclusiveMode(y,z) 0 #else #define WAL_SAVEPOINT_NDATA 4 /* Connection to a write-ahead log (WAL) file. ** There is one object of this type for each pager. */ typedef struct Wal Wal; /* Open and close a connection to a write-ahead log. */ | > | | 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | # define sqlite3WalUndo(x,y,z) 0 # define sqlite3WalSavepoint(y,z) # define sqlite3WalSavepointUndo(y,z) 0 # define sqlite3WalFrames(u,v,w,x,y,z) 0 # define sqlite3WalCheckpoint(u,v,w,x) 0 # define sqlite3WalCallback(z) 0 # define sqlite3WalExclusiveMode(y,z) 0 # define sqlite3WalHeapMemory(z) 0 #else #define WAL_SAVEPOINT_NDATA 4 /* Connection to a write-ahead log (WAL) file. ** There is one object of this type for each pager. */ typedef struct Wal Wal; /* Open and close a connection to a write-ahead log. */ int sqlite3WalOpen(sqlite3_vfs*, sqlite3_file*, const char *zName, int, Wal**); int sqlite3WalClose(Wal *pWal, int sync_flags, int, u8 *); /* Used by readers to open (lock) and close (unlock) a snapshot. A ** snapshot is like a read-transaction. It is the state of the database ** at an instant in time. sqlite3WalOpenSnapshot gets a read lock and ** preserves the current state even if the other threads or processes ** write to or checkpoint the WAL. sqlite3WalCloseSnapshot() closes the |
︙ | ︙ | |||
97 98 99 100 101 102 103 104 105 106 | */ 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_ */ | > > > > > > | 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | */ 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); /* Return true if the argument is non-NULL and the WAL module is using ** heap-memory for the wal-index. Otherwise, if the argument is NULL or the ** WAL module is using shared-memory, return false. */ int sqlite3WalHeapMemory(Wal *pWal); #endif /* ifndef SQLITE_OMIT_WAL */ #endif /* _WAL_H_ */ |
Changes to test/pager1.test.
︙ | ︙ | |||
1103 1104 1105 1106 1107 1108 1109 | 3 { BEGIN; SELECT * FROM t1; COMMIT; } {1 2} 0 -1 | | | | | | 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 | 3 { BEGIN; SELECT * FROM t1; COMMIT; } {1 2} 0 -1 4 { PRAGMA journal_mode = WAL } wal -1 -1 5 { INSERT INTO t1 VALUES(3, 4) } {} -1 [wal_file_size 1 1024] 6 { PRAGMA locking_mode = NORMAL } exclusive -1 [wal_file_size 1 1024] 7 { INSERT INTO t1 VALUES(5, 6); } {} -1 [wal_file_size 2 1024] 8 { PRAGMA journal_mode = TRUNCATE } truncate 0 -1 9 { INSERT INTO t1 VALUES(7, 8) } {} 0 -1 10 { SELECT * FROM t1 } {1 2 3 4 5 6 7 8} 0 -1 }] { do_execsql_test pager1-7.1.$tn.1 $sql $res |
︙ | ︙ |
Changes to test/permutations.test.
︙ | ︙ | |||
179 180 181 182 183 184 185 | # coverage-wal # test_suite "coverage-wal" -description { Coverage tests for file wal.c. } -files { wal.test wal2.test wal3.test walmode.test walbak.test walhook.test walcrash2.test walcksum.test | | | 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 | # coverage-wal # test_suite "coverage-wal" -description { Coverage tests for file wal.c. } -files { wal.test wal2.test wal3.test walmode.test walbak.test walhook.test walcrash2.test walcksum.test walfault.test walbig.test walnoshm.test } test_suite "coverage-pager" -description { Coverage tests for file pager.c. } -files { pager1.test pager2.test pagerfault.test pagerfault2.test walfault.test walbak.test journal2.test tkt-9d68c883.test |
︙ | ︙ |
Changes to test/tester.tcl.
︙ | ︙ | |||
298 299 300 301 302 303 304 305 306 307 308 309 310 311 | # Invoke the do_test procedure to run a single test # proc do_test {name cmd expected} { global argv cmdlinearg sqlite3_memdebug_settitle $name # if {[llength $argv]==0} { # set go 1 # } else { # set go 0 | > > | 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 | # Invoke the do_test procedure to run a single test # proc do_test {name cmd expected} { global argv cmdlinearg fix_testname name sqlite3_memdebug_settitle $name # if {[llength $argv]==0} { # set go 1 # } else { # set go 0 |
︙ | ︙ |
Changes to test/wal2.test.
︙ | ︙ | |||
426 427 428 429 430 431 432 | # connection silently remains in exclusive 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; | < | > > > > | 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 | # connection silently remains in exclusive 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; } } {wal} do_test wal2-6.1.2 { execsql { PRAGMA lock_status } } {main unlocked temp closed} do_test wal2-6.1.3 { execsql { SELECT * FROM sqlite_master; Pragma Locking_Mode = Exclusive; } execsql { BEGIN; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); COMMIT; PRAGMA lock_status; } |
︙ | ︙ | |||
482 483 484 485 486 487 488 489 490 491 492 493 494 495 | 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; } | > | 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 | COMMIT; Pragma loCK_STATus; } } {main exclusive temp closed} do_test wal2-6.2.3 { db close sqlite3 db test.db execsql { SELECT * FROM sqlite_master } execsql { PRAGMA LOCKING_MODE = EXCLUSIVE } } {exclusive} do_test wal2-6.2.4 { execsql { SELECT * FROM t1; pragma lock_status; } |
︙ | ︙ | |||
728 729 730 731 732 733 734 735 736 737 738 739 740 741 | return SQLITE_OK } do_test wal2-6.6.1 { testvfs T T script lock_control T filter {} sqlite3 db test.db -vfs T execsql { PRAGMA locking_mode = exclusive } execsql { INSERT INTO t2 VALUES('V', 'VI') } } {} do_test wal2-6.6.2 { execsql { PRAGMA locking_mode = normal } T filter xShmLock execsql { INSERT INTO t2 VALUES('VII', 'VIII') } | > | 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 | return SQLITE_OK } do_test wal2-6.6.1 { testvfs T T script lock_control T filter {} sqlite3 db test.db -vfs T execsql { SELECT * FROM sqlite_master } execsql { PRAGMA locking_mode = exclusive } execsql { INSERT INTO t2 VALUES('V', 'VI') } } {} do_test wal2-6.6.2 { execsql { PRAGMA locking_mode = normal } T filter xShmLock execsql { INSERT INTO t2 VALUES('VII', 'VIII') } |
︙ | ︙ | |||
751 752 753 754 755 756 757 | catchsql { SELECT * FROM t2 } db2 } {1 {database is locked}} do_test wal2-6.6.2 { db2 close T filter {} execsql { INSERT INTO t2 VALUES('IX', 'X') } } {} | | | 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 | catchsql { SELECT * FROM t2 } db2 } {1 {database is locked}} do_test wal2-6.6.2 { db2 close T filter {} execsql { INSERT INTO t2 VALUES('IX', 'X') } } {} do_test wal2-6.6.4 { # This time, we have successfully exited exclusive mode. So the second # connection can read the database. sqlite3 db2 test.db -vfs T catchsql { SELECT * FROM t2 } db2 } {0 {I II III IV V VI VII VIII IX X}} db close |
︙ | ︙ |
Changes to test/walfault.test.
︙ | ︙ | |||
442 443 444 445 446 447 448 449 450 | set rc [sqlite3_wal_checkpoint db] if {$rc != "SQLITE_OK"} { error [sqlite3_errmsg db] } } -test { db close faultsim_test_result {0 {}} } finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | set rc [sqlite3_wal_checkpoint db] if {$rc != "SQLITE_OK"} { error [sqlite3_errmsg db] } } -test { db close faultsim_test_result {0 {}} } #------------------------------------------------------------------------- # Test simple recovery, reading and writing a database file using a # heap-memory wal-index. # do_test walfault-13-pre-1 { faultsim_delete_and_reopen execsql { PRAGMA journal_mode = WAL; PRAGMA wal_autocheckpoint = 0; BEGIN; CREATE TABLE abc(a PRIMARY KEY); INSERT INTO abc VALUES(randomblob(1500)); INSERT INTO abc VALUES(randomblob(1500)); COMMIT; } faultsim_save_and_close file delete sv_test.db-shm } {} do_faultsim_test walfault-13.1 -prep { faultsim_restore_and_reopen } -body { db eval { PRAGMA locking_mode = exclusive } db eval { SELECT count(*) FROM abc } } -test { faultsim_test_result {0 2} if {[file exists test.db-shm]} { error "Not using heap-memory mode" } faultsim_integrity_check } do_faultsim_test walfault-13.2 -prep { faultsim_restore_and_reopen db eval { PRAGMA locking_mode = exclusive } } -body { db eval { PRAGMA journal_mode = delete } } -test { faultsim_test_result {0 delete} if {[file exists test.db-shm]} { error "Not using heap-memory mode" } faultsim_integrity_check } do_test walfault-13-pre-2 { faultsim_delete_and_reopen execsql { BEGIN; CREATE TABLE abc(a PRIMARY KEY); INSERT INTO abc VALUES(randomblob(1500)); INSERT INTO abc VALUES(randomblob(1500)); COMMIT; } faultsim_save_and_close } {} do_faultsim_test walfault-13.3 -prep { faultsim_restore_and_reopen } -body { db eval { PRAGMA locking_mode = exclusive; PRAGMA journal_mode = WAL; INSERT INTO abc VALUES(randomblob(1500)); } } -test { faultsim_test_result {0 {exclusive wal}} if {[file exists test.db-shm]} { error "Not using heap-memory mode" } faultsim_integrity_check set nRow [db eval {SELECT count(*) FROM abc}] if {!(($nRow==2 && $testrc) || $nRow==3)} { error "Bad db content" } } finish_test |
Added test/walnoshm.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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 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 106 107 108 109 110 111 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 137 138 139 140 141 142 143 144 145 146 | # 2010 November 1 # # 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 that WAL databases may be accessed without # using the xShm primitives if the connection is in exclusive-mode. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix walnoshm ifcapable !wal {finish_test ; return } db close testvfs tvfsshm testvfs tvfs -default 1 -iversion 1 sqlite3 db test.db #-------------------------------------------------------------------------- # Test that when using a version 1 VFS, a database can only be converted # to WAL mode after setting locking_mode=EXCLUSIVE. Also, test that if a # WAL database is opened using heap-memory for the WAL index, the connection # cannot change back to locking_mode=NORMAL while the database is still in # WAL mode. # do_execsql_test 1.1 { CREATE TABLE t1(x, y); INSERT INTO t1 VALUES(1, 2); } do_execsql_test 1.2 { PRAGMA journal_mode = WAL; SELECT * FROM t1; } {delete 1 2} do_test 1.3 { file exists test.db-wal } {0} do_execsql_test 1.4 { PRAGMA locking_mode = exclusive; PRAGMA journal_mode = WAL; SELECT * FROM t1; } {exclusive wal 1 2} do_test 1.5 { file exists test.db-wal } {1} do_execsql_test 1.6 { INSERT INTO t1 VALUES(3, 4) } do_execsql_test 1.7 { PRAGMA locking_mode = normal; } {exclusive} do_execsql_test 1.8 { PRAGMA journal_mode = delete; PRAGMA main.locking_mode; } {delete exclusive} do_execsql_test 1.9 { PRAGMA locking_mode = normal; } {normal} do_execsql_test 1.10 { SELECT * FROM t1; } {1 2 3 4} do_test 1.11 { file exists test.db-wal } {0} #------------------------------------------------------------------------- # # 2.1.*: Test that a connection using a version 1 VFS can open a WAL database # and convert it to rollback mode if it is set to use # locking_mode=exclusive. # # 2.2.*: Test that if the exclusive lock cannot be obtained while attempting # the above, the operation fails and the WAL file is not opened. # do_execsql_test 2.1.1 { CREATE TABLE t2(x, y); INSERT INTO t2 VALUES('a', 'b'); INSERT INTO t2 VALUES('c', 'd'); } do_execsql_test 2.1.2 { PRAGMA locking_mode = exclusive; PRAGMA journal_mode = WAL; INSERT INTO t2 VALUES('e', 'f'); INSERT INTO t2 VALUES('g', 'h'); } {exclusive wal} do_test 2.1.3 { file copy -force test.db test2.db file copy -force test.db-wal test2.db-wal sqlite3 db2 test2.db catchsql { SELECT * FROM t2 } db2 } {1 {unable to open database file}} do_test 2.1.4 { catchsql { PRAGMA journal_mode = delete } db2 } {1 {unable to open database file}} do_test 2.1.5 { execsql { PRAGMA locking_mode = exclusive; PRAGMA journal_mode = delete; SELECT * FROM t2; } db2 } {exclusive delete a b c d e f g h} do_test 2.2.1 { file copy -force test.db test2.db file copy -force test.db-wal test2.db-wal sqlite3 db3 test2.db -vfs tvfsshm sqlite3 db2 test2.db execsql { SELECT * FROM t2 } db3 } {a b c d e f g h} do_test 2.2.2 { execsql { PRAGMA locking_mode = exclusive } db2 catchsql { PRAGMA journal_mode = delete } db2 } {1 {database is locked}} do_test 2.2.3 { # This is to test that [db2] is not holding a PENDING lock (which can # happen when an attempt to obtain an EXCLUSIVE lock fails). sqlite3 db4 test2.db -vfs tvfsshm execsql { SELECT * FROM t2 } db4 } {a b c d e f g h} do_test 2.2.4 { catchsql { SELECT * FROM t2 } db2 } {1 {database is locked}} do_test 2.2.5 { db4 close sqlite3 db4 test2.db -vfs tvfsshm execsql { SELECT * FROM t2 } db4 } {a b c d e f g h} do_test 2.2.6 { db3 close db4 close execsql { SELECT * FROM t2 } db2 } {a b c d e f g h} db2 close finish_test |