From: dan Date: Sat, 22 Aug 2015 17:28:55 +0000 (+0000) Subject: Add further tests for deferred page allocation. And fixes for the same. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f5cebf71fe65cab9dc4fd5b9ce9b96467e3d283f;p=thirdparty%2Fsqlite.git Add further tests for deferred page allocation. And fixes for the same. FossilOrigin-Name: ed0a31be726e60115a5dd73d4ed580201b400ab7 --- diff --git a/manifest b/manifest index 6840db861c..bc04296ba0 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sfurther\strunk\schanges. -D 2015-08-22T07:56:49.673 +C Add\sfurther\stests\sfor\sdeferred\spage\sallocation.\sAnd\sfixes\sfor\sthe\ssame. +D 2015-08-22T17:28:55.533 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in e2218eb228374422969de7b1680eda6864affcef F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -279,9 +279,9 @@ F src/auth.c b56c78ebe40a2110fd361379f7e8162d23f92240 F src/backup.c 4d9134dc988a87838c06056c89c0e8c4700a0452 F src/bitvec.c d1f21d7d91690747881f03940584f4cc548c9d3d F src/btmutex.c 45a968cc85afed9b5e6cf55bf1f42f8d18107f79 -F src/btree.c dc80ac8374105e3caf298a6533e3312101691330 +F src/btree.c 245bc696824d5107f3570905325c6ac5b05c18f8 F src/btree.h 00d4cdb747c4172a5566faf037116985dbbc377e -F src/btreeInt.h 1f7258e1f35ba3cc2197f9019f523dd20d59a0d8 +F src/btreeInt.h 0c19847f87ab82e4f5e67750a069f10829475da6 F src/build.c e47b6fffe14a28d9050e6747beebb01597d37542 F src/callback.c 7b44ce59674338ad48b0e84e7b72f935ea4f68b0 F src/complete.c addcd8160b081131005d5bc2d34adf20c1c5c92f @@ -1228,7 +1228,8 @@ F test/unique.test 93f8b2ef5ea51b9495f8d6493429b1fd0f465264 F test/unique2.test 41e7f83c6827605991160a31380148a9fc5f1339 F test/unixexcl.test cd6c765f75e50e8e2c2ba763149e5d340ea19825 F test/unlocked.test d143a871bd874311d4e5c98ce458218ca6b579fd -F test/unlocked2.test 1e645e6f6808e4303f1766d8c615c22714f31541 +F test/unlocked2.test 7f913b404cda03f22bc7b299e8eb82a34d2880c5 +F test/unlocked3.test 4facd06286885faa9bd53713fa3d701ca403aeb2 F test/unordered.test ca7adce0419e4ca0c50f039885e76ed2c531eda8 F test/update.test 6c68446b8a0a33d522a7c72b320934596a2d7d32 F test/uri.test 23662b7b61958b0f0e47082de7d06341ccf85d5b @@ -1380,7 +1381,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 9021f7369f4b32db6126b3dc0ac75d11039625be 213a6c5ccbcfe4495d45e3608e99a6398751aeed -R d0dd8783a44e8da0a2326d10e188c817 +P c2327a3b8e5d604ab948b1e9f6cfc401429e51db +R c62a3f455b34280d099a1332c1c4f1e4 U dan -Z dd4c495d35a4ec7386b2f6d290b0dddd +Z 1442e612d3eba52580ab50755ceb943f diff --git a/manifest.uuid b/manifest.uuid index 4a13cf0ec4..545070a432 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c2327a3b8e5d604ab948b1e9f6cfc401429e51db \ No newline at end of file +ed0a31be726e60115a5dd73d4ed580201b400ab7 \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index fd7afc073c..53b769720d 100644 --- a/src/btree.c +++ b/src/btree.c @@ -439,19 +439,14 @@ static void downgradeAllSharedCacheTableLocks(Btree *p){ #endif /* SQLITE_OMIT_SHARED_CACHE */ + +#ifdef SQLITE_ENABLE_UNLOCKED /* -** The following structure stores the in-memory pointer map used for newly -** allocated pages in UNLOCKED transactions. Such pages are always allocated -** in a contiguous block (from the end of the file) starting with page -** BtreePtrmap.iFirst. -** -** The page number for the parent page iFirst is stored in aPtr[0]. For -** (iFirst+1), aPtr[1]. A zero value indicates that the page has not -** been allocated. -** -** +** The following structure - BtreePtrmap - stores the in-memory pointer map +** used for newly allocated pages in UNLOCKED 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 { @@ -463,7 +458,6 @@ struct RollbackEntry { Pgno parent; u8 eType; }; - struct BtreePtrmap { Pgno iFirst; /* First new page number aPtr[0] */ @@ -479,17 +473,22 @@ struct BtreePtrmap { RollbackEntry *aRollback; /* Array of rollback entries */ }; +/* +** If page number pgno is greater than or equal to BtreePtrmap.iFirst, +** store an entry for it in the pointer-map structure. +*/ static int btreePtrmapStore( - BtreePtrmap *pMap, - Pgno pgno, + BtShared *pBt, + Pgno pgno, u8 eType, Pgno parent ){ + BtreePtrmap *pMap = pBt->pMap; if( pgno>=pMap->iFirst ){ int iEntry = pgno - pMap->iFirst; - /* Grow the aPtr[] array if required */ - if( iEntry>=pMap->nPtrAlloc ){ + /* Grow the aPtr[] array as required */ + while( iEntry>=pMap->nPtrAlloc ){ int nNew = pMap->nPtrAlloc ? pMap->nPtrAlloc*2 : 16; PtrmapEntry *aNew = (PtrmapEntry*)sqlite3_realloc( pMap->aPtr, nNew*sizeof(PtrmapEntry) @@ -535,8 +534,9 @@ static int btreePtrmapStore( /* ** Open savepoint iSavepoint, if it is not already open. */ -static int btreePtrmapBegin(BtreePtrmap *pMap, int nSvpt){ - if( nSvptnSvpt ){ +static int btreePtrmapBegin(BtShared *pBt, int nSvpt){ + BtreePtrmap *pMap = pBt->pMap; + if( pMap && nSvptnSvpt ){ int i; if( nSvpt>=pMap->nSvptAlloc ){ int nNew = pMap->nSvptAlloc ? pMap->nSvptAlloc*2 : 16; @@ -562,28 +562,72 @@ static int btreePtrmapBegin(BtreePtrmap *pMap, int nSvpt){ ** Rollback (if op==SAVEPOINT_ROLLBACK) or release (if op==SAVEPOINT_RELEASE) ** savepoint iSvpt. */ -static void btreePtrmapEnd(BtreePtrmap *pMap, int op, int iSvpt){ - assert( op==SAVEPOINT_ROLLBACK || op==SAVEPOINT_RELEASE ); - assert( iSvpt>=0 || (iSvpt==-1 && op==SAVEPOINT_ROLLBACK) ); - if( iSvpt<0 ){ - pMap->nSvpt = 0; - pMap->nRollback = 0; - memset(pMap->aPtr, 0, sizeof(Pgno) * pMap->nPtrAlloc); - }else if( iSvptnSvpt ){ - if( op==SAVEPOINT_ROLLBACK ){ - int ii; - for(ii=pMap->nRollback-1; ii>=pMap->aSvpt[iSvpt]; ii--){ - RollbackEntry *p = &pMap->aRollback[ii]; - PtrmapEntry *pEntry = &pMap->aPtr[p->pgno - pMap->iFirst]; - pEntry->parent = p->parent; - pEntry->eType = p->eType; +static void btreePtrmapEnd(BtShared *pBt, int op, int iSvpt){ + BtreePtrmap *pMap = pBt->pMap; + if( pMap ){ + assert( op==SAVEPOINT_ROLLBACK || op==SAVEPOINT_RELEASE ); + assert( iSvpt>=0 || (iSvpt==-1 && op==SAVEPOINT_ROLLBACK) ); + if( iSvpt<0 ){ + pMap->nSvpt = 0; + pMap->nRollback = 0; + memset(pMap->aPtr, 0, sizeof(Pgno) * pMap->nPtrAlloc); + }else if( iSvptnSvpt ){ + if( op==SAVEPOINT_ROLLBACK ){ + int ii; + for(ii=pMap->nRollback-1; ii>=pMap->aSvpt[iSvpt]; ii--){ + RollbackEntry *p = &pMap->aRollback[ii]; + PtrmapEntry *pEntry = &pMap->aPtr[p->pgno - pMap->iFirst]; + pEntry->parent = p->parent; + pEntry->eType = p->eType; + } } + pMap->nSvpt = iSvpt + (op==SAVEPOINT_ROLLBACK); + pMap->nRollback = pMap->aSvpt[iSvpt]; } - pMap->nSvpt = iSvpt + (op==SAVEPOINT_ROLLBACK); - pMap->nRollback = pMap->aSvpt[iSvpt]; } } +/* +** This function is called after an UNLOCKED 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 && sqlite3PagerIsUnlocked(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; + } + return rc; +} + +/* +** Free any BtreePtrmap structure allocated by an earlier call to +** btreePtrmapAllocate(). +*/ +static void btreePtrmapDelete(BtShared *pBt){ + BtreePtrmap *pMap = pBt->pMap; + if( pMap ){ + sqlite3_free(pMap->aRollback); + sqlite3_free(pMap->aPtr); + sqlite3_free(pMap->aSvpt); + sqlite3_free(pMap); + pBt->pMap = 0; + } +} +#else +# define btreePtrmapAllocate(x) SQLITE_OK +# define btreePtrmapDelete(x) +# define btreePtrmapBegin(x,y) SQLITE_OK +# define btreePtrmapEnd(x,y,z) +#endif static void releasePage(MemPage *pPage); /* Forward reference */ @@ -1024,44 +1068,49 @@ static void ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent, int *pRC){ int offset; /* Offset in pointer map page */ int rc; /* Return code from subfunctions */ - assert( sqlite3_mutex_held(pBt->mutex) ); 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_UNLOCKED if( pBt->pMap ){ - *pRC = btreePtrmapStore(pBt->pMap, key, eType, parent); - }else{ - /* The master-journal page number must never be used as a ptr map page */ - assert( 0==PTRMAP_ISPAGE(pBt, PENDING_BYTE_PAGE(pBt)) ); + *pRC = btreePtrmapStore(pBt, key, eType, parent); + return; + } +#endif - assert( pBt->autoVacuum ); - if( key==0 ){ - *pRC = SQLITE_CORRUPT_BKPT; - return; - } - iPtrmap = PTRMAP_PAGENO(pBt, key); - rc = sqlite3PagerGet(pBt->pPager, iPtrmap, &pDbPage); - if( rc!=SQLITE_OK ){ - *pRC = rc; - return; - } - offset = PTRMAP_PTROFFSET(iPtrmap, key); - if( offset<0 ){ - *pRC = SQLITE_CORRUPT_BKPT; - }else{ - assert( offset <= (int)pBt->usableSize-5 ); - pPtrmap = (u8 *)sqlite3PagerGetData(pDbPage); + assert( pBt->autoVacuum ); + if( key==0 ){ + *pRC = SQLITE_CORRUPT_BKPT; + return; + } + iPtrmap = PTRMAP_PAGENO(pBt, key); + rc = sqlite3PagerGet(pBt->pPager, iPtrmap, &pDbPage); + if( rc!=SQLITE_OK ){ + *pRC = rc; + return; + } + offset = PTRMAP_PTROFFSET(iPtrmap, key); + if( offset<0 ){ + *pRC = SQLITE_CORRUPT_BKPT; + goto ptrmap_exit; + } + assert( offset <= (int)pBt->usableSize-5 ); + pPtrmap = (u8 *)sqlite3PagerGetData(pDbPage); - if( eType!=pPtrmap[offset] || get4byte(&pPtrmap[offset+1])!=parent ){ - TRACE(("PTRMAP_UPDATE: %d->(%d,%d)\n", key, eType, parent)); - *pRC= rc = sqlite3PagerWrite(pDbPage); - if( rc==SQLITE_OK ){ - pPtrmap[offset] = eType; - put4byte(&pPtrmap[offset+1], parent); - } - } + if( eType!=pPtrmap[offset] || get4byte(&pPtrmap[offset+1])!=parent ){ + TRACE(("PTRMAP_UPDATE: %d->(%d,%d)\n", key, eType, parent)); + *pRC= rc = sqlite3PagerWrite(pDbPage); + if( rc==SQLITE_OK ){ + pPtrmap[offset] = eType; + put4byte(&pPtrmap[offset+1], parent); } - sqlite3PagerUnref(pDbPage); } + +ptrmap_exit: + sqlite3PagerUnref(pDbPage); } /* @@ -2072,50 +2121,6 @@ u32 sqlite3BtreeLastPage(Btree *p){ return btreePagecount(p->pBt); } -#ifdef SQLITE_ENABLE_UNLOCKED -/* -** This function is called before allocating or freeing a b-tree page. If -** the current transaction is UNLOCKED, it allocates the BtreePtrmap -** structure and zeroes the nFree/iTrunk fields in the database header -** on page 1. -*/ -static int allocatePtrmap(BtShared *pBt){ - int rc = SQLITE_OK; - if( pBt->pMap==0 && sqlite3PagerIsUnlocked(pBt->pPager) ){ - /* If this is an unlocked transaction, set the header values - ** identifying the size of the free-list and the page number - ** of the first trunk page to zero. */ - BtreePtrmap *pMap = sqlite3_malloc(sizeof(BtreePtrmap)); - if( pMap==0 ){ - rc = SQLITE_NOMEM; - }else{ - memset(&pBt->pPage1->aData[32], 0, sizeof(u32)*2); - memset(pMap, 0, sizeof(BtreePtrmap)); - pMap->iFirst = btreePagecount(pBt) + 1; - pBt->pMap = pMap; - } - } - return rc; -} - -/* -** Free a pointer-map allocated by allocatePtrmap. -*/ -static void deletePtrmap(BtShared *pBt){ - BtreePtrmap *pMap = pBt->pMap; - if( pMap ){ - sqlite3_free(pMap->aRollback); - sqlite3_free(pMap->aPtr); - sqlite3_free(pMap->aSvpt); - sqlite3_free(pMap); - pBt->pMap = 0; - } -} -#else -#define allocatePtrmap(x) SQLITE_OK -#define deletePtrmap(x) -#endif - /* ** Get a page from the pager and initialize it. ** @@ -3341,8 +3346,8 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){ if( rc==SQLITE_OK ){ rc = newDatabase(pBt); } - if( rc==SQLITE_OK ){ - rc = allocatePtrmap(pBt); + if( rc==SQLITE_OK && sqlite3PagerIsUnlocked(pBt->pPager) ){ + rc = btreePtrmapAllocate(pBt); } } } @@ -3402,8 +3407,8 @@ trans_begun: */ int nSavepoint = p->db->nSavepoint; rc = sqlite3PagerOpenSavepoint(pBt->pPager, nSavepoint); - if( pBt->pMap && rc==SQLITE_OK && nSavepoint ){ - rc = btreePtrmapBegin(pBt->pMap, nSavepoint); + if( rc==SQLITE_OK && nSavepoint ){ + rc = btreePtrmapBegin(pBt, nSavepoint); } } @@ -3841,6 +3846,7 @@ static int autoVacuumCommit(BtShared *pBt){ # define setChildPtrmaps(x) SQLITE_OK #endif +#ifdef SQLITE_ENABLE_UNLOCKED /* ** The b-tree handle passed as the only argument is about to commit an ** UNLOCKED transaction. At this point it is guaranteed that this is @@ -3913,6 +3919,7 @@ static int btreeFixUnlocked(Btree *p){ Pgno iNew; /* New page number for pPg */ PtrmapEntry *pEntry; /* Pointer map entry for page iPg */ + if( iPg==PENDING_BYTE_PAGE(pBt) ) continue; pEntry = &pMap->aPtr[iPg - pMap->iFirst]; if( pEntry->eType==PTRMAP_FREEPAGE ){ MemPage *pFree = 0; @@ -3925,31 +3932,39 @@ static int btreeFixUnlocked(Btree *p){ assert( sqlite3PagerIswriteable(pPg->pDbPage) ); assert( sqlite3PagerPageRefcount(pPg->pDbPage)==1 ); iNew = ++nCurrent; + if( iNew==PENDING_BYTE_PAGE(pBt) ) iNew = ++nCurrent; rc = relocatePage(pBt, pPg, pEntry->eType, pEntry->parent, iNew, 1); releasePageNotNull(pPg); } } sqlite3PagerSetDbsize(pPager, nCurrent); + assert( nCurrent!=PENDING_BYTE_PAGE(pBt) ); nFree = get4byte(&p1[36]); nFinal = MAX(nCurrent-nFree, nHPage); + if( nCurrent>PENDING_BYTE_PAGE(pBt) && nFinal<=PENDING_BYTE_PAGE(pBt) ){ + nFinal--; + } - for(iPg=nFinal+1; rc==SQLITE_OK && iPgaPtr[iPg - pMap->iFirst]; if( pEntry->eType==PTRMAP_FREEPAGE ){ rc = allocateBtreePage(pBt, &pFree, &iNew, iPg, BTALLOC_EXACT); releasePage(pFree); }else{ rc = allocateBtreePage(pBt, &pFree, &iNew, nFinal, BTALLOC_LE); + assert( rc!=SQLITE_OK || iNew<=nFinal ); releasePage(pFree); if( rc==SQLITE_OK ){ MemPage *pPg = 0; btreeGetPage(pBt, iPg, &pPg, 0); rc = relocatePage(pBt, pPg, pEntry->eType, pEntry->parent,iNew,1); + releasePage(pPg); } } } @@ -3961,6 +3976,7 @@ static int btreeFixUnlocked(Btree *p){ return rc; } +#endif /* ** This routine does the first phase of a two-phase commit. This routine @@ -3995,11 +4011,8 @@ int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zMaster){ sqlite3BtreeEnter(p); #ifndef SQLITE_OMIT_AUTOVACUUM - /* Figure out if this is a commit of an UNLOCKED transaction that - ** requires a snapshot upgrade. If so, skip any auto-vacuum - ** processing. */ if( pBt->autoVacuum ){ - assert( sqlite3PagerIsUnlocked(pBt->pPager)==0 ); + assert( ISUNLOCKED==0 ); rc = autoVacuumCommit(pBt); if( rc!=SQLITE_OK ){ sqlite3BtreeLeave(p); @@ -4010,7 +4023,7 @@ int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zMaster){ sqlite3PagerTruncateImage(pBt->pPager, pBt->nPage); } #endif - if( rc==SQLITE_OK && sqlite3PagerIsUnlocked(pBt->pPager) ){ + if( rc==SQLITE_OK && ISUNLOCKED ){ rc = btreeFixUnlocked(p); } if( rc==SQLITE_OK ){ @@ -4059,7 +4072,7 @@ static void btreeEndTransaction(Btree *p){ } /* If this was an UNLOCKED transaction, delete the pBt->pMap object */ - deletePtrmap(pBt); + btreePtrmapDelete(pBt); btreeIntegrity(p); } @@ -4285,8 +4298,8 @@ int sqlite3BtreeBeginStmt(Btree *p, int iStatement){ ** such savepoints while the statement transaction savepoint is active. */ rc = sqlite3PagerOpenSavepoint(pBt->pPager, iStatement); - if( rc==SQLITE_OK && pBt->pMap ){ - rc = btreePtrmapBegin(pBt->pMap, iStatement); + if( rc==SQLITE_OK ){ + rc = btreePtrmapBegin(pBt, iStatement); } sqlite3BtreeLeave(p); return rc; @@ -4311,7 +4324,7 @@ int sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){ assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK ); assert( iSavepoint>=0 || (iSavepoint==-1 && op==SAVEPOINT_ROLLBACK) ); sqlite3BtreeEnter(p); - if( pBt->pMap ) btreePtrmapEnd(pBt->pMap, op, iSavepoint); + btreePtrmapEnd(pBt, op, iSavepoint); rc = sqlite3PagerSavepoint(pBt->pPager, op, iSavepoint); if( rc==SQLITE_OK ){ if( iSavepoint<0 && (pBt->btsFlags & BTS_INITIALLY_EMPTY)!=0 ){ @@ -5817,7 +5830,7 @@ static int allocateBtreePage( ** stores stores the total number of pages on the freelist. */ n = get4byte(&pPage1->aData[36]); testcase( n==mxPage-1 ); - if( sqlite3PagerIsUnlocked(pBt->pPager)==0 && n>=mxPage ){ + if( ISUNLOCKED==0 && n>=mxPage ){ return SQLITE_CORRUPT_BKPT; } @@ -5840,7 +5853,7 @@ static int allocateBtreePage( ** the entire-list will be searched for that page. */ if( eMode==BTALLOC_EXACT ){ - assert( ISAUTOVACUUM==!sqlite3PagerIsUnlocked(pBt->pPager) ); + assert( ISAUTOVACUUM!=ISUNLOCKED ); if( ISAUTOVACUUM ){ if( nearby<=mxPage ){ u8 eType; @@ -9926,6 +9939,23 @@ int sqlite3BtreeIsReadonly(Btree *p){ */ 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 UNLOCKED transaction on a wal-mode database, the WRITER +** lock on the wal file. In this case this function also checks that the +** UNLOCKED 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 UNLOCKED 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; diff --git a/src/btreeInt.h b/src/btreeInt.h index a60f2d5c17..4c23629cc8 100644 --- a/src/btreeInt.h +++ b/src/btreeInt.h @@ -664,11 +664,12 @@ struct BtCursor { #endif #ifdef SQLITE_ENABLE_UNLOCKED -# define REQUIRE_PTRMAP (ISAUTOVACUUM || pBt->pMap) +# define ISUNLOCKED (pBt->pMap!=0) #else -# define REQUIRE_PTRMAP ISAUTOVACUUM +# define ISUNLOCKED 0 #endif +#define REQUIRE_PTRMAP (ISAUTOVACUUM || ISUNLOCKED) /* ** This structure is passed around through all the sanity checking routines diff --git a/test/unlocked2.test b/test/unlocked2.test index e5b102aa02..927fe3e675 100644 --- a/test/unlocked2.test +++ b/test/unlocked2.test @@ -68,7 +68,6 @@ do_multiclient_test tn { sql2 { INSERT INTO t2 VALUES(8); } - breakpoint sql1 { COMMIT; } @@ -225,5 +224,38 @@ do_multiclient_test tn { } {} } +#------------------------------------------------------------------------- +# +do_multiclient_test tn { + do_test 5.$tn.1 { + sql1 { + PRAGMA journal_mode = wal; + CREATE TABLE t1(x); + CREATE TABLE t2(x); + INSERT INTO t1 VALUES(randomblob(1500)); + PRAGMA page_count; + } + } {wal 4} + + do_test 5.$tn.2 { + sql1 { + BEGIN UNLOCKED; + INSERT INTO t2 VALUES(randomblob(1500)); + PRAGMA page_count; + } + } {5} + + do_test 5.$tn.3 { + sql2 { + DELETE FROM t1; + PRAGMA freelist_count; + PRAGMA page_count; + } + } {1 4} + + do_test 5.$tn.4 { sql1 COMMIT } {} + do_test 5.$tn.5 { sql3 { PRAGMA integrity_check } } {ok} +} + finish_test diff --git a/test/unlocked3.test b/test/unlocked3.test new file mode 100644 index 0000000000..04e741dc5b --- /dev/null +++ b/test/unlocked3.test @@ -0,0 +1,110 @@ +# 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 UNLOCKED. 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 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); + CREATE TABLE t3(x, y); + CREATE TABLE t4(x, y); + + CREATE INDEX i1 ON t1(y, x); + CREATE INDEX i2 ON t2(y, x); + CREATE INDEX i3 ON t3(y, x); + CREATE INDEX i4 ON t4(y, x); + } +} + +proc do_sql_op {iTbl iOp} { + set db "db$iTbl" + + switch $iOp { + "i" { + set sql " + WITH cnt(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM cnt WHERE i<10) + INSERT INTO t$iTbl SELECT randomblob(800), randomblob(800) FROM cnt; + " + } + + "d" { + set sql " + DELETE FROM t$iTbl WHERE rowid IN ( + SELECT rowid FROM t$iTbl ORDER BY 1 ASC LIMIT 10 + ) + " + } + + default { + error "bad iOp parameter: $iOp" + } + } + + $db eval $sql +} + + +set DBLIST {db1 db2 db3 db4} + +create_schema +foreach {tn oplist} { + 1 {1i 2i 3i 4i} + 2 {1iii 2iii 3iii 4iii} + 3 {1d 2d 3d 4d} + . ----------------------- + 4 {1i} + 5 {1d 2i} + . ----------------------- + 6 {1iii 2iii 3iii 4iii} + 7 {1di 2id 3iii 4ddd} + 8 {1iii 2iii 3iii 4iii} +} { + if {[string range $oplist 0 0]=="-"} { + reset_db + create_schema + continue + } + foreach db $DBLIST { sqlite3 $db test.db } + + do_test 1.$tn { + foreach db $DBLIST { $db eval "BEGIN UNLOCKED" } + + foreach op $oplist { + set iTbl [string range $op 0 0] + foreach char [split [string range $op 1 end] {}] { + do_sql_op $iTbl $char + } + } + + foreach db $DBLIST { $db eval "COMMIT" } + db eval {PRAGMA integrity_check} + } {ok} + + foreach db $DBLIST { + if {$db=="db2"} breakpoint + $db close + } +} + +finish_test +