-C Handle\swrites\sto\sauto-vacuum\sdatabases\swithin\sUNLOCKED\stransactions\sin\sthe\ssame\sway\sas\sfor\snon-UNLOCKED\stransactions.
-D 2015-08-15T18:16:46.463
+C When\scommitting\san\sunlocked\stransaction,\srelocate\snewly\sallocated\sdatabase\spages\swithin\sthe\sfile\sto\savoid\sconflicting\swith\scommitted\stransactions.\sThere\sare\slots\sof\sthings\sstill\sto\sfix\sin\sthis\scode.
+D 2015-08-19T20:27:05.840
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 4de3ef40c8b3b75c0c55ff4242a43c8ce1ad90ee
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
F src/backup.c 4d9134dc988a87838c06056c89c0e8c4700a0452
F src/bitvec.c d1f21d7d91690747881f03940584f4cc548c9d3d
F src/btmutex.c 45a968cc85afed9b5e6cf55bf1f42f8d18107f79
-F src/btree.c c1bbc83539de1e749aa36ce9c283f95b50543bcb
-F src/btree.h 40bd41ef0b71d6f7502725dc159fa0d6bebd8bb3
-F src/btreeInt.h 2ad754dd4528baa8d0946a593cc373b890bf859e
+F src/btree.c a00a7c4809d642bf6c63979e73face9fed53dc66
+F src/btree.h 00d4cdb747c4172a5566faf037116985dbbc377e
+F src/btreeInt.h 9f77bac260d81cc74ecd96cccf73a8c5af1688c4
F src/build.c 28c15c43eefc0066ff64040526ff649c32fe5523
F src/callback.c 7b44ce59674338ad48b0e84e7b72f935ea4f68b0
F src/complete.c addcd8160b081131005d5bc2d34adf20c1c5c92f
F src/os_unix.c 388c023582b17890f10c980b30ec1922b471753b
F src/os_win.c 40b3af7a47eb1107d0d69e592bec345a3b7b798a
F src/os_win.h eb7a47aa17b26b77eb97e4823f20a00b8bda12ca
-F src/pager.c 18aa67541bd2c7d7963f180669ea4e48cd5246f4
-F src/pager.h 41938212a5d80a7068883bb29ea6ac0d4ef3a6d8
+F src/pager.c fe82e368600278cfd35024c20b888e945236f675
+F src/pager.h 3fc23bca2e7d606ca4fbdd923713cdcdcec48297
F src/parse.y 199145cd982587d70fa3db2f9b11f7ab118f0acd
F src/pcache.c cde06aa50962595e412d497e22fd2e07878ba1f0
F src/pcache.h 9968603796240cdf83da7e7bef76edf90619cea9
F src/vdbe.h 7a75045d879118b9d3af7e8b3c108f2f27c51473
F src/vdbeInt.h 8b54e01ad0463590e7cffabce0bc36da9ee4f816
F src/vdbeapi.c adabbd66eb2e3a10f3998485ee0be7e326d06ee4
-F src/vdbeaux.c 24a03ccbadc8d4cfecf25b6399db95851b10dd21
+F src/vdbeaux.c 27feae106f3f4db298e65d37a9da4962aaff724b
F src/vdbeblob.c 4f2e8e075d238392df98c5e03a64342465b03f90
F src/vdbemem.c ae38a0d35ae71cf604381a887c170466ba518090
F src/vdbesort.c f5009e7a35e3065635d8918b9a31f498a499976b
F src/vdbetrace.c 8befe829faff6d9e6f6e4dee5a7d3f85cc85f1a0
F src/vtab.c 082b35a25a26e3d36f365ca8cd73c1922532f05e
F src/vxworks.h c18586c8edc1bddbc15c004fa16aeb1e1342b4fb
-F src/wal.c 4497b466fee8ed56c31672bb35741dd0321ec999
-F src/wal.h cd7ea52439d56571d549b7d4eacdeed58d841385
+F src/wal.c 8bb1130dc1876e1c1c3fa082a5b0ec6437feb8e8
+F src/wal.h 5aaed8ca6cad1406088042ff8767951a6a3c7b56
F src/walker.c c253b95b4ee44b21c406e2a1052636c31ea27804
F src/where.c 909eba3b6db984eb2adfbca9de2c237ee7056adb
F src/whereInt.h 5f87e3c4b0551747d119730dfebddd3c54f04047
F test/unique.test 93f8b2ef5ea51b9495f8d6493429b1fd0f465264
F test/unique2.test 41e7f83c6827605991160a31380148a9fc5f1339
F test/unixexcl.test cd6c765f75e50e8e2c2ba763149e5d340ea19825
-F test/unlocked.test 644a84d7f032ca029d490f0b0bc8fb9f86c7a1a5
+F test/unlocked.test d143a871bd874311d4e5c98ce458218ca6b579fd
+F test/unlocked2.test fc4c730a44c3650250467ecacca6ddb969f64405
F test/unordered.test ca7adce0419e4ca0c50f039885e76ed2c531eda8
F test/update.test 6c68446b8a0a33d522a7c72b320934596a2d7d32
F test/uri.test 23662b7b61958b0f0e47082de7d06341ccf85d5b
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P 0b9718426e44df092850c5d095ce1b84a1e567cf
-R ac1d3857424ab06ed70d671704c3737d
+P de1ea450db33b140b11af5b801ea6a15875e774e
+R db05422c8fb37e882a7e6cc4bd7c8ffb
U dan
-Z 12da9addf6141c212c3c7cdffe33d767
+Z cccfd407eb244cf1cd3b5cab72e65fda
#endif /* SQLITE_OMIT_SHARED_CACHE */
+/*
+** 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.
+**
+**
+*/
+
+typedef struct RollbackEntry RollbackEntry;
+typedef struct PtrmapEntry PtrmapEntry;
+struct PtrmapEntry {
+ Pgno parent;
+ u8 eType;
+};
+struct RollbackEntry {
+ Pgno pgno;
+ Pgno parent;
+ u8 eType;
+};
+
+struct BtreePtrmap {
+ Pgno iFirst; /* First new page number aPtr[0] */
+
+ int nPtrAlloc; /* Allocated size of aPtr[] array */
+ PtrmapEntry *aPtr; /* Array of parent page numbers */
+
+ int nSvpt; /* Used size of aSvpt[] array */
+ int nSvptAlloc; /* Allocated size of aSvpt[] */
+ int *aSvpt; /* First aRollback[] entry for savepoint i */
+
+ int nRollback; /* Used size of aRollback[] array */
+ int nRollbackAlloc; /* Allocated size of aRollback[] array */
+ RollbackEntry *aRollback; /* Array of rollback entries */
+};
+
+static int btreePtrmapStore(
+ BtreePtrmap *pMap,
+ Pgno pgno,
+ u8 eType,
+ Pgno parent
+){
+ if( pgno>=pMap->iFirst ){
+ int iEntry = pgno - pMap->iFirst;
+
+ /* Grow the aPtr[] array if required */
+ if( iEntry>=pMap->nPtrAlloc ){
+ int nNew = pMap->nPtrAlloc ? pMap->nPtrAlloc*2 : 16;
+ PtrmapEntry *aNew = (PtrmapEntry*)sqlite3_realloc(
+ pMap->aPtr, nNew*sizeof(PtrmapEntry)
+ );
+ if( aNew==0 ){
+ return SQLITE_NOMEM;
+ }else{
+ int nByte = (nNew-pMap->nPtrAlloc)*sizeof(PtrmapEntry);
+ memset(&aNew[pMap->nPtrAlloc], 0, nByte);
+ pMap->aPtr = aNew;
+ pMap->nPtrAlloc = nNew;
+ }
+ }
+
+ /* Add an entry to the rollback log if required */
+ if( pMap->nSvpt>0 && pMap->aPtr[iEntry].parent ){
+ if( pMap->nRollback>=pMap->nRollbackAlloc ){
+ int nNew = pMap->nRollback ? pMap->nRollback*2 : 16;
+ RollbackEntry *aNew = (RollbackEntry*)sqlite3_realloc(
+ pMap->aRollback, nNew*sizeof(RollbackEntry)
+ );
+ if( aNew==0 ){
+ return SQLITE_NOMEM;
+ }else{
+ pMap->aRollback = aNew;
+ pMap->nRollbackAlloc = nNew;
+ }
+ }
+
+ pMap->aRollback[pMap->nRollback].pgno = pgno;
+ pMap->aRollback[pMap->nRollback].parent = pMap->aPtr[iEntry].parent;
+ pMap->aRollback[pMap->nRollback].eType = pMap->aPtr[iEntry].eType;
+ }
+
+ /* Update the aPtr[] array */
+ pMap->aPtr[iEntry].parent = parent;
+ pMap->aPtr[iEntry].eType = eType;
+ }
+
+ return SQLITE_OK;
+}
+
+/*
+** Open savepoint iSavepoint, if it is not already open.
+*/
+static int btreePtrmapBegin(BtreePtrmap *pMap, int nSvpt){
+ if( nSvpt<pMap->nSvpt ){
+ int i;
+ if( nSvpt>=pMap->nSvptAlloc ){
+ int nNew = pMap->nSvptAlloc ? pMap->nSvptAlloc*2 : 16;
+ int *aNew = sqlite3_realloc(pMap->aSvpt, sizeof(int) * nNew);
+ if( aNew==0 ){
+ return SQLITE_NOMEM;
+ }else{
+ pMap->aSvpt = aNew;
+ pMap->nSvptAlloc = nNew;
+ }
+ }
+
+ for(i=pMap->nSvpt; i<nSvpt; i++){
+ pMap->aSvpt[i] = pMap->nRollback;
+ }
+ pMap->nSvpt = nSvpt;
+ }
+
+ return SQLITE_OK;
+}
+
+/*
+** 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( iSvpt<pMap->nSvpt ){
+ 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];
+ }
+}
+
+
static void releasePage(MemPage *pPage); /* Forward reference */
/*
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 must never be used as a pointer map page */
- assert( 0==PTRMAP_ISPAGE(pBt, PENDING_BYTE_PAGE(pBt)) );
+ 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)) );
- 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);
+ 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);
- 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);
}
/*
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.
**
if( rc==SQLITE_OK ){
rc = newDatabase(pBt);
}
+ if( rc==SQLITE_OK ){
+ rc = allocatePtrmap(pBt);
+ }
}
}
** open savepoints. If the second parameter is greater than 0 and
** the sub-journal is not already open, then it will be opened here.
*/
- rc = sqlite3PagerOpenSavepoint(pBt->pPager, p->db->nSavepoint);
+ int nSavepoint = p->db->nSavepoint;
+ rc = sqlite3PagerOpenSavepoint(pBt->pPager, nSavepoint);
+ if( pBt->pMap && rc==SQLITE_OK && nSavepoint ){
+ rc = btreePtrmapBegin(pBt->pMap, nSavepoint);
+ }
}
btreeIntegrity(p);
# define setChildPtrmaps(x) SQLITE_OK
#endif
+/*
+** 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
+** 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);
+ Pgno nOrig = pMap->iFirst-1;
+ u32 nFree = get4byte(&p1[36]);
+
+ assert( sqlite3PagerIsUnlocked(pPager) );
+ assert( pBt->pMap );
+ rc = sqlite3PagerUpgradeSnapshot(pPager, pPage1->pDbPage);
+ assert( p1==pPage1->aData );
+
+ if( rc==SQLITE_OK ){
+ Pgno nHPage = get4byte(&p1[28]);
+ Pgno nFinal = nHPage;
+
+ if( sqlite3PagerIswriteable(pPage1->pDbPage) ){
+ Pgno iHTrunk = get4byte(&p1[32]);
+ u32 nHFree = get4byte(&p1[36]);
+
+ /* Attach the head database free list to the end of the current
+ ** transactions free-list (if any). */
+ if( iTrunk!=0 ){
+ put4byte(&p1[36], nHFree + nFree);
+ put4byte(&p1[32], iTrunk);
+ while( iTrunk ){
+ DbPage *pTrunk = sqlite3PagerLookup(pPager, iTrunk);
+ iTrunk = get4byte((u8*)pTrunk->pData);
+ if( iTrunk==0 ){
+ put4byte((u8*)pTrunk->pData, iHTrunk);
+ }
+ sqlite3PagerUnref(pTrunk);
+ };
+ }
+
+ if( nHPage<nOrig ){
+ rc = SQLITE_CORRUPT_BKPT;
+ }else{
+ /* The current transaction allocated pages pMap->iFirst through
+ ** nPage (inclusive) at the end of the database file. Meanwhile,
+ ** other transactions have allocated (iFirst..nHPage). So move
+ ** pages (iFirst..MIN(nPage,nHPage)) to (MAX(nPage,nHPage)+1).
+ */
+ Pgno iLast = MIN(nPage, nHPage); /* Last page to move */
+ Pgno iPg;
+
+ nFinal = MAX(nPage, nHPage); /* Final size of database */
+ for(iPg=pMap->iFirst; iPg<=iLast && rc==SQLITE_OK; iPg++){
+ MemPage *pPg = 0;
+ Pgno iNew; /* New page number for pPg */
+ PtrmapEntry *pEntry; /* Pointer map entry for page iPg */
+
+ btreeGetPage(pBt, iPg, &pPg, 0);
+ assert( sqlite3PagerIswriteable(pPg->pDbPage) );
+ assert( sqlite3PagerPageRefcount(pPg->pDbPage)==1 );
+ pEntry = &pMap->aPtr[iPg - pMap->iFirst];
+ iNew = ++nFinal;
+
+ rc = relocatePage(pBt, pPg, pEntry->eType, pEntry->parent, iNew, 1);
+ releasePageNotNull(pPg);
+ }
+
+ put4byte(&p1[28], nFinal);
+ }
+ }
+ sqlite3PagerSetDbsize(pPager, nFinal);
+ }
+
+ return rc;
+}
+
/*
** This routine does the first phase of a two-phase commit. This routine
** causes a rollback journal to be created (if it does not already exist)
/* 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 && (
- 0==pBt->db->bUnlocked
- || 0==sqlite3PagerCommitRequiresUpgrade(pBt->pPager)
- )){
+ if( pBt->autoVacuum ){
+ assert( sqlite3PagerIsUnlocked(pBt->pPager)==0 );
rc = autoVacuumCommit(pBt);
if( rc!=SQLITE_OK ){
sqlite3BtreeLeave(p);
sqlite3PagerTruncateImage(pBt->pPager, pBt->nPage);
}
#endif
+ if( rc==SQLITE_OK && sqlite3PagerIsUnlocked(pBt->pPager) ){
+ rc = btreeFixUnlocked(p);
+ }
if( rc==SQLITE_OK ){
rc = sqlite3PagerCommitPhaseOne(pBt->pPager, zMaster, 0);
}
unlockBtreeIfUnused(pBt);
}
+ /* If this was an UNLOCKED transaction, delete the pBt->pMap object */
+ deletePtrmap(pBt);
btreeIntegrity(p);
}
** 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);
+ }
sqlite3BtreeLeave(p);
return rc;
}
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);
rc = sqlite3PagerSavepoint(pBt->pPager, op, iSavepoint);
if( rc==SQLITE_OK ){
if( iSavepoint<0 && (pBt->btsFlags & BTS_INITIALLY_EMPTY)!=0 ){
if( 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 UNLOCKED
+ ** transaction, ensure the BtreePtrmap structure has been allocated. */
+ assert( eMode!=BTALLOC_EXACT || sqlite3PagerIsUnlocked(pBt->pPager)==0 );
+ rc = sqlite3PagerWrite(pPage1->pDbPage);
+ if( rc ) return rc;
+
if( n>0 ){
/* There are pages on the freelist. Reuse one of those pages. */
Pgno iTrunk;
/* Decrement the free-list count by 1. Set iTrunk to the index of the
** first free-list trunk page. iPrevTrunk is initially 1.
*/
- rc = sqlite3PagerWrite(pPage1->pDbPage);
- if( rc ) return rc;
put4byte(&pPage1->aData[36], n-1);
/* The code within this loop is run only once if the 'searchList' variable
/* If the database supports auto-vacuum, write an entry in the pointer-map
** to indicate that the page is free.
*/
- if( ISAUTOVACUUM ){
+ if( REQUIRE_PTRMAP ){
ptrmapPut(pBt, iPage, PTRMAP_FREEPAGE, 0, &rc);
if( rc ) goto freepage_out;
}
}
#endif
rc = allocateBtreePage(pBt, &pOvfl, &pgnoOvfl, pgnoOvfl, 0);
-#ifndef SQLITE_OMIT_AUTOVACUUM
+
/* If the database supports auto-vacuum, and the second or subsequent
** overflow page is being allocated, add an entry to the pointer-map
** for that page now.
** may misinterpret the uninitialized values and delete the
** wrong pages from the database.
*/
- if( pBt->autoVacuum && rc==SQLITE_OK ){
+ if( REQUIRE_PTRMAP && rc==SQLITE_OK ){
u8 eType = (pgnoPtrmap?PTRMAP_OVERFLOW2:PTRMAP_OVERFLOW1);
ptrmapPut(pBt, pgnoOvfl, eType, pgnoPtrmap, &rc);
if( rc ){
releasePage(pOvfl);
}
}
-#endif
if( rc ){
releasePage(pToRelease);
return rc;
assert( j==0 || pPage->aiOvfl[j-1]<(u16)i ); /* Overflows in sorted order */
assert( j==0 || i==pPage->aiOvfl[j-1]+1 ); /* Overflows are sequential */
}else{
+ BtShared *pBt = pPage->pBt;
int rc = sqlite3PagerWrite(pPage->pDbPage);
if( rc!=SQLITE_OK ){
*pRC = rc;
** if it returns successfully */
assert( idx >= 0 );
assert( idx >= pPage->cellOffset+2*pPage->nCell+2 || CORRUPT_DB );
- assert( idx+sz <= (int)pPage->pBt->usableSize );
+ assert( idx+sz <= (int)pBt->usableSize );
pPage->nFree -= (u16)(2 + sz);
memcpy(&data[idx], pCell, sz);
if( iChild ){
/* increment the cell count */
if( (++data[pPage->hdrOffset+4])==0 ) data[pPage->hdrOffset+3]++;
assert( get2byte(&data[pPage->hdrOffset+3])==pPage->nCell );
-#ifndef SQLITE_OMIT_AUTOVACUUM
- if( pPage->pBt->autoVacuum ){
+ if( REQUIRE_PTRMAP ){
/* The cell may contain a pointer to an overflow page. If so, write
** the entry for the overflow page into the pointer map.
*/
ptrmapPutOvflPtr(pPage, pCell, pRC);
}
-#endif
}
}
** be marked as dirty. Returning an error code will cause a
** rollback, undoing any changes made to the parent page.
*/
- if( ISAUTOVACUUM ){
+ if( REQUIRE_PTRMAP ){
ptrmapPut(pBt, pgnoNew, PTRMAP_BTREE, pParent->pgno, &rc);
if( szCell>pNew->minLocal ){
ptrmapPutOvflPtr(pNew, pCell, &rc);
/* If this is an auto-vacuum database, update the pointer-map entries
** for any b-tree or overflow pages that pTo now contains the pointers to.
*/
- if( ISAUTOVACUUM ){
+ if( REQUIRE_PTRMAP ){
*pRC = setChildPtrmaps(pTo);
}
}
cntOld[i] = b.nCell;
/* Set the pointer-map entry for the new sibling page. */
- if( ISAUTOVACUUM ){
+ if( REQUIRE_PTRMAP ){
ptrmapPut(pBt, pNew->pgno, PTRMAP_BTREE, pParent->pgno, &rc);
if( rc!=SQLITE_OK ){
goto balance_cleanup;
** updated. This happens below, after the sibling pages have been
** populated, not here.
*/
- if( ISAUTOVACUUM ){
+ if( REQUIRE_PTRMAP ){
MemPage *pNew = apNew[0];
u8 *aOld = pNew->aData;
int cntOldNext = pNew->nCell + pNew->nOverflow;
);
copyNodeContent(apNew[0], pParent, &rc);
freePage(apNew[0], &rc);
- }else if( ISAUTOVACUUM && !leafCorrection ){
+ }else if( REQUIRE_PTRMAP && !leafCorrection ){
/* Fix the pointer map entries associated with the right-child of each
** sibling page. All other pointer map entries have already been taken
** care of. */
if( rc==SQLITE_OK ){
rc = allocateBtreePage(pBt,&pChild,&pgnoChild,pRoot->pgno,0);
copyNodeContent(pRoot, pChild, &rc);
- if( ISAUTOVACUUM ){
+ if( REQUIRE_PTRMAP ){
ptrmapPut(pBt, pgnoChild, PTRMAP_BTREE, pRoot->pgno, &rc);
}
}
** Return the size of the header added to each page by this module.
*/
int sqlite3HeaderSizeBtree(void){ return ROUND8(sizeof(MemPage)); }
+
+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;
+}
** transaction. It may be assumed that no frames have been written to
** the wal file.
*/
-int sqlite3WalLockForCommit(Wal *pWal, Bitvec *pAllRead){
+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,
if( rc==SQLITE_OK ){
volatile WalIndexHdr *pHead; /* Head of the wal file */
pHead = walIndexHdr(pWal);
+
+ /* TODO: Check header checksum is good here. */
+
if( memcmp(&pWal->hdr, (void*)pHead, sizeof(WalIndexHdr))!=0 ){
/* TODO: Is this safe? Because it holds the WRITER lock this thread
** has exclusive access to the live header, but might it be corrupt?
if( iMin<1 ) iMin = 1;
if( iMax>pHead->mxFrame ) iMax = pHead->mxFrame;
for(i=iMin; i<=iMax; i++){
- if( sqlite3BitvecTestNotNull(pAllRead, aPgno[i]) ){
+ PgHdr *pPg;
+ if( aPgno[i]==1 ){
+ /* Check that the schema cookie has not been modified. If
+ ** it has not, the commit can proceed. */
+ u8 aNew[4];
+ u8 *aOld = &((u8*)pPage1->pData)[40];
+ int sz;
+ i64 iOffset;
+ sz = pWal->hdr.szPage;
+ sz = (sz&0xfe00) + ((sz&0x0001)<<16);
+ 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 UNLOCKED transaction (conflict at page %d)",
(int)aPgno[i]
);
rc = SQLITE_BUSY_SNAPSHOT;
+ }else if( (pPg = sqlite3PagerLookup(pPager, aPgno[i])) ){
+ if( sqlite3PagerIswriteable(pPg)==0 ){
+ sqlite3PcacheDrop(pPg);
+ }else{
+ sqlite3PagerUnref(pPg);
+ }
}
}
}
return rc;
}
+void sqlite3WalUpgradeSnapshot(Wal *pWal){
+ assert( pWal->writeLock );
+ memcpy(&pWal->hdr, (void*)walIndexHdr(pWal), sizeof(WalIndexHdr));
+}
+
/*
** This function is only ever called while committing an UNLOCKED
** transaction, after the caller has already obtained the WRITER lock
int szFrame; /* The size of a single frame */
i64 iOffset; /* Next byte to write in WAL file */
WalWriter w; /* The writer */
- int bUpgrade = 0; /* True if commit requires snapshot upgrade */
assert( pList );
assert( pWal->writeLock );
}
#endif
- if( isCommit ){
- volatile WalIndexHdr *pHead = walIndexHdr(pWal);
- if( pHead->mxFrame>pWal->hdr.mxFrame ){
- if( memcmp((void*)&pHead[0], (void*)&pHead[1], sizeof(WalIndexHdr))!=0 ){
- /* TODO: Deal with this case. It's quite possible, but fiddly. */
- return SQLITE_CORRUPT_BKPT;
- }
- memcpy(&pWal->hdr, (void*)pHead, sizeof(WalIndexHdr));
- if( nTruncate<pWal->hdr.nPage ){
- /* Do not truncate the database file in this case */
- nTruncate = pWal->hdr.nPage;
- }
- bUpgrade = 1;
- }
- }
-
/* See if it is possible to write these frames into the start of the
** log file, instead of appending to it at pWal->hdr.mxFrame.
*/
}
}
- if( rc==SQLITE_OK && bUpgrade ){
- /* If this commit required a snapshot upgrade, the pager cache is
- ** not currently consistent with the head of the wal file. Zeroing
- ** Wal.hdr here forces the next transaction to reset the cache
- ** before beginning to read the db. */
- memset(&pWal->hdr, 0, sizeof(WalIndexHdr));
- }
-
WALTRACE(("WAL%p: frame write %s\n", pWal, rc ? "failed" : "ok"));
return rc;
}