Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Fix compilation without SQLITE_ENABLE_UNLOCKED. Also other code organization issues. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | begin-concurrent |
Files: | files | file ages | folders |
SHA1: |
041135575417201bbcf0544cc69dcb73 |
User & Date: | dan 2015-08-24 16:00:08.023 |
Context
2015-08-24
| ||
19:08 | Fix handling of attempts to modify the database schema, application_id or user_version within an UNLOCKED transaction. (check-in: 5b9f272113 user: dan tags: begin-concurrent) | |
16:00 | Fix compilation without SQLITE_ENABLE_UNLOCKED. Also other code organization issues. (check-in: 0411355754 user: dan tags: begin-concurrent) | |
10:05 | Consolidate two blocks of similar code in btreeFixUnlocked(). (check-in: 701302b4bd user: dan tags: begin-concurrent) | |
Changes
Changes to src/btree.c.
︙ | ︙ | |||
3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 | int exFlag = (p->db->bUnlocked && !ISAUTOVACUUM) ? -1 : (wrflag>1); int bSubjInMem = sqlite3TempInMemory(p->db); assert( p->db->bUnlocked==0 || wrflag==1 ); rc = sqlite3PagerBegin(pBt->pPager, exFlag, bSubjInMem); if( rc==SQLITE_OK ){ rc = newDatabase(pBt); } if( rc==SQLITE_OK && sqlite3PagerIsUnlocked(pBt->pPager) ){ rc = btreePtrmapAllocate(pBt); } } } if( rc!=SQLITE_OK ){ unlockBtreeIfUnused(pBt); } }while( (rc&0xFF)==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE && | > > | 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 | int exFlag = (p->db->bUnlocked && !ISAUTOVACUUM) ? -1 : (wrflag>1); int bSubjInMem = sqlite3TempInMemory(p->db); assert( p->db->bUnlocked==0 || wrflag==1 ); rc = sqlite3PagerBegin(pBt->pPager, exFlag, bSubjInMem); if( rc==SQLITE_OK ){ rc = newDatabase(pBt); } #ifdef SQLITE_ENABLE_UNLOCKED if( rc==SQLITE_OK && sqlite3PagerIsUnlocked(pBt->pPager) ){ rc = btreePtrmapAllocate(pBt); } #endif } } if( rc!=SQLITE_OK ){ unlockBtreeIfUnused(pBt); } }while( (rc&0xFF)==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE && |
︙ | ︙ |
Changes to src/pager.c.
︙ | ︙ | |||
1742 1743 1744 1745 1746 1747 1748 | } /* ** Free the Pager.pInJournal and Pager.pAllRead bitvec objects. */ static void pagerFreeBitvecs(Pager *pPager){ sqlite3BitvecDestroy(pPager->pInJournal); | < > > > | 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 | } /* ** Free the Pager.pInJournal and Pager.pAllRead bitvec objects. */ static void pagerFreeBitvecs(Pager *pPager){ sqlite3BitvecDestroy(pPager->pInJournal); pPager->pInJournal = 0; #ifdef SQLITE_ENABLE_UNLOCKED sqlite3BitvecDestroy(pPager->pAllRead); pPager->pAllRead = 0; #endif } /* ** This function is a no-op if the pager is in exclusive mode and not ** in the ERROR state. Otherwise, it switches the pager to PAGER_OPEN ** state. ** |
︙ | ︙ | |||
3017 3018 3019 3020 3021 3022 3023 | /* ** This function is called to rollback a transaction on a WAL database. */ static int pagerRollbackWal(Pager *pPager){ int rc; /* Return Code */ PgHdr *pList; /* List of dirty pages to revert */ | < > > > > > > > > > > < < < < < < < < | 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 | /* ** This function is called to rollback a transaction on a WAL database. */ static int pagerRollbackWal(Pager *pPager){ int rc; /* Return Code */ PgHdr *pList; /* List of dirty pages to revert */ /* For all pages in the cache that are currently dirty or have already ** been written (but not committed) to the log file, do one of the ** following: ** ** + Discard the cached page (if refcount==0), or ** + Reload page content from the database (if refcount>0). */ pPager->dbSize = pPager->dbOrigSize; rc = sqlite3WalUndo(pPager->pWal, pagerUndoCallback, (void *)pPager); pList = sqlite3PcacheDirtyList(pPager->pPCache); /* If this is an UNLOCKED transaction, then page 1 must be reread from ** the db file, even if it is not dirty. This is because the b-tree layer ** may have already zeroed the nFree and iTrunk header fields. */ #ifdef SQLITE_ENABLE_UNLOCKED if( rc==SQLITE_OK && (pList==0 || pList->pgno!=1) && pPager->pAllRead ){ rc = pagerUndoCallback((void*)pPager, 1); } #endif while( pList && rc==SQLITE_OK ){ PgHdr *pNext = pList->pDirty; rc = pagerUndoCallback((void *)pPager, pList->pgno); pList = pNext; } return rc; } /* ** This function is a wrapper around sqlite3WalFrames(). As well as logging ** the contents of the list of pages headed by pList (connected by pDirty), ** this function notifies any active backup processes that the pages have |
︙ | ︙ | |||
3082 3083 3084 3085 3086 3087 3088 | if( isCommit ){ /* If a WAL transaction is being committed, there is no point in writing ** any pages with page numbers greater than nTruncate into the WAL file. ** They will never be read by any client. So remove them from the pDirty ** list here. */ PgHdr **ppNext = &pList; nList = 0; | < | 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 | if( isCommit ){ /* If a WAL transaction is being committed, there is no point in writing ** any pages with page numbers greater than nTruncate into the WAL file. ** They will never be read by any client. So remove them from the pDirty ** list here. */ PgHdr **ppNext = &pList; nList = 0; for(p=pList; (*ppNext = p)!=0; p=p->pDirty){ if( p->pgno<=nTruncate ){ ppNext = &p->pDirty; nList++; } } assert( pList ); |
︙ | ︙ | |||
4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 | return SQLITE_OK; } pPg->pDirty = 0; if( pagerUseWal(pPager) ){ /* If the transaction is a "BEGIN UNLOCKED" transaction, the page ** cannot be flushed to disk. Return early in this case. */ if( pPager->pAllRead ) return SQLITE_OK; /* Write a single frame for this page to the log. */ rc = subjournalPageIfRequired(pPg); if( rc==SQLITE_OK ){ rc = pagerWalFrames(pPager, pPg, 0, 0); } }else{ | > > | 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 | return SQLITE_OK; } pPg->pDirty = 0; if( pagerUseWal(pPager) ){ /* If the transaction is a "BEGIN UNLOCKED" transaction, the page ** cannot be flushed to disk. Return early in this case. */ #ifdef SQLITE_ENABLE_UNLOCKED if( pPager->pAllRead ) return SQLITE_OK; #endif /* Write a single frame for this page to the log. */ rc = subjournalPageIfRequired(pPg); if( rc==SQLITE_OK ){ rc = pagerWalFrames(pPager, pPg, 0, 0); } }else{ |
︙ | ︙ | |||
5293 5294 5295 5296 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 | return SQLITE_CORRUPT_BKPT; } pPager->hasBeenUsed = 1; /* If this is an UNLOCKED transaction and the page being read was ** present in the database file when the transaction was opened, ** mark it as read in the pAllRead vector. */ if( pPager->pAllRead && pgno<=pPager->dbOrigSize ){ rc = sqlite3BitvecSet(pPager->pAllRead, pgno); if( rc!=SQLITE_OK ) goto pager_acquire_err; } /* If the pager is in the error state, return an error immediately. ** Otherwise, request the page from the PCache layer. */ if( pPager->errCode!=SQLITE_OK ){ rc = pPager->errCode; }else{ if( bMmapOk && pagerUseWal(pPager) ){ | > > | 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 5311 5312 5313 5314 5315 5316 | return SQLITE_CORRUPT_BKPT; } pPager->hasBeenUsed = 1; /* If this is an UNLOCKED transaction and the page being read was ** present in the database file when the transaction was opened, ** mark it as read in the pAllRead vector. */ #ifdef SQLITE_ENABLE_UNLOCKED if( pPager->pAllRead && pgno<=pPager->dbOrigSize ){ rc = sqlite3BitvecSet(pPager->pAllRead, pgno); if( rc!=SQLITE_OK ) goto pager_acquire_err; } #endif /* If the pager is in the error state, return an error immediately. ** Otherwise, request the page from the PCache layer. */ if( pPager->errCode!=SQLITE_OK ){ rc = pPager->errCode; }else{ if( bMmapOk && pagerUseWal(pPager) ){ |
︙ | ︙ | |||
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 | if( pPager->errCode ) return pPager->errCode; assert( pPager->eState>=PAGER_READER && pPager->eState<PAGER_ERROR ); pPager->subjInMemory = (u8)subjInMemory; if( ALWAYS(pPager->eState==PAGER_READER) ){ assert( pPager->pInJournal==0 ); assert( pPager->pAllRead==0 ); if( pagerUseWal(pPager) ){ /* If the pager is configured to use locking_mode=exclusive, and an ** exclusive lock on the database is not already held, obtain it now. */ if( pPager->exclusiveMode && sqlite3WalExclusiveMode(pPager->pWal, -1) ){ rc = pagerLockDb(pPager, EXCLUSIVE_LOCK); 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. */ | > > > | < < > > > > | 5608 5609 5610 5611 5612 5613 5614 5615 5616 5617 5618 5619 5620 5621 5622 5623 5624 5625 5626 5627 5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 | if( pPager->errCode ) return pPager->errCode; assert( pPager->eState>=PAGER_READER && pPager->eState<PAGER_ERROR ); pPager->subjInMemory = (u8)subjInMemory; if( ALWAYS(pPager->eState==PAGER_READER) ){ assert( pPager->pInJournal==0 ); #ifdef SQLITE_ENABLE_UNLOCKED assert( pPager->pAllRead==0 ); #endif 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 = pagerLockDb(pPager, EXCLUSIVE_LOCK); 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. */ #ifdef SQLITE_ENABLE_UNLOCKED if( exFlag<0 ){ pPager->pAllRead = sqlite3BitvecCreate(pPager->dbSize); if( pPager->pAllRead==0 ){ rc = SQLITE_NOMEM; } }else #endif { rc = sqlite3WalBeginWriteTransaction(pPager->pWal); } }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. */ |
︙ | ︙ | |||
5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 5944 5945 5946 5947 5948 5949 5950 | } /* ** Return TRUE if the page given in the argument was previously passed ** to sqlite3PagerWrite(). In other words, return TRUE if it is ok ** to change the content of the page. */ int sqlite3PagerIswriteable(DbPage *pPg){ return pPg->flags & PGHDR_WRITEABLE; } /* ** A call to this routine tells the pager that it is not necessary to ** write the information on page pPg back to the disk, even though ** that page might be marked as dirty. This happens, for example, when ** the page has been added as a leaf of the freelist and so its ** content no longer matters. | > > | 5945 5946 5947 5948 5949 5950 5951 5952 5953 5954 5955 5956 5957 5958 5959 5960 5961 5962 5963 | } /* ** Return TRUE if the page given in the argument was previously passed ** to sqlite3PagerWrite(). In other words, return TRUE if it is ok ** to change the content of the page. */ #if defined(SQLITE_ENABLE_UNLOCKED) || !defined(NDEBUG) int sqlite3PagerIswriteable(DbPage *pPg){ return pPg->flags & PGHDR_WRITEABLE; } #endif /* ** A call to this routine tells the pager that it is not necessary to ** write the information on page pPg back to the disk, even though ** that page might be marked as dirty. This happens, for example, when ** the page has been added as a leaf of the freelist and so its ** content no longer matters. |
︙ | ︙ | |||
6084 6085 6086 6087 6088 6089 6090 | assert( !MEMDB ); rc = sqlite3OsSync(pPager->fd, pPager->syncFlags); } return rc; } /* | | | | > | > > > > > > | | | > > | > > | < > > > > > > > > > > > > > > > > > | | > > > > > > > | < < < < < < | 6097 6098 6099 6100 6101 6102 6103 6104 6105 6106 6107 6108 6109 6110 6111 6112 6113 6114 6115 6116 6117 6118 6119 6120 6121 6122 6123 6124 6125 6126 6127 6128 6129 6130 6131 6132 6133 6134 6135 6136 6137 6138 6139 6140 6141 6142 6143 6144 6145 6146 6147 6148 6149 6150 6151 6152 6153 6154 6155 6156 6157 6158 6159 6160 6161 6162 6163 6164 6165 6166 6167 6168 6169 6170 6171 6172 6173 6174 6175 6176 6177 6178 6179 6180 6181 6182 6183 6184 6185 6186 6187 6188 6189 6190 6191 6192 6193 6194 6195 6196 6197 6198 6199 6200 6201 6202 6203 6204 6205 6206 6207 6208 6209 6210 | assert( !MEMDB ); rc = sqlite3OsSync(pPager->fd, pPager->syncFlags); } return rc; } /* ** This function is called to ensure that all locks required to commit the ** current write-transaction to the database file are held. If the db is ** in rollback mode, this means the EXCLUSIVE lock on the database file. ** ** Or, if this is a non-UNLOCKED transaction on a wal-mode database, this ** function is a no-op. ** ** If this is an UNLOCKED transaction on a wal-mode database, this function ** attempts to obtain the WRITER lock on the wal file and also checks to ** see that the transaction can be safely committed (does not commit with ** any other transaction committed since it was opened). ** ** If the required locks are already held or successfully obtained and ** the transaction can be committed, SQLITE_OK is returned. If a required lock ** cannot be obtained, SQLITE_BUSY is returned. Or, if the current transaction ** is UNLOCKED and cannot be committed due to a conflict, SQLITE_BUSY_SNAPSHOT ** is returned. Otherwise, if some other error occurs (IO error, OOM etc.), ** and SQLite error code is returned. */ int sqlite3PagerExclusiveLock(Pager *pPager, PgHdr *pPage1){ int rc = SQLITE_OK; assert( pPager->eState==PAGER_WRITER_CACHEMOD || pPager->eState==PAGER_WRITER_DBMOD || pPager->eState==PAGER_WRITER_LOCKED ); assert( assert_pager_state(pPager) ); if( 0==pagerUseWal(pPager) ){ rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK); } #ifdef SQLITE_ENABLE_UNLOCKED else{ if( pPager->pAllRead ){ /* This is an UNLOCKED transaction. Attempt to lock the wal database ** here. If SQLITE_BUSY (but not SQLITE_BUSY_SNAPSHOT) is returned, ** invoke the busy-handler and try again for as long as it returns ** non-zero. */ do { rc = sqlite3WalLockForCommit(pPager->pWal, pPage1, pPager->pAllRead); }while( rc==SQLITE_BUSY && pPager->xBusyHandler(pPager->pBusyHandlerArg) ); } } #endif return rc; } #ifdef SQLITE_ENABLE_UNLOCKED /* ** This function is called as part of committing an UNLOCKED transaction. ** At this point the wal WRITER lock is held, and all pages in the cache ** except for page 1 are compatible with the snapshot at the head of the ** wal file. ** ** This function updates the in-memory data structures and reloads the ** contents of page 1 so that the client is operating on the snapshot ** at the head of the wal file. ** ** SQLITE_OK is returned if successful, or an SQLite error code otherwise. */ int sqlite3PagerUpgradeSnapshot(Pager *pPager, DbPage *pPage1){ int rc; u32 iFrame = 0; assert( pPager->pWal && pPager->pAllRead ); rc = sqlite3WalUpgradeSnapshot(pPager->pWal); if( rc==SQLITE_OK ){ rc = sqlite3WalFindFrame(pPager->pWal, 1, &iFrame); } if( rc==SQLITE_OK ){ rc = readDbPage(pPage1, iFrame); } return rc; } /* ** Set the in-memory cache of the database file size to nSz pages. */ void sqlite3PagerSetDbsize(Pager *pPager, Pgno nSz){ pPager->dbSize = nSz; } /* ** Return true if this pager is currently within an UNLOCKED transaction. */ int sqlite3PagerIsUnlocked(Pager *pPager){ return pPager->pAllRead!=0; } /* ** If this is a WAL mode connection and the WRITER lock is currently held, ** relinquish it. */ void sqlite3PagerDropExclusiveLock(Pager *pPager){ if( pagerUseWal(pPager) ){ sqlite3WalEndWriteTransaction(pPager->pWal); } } #endif /* ifdef SQLITE_ENABLE_UNLOCKED */ /* ** Sync the database file for the pager pPager. zMaster points to the name ** of a master journal file that should be written into the individual ** journal file. zMaster may be NULL, which is interpreted as no master ** journal (a single database transaction). |
︙ | ︙ |
Changes to src/test_config.c.
︙ | ︙ | |||
568 569 570 571 572 573 574 575 576 577 578 579 580 581 | #endif #ifdef SQLITE_OMIT_TRUNCATE_OPTIMIZATION Tcl_SetVar2(interp, "sqlite_options", "truncate_opt", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "truncate_opt", "1", TCL_GLOBAL_ONLY); #endif #ifdef SQLITE_OMIT_UTF16 Tcl_SetVar2(interp, "sqlite_options", "utf16", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "utf16", "1", TCL_GLOBAL_ONLY); #endif | > > > > > > | 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 | #endif #ifdef SQLITE_OMIT_TRUNCATE_OPTIMIZATION Tcl_SetVar2(interp, "sqlite_options", "truncate_opt", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "truncate_opt", "1", TCL_GLOBAL_ONLY); #endif #ifdef SQLITE_ENABLE_UNLOCKED Tcl_SetVar2(interp, "sqlite_options", "unlocked", "1", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "unlocked", "0", TCL_GLOBAL_ONLY); #endif #ifdef SQLITE_OMIT_UTF16 Tcl_SetVar2(interp, "sqlite_options", "utf16", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "utf16", "1", TCL_GLOBAL_ONLY); #endif |
︙ | ︙ |
Changes to src/vdbeaux.c.
︙ | ︙ | |||
2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 | if( sqlite3BtreeIsInTrans(pBt) ){ needXcommit = 1; if( i!=1 ) nTrans++; rc = sqlite3BtreeExclusiveLock(pBt); } } if( db->bUnlocked && (rc & 0xFF)==SQLITE_BUSY ){ /* An SQLITE_BUSY or SQLITE_BUSY_SNAPSHOT was encountered while ** attempting to take the WRITER lock on a wal file. Release the ** WRITER locks on all wal files and return early. */ for(i=0; i<db->nDb; i++){ Btree *pBt = db->aDb[i].pBt; if( sqlite3BtreeIsInTrans(pBt) ){ sqlite3BtreeEnter(pBt); sqlite3PagerDropExclusiveLock(sqlite3BtreePager(pBt)); sqlite3BtreeLeave(pBt); } } } if( rc!=SQLITE_OK ){ return rc; } /* If there are any write-transactions at all, invoke the commit hook */ if( needXcommit && db->xCommitCallback ){ | > > | 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 | if( sqlite3BtreeIsInTrans(pBt) ){ needXcommit = 1; if( i!=1 ) nTrans++; rc = sqlite3BtreeExclusiveLock(pBt); } } #ifdef SQLITE_ENABLE_UNLOCKED if( db->bUnlocked && (rc & 0xFF)==SQLITE_BUSY ){ /* An SQLITE_BUSY or SQLITE_BUSY_SNAPSHOT was encountered while ** attempting to take the WRITER lock on a wal file. Release the ** WRITER locks on all wal files and return early. */ for(i=0; i<db->nDb; i++){ Btree *pBt = db->aDb[i].pBt; if( sqlite3BtreeIsInTrans(pBt) ){ sqlite3BtreeEnter(pBt); sqlite3PagerDropExclusiveLock(sqlite3BtreePager(pBt)); sqlite3BtreeLeave(pBt); } } } #endif if( rc!=SQLITE_OK ){ return rc; } /* If there are any write-transactions at all, invoke the commit hook */ if( needXcommit && db->xCommitCallback ){ |
︙ | ︙ |
Changes to src/wal.c.
︙ | ︙ | |||
2562 2563 2564 2565 2566 2567 2568 | pWal->writeLock = 0; rc = SQLITE_BUSY_SNAPSHOT; } } return rc; } | | | > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > | 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 | pWal->writeLock = 0; rc = SQLITE_BUSY_SNAPSHOT; } } return rc; } /* ** This function is called by a writer that has a read-lock on aReadmark[0] ** (pWal->readLock==0). This function relinquishes that lock and takes a ** lock on a different aReadmark[] slot. ** ** SQLITE_OK is returned if successful, or an SQLite error code otherwise. */ static int walUpgradeReadlock(Wal *pWal){ int cnt; int rc; assert( pWal->writeLock && pWal->readLock==0 ); walUnlockShared(pWal, WAL_READ_LOCK(0)); pWal->readLock = -1; cnt = 0; do{ int notUsed; rc = walTryBeginRead(pWal, ¬Used, 1, ++cnt); }while( rc==WAL_RETRY ); assert( (rc&0xff)!=SQLITE_BUSY ); /* BUSY not possible when useWal==1 */ testcase( (rc&0xff)==SQLITE_IOERR ); testcase( rc==SQLITE_PROTOCOL ); testcase( rc==SQLITE_OK ); return rc; } #ifdef SQLITE_ENABLE_UNLOCKED /* ** This function is only ever called when committing a "BEGIN UNLOCKED" ** transaction. It may be assumed that no frames have been written to ** the wal file. The second parameter is a pointer to the in-memory ** representation of page 1 of the database (which may or may not be ** dirty). The third is a bitvec with a bit set for each page in the ** database file that was read by the current unlocked transaction. ** ** This function performs three tasks: ** ** 1) It obtains the WRITER lock on the wal file, ** ** 2) It checks that there are no conflicts between the current ** transaction and any transactions committed to the wal file since ** it was opened, and ** ** 3) It ejects any non-dirty pages from the page-cache that have been ** written by another client since the UNLOCKED transaction was started ** (so as to avoid ending up with an inconsistent cache after the ** current transaction is committed). ** ** If no error occurs and the caller may proceed with committing the ** transaction, SQLITE_OK is returned. SQLITE_BUSY is returned if the WRITER ** lock cannot be obtained. Or, if the WRITER lock can be obtained but there ** are conflicts with a committed transaction, SQLITE_BUSY_SNAPSHOT. Finally, ** if an error (i.e. an OOM condition or IO error), an SQLite error code ** is returned. */ int sqlite3WalLockForCommit(Wal *pWal, PgHdr *pPage1, Bitvec *pAllRead){ Pager *pPager = pPage1->pPager; int rc = walWriteLock(pWal); /* If the database has been modified since this transaction was started, ** check if it is still possible to commit. The transaction can be |
︙ | ︙ | |||
2664 2665 2666 2667 2668 2669 2670 | } } return rc; } /* | | > > > > | < > > < < < < < < < < < < < < < < < < < < > | 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 | } } return rc; } /* ** This function is called as part of committing an UNLOCKED transaction. ** It is assumed that sqlite3WalLockForCommit() has already been successfully ** called and so (a) the WRITER lock is held and (b) it is known that the ** wal-index-header stored in shared memory is not corrupt. ** ** Before returning, this function upgrades the client so that it is ** operating on the database snapshot currently at the head of the wal file ** (even if the UNLOCKED transaction ran against an older snapshot). ** ** SQLITE_OK is returned if successful, or an SQLite error code otherwise. */ int sqlite3WalUpgradeSnapshot(Wal *pWal){ int rc = SQLITE_OK; assert( pWal->writeLock ); memcpy(&pWal->hdr, (void*)walIndexHdr(pWal), sizeof(WalIndexHdr)); /* If this client has its read-lock on slot aReadmark[0] and the entire ** wal has not been checkpointed, switch it to a different slot. Otherwise ** any reads performed between now and committing the transaction will ** read from the old snapshot - not the one just upgraded to. */ if( pWal->readLock==0 && pWal->hdr.mxFrame!=walCkptInfo(pWal)->nBackfill ){ rc = walUpgradeReadlock(pWal); } return rc; } #endif /* SQLITE_ENABLE_UNLOCKED */ /* ** End a write transaction. The commit has already been done. This ** routine merely releases the lock. */ int sqlite3WalEndWriteTransaction(Wal *pWal){ if( pWal->writeLock ){ |
︙ | ︙ |
Changes to test/unlocked.test.
︙ | ︙ | |||
11 12 13 14 15 16 17 18 19 20 21 22 23 24 | # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl set ::testprefix unlocked do_execsql_test 1.0 { PRAGMA journal_mode = wal; } {wal} do_execsql_test 1.1 { CREATE TABLE t1(k INTEGER PRIMARY KEY, v); | > > > > | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl set ::testprefix unlocked ifcapable !unlocked { finish_test return } do_execsql_test 1.0 { PRAGMA journal_mode = wal; } {wal} do_execsql_test 1.1 { CREATE TABLE t1(k INTEGER PRIMARY KEY, v); |
︙ | ︙ |
Changes to test/unlocked2.test.
︙ | ︙ | |||
11 12 13 14 15 16 17 18 19 20 21 22 23 24 | # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl set ::testprefix unlocked2 do_multiclient_test tn { do_test 1.$tn.1 { sql1 { PRAGMA journal_mode = wal; CREATE TABLE t1(x); | > > > > | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl set ::testprefix unlocked2 ifcapable !unlocked { finish_test return } do_multiclient_test tn { do_test 1.$tn.1 { sql1 { PRAGMA journal_mode = wal; CREATE TABLE t1(x); |
︙ | ︙ |
Changes to test/unlocked3.test.
︙ | ︙ | |||
15 16 17 18 19 20 21 22 23 24 25 26 27 28 | set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl set ::testprefix unlocked3 if {$AUTOVACUUM} { finish_test ; return } proc create_schema {} { db eval { PRAGMA journal_mode = wal; CREATE TABLE t1(x, y); CREATE TABLE t2(x, y); | > > > > | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl set ::testprefix unlocked3 if {$AUTOVACUUM} { finish_test ; return } ifcapable !unlocked { finish_test return } proc create_schema {} { db eval { PRAGMA journal_mode = wal; CREATE TABLE t1(x, y); CREATE TABLE t2(x, y); |
︙ | ︙ |