Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Change "BEGIN UNLOCKED" to "BEGIN CONCURRENT". |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | begin-concurrent |
Files: | files | file ages | folders |
SHA1: |
ba1ab858e2997c88dd7eee6e6893a861 |
User & Date: | dan 2015-08-24 19:56:04.567 |
Context
2015-08-24
| ||
22:06 | Remove duplicated line of code. (check-in: 47280f2a2b user: mistachkin tags: begin-concurrent) | |
19:56 | Change "BEGIN UNLOCKED" to "BEGIN CONCURRENT". (check-in: ba1ab858e2 user: dan tags: begin-concurrent) | |
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) | |
Changes
Changes to src/btree.c.
︙ | ︙ | |||
436 437 438 439 440 441 442 | } } } #endif /* SQLITE_OMIT_SHARED_CACHE */ | | | | 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 | } } } #endif /* SQLITE_OMIT_SHARED_CACHE */ #ifdef SQLITE_ENABLE_CONCURRENT /* ** The following structure - BtreePtrmap - stores the in-memory pointer map ** used for newly allocated pages in CONCURRENT transactions. Such pages are ** always allocated in a contiguous block (from the end of the file) starting ** with page BtreePtrmap.iFirst. */ typedef struct RollbackEntry RollbackEntry; typedef struct PtrmapEntry PtrmapEntry; struct PtrmapEntry { Pgno parent; |
︙ | ︙ | |||
584 585 586 587 588 589 590 | pMap->nSvpt = iSvpt + (op==SAVEPOINT_ROLLBACK); pMap->nRollback = pMap->aSvpt[iSvpt]; } } } /* | | | | 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 | pMap->nSvpt = iSvpt + (op==SAVEPOINT_ROLLBACK); pMap->nRollback = pMap->aSvpt[iSvpt]; } } } /* ** This function is called after an CONCURRENT transaction is opened on the ** database. It allocates the BtreePtrmap structure used to track pointers ** to allocated pages and zeroes the nFree/iTrunk fields in the database ** header on page 1. */ static int btreePtrmapAllocate(BtShared *pBt){ int rc = SQLITE_OK; BtreePtrmap *pMap = sqlite3_malloc(sizeof(BtreePtrmap)); assert( pBt->pMap==0 && sqlite3PagerIsConcurrent(pBt->pPager) ); if( pMap==0 ){ rc = SQLITE_NOMEM; }else{ memset(&pBt->pPage1->aData[32], 0, sizeof(u32)*2); memset(pMap, 0, sizeof(BtreePtrmap)); pMap->iFirst = pBt->nPage + 1; pBt->pMap = pMap; |
︙ | ︙ | |||
1070 1071 1072 1073 1074 1075 1076 | if( *pRC ) return; assert( sqlite3_mutex_held(pBt->mutex) ); /* The master-journal page number is never added to a pointer-map page */ assert( 0==PTRMAP_ISPAGE(pBt, PENDING_BYTE_PAGE(pBt)) ); | | | 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 | if( *pRC ) return; assert( sqlite3_mutex_held(pBt->mutex) ); /* The master-journal page number is never added to a pointer-map page */ assert( 0==PTRMAP_ISPAGE(pBt, PENDING_BYTE_PAGE(pBt)) ); #ifdef SQLITE_ENABLE_CONCURRENT if( pBt->pMap ){ *pRC = btreePtrmapStore(pBt, key, eType, parent); return; } #endif assert( pBt->autoVacuum ); |
︙ | ︙ | |||
3335 3336 3337 3338 3339 3340 3341 | */ while( pBt->pPage1==0 && SQLITE_OK==(rc = lockBtree(pBt)) ); if( rc==SQLITE_OK && wrflag ){ if( (pBt->btsFlags & BTS_READ_ONLY)!=0 ){ rc = SQLITE_READONLY; }else{ | | | | | | 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 | */ while( pBt->pPage1==0 && SQLITE_OK==(rc = lockBtree(pBt)) ); if( rc==SQLITE_OK && wrflag ){ if( (pBt->btsFlags & BTS_READ_ONLY)!=0 ){ rc = SQLITE_READONLY; }else{ int exFlag = (p->db->bConcurrent && !ISAUTOVACUUM) ? -1 : (wrflag>1); int bSubjInMem = sqlite3TempInMemory(p->db); assert( p->db->bConcurrent==0 || wrflag==1 ); rc = sqlite3PagerBegin(pBt->pPager, exFlag, bSubjInMem); if( rc==SQLITE_OK ){ rc = newDatabase(pBt); } #ifdef SQLITE_ENABLE_CONCURRENT if( rc==SQLITE_OK && sqlite3PagerIsConcurrent(pBt->pPager) ){ rc = btreePtrmapAllocate(pBt); } #endif } } if( rc!=SQLITE_OK ){ |
︙ | ︙ | |||
3844 3845 3846 3847 3848 3849 3850 | return rc; } #else /* ifndef SQLITE_OMIT_AUTOVACUUM */ # define setChildPtrmaps(x) SQLITE_OK #endif | | | | 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 | return rc; } #else /* ifndef SQLITE_OMIT_AUTOVACUUM */ # define setChildPtrmaps(x) SQLITE_OK #endif #ifdef SQLITE_ENABLE_CONCURRENT /* ** This function is called as part of merging an CONCURRENT transaction with ** the snapshot at the head of the wal file. It relocates all pages in the ** range iFirst..iLast, inclusive. It is assumed that the BtreePtrmap ** structure at BtShared.pMap contains the location of the pointers to each ** page in the range. ** ** If pnCurrent is NULL, then all pages in the range are moved to currently ** free locations (i.e. free-list entries) within the database file before page |
︙ | ︙ | |||
3914 3915 3916 3917 3918 3919 3920 | } } return rc; } /* ** The b-tree handle passed as the only argument is about to commit an | | | | 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 | } } return rc; } /* ** The b-tree handle passed as the only argument is about to commit an ** CONCURRENT transaction. At this point it is guaranteed that this is ** possible - the wal WRITER lock is held and it is known that there are ** no conflicts with committed transactions. */ static int btreeFixUnlocked(Btree *p){ BtShared *pBt = p->pBt; MemPage *pPage1 = pBt->pPage1; u8 *p1 = pPage1->aData; Pager *pPager = pBt->pPager; int rc = SQLITE_OK; /* If page 1 of the database is not writable, then no pages were allocated ** or freed by this transaction. In this case no special handling is ** required. Otherwise, if page 1 is dirty, proceed. */ BtreePtrmap *pMap = pBt->pMap; Pgno iTrunk = get4byte(&p1[32]); Pgno nPage = btreePagecount(pBt); u32 nFree = get4byte(&p1[36]); assert( sqlite3PagerIsConcurrent(pPager) ); assert( pBt->pMap ); rc = sqlite3PagerUpgradeSnapshot(pPager, pPage1->pDbPage); assert( p1==pPage1->aData ); if( rc==SQLITE_OK ){ Pgno nHPage = get4byte(&p1[28]); Pgno nFin = nHPage; /* Size of db after transaction merge */ |
︙ | ︙ | |||
3963 3964 3965 3966 3967 3968 3969 | } sqlite3PagerUnref(pTrunk); }; } if( nHPage<(pMap->iFirst-1) ){ /* The database consisted of (pMap->iFirst-1) pages when the current | | | 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 | } sqlite3PagerUnref(pTrunk); }; } if( nHPage<(pMap->iFirst-1) ){ /* The database consisted of (pMap->iFirst-1) pages when the current ** concurrent transaction was opened. And an concurrent transaction may ** not be executed on an auto-vacuum database - so the db should ** not have shrunk since the transaction was opened. Therefore nHPage ** should be set to (pMap->iFirst-1) or greater. */ rc = SQLITE_CORRUPT_BKPT; }else{ /* The current transaction allocated pages pMap->iFirst through ** nPage (inclusive) at the end of the database file. Meanwhile, |
︙ | ︙ | |||
4038 4039 4040 4041 4042 4043 4044 | int rc = SQLITE_OK; if( p->inTrans==TRANS_WRITE ){ BtShared *pBt = p->pBt; sqlite3BtreeEnter(p); #ifndef SQLITE_OMIT_AUTOVACUUM if( pBt->autoVacuum ){ | | | | 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 | int rc = SQLITE_OK; if( p->inTrans==TRANS_WRITE ){ BtShared *pBt = p->pBt; sqlite3BtreeEnter(p); #ifndef SQLITE_OMIT_AUTOVACUUM if( pBt->autoVacuum ){ assert( ISCONCURRENT==0 ); rc = autoVacuumCommit(pBt); if( rc!=SQLITE_OK ){ sqlite3BtreeLeave(p); return rc; } } if( pBt->bDoTruncate ){ sqlite3PagerTruncateImage(pBt->pPager, pBt->nPage); } #endif if( rc==SQLITE_OK && ISCONCURRENT ){ rc = btreeFixUnlocked(p); } if( rc==SQLITE_OK ){ rc = sqlite3PagerCommitPhaseOne(pBt->pPager, zMaster, 0); } sqlite3BtreeLeave(p); } |
︙ | ︙ | |||
4097 4098 4099 4100 4101 4102 4103 | /* Set the current transaction state to TRANS_NONE and unlock the ** pager if this call closed the only read or write transaction. */ p->inTrans = TRANS_NONE; unlockBtreeIfUnused(pBt); } | | | 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 | /* Set the current transaction state to TRANS_NONE and unlock the ** pager if this call closed the only read or write transaction. */ p->inTrans = TRANS_NONE; unlockBtreeIfUnused(pBt); } /* If this was an CONCURRENT transaction, delete the pBt->pMap object */ btreePtrmapDelete(pBt); btreeIntegrity(p); } /* ** Commit the transaction currently in progress. ** |
︙ | ︙ | |||
5856 5857 5858 5859 5860 5861 5862 | assert( eMode==BTALLOC_ANY || (nearby>0 && REQUIRE_PTRMAP ) ); pPage1 = pBt->pPage1; mxPage = btreePagecount(pBt); /* EVIDENCE-OF: R-05119-02637 The 4-byte big-endian integer at offset 36 ** stores stores the total number of pages on the freelist. */ n = get4byte(&pPage1->aData[36]); testcase( n==mxPage-1 ); | | | | | 5856 5857 5858 5859 5860 5861 5862 5863 5864 5865 5866 5867 5868 5869 5870 5871 5872 5873 5874 5875 5876 5877 5878 5879 5880 5881 5882 5883 5884 5885 5886 5887 5888 5889 5890 5891 5892 5893 | assert( eMode==BTALLOC_ANY || (nearby>0 && REQUIRE_PTRMAP ) ); pPage1 = pBt->pPage1; mxPage = btreePagecount(pBt); /* EVIDENCE-OF: R-05119-02637 The 4-byte big-endian integer at offset 36 ** stores stores the total number of pages on the freelist. */ n = get4byte(&pPage1->aData[36]); testcase( n==mxPage-1 ); if( ISCONCURRENT==0 && n>=mxPage ){ return SQLITE_CORRUPT_BKPT; } /* Ensure page 1 is writable. This function will either change the number ** of pages in the free-list or the size of the database file. Since both ** of these operations involve modifying page 1 header fields, page 1 ** will definitely be written by this transaction. If this is an CONCURRENT ** transaction, ensure the BtreePtrmap structure has been allocated. */ rc = sqlite3PagerWrite(pPage1->pDbPage); if( rc ) return rc; if( n>0 ){ /* There are pages on the freelist. Reuse one of those pages. */ Pgno iTrunk; u8 searchList = 0; /* If the free-list must be searched for 'nearby' */ u32 nSearch = 0; /* Count of the number of search attempts */ /* If eMode==BTALLOC_EXACT and a query of the pointer-map ** shows that the page 'nearby' is somewhere on the free-list, then ** the entire-list will be searched for that page. */ if( eMode==BTALLOC_EXACT ){ assert( ISAUTOVACUUM!=ISCONCURRENT ); if( ISAUTOVACUUM ){ if( nearby<=mxPage ){ u8 eType; assert( nearby>0 ); assert( pBt->autoVacuum ); rc = ptrmapGet(pBt, nearby, &eType, 0); if( rc ) return rc; |
︙ | ︙ | |||
9970 9971 9972 9973 9974 9975 9976 | int sqlite3HeaderSizeBtree(void){ return ROUND8(sizeof(MemPage)); } /* ** 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. ** | | | | | 9970 9971 9972 9973 9974 9975 9976 9977 9978 9979 9980 9981 9982 9983 9984 9985 9986 9987 9988 9989 9990 9991 9992 9993 9994 9995 9996 9997 | int sqlite3HeaderSizeBtree(void){ return ROUND8(sizeof(MemPage)); } /* ** 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 an CONCURRENT transaction on a wal-mode database, the WRITER ** lock on the wal file. In this case this function also checks that the ** CONCURRENT transaction can be safely committed (does not commit with any ** other transaction committed since it was opened). ** ** SQLITE_OK is returned if successful. SQLITE_BUSY if the required locks ** cannot be obtained due to a conflicting lock. If the locks cannot be ** obtained for an CONCURRENT transaction due to a conflict with an already ** committed transaction, SQLITE_BUSY_SNAPSHOT is returned. Otherwise, if ** some other error (OOM, IO, etc.) occurs, the relevant SQLite error code ** is returned. */ int sqlite3BtreeExclusiveLock(Btree *p){ int rc; BtShared *pBt = p->pBt; assert( p->inTrans==TRANS_WRITE && pBt->pPage1 ); sqlite3BtreeEnter(p); rc = sqlite3PagerExclusiveLock(pBt->pPager, pBt->pPage1->pDbPage); sqlite3BtreeLeave(p); return rc; } |
Changes to src/btreeInt.h.
︙ | ︙ | |||
444 445 446 447 448 449 450 | #ifndef SQLITE_OMIT_SHARED_CACHE int nRef; /* Number of references to this structure */ BtShared *pNext; /* Next on a list of sharable BtShared structs */ BtLock *pLock; /* List of locks held on this shared-btree struct */ Btree *pWriter; /* Btree with currently open write transaction */ #endif u8 *pTmpSpace; /* Temp space sufficient to hold a single cell */ | | | 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 | #ifndef SQLITE_OMIT_SHARED_CACHE int nRef; /* Number of references to this structure */ BtShared *pNext; /* Next on a list of sharable BtShared structs */ BtLock *pLock; /* List of locks held on this shared-btree struct */ Btree *pWriter; /* Btree with currently open write transaction */ #endif u8 *pTmpSpace; /* Temp space sufficient to hold a single cell */ #ifdef SQLITE_ENABLE_CONCURRENT BtreePtrmap *pMap; #endif }; /* ** Allowed values for BtShared.btsFlags */ |
︙ | ︙ | |||
659 660 661 662 663 664 665 | */ #ifndef SQLITE_OMIT_AUTOVACUUM #define ISAUTOVACUUM (pBt->autoVacuum) #else #define ISAUTOVACUUM 0 #endif | | | | | | 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 | */ #ifndef SQLITE_OMIT_AUTOVACUUM #define ISAUTOVACUUM (pBt->autoVacuum) #else #define ISAUTOVACUUM 0 #endif #ifdef SQLITE_ENABLE_CONCURRENT # define ISCONCURRENT (pBt->pMap!=0) #else # define ISCONCURRENT 0 #endif #define REQUIRE_PTRMAP (ISAUTOVACUUM || ISCONCURRENT) /* ** This structure is passed around through all the sanity checking routines ** in order to keep track of some global state information. ** ** The aRef[] array is allocated so that there is 1 bit for each page in ** the database. As the integrity-check proceeds, for each page used in |
︙ | ︙ |
Changes to src/build.c.
︙ | ︙ | |||
3858 3859 3860 3861 3862 3863 3864 | if( !v ) return; if( type==TK_IMMEDIATE || type==TK_EXCLUSIVE ){ for(i=0; i<db->nDb; i++){ sqlite3VdbeAddOp2(v, OP_Transaction, i, (type==TK_EXCLUSIVE)+1); sqlite3VdbeUsesBtree(v, i); } } | | | 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 | if( !v ) return; if( type==TK_IMMEDIATE || type==TK_EXCLUSIVE ){ for(i=0; i<db->nDb; i++){ sqlite3VdbeAddOp2(v, OP_Transaction, i, (type==TK_EXCLUSIVE)+1); sqlite3VdbeUsesBtree(v, i); } } sqlite3VdbeAddOp3(v, OP_AutoCommit, 0, 0, (type==TK_CONCURRENT)); } /* ** Commit a transaction */ void sqlite3CommitTransaction(Parse *pParse){ Vdbe *v; |
︙ | ︙ |
Changes to src/pager.c.
︙ | ︙ | |||
653 654 655 656 657 658 659 | Pgno dbFileSize; /* Number of pages in the database file */ Pgno dbHintSize; /* Value passed to FCNTL_SIZE_HINT call */ int errCode; /* One of several kinds of errors */ int nRec; /* Pages journalled since last j-header written */ u32 cksumInit; /* Quasi-random value added to every checksum */ u32 nSubRec; /* Number of records written to sub-journal */ Bitvec *pInJournal; /* One bit for each page in the database file */ | | | | 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 | Pgno dbFileSize; /* Number of pages in the database file */ Pgno dbHintSize; /* Value passed to FCNTL_SIZE_HINT call */ int errCode; /* One of several kinds of errors */ int nRec; /* Pages journalled since last j-header written */ u32 cksumInit; /* Quasi-random value added to every checksum */ u32 nSubRec; /* Number of records written to sub-journal */ Bitvec *pInJournal; /* One bit for each page in the database file */ #ifdef SQLITE_ENABLE_CONCURRENT Bitvec *pAllRead; /* Pages read within current CONCURRENT trans. */ #endif sqlite3_file *fd; /* File descriptor for database */ sqlite3_file *jfd; /* File descriptor for main journal */ sqlite3_file *sjfd; /* File descriptor for sub-journal */ i64 journalOff; /* Current write offset in the journal file */ i64 journalHdr; /* Byte offset to previous journal header */ sqlite3_backup *pBackup; /* Pointer to list of ongoing backup processes */ |
︙ | ︙ | |||
1743 1744 1745 1746 1747 1748 1749 | /* ** Free the Pager.pInJournal and Pager.pAllRead bitvec objects. */ static void pagerFreeBitvecs(Pager *pPager){ sqlite3BitvecDestroy(pPager->pInJournal); pPager->pInJournal = 0; | | | 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 | /* ** Free the Pager.pInJournal and Pager.pAllRead bitvec objects. */ static void pagerFreeBitvecs(Pager *pPager){ sqlite3BitvecDestroy(pPager->pInJournal); pPager->pInJournal = 0; #ifdef SQLITE_ENABLE_CONCURRENT sqlite3BitvecDestroy(pPager->pAllRead); pPager->pAllRead = 0; #endif } /* ** This function is a no-op if the pager is in exclusive mode and not |
︙ | ︙ | |||
3031 3032 3033 3034 3035 3036 3037 | ** + 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); | | | | 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 | ** + 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 CONCURRENT 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_CONCURRENT 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; |
︙ | ︙ | |||
4448 4449 4450 4451 4452 4453 4454 | || (pPg->flags & PGHDR_NEED_SYNC)!=0) ){ return SQLITE_OK; } pPg->pDirty = 0; if( pagerUseWal(pPager) ){ | | | | 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 | || (pPg->flags & PGHDR_NEED_SYNC)!=0) ){ return SQLITE_OK; } pPg->pDirty = 0; if( pagerUseWal(pPager) ){ /* If the transaction is a "BEGIN CONCURRENT" transaction, the page ** cannot be flushed to disk. Return early in this case. */ #ifdef SQLITE_ENABLE_CONCURRENT 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); |
︙ | ︙ | |||
5294 5295 5296 5297 5298 5299 5300 | assert( noContent==0 || bMmapOk==0 ); if( pgno==0 ){ return SQLITE_CORRUPT_BKPT; } pPager->hasBeenUsed = 1; | | | | 5294 5295 5296 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 5311 | assert( noContent==0 || bMmapOk==0 ); if( pgno==0 ){ return SQLITE_CORRUPT_BKPT; } pPager->hasBeenUsed = 1; /* If this is an CONCURRENT 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_CONCURRENT 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. |
︙ | ︙ | |||
5589 5590 5591 5592 5593 5594 5595 | ** ** If the exFlag argument is 0, then acquire at least a RESERVED ** lock on the database file. If exFlag is >0, then acquire at least ** an EXCLUSIVE lock. If such a lock is already held, no locking ** functions need be called. ** ** If (exFlag<0) and the database is in WAL mode, do not take any locks. | | | | | 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 5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641 5642 5643 | ** ** If the exFlag argument is 0, then acquire at least a RESERVED ** lock on the database file. If exFlag is >0, then acquire at least ** an EXCLUSIVE lock. If such a lock is already held, no locking ** functions need be called. ** ** If (exFlag<0) and the database is in WAL mode, do not take any locks. ** The transaction will run in CONCURRENT mode instead. ** ** If the subjInMemory argument is non-zero, then any sub-journal opened ** within this transaction will be opened as an in-memory file. This ** has no effect if the sub-journal is already opened (as it may be when ** running in exclusive mode) or if the transaction does not require a ** sub-journal. If the subjInMemory argument is zero, then any required ** sub-journal is implemented in-memory if pPager is an in-memory database, ** or using a temporary file otherwise. */ int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){ int rc = SQLITE_OK; 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_CONCURRENT 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_CONCURRENT if( exFlag<0 ){ pPager->pAllRead = sqlite3BitvecCreate(pPager->dbSize); if( pPager->pAllRead==0 ){ rc = SQLITE_NOMEM; } }else #endif |
︙ | ︙ | |||
5945 5946 5947 5948 5949 5950 5951 | } /* ** 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. */ | | | 5945 5946 5947 5948 5949 5950 5951 5952 5953 5954 5955 5956 5957 5958 5959 | } /* ** 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_CONCURRENT) || !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 |
︙ | ︙ | |||
6101 6102 6103 6104 6105 6106 6107 | } /* ** 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. ** | | | | | | | | | 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 | } /* ** 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-CONCURRENT transaction on a wal-mode database, this ** function is a no-op. ** ** If this is an CONCURRENT 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 CONCURRENT 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_CONCURRENT else{ if( pPager->pAllRead ){ /* This is an CONCURRENT 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_CONCURRENT /* ** This function is called as part of committing an CONCURRENT 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. |
︙ | ︙ | |||
6181 6182 6183 6184 6185 6186 6187 | ** Set the in-memory cache of the database file size to nSz pages. */ void sqlite3PagerSetDbsize(Pager *pPager, Pgno nSz){ pPager->dbSize = nSz; } /* | | | | | 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 | ** 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 CONCURRENT transaction. */ int sqlite3PagerIsConcurrent(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_CONCURRENT */ /* ** 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/pager.h.
︙ | ︙ | |||
191 192 193 194 195 196 197 | /* Functions used to truncate the database file. */ void sqlite3PagerTruncateImage(Pager*,Pgno); void sqlite3PagerRekey(DbPage*, Pgno, u16); void sqlite3PagerDropExclusiveLock(Pager*); | | | 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | /* Functions used to truncate the database file. */ void sqlite3PagerTruncateImage(Pager*,Pgno); void sqlite3PagerRekey(DbPage*, Pgno, u16); void sqlite3PagerDropExclusiveLock(Pager*); int sqlite3PagerIsConcurrent(Pager*); int sqlite3PagerIswriteable(DbPage*); int sqlite3PagerUpgradeSnapshot(Pager *pPager, DbPage*); void sqlite3PagerSetDbsize(Pager *pPager, Pgno); #if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_WAL) void *sqlite3PagerCodec(DbPage *); #endif |
︙ | ︙ |
Changes to src/parse.y.
︙ | ︙ | |||
117 118 119 120 121 122 123 | trans_opt ::= TRANSACTION. trans_opt ::= TRANSACTION nm. %type transtype {int} transtype(A) ::= . {A = TK_DEFERRED;} transtype(A) ::= DEFERRED(X). {A = @X;} transtype(A) ::= IMMEDIATE(X). {A = @X;} transtype(A) ::= EXCLUSIVE(X). {A = @X;} | | | 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | trans_opt ::= TRANSACTION. trans_opt ::= TRANSACTION nm. %type transtype {int} transtype(A) ::= . {A = TK_DEFERRED;} transtype(A) ::= DEFERRED(X). {A = @X;} transtype(A) ::= IMMEDIATE(X). {A = @X;} transtype(A) ::= EXCLUSIVE(X). {A = @X;} transtype(A) ::= CONCURRENT(X). {A = @X;} cmd ::= COMMIT trans_opt. {sqlite3CommitTransaction(pParse);} cmd ::= END trans_opt. {sqlite3CommitTransaction(pParse);} cmd ::= ROLLBACK trans_opt. {sqlite3RollbackTransaction(pParse);} savepoint_opt ::= SAVEPOINT. savepoint_opt ::= . cmd ::= SAVEPOINT nm(X). { |
︙ | ︙ |
Changes to src/sqliteInt.h.
︙ | ︙ | |||
1146 1147 1148 1149 1150 1151 1152 | i64 szMmap; /* Default mmap_size setting */ unsigned int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */ int errCode; /* Most recent error code (SQLITE_*) */ int errMask; /* & result codes with this before returning */ u16 dbOptFlags; /* Flags to enable/disable optimizations */ u8 enc; /* Text encoding */ u8 autoCommit; /* The auto-commit flag. */ | | | 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 | i64 szMmap; /* Default mmap_size setting */ unsigned int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */ int errCode; /* Most recent error code (SQLITE_*) */ int errMask; /* & result codes with this before returning */ u16 dbOptFlags; /* Flags to enable/disable optimizations */ u8 enc; /* Text encoding */ u8 autoCommit; /* The auto-commit flag. */ u8 bConcurrent; /* Current transaction is "CONCURRENT" */ u8 temp_store; /* 1: file 2: memory 0: default */ u8 mallocFailed; /* True if we have seen a malloc failure */ u8 dfltLockMode; /* Default locking-mode for attached dbs */ signed char nextAutovac; /* Autovac setting after VACUUM if >=0 */ u8 suppressErr; /* Do not issue error messages if true */ u8 vtabOnConflict; /* Value to return for s3_vtab_on_conflict() */ u8 isTransactionSavepoint; /* True if the outermost savepoint is a TS */ |
︙ | ︙ |
Changes to src/test_config.c.
︙ | ︙ | |||
569 570 571 572 573 574 575 | #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 | | | | | 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 | #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_CONCURRENT Tcl_SetVar2(interp, "sqlite_options", "concurrent", "1", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "concurrent", "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/vacuum.c.
︙ | ︙ | |||
352 353 354 355 356 357 358 | ** database. No locks are held on any other files (since the main file ** was committed at the btree level). So it safe to end the transaction ** by manually setting the autoCommit flag to true and detaching the ** vacuum database. The vacuum_db journal file is deleted when the pager ** is closed by the DETACH. */ db->autoCommit = 1; | | | 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 | ** database. No locks are held on any other files (since the main file ** was committed at the btree level). So it safe to end the transaction ** by manually setting the autoCommit flag to true and detaching the ** vacuum database. The vacuum_db journal file is deleted when the pager ** is closed by the DETACH. */ db->autoCommit = 1; assert( db->bConcurrent==0 ); if( pDb ){ sqlite3BtreeClose(pDb->pBt); pDb->pBt = 0; pDb->pSchema = 0; } |
︙ | ︙ |
Changes to src/vdbe.c.
︙ | ︙ | |||
2891 2892 2893 2894 2895 2896 2897 | }else{ /* Determine whether or not this is a transaction savepoint. If so, ** and this is a RELEASE command, then the current transaction ** is committed. */ int isTransaction = pSavepoint->pNext==0 && db->isTransactionSavepoint; | | | 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 | }else{ /* Determine whether or not this is a transaction savepoint. If so, ** and this is a RELEASE command, then the current transaction ** is committed. */ int isTransaction = pSavepoint->pNext==0 && db->isTransactionSavepoint; assert( db->bConcurrent==0 || db->isTransactionSavepoint==0 ); if( isTransaction && p1==SAVEPOINT_RELEASE ){ if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){ goto vdbe_return; } db->autoCommit = 1; if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){ p->pc = (int)(pOp - aOp); |
︙ | ︙ | |||
2975 2976 2977 2978 2979 2980 2981 | ** ** Set the database auto-commit flag to P1 (1 or 0). If P2 is true, roll ** back any currently active btree transactions. If there are any active ** VMs (apart from this one), then a ROLLBACK fails. A COMMIT fails if ** there are active writing VMs or active VMs that use shared cache. ** ** If P3 is non-zero, then this instruction is being executed as part of | | | | | | | | | | | 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 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 | ** ** Set the database auto-commit flag to P1 (1 or 0). If P2 is true, roll ** back any currently active btree transactions. If there are any active ** VMs (apart from this one), then a ROLLBACK fails. A COMMIT fails if ** there are active writing VMs or active VMs that use shared cache. ** ** If P3 is non-zero, then this instruction is being executed as part of ** a "BEGIN CONCURRENT" command. ** ** This instruction causes the VM to halt. */ case OP_AutoCommit: { int desiredAutoCommit; int iRollback; int turnOnAC; int bConcurrent; int hrc; desiredAutoCommit = pOp->p1; iRollback = pOp->p2; bConcurrent = pOp->p3; turnOnAC = desiredAutoCommit && !db->autoCommit; assert( desiredAutoCommit==1 || desiredAutoCommit==0 ); assert( desiredAutoCommit==1 || iRollback==0 ); assert( desiredAutoCommit==0 || bConcurrent==0 ); assert( db->autoCommit==0 || db->bConcurrent==0 ); assert( db->nVdbeActive>0 ); /* At least this one VM is active */ assert( p->bIsReader ); if( turnOnAC && !iRollback && (db->nVdbeWrite>0 || (db->bConcurrent && db->nVdbeActive>1)) ){ /* A transaction may only be committed if there are no other active ** writer VMs. If the transaction is CONCURRENT, then it may only be ** committed if there are no active VMs at all (readers or writers). ** ** If this instruction is a COMMIT and the transaction may not be ** committed due to one of the conditions above, return an error ** indicating that other VMs must complete before the COMMIT can ** be processed. */ sqlite3VdbeError(p, "cannot commit transaction - " "SQL statements in progress"); rc = SQLITE_BUSY; }else if( desiredAutoCommit!=db->autoCommit ){ if( iRollback ){ assert( desiredAutoCommit==1 ); sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK); db->autoCommit = 1; db->bConcurrent = 0; }else if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){ goto vdbe_return; }else{ db->autoCommit = (u8)desiredAutoCommit; } hrc = sqlite3VdbeHalt(p); if( (hrc & 0xFF)==SQLITE_BUSY ){ p->pc = (int)(pOp - aOp); db->autoCommit = (u8)(1-desiredAutoCommit); p->rc = hrc; rc = SQLITE_BUSY; goto vdbe_return; } db->bConcurrent = (u8)bConcurrent; assert( db->nStatement==0 ); sqlite3CloseSavepoints(db); if( p->rc==SQLITE_OK ){ rc = SQLITE_DONE; }else{ rc = SQLITE_ERROR; } |
︙ | ︙ | |||
3223 3224 3225 3226 3227 3228 3229 | assert( DbMaskTest(p->btreeMask, pOp->p1) ); assert( p->readOnly==0 ); pDb = &db->aDb[pOp->p1]; assert( pDb->pBt!=0 ); assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) ); pIn3 = &aMem[pOp->p3]; sqlite3VdbeMemIntegerify(pIn3); | | | | | | 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 | assert( DbMaskTest(p->btreeMask, pOp->p1) ); assert( p->readOnly==0 ); pDb = &db->aDb[pOp->p1]; assert( pDb->pBt!=0 ); assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) ); pIn3 = &aMem[pOp->p3]; sqlite3VdbeMemIntegerify(pIn3); #ifdef SQLITE_ENABLE_CONCURRENT if( db->bConcurrent && (pOp->p2==BTREE_USER_VERSION || pOp->p2==BTREE_APPLICATION_ID) ){ rc = SQLITE_ERROR; sqlite3VdbeError(p, "cannot modify %s within CONCURRENT transaction", pOp->p2==BTREE_USER_VERSION ? "user_version" : "application_id" ); break; } #endif /* See note about index shifting on OP_ReadCookie */ rc = sqlite3BtreeUpdateMeta(pDb->pBt, pOp->p2, (int)pIn3->u.i); if( pOp->p2==BTREE_SCHEMA_VERSION ){ /* When the schema cookie changes, record the new cookie internally */ assert( db->bConcurrent==0 ); pDb->pSchema->schema_cookie = (int)pIn3->u.i; db->flags |= SQLITE_InternChanges; }else if( pOp->p2==BTREE_FILE_FORMAT ){ /* Record changes in the file format */ pDb->pSchema->file_format = (u8)pIn3->u.i; } if( pOp->p1==1 ){ |
︙ | ︙ | |||
6111 6112 6113 6114 6115 6116 6117 | ** P2 contains the root-page of the table to lock. ** ** P4 contains a pointer to the name of the table being locked. This is only ** used to generate an error message if the lock cannot be obtained. */ case OP_TableLock: { u8 isWriteLock = (u8)pOp->p3; | | | | | 6111 6112 6113 6114 6115 6116 6117 6118 6119 6120 6121 6122 6123 6124 6125 6126 6127 6128 6129 | ** P2 contains the root-page of the table to lock. ** ** P4 contains a pointer to the name of the table being locked. This is only ** used to generate an error message if the lock cannot be obtained. */ case OP_TableLock: { u8 isWriteLock = (u8)pOp->p3; #ifdef SQLITE_ENABLE_CONCURRENT if( isWriteLock && db->bConcurrent && pOp->p2==1 ){ rc = SQLITE_ERROR; sqlite3VdbeError(p, "cannot modify database schema within CONCURRENT transaction"); rc = SQLITE_ERROR; break; } #endif if( isWriteLock || 0==(db->flags&SQLITE_ReadUncommitted) ){ int p1 = pOp->p1; assert( p1>=0 && p1<db->nDb ); |
︙ | ︙ |
Changes to src/vdbeaux.c.
︙ | ︙ | |||
2021 2022 2023 2024 2025 2026 2027 | if( sqlite3BtreeIsInTrans(pBt) ){ needXcommit = 1; if( i!=1 ) nTrans++; rc = sqlite3BtreeExclusiveLock(pBt); } } | | | | 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 | if( sqlite3BtreeIsInTrans(pBt) ){ needXcommit = 1; if( i!=1 ) nTrans++; rc = sqlite3BtreeExclusiveLock(pBt); } } #ifdef SQLITE_ENABLE_CONCURRENT if( db->bConcurrent && (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); |
︙ | ︙ | |||
2439 2440 2441 2442 2443 2444 2445 | }else{ /* We are forced to roll back the active transaction. Before doing ** so, abort any other statements this handle currently has active. */ sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK); sqlite3CloseSavepoints(db); db->autoCommit = 1; | | | 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 | }else{ /* We are forced to roll back the active transaction. Before doing ** so, abort any other statements this handle currently has active. */ sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK); sqlite3CloseSavepoints(db); db->autoCommit = 1; db->bConcurrent = 0; p->nChange = 0; } } } /* Check for immediate foreign key violations. */ if( p->rc==SQLITE_OK ){ |
︙ | ︙ | |||
2502 2503 2504 2505 2506 2507 2508 | eStatementOp = SAVEPOINT_RELEASE; }else if( p->errorAction==OE_Abort ){ eStatementOp = SAVEPOINT_ROLLBACK; }else{ sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK); sqlite3CloseSavepoints(db); db->autoCommit = 1; | | | 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 | eStatementOp = SAVEPOINT_RELEASE; }else if( p->errorAction==OE_Abort ){ eStatementOp = SAVEPOINT_ROLLBACK; }else{ sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK); sqlite3CloseSavepoints(db); db->autoCommit = 1; db->bConcurrent = 0; p->nChange = 0; } } /* If eStatementOp is non-zero, then a statement transaction needs to ** be committed or rolled back. Call sqlite3VdbeCloseStatement() to ** do so. If this operation returns an error, and the current statement |
︙ | ︙ | |||
2524 2525 2526 2527 2528 2529 2530 | p->rc = rc; sqlite3DbFree(db, p->zErrMsg); p->zErrMsg = 0; } sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK); sqlite3CloseSavepoints(db); db->autoCommit = 1; | | | 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 | p->rc = rc; sqlite3DbFree(db, p->zErrMsg); p->zErrMsg = 0; } sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK); sqlite3CloseSavepoints(db); db->autoCommit = 1; db->bConcurrent = 0; p->nChange = 0; } } /* If this was an INSERT, UPDATE or DELETE and no statement transaction ** has been rolled back, update the database connection change-counter. */ |
︙ | ︙ |
Changes to src/wal.c.
︙ | ︙ | |||
764 765 766 767 768 769 770 | } #endif /*defined(SQLITE_TEST) || defined(SQLITE_DEBUG) */ /* ** Set or release locks on the WAL. Locks are either shared or exclusive. ** A lock cannot be moved directly between shared and exclusive - it must go | | | 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 | } #endif /*defined(SQLITE_TEST) || defined(SQLITE_DEBUG) */ /* ** Set or release locks on the WAL. Locks are either shared or exclusive. ** A lock cannot be moved directly between shared and exclusive - it must go ** through the concurrent state first. ** ** In locking_mode=EXCLUSIVE, all of these routines become no-ops. */ static int walLockShared(Wal *pWal, int lockIdx){ int rc; if( pWal->exclusiveMode ) return SQLITE_OK; rc = sqlite3OsShmLock(pWal->pDbFd, lockIdx, 1, |
︙ | ︙ | |||
1064 1065 1066 1067 1068 1069 1070 | u32 aFrameCksum[2] = {0, 0}; int iLock; /* Lock offset to lock for checkpoint */ int nLock; /* Number of locks to hold */ /* Obtain an exclusive lock on all byte in the locking range not already ** locked by the caller. The caller is guaranteed to have locked the ** WAL_WRITE_LOCK byte, and may have also locked the WAL_CKPT_LOCK byte. | | | 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 | u32 aFrameCksum[2] = {0, 0}; int iLock; /* Lock offset to lock for checkpoint */ int nLock; /* Number of locks to hold */ /* Obtain an exclusive lock on all byte in the locking range not already ** locked by the caller. The caller is guaranteed to have locked the ** WAL_WRITE_LOCK byte, and may have also locked the WAL_CKPT_LOCK byte. ** If successful, the same bytes that are locked here are concurrent before ** this function returns. */ assert( pWal->ckptLock==1 || pWal->ckptLock==0 ); assert( WAL_ALL_BUT_WRITE==WAL_WRITE_LOCK+1 ); assert( WAL_CKPT_LOCK==WAL_ALL_BUT_WRITE ); assert( pWal->writeLock ); iLock = WAL_ALL_BUT_WRITE + pWal->ckptLock; |
︙ | ︙ | |||
2604 2605 2606 2607 2608 2609 2610 | testcase( (rc&0xff)==SQLITE_IOERR ); testcase( rc==SQLITE_PROTOCOL ); testcase( rc==SQLITE_OK ); return rc; } | | | | | | 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 2630 2631 2632 2633 2634 2635 2636 | testcase( (rc&0xff)==SQLITE_IOERR ); testcase( rc==SQLITE_PROTOCOL ); testcase( rc==SQLITE_OK ); return rc; } #ifdef SQLITE_ENABLE_CONCURRENT /* ** This function is only ever called when committing a "BEGIN CONCURRENT" ** 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 concurrent 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 CONCURRENT 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, |
︙ | ︙ | |||
2653 2654 2655 2656 2657 2658 2659 | */ if( rc==SQLITE_OK ){ WalIndexHdr head; if( walIndexLoadHdr(pWal, &head) ){ /* This branch is taken if the wal-index header is corrupted. This ** occurs if some other writer has crashed while committing a | | | 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 | */ if( rc==SQLITE_OK ){ WalIndexHdr head; if( walIndexLoadHdr(pWal, &head) ){ /* This branch is taken if the wal-index header is corrupted. This ** occurs if some other writer has crashed while committing a ** transaction to this database since the current concurrent transaction ** was opened. */ rc = SQLITE_BUSY_SNAPSHOT; }else if( memcmp(&pWal->hdr, (void*)&head, sizeof(WalIndexHdr))!=0 ){ int iHash; int iLastHash = walFramePage(head.mxFrame); u32 iFirst = pWal->hdr.mxFrame+1; /* First wal frame to check */ if( memcmp(pWal->hdr.aSalt, (u32*)head.aSalt, sizeof(u32)*2) ){ |
︙ | ︙ | |||
2694 2695 2696 2697 2698 2699 2700 | iOffset = walFrameOffset(i+iZero, sz) + WAL_FRAME_HDRSIZE + 40; rc = sqlite3OsRead(pWal->pWalFd, aNew, sizeof(aNew), iOffset); if( rc==SQLITE_OK && memcmp(aOld, aNew, sizeof(aNew)) ){ rc = SQLITE_BUSY_SNAPSHOT; } }else if( sqlite3BitvecTestNotNull(pAllRead, aPgno[i]) ){ sqlite3_log(SQLITE_OK, | | | | 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 | iOffset = walFrameOffset(i+iZero, sz) + WAL_FRAME_HDRSIZE + 40; rc = sqlite3OsRead(pWal->pWalFd, aNew, sizeof(aNew), iOffset); if( rc==SQLITE_OK && memcmp(aOld, aNew, sizeof(aNew)) ){ rc = SQLITE_BUSY_SNAPSHOT; } }else if( sqlite3BitvecTestNotNull(pAllRead, aPgno[i]) ){ sqlite3_log(SQLITE_OK, "cannot commit CONCURRENT transaction (conflict at page %d)", (int)aPgno[i] ); rc = SQLITE_BUSY_SNAPSHOT; }else if( (pPg = sqlite3PagerLookup(pPager, aPgno[i])) ){ /* Page aPgno[i], which is present in the pager cache, has been ** modified since the current CONCURRENT transaction was started. ** However it was not read by the current transaction, so is not ** a conflict. There are two possibilities: (a) the page was ** allocated at the of the file by the current transaction or ** (b) was present in the cache at the start of the transaction. ** ** For case (a), do nothing. This page will be moved within the ** database file by the commit code to avoid the conflict. The |
︙ | ︙ | |||
2731 2732 2733 2734 2735 2736 2737 | } } return rc; } /* | | | | | 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 | } } return rc; } /* ** This function is called as part of committing an CONCURRENT 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 CONCURRENT 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_CONCURRENT */ /* ** End a write transaction. The commit has already been done. This ** routine merely releases the lock. */ int sqlite3WalEndWriteTransaction(Wal *pWal){ if( pWal->writeLock ){ |
︙ | ︙ |
Name change from test/unlocked.test to test/concurrent.test.
︙ | ︙ | |||
9 10 11 12 13 14 15 | # #*********************************************************************** # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl | | | | | | | | 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 | # #*********************************************************************** # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl set ::testprefix concurrent ifcapable !concurrent { 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); BEGIN CONCURRENT; INSERT INTO t1 VALUES(1, 'abcd'); COMMIT; } do_execsql_test 1.2 { SELECT * FROM t1; } {1 abcd} do_execsql_test 1.3 { BEGIN CONCURRENT; INSERT INTO t1 VALUES(2, 'efgh'); ROLLBACK; } do_execsql_test 1.4 { SELECT * FROM t1; } {1 abcd} #------------------------------------------------------------------------- # CONCURRENT transactions cannot do cache spills. # foreach {tn trans spill} { 1 {BEGIN CONCURRENT} 0 2 {BEGIN} 1 } { do_test 1.5.$tn { sqlite3 db2 test.db set walsz [file size test.db-wal] execsql { PRAGMA cache_size = 10 } db2 |
︙ | ︙ | |||
68 69 70 71 72 73 74 | } [expr !$spill] execsql ROLLBACK db2 db2 close } #------------------------------------------------------------------------- | | | | 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 | } [expr !$spill] execsql ROLLBACK db2 db2 close } #------------------------------------------------------------------------- # CONCURRENT transactions man not be committed while there are active # readers. do_execsql_test 1.6.setup { DROP TABLE t1; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); INSERT INTO t1 VALUES(3, 4); INSERT INTO t1 VALUES(5, 6); } foreach {tn trans commit_ok} { 1 {BEGIN CONCURRENT} 0 2 {BEGIN} 1 } { do_test 1.6.$tn.1 { set stmt [sqlite3_prepare db "SELECT * FROM t1" -1 dummy] sqlite3_step $stmt } SQLITE_ROW do_test 1.6.$tn.2 { |
︙ | ︙ | |||
101 102 103 104 105 106 107 | } sqlite3_finalize $stmt catchsql ROLLBACK } #------------------------------------------------------------------------- | | | | | | | | | | | | | | | 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 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 | } sqlite3_finalize $stmt catchsql ROLLBACK } #------------------------------------------------------------------------- # CONCURRENT transactions may not modify the db schema. # foreach {tn sql} { 1 { CREATE TABLE xx(a, b) } 2 { DROP TABLE t1 } 3 { CREATE INDEX i1 ON t1(a) } 4 { CREATE VIEW v1 AS SELECT * FROM t1 } 5 { CREATE TEMP TABLE xx(a, b) } } { do_catchsql_test 1.7.$tn.1 " BEGIN CONCURRENT; $sql " {1 {cannot modify database schema within CONCURRENT transaction}} do_execsql_test 1.7.$tn.2 { SELECT sql FROM sqlite_master; SELECT sql FROM sqlite_temp_master; } {{CREATE TABLE t1(a, b)}} do_execsql_test 1.7.$tn.3 COMMIT } #------------------------------------------------------------------------- # If an auto-vacuum database is written within an CONCURRENT transaction, it # is handled in the same way as for a non-CONCURRENT transaction. # reset_db do_execsql_test 1.8.1 { PRAGMA auto_vacuum = 1; PRAGMA journal_mode = wal; CREATE TABLE t1(x, y); INSERT INTO t1 VALUES('x', 'y'); } {wal} do_execsql_test 1.8.2 { BEGIN CONCURRENT; SELECT * FROM t1; COMMIT; } {x y} do_catchsql_test 1.8.3 { BEGIN CONCURRENT; INSERT INTO t1 VALUES('a', 'b'); } {0 {}} do_test 1.8.4 { sqlite3 db2 test.db catchsql { BEGIN CONCURRENT; INSERT INTO t1 VALUES('c', 'd'); } db2 } {1 {database is locked}} do_test 1.8.5 { db eval COMMIT db2 eval COMMIT } {} do_multiclient_test tn { #----------------------------------------------------------------------- # 1. Start an CONCURRENT transaction using [db1]. # # 2. Start and then rollback a regular transaction using [db2]. This # can be done as the ongoing [db1] transaction is CONCURRENT. # # 3. The [db1] transaction can now be committed, as [db2] has relinquished # the write lock. # do_test 2.$tn.1.1 { sql1 { PRAGMA journal_mode = wal; CREATE TABLE t1(k INTEGER PRIMARY KEY, v); INSERT INTO t1 VALUES(1, 'one'); } sql1 { BEGIN CONCURRENT; INSERT INTO t1 VALUES(2, 'two'); } code1 { sqlite3_get_autocommit db } } 0 do_test 2.$tn.1.2 { sql2 { BEGIN; INSERT INTO t1 VALUES(3, 'three'); ROLLBACK; } } {} do_test 2.$tn.1.3 { sql1 COMMIT sql2 { SELECT * FROM t1 } } {1 one 2 two} #----------------------------------------------------------------------- # 1. Start an CONCURRENT transaction using [db1]. # # 2. Commit a transaction using [db2]. # # 3. Try to commit with [db1]. Check that SQLITE_BUSY_SNAPSHOT is returned, # and the transaction is not rolled back. # do_test 2.$tn.2.1 { sql1 { BEGIN CONCURRENT; INSERT INTO t1 VALUES(-1, 'hello world'); } } {} do_test 2.$tn.2.2 { sql2 { INSERT INTO t1 VALUES(3, 'three'); |
︙ | ︙ | |||
234 235 236 237 238 239 240 | sql1 { SELECT * FROM t1; ROLLBACK; } } {-1 {hello world} 1 one 2 two} #----------------------------------------------------------------------- | | | | 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 | sql1 { SELECT * FROM t1; ROLLBACK; } } {-1 {hello world} 1 one 2 two} #----------------------------------------------------------------------- # 1. Start an CONCURRENT transaction using [db1]. # # 2. Open a transaction using [db2]. # # 3. Try to commit with [db1]. Check that SQLITE_BUSY is returned, # and the transaction is not rolled back. # # 4. Have [db2] roll its transaction back. Then check that [db1] can # commit. # do_test 2.$tn.3.1 { sql1 { BEGIN CONCURRENT; INSERT INTO t1 VALUES(4, 'four'); } } {} do_test 2.$tn.3.2 { sql2 { BEGIN; |
︙ | ︙ | |||
289 290 291 292 293 294 295 | # 3. See if it worked. # do_test 2.$tn.4.1 { sql1 { CREATE TABLE t2(a, b) } } {} do_test 2.$tn.4.2 { sql2 { | | | | | | | | | | 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 | # 3. See if it worked. # do_test 2.$tn.4.1 { sql1 { CREATE TABLE t2(a, b) } } {} do_test 2.$tn.4.2 { sql2 { BEGIN CONCURRENT; INSERT INTO t2 VALUES('i', 'n'); } sql1 { BEGIN CONCURRENT; INSERT INTO t1 VALUES(5, 'five'); COMMIT; } sql2 COMMIT } {} do_test 2.$tn.4.3.1 { sql2 {SELECT * FROM t1} } {1 one 2 two 3 three 4 four 5 five} do_test 2.$tn.4.3.2 { sql1 {SELECT * FROM t1} } {1 one 2 two 3 three 4 four 5 five} do_test 2.$tn.4.3.3 { sql2 {SELECT * FROM t2} } {i n} do_test 2.$tn.4.3.4 { sql1 {SELECT * FROM t2} } {i n} #----------------------------------------------------------------------- # The "schema cookie" issue. # # 1. Begin and CONCURRENT write to "t1" using [db] # # 2. Create an index on t1 using [db2]. # # 3. Attempt to commit the CONCURRENT write. This is an SQLITE_BUSY_SNAPSHOT, # even though there is no page collision. # do_test 2.$tn.5.1 { sql1 { BEGIN CONCURRENT; INSERT INTO t1 VALUES(6, 'six'); } } {} do_test 2.$tn.5.2 { sql2 { CREATE INDEX i1 ON t1(v); } } {} do_test 2.$tn.5.3 { list [catch { sql1 { COMMIT } } msg] $msg [sqlite3_errcode db] } {1 {database is locked} SQLITE_BUSY_SNAPSHOT} do_test 2.$tn.5.4 { sql2 { PRAGMA integrity_check } } {ok} catch { sql1 ROLLBACK } #----------------------------------------------------------------------- # # 1. Begin an CONCURRENT write to "t1" using [db] # # 2. Lots of inserts into t2. Enough to grow the db file and modify page 1. # # 3. Check that the CONCURRENT transaction can not be committed. # do_test 2.$tn.6.1 { sql1 { BEGIN CONCURRENT; INSERT INTO t1 VALUES(6, 'six'); } } {} do_test 2.$tn.6.2 { sql2 { WITH src(a,b) AS ( |
︙ | ︙ | |||
380 381 382 383 384 385 386 | SELECT count(*) FROM t1; SELECT count(*) FROM t2; } } {5 10001} #----------------------------------------------------------------------- # | | | | | 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 | SELECT count(*) FROM t1; SELECT count(*) FROM t2; } } {5 10001} #----------------------------------------------------------------------- # # 1. Begin an big CONCURRENT write to "t1" using [db] - large enough to # grow the db file. # # 2. Lots of inserts into t2. Also enough to grow the db file. # # 3. Check that the CONCURRENT transaction cannot be committed (due to a clash # on page 1 - the db size field). # do_test 2.$tn.7.1 { sql1 { BEGIN CONCURRENT; WITH src(a,b) AS ( VALUES(10000,10000) UNION ALL SELECT a+1,b+1 FROM src WHERE a<20000 ) INSERT INTO t1 SELECT * FROM src; } } {} do_test 2.$tn.7.2 { |
︙ | ︙ | |||
423 424 425 426 427 428 429 | do_execsql_test 3.0 { PRAGMA journal_mode = wal; CREATE TABLE t1(x, y); INSERT INTO t1 VALUES('a', 'b'); PRAGMA user_version = 10; } {wal} do_execsql_test 3.1 { | | | | | 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 | do_execsql_test 3.0 { PRAGMA journal_mode = wal; CREATE TABLE t1(x, y); INSERT INTO t1 VALUES('a', 'b'); PRAGMA user_version = 10; } {wal} do_execsql_test 3.1 { BEGIN CONCURRENT; INSERT INTO t1 VALUES('c', 'd'); SELECT * FROM t1; } {a b c d} do_catchsql_test 3.2 { PRAGMA user_version = 11; } {1 {cannot modify user_version within CONCURRENT transaction}} do_execsql_test 3.3 { PRAGMA user_version; SELECT * FROM t1; } {10 a b c d} do_catchsql_test 3.4 { PRAGMA application_id = 11; } {1 {cannot modify application_id within CONCURRENT transaction}} do_execsql_test 3.5 { COMMIT; PRAGMA user_version; PRAGMA application_id; SELECT * FROM t1; } {10 0 a b c d} finish_test |
Name change from test/unlocked2.test to test/concurrent2.test.
︙ | ︙ | |||
9 10 11 12 13 14 15 | # #*********************************************************************** # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl | | | | | | | | | | 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 | # #*********************************************************************** # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl set ::testprefix concurrent2 ifcapable !concurrent { finish_test return } do_multiclient_test tn { do_test 1.$tn.1 { sql1 { PRAGMA journal_mode = wal; CREATE TABLE t1(x); CREATE TABLE t2(y); } } {wal} do_test 1.$tn.5 { sql3 { PRAGMA integrity_check } } {ok} # Test that an CONCURRENT transaction that allocates/frees no pages does # not conflict with a transaction that does allocate pages. do_test 1.$tn.2 { sql1 { BEGIN CONCURRENT; INSERT INTO t1 VALUES(4); } sql2 { INSERT INTO t2 VALUES(randomblob(1500)); } sql1 { COMMIT; } } {} do_test 1.$tn.5 { sql3 { PRAGMA integrity_check } } {ok} # But that an CONCURRENT transaction does conflict with a transaction # that modifies the db schema. do_test 1.$tn.3 { sql1 { BEGIN CONCURRENT; INSERT INTO t1 VALUES(5); } sql2 { CREATE TABLE t3(z); } list [catch { sql1 COMMIT } msg] $msg } {1 {database is locked}} do_test 1.$tn.5 { sql3 { PRAGMA integrity_check } } {ok} # Test that an CONCURRENT transaction that allocates at least one page # does not conflict with a transaction that allocates no pages. do_test 1.$tn.4 { sql1 { ROLLBACK; BEGIN CONCURRENT; INSERT INTO t1 VALUES(randomblob(1500)); } sql2 { INSERT INTO t2 VALUES(8); } sql1 { COMMIT; |
︙ | ︙ | |||
87 88 89 90 91 92 93 | CREATE TABLE t1(x UNIQUE); CREATE TABLE t2(y UNIQUE); } } {wal} do_test 2.$tn.2 { sql1 { | | | | | | | | | | | | | | 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 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 | CREATE TABLE t1(x UNIQUE); CREATE TABLE t2(y UNIQUE); } } {wal} do_test 2.$tn.2 { sql1 { BEGIN CONCURRENT; INSERT INTO t1 VALUES(randomblob(1500)); } sql2 { INSERT INTO t2 VALUES(randomblob(1500)); } sql1 COMMIT } {} do_test 2.$tn.3 { sql3 { PRAGMA integrity_check } } {ok} do_test 2.$tn.4 { sql1 { BEGIN CONCURRENT; DELETE FROM t1; } sql2 { DELETE FROM t2; } sql1 COMMIT } {} do_test 2.$tn.5 { sql3 { PRAGMA integrity_check } } {ok} do_test 2.$tn.6 { sql1 { INSERT INTO t1 VALUES(randomblob(1500)); INSERT INTO t1 VALUES(randomblob(1500)); INSERT INTO t2 VALUES(randomblob(1500)); DELETE FROM t1 WHERE rowid=1; } sql1 { BEGIN CONCURRENT; DELETE FROM t1 WHERE rowid=2; } sql2 { DELETE FROM t2; } sql1 COMMIT } {} do_test 2.$tn.7 { sql3 { PRAGMA integrity_check } } {ok} } #------------------------------------------------------------------------- # When an CONCURRENT transaction is opened on a database, the nFree and # iTrunk header fields of the cached version of page 1 are both set # to 0. This allows an CONCURRENT transaction to use its own private # free-page-list, which is merged with the main database free-list when # the transaction is committed. # # The following tests check that nFree/iTrunk are correctly restored if # an CONCURRENT transaction is rolled back, and that savepoint rollbacks # that occur within CONCURRENT transactions do not incorrectly restore # these fields to their on-disk values. # reset_db do_execsql_test 3.0 { PRAGMA journal_mode = wal; CREATE TABLE t1(x, y); INSERT INTO t1 VALUES(randomblob(1500), randomblob(1500)); DELETE FROM t1; } {wal} do_execsql_test 3.1 { BEGIN CONCURRENT; INSERT INTO t1 VALUES(1, 2); ROLLBACK; } do_execsql_test 3.2 { PRAGMA integrity_check } {ok} do_execsql_test 3.3 { PRAGMA freelist_count } {2} do_execsql_test 3.4.1 { BEGIN CONCURRENT; PRAGMA freelist_count; } {2} do_execsql_test 3.4.2 { SAVEPOINT xyz; INSERT INTO t1 VALUES(randomblob(1500), NULL); PRAGMA freelist_count; } {0} do_execsql_test 3.4.3 { ROLLBACK TO xyz; } {} do_execsql_test 3.4.4 { PRAGMA freelist_count } {0} do_execsql_test 3.4.5 { COMMIT; PRAGMA freelist_count } {2} do_execsql_test 3.4.6 { PRAGMA integrity_check } {ok} do_execsql_test 3.5.1 { BEGIN CONCURRENT; UPDATE t1 SET x=randomblob(10) WHERE y=555; PRAGMA freelist_count; } {0} do_execsql_test 3.5.2 { ROLLBACK; PRAGMA freelist_count; } {2} do_execsql_test 3.5.3 { PRAGMA integrity_check } {ok} #------------------------------------------------------------------------- # Test that nothing goes wrong if an CONCURRENT transaction allocates a # page at the end of the file, frees it within the same transaction, and # then has to move the same page to avoid a conflict on COMMIT. # do_multiclient_test tn { do_test 4.$tn.1 { sql1 { PRAGMA journal_mode = wal; CREATE TABLE t1(x); CREATE TABLE t2(x); } } {wal} do_test 4.$tn.2 { sql1 { BEGIN CONCURRENT; INSERT INTO t1 VALUES(randomblob(1500)); INSERT INTO t1 VALUES(randomblob(1500)); DELETE FROM t1 WHERE rowid = 1; } sql2 { INSERT INTO t2 VALUES(randomblob(1500)); |
︙ | ︙ | |||
239 240 241 242 243 244 245 | INSERT INTO t1 VALUES(randomblob(1500)); PRAGMA page_count; } } {wal 4} do_test 5.$tn.2 { sql1 { | | | 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 | INSERT INTO t1 VALUES(randomblob(1500)); PRAGMA page_count; } } {wal 4} do_test 5.$tn.2 { sql1 { BEGIN CONCURRENT; INSERT INTO t2 VALUES(randomblob(1500)); PRAGMA page_count; } } {5} do_test 5.$tn.3 { sql2 { |
︙ | ︙ | |||
271 272 273 274 275 276 277 | INSERT INTO t1 VALUES(randomblob(1500)); PRAGMA wal_checkpoint; } } {wal 0 5 5} do_test 6.$tn.2 { sql1 { | | | 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 | INSERT INTO t1 VALUES(randomblob(1500)); PRAGMA wal_checkpoint; } } {wal 0 5 5} do_test 6.$tn.2 { sql1 { BEGIN CONCURRENT; INSERT INTO t1 VALUES(randomblob(1500)); INSERT INTO t1 VALUES(randomblob(1500)); } } {} do_test 6.$tn.3 { sql2 { |
︙ | ︙ |
Name change from test/unlocked3.test to test/concurrent3.test.
1 2 3 4 5 6 7 8 9 10 11 | # 2015 July 26 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | # 2015 July 26 # # 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. # #*********************************************************************** # # Tests for transactions started with BEGIN CONCURRENT. The tests in this # file focus on testing that deferred page allocation works properly. # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl set ::testprefix concurrent3 if {$AUTOVACUUM} { finish_test ; return } ifcapable !concurrent { finish_test return } proc create_schema {} { db eval { PRAGMA journal_mode = wal; |
︙ | ︙ | |||
106 107 108 109 110 111 112 | reset_db create_schema continue } foreach db $DBLIST { sqlite3 $db test.db } do_test 1.$tn { | | | 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | reset_db create_schema continue } foreach db $DBLIST { sqlite3 $db test.db } do_test 1.$tn { foreach db $DBLIST { $db eval "BEGIN CONCURRENT" } foreach op $oplist { set iTbl [string range $op 0 0] foreach char [split [string range $op 1 end] {}] { do_sql_op $iTbl $char } } |
︙ | ︙ |
Changes to tool/mkkeywordhash.c.
︙ | ︙ | |||
167 168 169 170 171 172 173 174 175 176 177 178 179 180 | { "CASCADE", "TK_CASCADE", FKEY }, { "CASE", "TK_CASE", ALWAYS }, { "CAST", "TK_CAST", CAST }, { "CHECK", "TK_CHECK", ALWAYS }, { "COLLATE", "TK_COLLATE", ALWAYS }, { "COLUMN", "TK_COLUMNKW", ALTER }, { "COMMIT", "TK_COMMIT", ALWAYS }, { "CONFLICT", "TK_CONFLICT", CONFLICT }, { "CONSTRAINT", "TK_CONSTRAINT", ALWAYS }, { "CREATE", "TK_CREATE", ALWAYS }, { "CROSS", "TK_JOIN_KW", ALWAYS }, { "CURRENT_DATE", "TK_CTIME_KW", ALWAYS }, { "CURRENT_TIME", "TK_CTIME_KW", ALWAYS }, { "CURRENT_TIMESTAMP","TK_CTIME_KW", ALWAYS }, | > | 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | { "CASCADE", "TK_CASCADE", FKEY }, { "CASE", "TK_CASE", ALWAYS }, { "CAST", "TK_CAST", CAST }, { "CHECK", "TK_CHECK", ALWAYS }, { "COLLATE", "TK_COLLATE", ALWAYS }, { "COLUMN", "TK_COLUMNKW", ALTER }, { "COMMIT", "TK_COMMIT", ALWAYS }, { "CONCURRENT", "TK_CONCURRENT", ALWAYS }, { "CONFLICT", "TK_CONFLICT", CONFLICT }, { "CONSTRAINT", "TK_CONSTRAINT", ALWAYS }, { "CREATE", "TK_CREATE", ALWAYS }, { "CROSS", "TK_JOIN_KW", ALWAYS }, { "CURRENT_DATE", "TK_CTIME_KW", ALWAYS }, { "CURRENT_TIME", "TK_CTIME_KW", ALWAYS }, { "CURRENT_TIMESTAMP","TK_CTIME_KW", ALWAYS }, |
︙ | ︙ | |||
258 259 260 261 262 263 264 | { "TEMPORARY", "TK_TEMP", ALWAYS }, { "THEN", "TK_THEN", ALWAYS }, { "TO", "TK_TO", ALWAYS }, { "TRANSACTION", "TK_TRANSACTION", ALWAYS }, { "TRIGGER", "TK_TRIGGER", TRIGGER }, { "UNION", "TK_UNION", COMPOUND }, { "UNIQUE", "TK_UNIQUE", ALWAYS }, | < | 259 260 261 262 263 264 265 266 267 268 269 270 271 272 | { "TEMPORARY", "TK_TEMP", ALWAYS }, { "THEN", "TK_THEN", ALWAYS }, { "TO", "TK_TO", ALWAYS }, { "TRANSACTION", "TK_TRANSACTION", ALWAYS }, { "TRIGGER", "TK_TRIGGER", TRIGGER }, { "UNION", "TK_UNION", COMPOUND }, { "UNIQUE", "TK_UNIQUE", ALWAYS }, { "UPDATE", "TK_UPDATE", ALWAYS }, { "USING", "TK_USING", ALWAYS }, { "VACUUM", "TK_VACUUM", VACUUM }, { "VALUES", "TK_VALUES", ALWAYS }, { "VIEW", "TK_VIEW", VIEW }, { "VIRTUAL", "TK_VIRTUAL", VTAB }, { "WITH", "TK_WITH", CTE }, |
︙ | ︙ |