Index: third_party/sqlite/src/src/btree.c |
diff --git a/third_party/sqlite/src/src/btree.c b/third_party/sqlite/src/src/btree.c |
index f5feff8a4c597c8b490c9e154dfa5b056e9b93a1..de553423b8847ff23b58c2ce3ec8218314c7a2e8 100644 |
--- a/third_party/sqlite/src/src/btree.c |
+++ b/third_party/sqlite/src/src/btree.c |
@@ -350,7 +350,7 @@ static int setSharedCacheTableLock(Btree *p, Pgno iTable, u8 eLock){ |
if( !pLock ){ |
pLock = (BtLock *)sqlite3MallocZero(sizeof(BtLock)); |
if( !pLock ){ |
- return SQLITE_NOMEM; |
+ return SQLITE_NOMEM_BKPT; |
} |
pLock->iTable = iTable; |
pLock->pBtree = p; |
@@ -450,6 +450,19 @@ static void releasePage(MemPage *pPage); /* Forward reference */ |
static int cursorHoldsMutex(BtCursor *p){ |
return sqlite3_mutex_held(p->pBt->mutex); |
} |
+ |
+/* Verify that the cursor and the BtShared agree about what is the current |
+** database connetion. This is important in shared-cache mode. If the database |
+** connection pointers get out-of-sync, it is possible for routines like |
+** btreeInitPage() to reference an stale connection pointer that references a |
+** a connection that has already closed. This routine is used inside assert() |
+** statements only and for the purpose of double-checking that the btree code |
+** does keep the database connection pointers up-to-date. |
+*/ |
+static int cursorOwnsBtShared(BtCursor *p){ |
+ assert( cursorHoldsMutex(p) ); |
+ return (p->pBtree->db==p->pBt->db); |
+} |
#endif |
/* |
@@ -549,7 +562,7 @@ static int btreeSetHasContent(BtShared *pBt, Pgno pgno){ |
assert( pgno<=pBt->nPage ); |
pBt->pHasContent = sqlite3BitvecCreate(pBt->nPage); |
if( !pBt->pHasContent ){ |
- rc = SQLITE_NOMEM; |
+ rc = SQLITE_NOMEM_BKPT; |
} |
} |
if( rc==SQLITE_OK && pgno<=sqlite3BitvecSize(pBt->pHasContent) ){ |
@@ -605,30 +618,28 @@ static void btreeReleaseAllCursorPages(BtCursor *pCur){ |
** the key. |
*/ |
static int saveCursorKey(BtCursor *pCur){ |
- int rc; |
+ int rc = SQLITE_OK; |
assert( CURSOR_VALID==pCur->eState ); |
assert( 0==pCur->pKey ); |
assert( cursorHoldsMutex(pCur) ); |
- rc = sqlite3BtreeKeySize(pCur, &pCur->nKey); |
- assert( rc==SQLITE_OK ); /* KeySize() cannot fail */ |
- |
- /* If this is an intKey table, then the above call to BtreeKeySize() |
- ** stores the integer key in pCur->nKey. In this case this value is |
- ** all that is required. Otherwise, if pCur is not open on an intKey |
- ** table, then malloc space for and store the pCur->nKey bytes of key |
- ** data. */ |
- if( 0==pCur->curIntKey ){ |
- void *pKey = sqlite3Malloc( pCur->nKey ); |
+ if( pCur->curIntKey ){ |
+ /* Only the rowid is required for a table btree */ |
+ pCur->nKey = sqlite3BtreeIntegerKey(pCur); |
+ }else{ |
+ /* For an index btree, save the complete key content */ |
+ void *pKey; |
+ pCur->nKey = sqlite3BtreePayloadSize(pCur); |
+ pKey = sqlite3Malloc( pCur->nKey ); |
if( pKey ){ |
- rc = sqlite3BtreeKey(pCur, 0, (int)pCur->nKey, pKey); |
+ rc = sqlite3BtreePayload(pCur, 0, (int)pCur->nKey, pKey); |
if( rc==SQLITE_OK ){ |
pCur->pKey = pKey; |
}else{ |
sqlite3_free(pKey); |
} |
}else{ |
- rc = SQLITE_NOMEM; |
+ rc = SQLITE_NOMEM_BKPT; |
} |
} |
assert( !pCur->curIntKey || !pCur->pKey ); |
@@ -752,26 +763,23 @@ static int btreeMoveto( |
){ |
int rc; /* Status code */ |
UnpackedRecord *pIdxKey; /* Unpacked index key */ |
- char aSpace[200]; /* Temp space for pIdxKey - to avoid a malloc */ |
- char *pFree = 0; |
if( pKey ){ |
assert( nKey==(i64)(int)nKey ); |
- pIdxKey = sqlite3VdbeAllocUnpackedRecord( |
- pCur->pKeyInfo, aSpace, sizeof(aSpace), &pFree |
- ); |
- if( pIdxKey==0 ) return SQLITE_NOMEM; |
+ pIdxKey = sqlite3VdbeAllocUnpackedRecord(pCur->pKeyInfo); |
+ if( pIdxKey==0 ) return SQLITE_NOMEM_BKPT; |
sqlite3VdbeRecordUnpack(pCur->pKeyInfo, (int)nKey, pKey, pIdxKey); |
if( pIdxKey->nField==0 ){ |
- sqlite3DbFree(pCur->pKeyInfo->db, pFree); |
- return SQLITE_CORRUPT_BKPT; |
+ rc = SQLITE_CORRUPT_BKPT; |
+ goto moveto_done; |
} |
}else{ |
pIdxKey = 0; |
} |
rc = sqlite3BtreeMovetoUnpacked(pCur, pIdxKey, nKey, bias, pRes); |
- if( pFree ){ |
- sqlite3DbFree(pCur->pKeyInfo->db, pFree); |
+moveto_done: |
+ if( pIdxKey ){ |
+ sqlite3DbFree(pCur->pKeyInfo->db, pIdxKey); |
} |
return rc; |
} |
@@ -786,7 +794,7 @@ static int btreeMoveto( |
static int btreeRestoreCursorPosition(BtCursor *pCur){ |
int rc; |
int skipNext; |
- assert( cursorHoldsMutex(pCur) ); |
+ assert( cursorOwnsBtShared(pCur) ); |
assert( pCur->eState>=CURSOR_REQUIRESEEK ); |
if( pCur->eState==CURSOR_FAULT ){ |
return pCur->skipNext; |
@@ -1075,7 +1083,6 @@ static void btreeParseCellPtrNoPayload( |
){ |
assert( sqlite3_mutex_held(pPage->pBt->mutex) ); |
assert( pPage->leaf==0 ); |
- assert( pPage->noPayload ); |
assert( pPage->childPtrSize==4 ); |
#ifndef SQLITE_DEBUG |
UNUSED_PARAMETER(pPage); |
@@ -1097,8 +1104,6 @@ static void btreeParseCellPtr( |
assert( sqlite3_mutex_held(pPage->pBt->mutex) ); |
assert( pPage->leaf==0 || pPage->leaf==1 ); |
- assert( pPage->intKeyLeaf || pPage->noPayload ); |
- assert( pPage->noPayload==0 ); |
assert( pPage->intKeyLeaf ); |
assert( pPage->childPtrSize==0 ); |
pIter = pCell; |
@@ -1167,7 +1172,6 @@ static void btreeParseCellPtrIndex( |
assert( sqlite3_mutex_held(pPage->pBt->mutex) ); |
assert( pPage->leaf==0 || pPage->leaf==1 ); |
assert( pPage->intKeyLeaf==0 ); |
- assert( pPage->noPayload==0 ); |
pIter = pCell + pPage->childPtrSize; |
nPayload = *pIter; |
if( nPayload>=0x80 ){ |
@@ -1228,7 +1232,6 @@ static u16 cellSizePtr(MemPage *pPage, u8 *pCell){ |
pPage->xParseCell(pPage, pCell, &debuginfo); |
#endif |
- assert( pPage->noPayload==0 ); |
nSize = *pIter; |
if( nSize>=0x80 ){ |
pEnd = &pIter[8]; |
@@ -1599,8 +1602,11 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ |
if( data[iPtr+1]==0 && data[iPtr]==0 ){ |
iFreeBlk = 0; /* Shortcut for the case when the freelist is empty */ |
}else{ |
- while( (iFreeBlk = get2byte(&data[iPtr]))>0 && iFreeBlk<iStart ){ |
- if( iFreeBlk<iPtr+4 ) return SQLITE_CORRUPT_BKPT; |
+ while( (iFreeBlk = get2byte(&data[iPtr]))<iStart ){ |
+ if( iFreeBlk<iPtr+4 ){ |
+ if( iFreeBlk==0 ) break; |
+ return SQLITE_CORRUPT_BKPT; |
+ } |
iPtr = iFreeBlk; |
} |
if( iFreeBlk>iLast ) return SQLITE_CORRUPT_BKPT; |
@@ -1677,35 +1683,32 @@ static int decodeFlags(MemPage *pPage, int flagByte){ |
pPage->xCellSize = cellSizePtr; |
pBt = pPage->pBt; |
if( flagByte==(PTF_LEAFDATA | PTF_INTKEY) ){ |
- /* EVIDENCE-OF: R-03640-13415 A value of 5 means the page is an interior |
- ** table b-tree page. */ |
+ /* EVIDENCE-OF: R-07291-35328 A value of 5 (0x05) means the page is an |
+ ** interior table b-tree page. */ |
assert( (PTF_LEAFDATA|PTF_INTKEY)==5 ); |
- /* EVIDENCE-OF: R-20501-61796 A value of 13 means the page is a leaf |
- ** table b-tree page. */ |
+ /* EVIDENCE-OF: R-26900-09176 A value of 13 (0x0d) means the page is a |
+ ** leaf table b-tree page. */ |
assert( (PTF_LEAFDATA|PTF_INTKEY|PTF_LEAF)==13 ); |
pPage->intKey = 1; |
if( pPage->leaf ){ |
pPage->intKeyLeaf = 1; |
- pPage->noPayload = 0; |
pPage->xParseCell = btreeParseCellPtr; |
}else{ |
pPage->intKeyLeaf = 0; |
- pPage->noPayload = 1; |
pPage->xCellSize = cellSizePtrNoPayload; |
pPage->xParseCell = btreeParseCellPtrNoPayload; |
} |
pPage->maxLocal = pBt->maxLeaf; |
pPage->minLocal = pBt->minLeaf; |
}else if( flagByte==PTF_ZERODATA ){ |
- /* EVIDENCE-OF: R-27225-53936 A value of 2 means the page is an interior |
- ** index b-tree page. */ |
+ /* EVIDENCE-OF: R-43316-37308 A value of 2 (0x02) means the page is an |
+ ** interior index b-tree page. */ |
assert( (PTF_ZERODATA)==2 ); |
- /* EVIDENCE-OF: R-16571-11615 A value of 10 means the page is a leaf |
- ** index b-tree page. */ |
+ /* EVIDENCE-OF: R-59615-42828 A value of 10 (0x0a) means the page is a |
+ ** leaf index b-tree page. */ |
assert( (PTF_ZERODATA|PTF_LEAF)==10 ); |
pPage->intKey = 0; |
pPage->intKeyLeaf = 0; |
- pPage->noPayload = 0; |
pPage->xParseCell = btreeParseCellPtrIndex; |
pPage->maxLocal = pBt->maxLocal; |
pPage->minLocal = pBt->minLocal; |
@@ -1737,7 +1740,7 @@ static int btreeInitPage(MemPage *pPage){ |
assert( pPage->aData == sqlite3PagerGetData(pPage->pDbPage) ); |
if( !pPage->isInit ){ |
- u16 pc; /* Address of a freeblock within pPage->aData[] */ |
+ int pc; /* Address of a freeblock within pPage->aData[] */ |
u8 hdr; /* Offset to beginning of page header */ |
u8 *data; /* Equal to pPage->aData */ |
BtShared *pBt; /* The main btree structure */ |
@@ -1817,25 +1820,30 @@ static int btreeInitPage(MemPage *pPage){ |
** freeblocks. */ |
pc = get2byte(&data[hdr+1]); |
nFree = data[hdr+7] + top; /* Init nFree to non-freeblock free space */ |
- while( pc>0 ){ |
- u16 next, size; |
- if( pc<iCellFirst || pc>iCellLast ){ |
+ if( pc>0 ){ |
+ u32 next, size; |
+ if( pc<iCellFirst ){ |
/* EVIDENCE-OF: R-55530-52930 In a well-formed b-tree page, there will |
** always be at least one cell before the first freeblock. |
- ** |
- ** Or, the freeblock is off the end of the page |
*/ |
return SQLITE_CORRUPT_BKPT; |
} |
- next = get2byte(&data[pc]); |
- size = get2byte(&data[pc+2]); |
- if( (next>0 && next<=pc+size+3) || pc+size>usableSize ){ |
- /* Free blocks must be in ascending order. And the last byte of |
- ** the free-block must lie on the database page. */ |
- return SQLITE_CORRUPT_BKPT; |
+ while( 1 ){ |
+ if( pc>iCellLast ){ |
+ return SQLITE_CORRUPT_BKPT; /* Freeblock off the end of the page */ |
+ } |
+ next = get2byte(&data[pc]); |
+ size = get2byte(&data[pc+2]); |
+ nFree = nFree + size; |
+ if( next<=pc+size+3 ) break; |
+ pc = next; |
+ } |
+ if( next>0 ){ |
+ return SQLITE_CORRUPT_BKPT; /* Freeblock not in ascending order */ |
+ } |
+ if( pc+size>(unsigned int)usableSize ){ |
+ return SQLITE_CORRUPT_BKPT; /* Last freeblock extends past page end */ |
} |
- nFree = nFree + size; |
- pc = next; |
} |
/* At this point, nFree contains the sum of the offset to the start |
@@ -2180,7 +2188,7 @@ int sqlite3BtreeOpen( |
} |
p = sqlite3MallocZero(sizeof(Btree)); |
if( !p ){ |
- return SQLITE_NOMEM; |
+ return SQLITE_NOMEM_BKPT; |
} |
p->inTrans = TRANS_NONE; |
p->db = db; |
@@ -2204,7 +2212,7 @@ int sqlite3BtreeOpen( |
p->sharable = 1; |
if( !zFullPathname ){ |
sqlite3_free(p); |
- return SQLITE_NOMEM; |
+ return SQLITE_NOMEM_BKPT; |
} |
if( isMemdb ){ |
memcpy(zFullPathname, zFilename, nFilename); |
@@ -2272,11 +2280,11 @@ int sqlite3BtreeOpen( |
pBt = sqlite3MallocZero( sizeof(*pBt) ); |
if( pBt==0 ){ |
- rc = SQLITE_NOMEM; |
+ rc = SQLITE_NOMEM_BKPT; |
goto btree_open_out; |
} |
rc = sqlite3PagerOpen(pVfs, &pBt->pPager, zFilename, |
- EXTRA_SIZE, flags, vfsFlags, pageReinit); |
+ sizeof(MemPage), flags, vfsFlags, pageReinit); |
if( rc==SQLITE_OK ){ |
sqlite3PagerSetMmapLimit(pBt->pPager, db->szMmap); |
rc = sqlite3PagerReadFileheader(pBt->pPager,sizeof(zDbHeader),zDbHeader); |
@@ -2334,15 +2342,14 @@ int sqlite3BtreeOpen( |
#if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO) |
/* Add the new BtShared object to the linked list sharable BtShareds. |
*/ |
+ pBt->nRef = 1; |
if( p->sharable ){ |
MUTEX_LOGIC( sqlite3_mutex *mutexShared; ) |
- pBt->nRef = 1; |
MUTEX_LOGIC( mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);) |
if( SQLITE_THREADSAFE && sqlite3GlobalConfig.bCoreMutex ){ |
pBt->mutex = sqlite3MutexAlloc(SQLITE_MUTEX_FAST); |
if( pBt->mutex==0 ){ |
- rc = SQLITE_NOMEM; |
- db->mallocFailed = 0; |
+ rc = SQLITE_NOMEM_BKPT; |
goto btree_open_out; |
} |
} |
@@ -2365,12 +2372,12 @@ int sqlite3BtreeOpen( |
for(i=0; i<db->nDb; i++){ |
if( (pSib = db->aDb[i].pBt)!=0 && pSib->sharable ){ |
while( pSib->pPrev ){ pSib = pSib->pPrev; } |
- if( p->pBt<pSib->pBt ){ |
+ if( (uptr)p->pBt<(uptr)pSib->pBt ){ |
p->pNext = pSib; |
p->pPrev = 0; |
pSib->pPrev = p; |
}else{ |
- while( pSib->pNext && pSib->pNext->pBt<p->pBt ){ |
+ while( pSib->pNext && (uptr)pSib->pNext->pBt<(uptr)p->pBt ){ |
pSib = pSib->pNext; |
} |
p->pNext = pSib->pNext; |
@@ -2390,12 +2397,14 @@ int sqlite3BtreeOpen( |
btree_open_out: |
if( rc!=SQLITE_OK ){ |
if( pBt && pBt->pPager ){ |
- sqlite3PagerClose(pBt->pPager); |
+ sqlite3PagerClose(pBt->pPager, 0); |
} |
sqlite3_free(pBt); |
sqlite3_free(p); |
*ppBtree = 0; |
}else{ |
+ sqlite3_file *pFile; |
+ |
/* If the B-Tree was successfully opened, set the pager-cache size to the |
** default value. Except, when opening on an existing shared pager-cache, |
** do not change the pager-cache size. |
@@ -2403,11 +2412,17 @@ btree_open_out: |
if( sqlite3BtreeSchema(p, 0, 0)==0 ){ |
sqlite3PagerSetCachesize(p->pBt->pPager, SQLITE_DEFAULT_CACHE_SIZE); |
} |
+ |
+ pFile = sqlite3PagerFile(pBt->pPager); |
+ if( pFile->pMethods ){ |
+ sqlite3OsFileControlHint(pFile, SQLITE_FCNTL_PDB, (void*)&pBt->db); |
+ } |
} |
if( mutexOpen ){ |
assert( sqlite3_mutex_held(mutexOpen) ); |
sqlite3_mutex_leave(mutexOpen); |
} |
+ assert( rc!=SQLITE_OK || sqlite3BtreeConnectionCount(*ppBtree)>0 ); |
return rc; |
} |
@@ -2531,7 +2546,7 @@ int sqlite3BtreeClose(Btree *p){ |
** Clean out and delete the BtShared object. |
*/ |
assert( !pBt->pCursor ); |
- sqlite3PagerClose(pBt->pPager); |
+ sqlite3PagerClose(pBt->pPager, p->db); |
if( pBt->xFreeSchema && pBt->pSchema ){ |
pBt->xFreeSchema(pBt->pSchema); |
} |
@@ -2625,21 +2640,6 @@ int sqlite3BtreeSetPagerFlags( |
#endif |
/* |
-** Return TRUE if the given btree is set to safety level 1. In other |
-** words, return TRUE if no sync() occurs on the disk files. |
-*/ |
-int sqlite3BtreeSyncDisabled(Btree *p){ |
- BtShared *pBt = p->pBt; |
- int rc; |
- assert( sqlite3_mutex_held(p->db->mutex) ); |
- sqlite3BtreeEnter(p); |
- assert( pBt && pBt->pPager ); |
- rc = sqlite3PagerNosync(pBt->pPager); |
- sqlite3BtreeLeave(p); |
- return rc; |
-} |
- |
-/* |
** Change the default pages size and the number of reserved bytes per page. |
** Or, if the page size has already been fixed, return SQLITE_READONLY |
** without changing anything. |
@@ -2884,9 +2884,25 @@ static int lockBtree(BtShared *pBt){ |
rc = sqlite3PagerOpenWal(pBt->pPager, &isOpen); |
if( rc!=SQLITE_OK ){ |
goto page1_init_failed; |
- }else if( isOpen==0 ){ |
- releasePage(pPage1); |
- return SQLITE_OK; |
+ }else{ |
+#if SQLITE_DEFAULT_SYNCHRONOUS!=SQLITE_DEFAULT_WAL_SYNCHRONOUS |
+ sqlite3 *db; |
+ Db *pDb; |
+ if( (db=pBt->db)!=0 && (pDb=db->aDb)!=0 ){ |
+ while( pDb->pBt==0 || pDb->pBt->pBt!=pBt ){ pDb++; } |
+ if( pDb->bSyncSet==0 |
+ && pDb->safety_level==SQLITE_DEFAULT_SYNCHRONOUS+1 |
+ ){ |
+ pDb->safety_level = SQLITE_DEFAULT_WAL_SYNCHRONOUS+1; |
+ sqlite3PagerSetFlags(pBt->pPager, |
+ pDb->safety_level | (db->flags & PAGER_FLAGS_MASK)); |
+ } |
+ } |
+#endif |
+ if( isOpen==0 ){ |
+ releasePage(pPage1); |
+ return SQLITE_OK; |
+ } |
} |
rc = SQLITE_NOTADB; |
} |
@@ -3126,7 +3142,6 @@ int sqlite3BtreeNewDb(Btree *p){ |
** proceed. |
*/ |
int sqlite3BtreeBeginTrans(Btree *p, int wrflag){ |
- sqlite3 *pBlock = 0; |
BtShared *pBt = p->pBt; |
int rc = SQLITE_OK; |
@@ -3149,27 +3164,30 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){ |
} |
#ifndef SQLITE_OMIT_SHARED_CACHE |
- /* If another database handle has already opened a write transaction |
- ** on this shared-btree structure and a second write transaction is |
- ** requested, return SQLITE_LOCKED. |
- */ |
- if( (wrflag && pBt->inTransaction==TRANS_WRITE) |
- || (pBt->btsFlags & BTS_PENDING)!=0 |
- ){ |
- pBlock = pBt->pWriter->db; |
- }else if( wrflag>1 ){ |
- BtLock *pIter; |
- for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ |
- if( pIter->pBtree!=p ){ |
- pBlock = pIter->pBtree->db; |
- break; |
+ { |
+ sqlite3 *pBlock = 0; |
+ /* If another database handle has already opened a write transaction |
+ ** on this shared-btree structure and a second write transaction is |
+ ** requested, return SQLITE_LOCKED. |
+ */ |
+ if( (wrflag && pBt->inTransaction==TRANS_WRITE) |
+ || (pBt->btsFlags & BTS_PENDING)!=0 |
+ ){ |
+ pBlock = pBt->pWriter->db; |
+ }else if( wrflag>1 ){ |
+ BtLock *pIter; |
+ for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ |
+ if( pIter->pBtree!=p ){ |
+ pBlock = pIter->pBtree->db; |
+ break; |
+ } |
} |
} |
- } |
- if( pBlock ){ |
- sqlite3ConnectionBlocked(p->db, pBlock); |
- rc = SQLITE_LOCKED_SHAREDCACHE; |
- goto trans_begun; |
+ if( pBlock ){ |
+ sqlite3ConnectionBlocked(p->db, pBlock); |
+ rc = SQLITE_LOCKED_SHAREDCACHE; |
+ goto trans_begun; |
+ } |
} |
#endif |
@@ -3275,14 +3293,11 @@ static int setChildPtrmaps(MemPage *pPage){ |
int nCell; /* Number of cells in page pPage */ |
int rc; /* Return code */ |
BtShared *pBt = pPage->pBt; |
- u8 isInitOrig = pPage->isInit; |
Pgno pgno = pPage->pgno; |
assert( sqlite3_mutex_held(pPage->pBt->mutex) ); |
rc = btreeInitPage(pPage); |
- if( rc!=SQLITE_OK ){ |
- goto set_child_ptrmaps_out; |
- } |
+ if( rc!=SQLITE_OK ) return rc; |
nCell = pPage->nCell; |
for(i=0; i<nCell; i++){ |
@@ -3301,8 +3316,6 @@ static int setChildPtrmaps(MemPage *pPage){ |
ptrmapPut(pBt, childPgno, PTRMAP_BTREE, pgno, &rc); |
} |
-set_child_ptrmaps_out: |
- pPage->isInit = isInitOrig; |
return rc; |
} |
@@ -3330,7 +3343,6 @@ static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){ |
} |
put4byte(pPage->aData, iTo); |
}else{ |
- u8 isInitOrig = pPage->isInit; |
int i; |
int nCell; |
int rc; |
@@ -3344,12 +3356,14 @@ static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){ |
if( eType==PTRMAP_OVERFLOW1 ){ |
CellInfo info; |
pPage->xParseCell(pPage, pCell, &info); |
- if( info.nLocal<info.nPayload |
- && pCell+info.nSize-1<=pPage->aData+pPage->maskPage |
- && iFrom==get4byte(pCell+info.nSize-4) |
- ){ |
- put4byte(pCell+info.nSize-4, iTo); |
- break; |
+ if( info.nLocal<info.nPayload ){ |
+ if( pCell+info.nSize > pPage->aData+pPage->pBt->usableSize ){ |
+ return SQLITE_CORRUPT_BKPT; |
+ } |
+ if( iFrom==get4byte(pCell+info.nSize-4) ){ |
+ put4byte(pCell+info.nSize-4, iTo); |
+ break; |
+ } |
} |
}else{ |
if( get4byte(pCell)==iFrom ){ |
@@ -3366,8 +3380,6 @@ static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){ |
} |
put4byte(&pPage->aData[pPage->hdrOffset+8], iTo); |
} |
- |
- pPage->isInit = isInitOrig; |
} |
return SQLITE_OK; |
} |
@@ -4026,7 +4038,12 @@ 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); |
- rc = sqlite3PagerSavepoint(pBt->pPager, op, iSavepoint); |
+ if( op==SAVEPOINT_ROLLBACK ){ |
+ rc = saveAllCursors(pBt, 0, 0); |
+ } |
+ if( rc==SQLITE_OK ){ |
+ rc = sqlite3PagerSavepoint(pBt->pPager, op, iSavepoint); |
+ } |
if( rc==SQLITE_OK ){ |
if( iSavepoint<0 && (pBt->btsFlags & BTS_INITIALLY_EMPTY)!=0 ){ |
pBt->nPage = 0; |
@@ -4051,13 +4068,13 @@ int sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){ |
** on the database already. If a write-cursor is requested, then |
** the caller is assumed to have an open write transaction. |
** |
-** If wrFlag==0, then the cursor can only be used for reading. |
-** If wrFlag==1, then the cursor can be used for reading or for |
-** writing if other conditions for writing are also met. These |
-** are the conditions that must be met in order for writing to |
-** be allowed: |
+** If the BTREE_WRCSR bit of wrFlag is clear, then the cursor can only |
+** be used for reading. If the BTREE_WRCSR bit is set, then the cursor |
+** can be used for reading or for writing if other conditions for writing |
+** are also met. These are the conditions that must be met in order |
+** for writing to be allowed: |
** |
-** 1: The cursor must have been opened with wrFlag==1 |
+** 1: The cursor must have been opened with wrFlag containing BTREE_WRCSR |
** |
** 2: Other database connections that share the same pager cache |
** but which are not in the READ_UNCOMMITTED state may not have |
@@ -4069,6 +4086,16 @@ int sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){ |
** |
** 4: There must be an active transaction. |
** |
+** The BTREE_FORDELETE bit of wrFlag may optionally be set if BTREE_WRCSR |
+** is set. If FORDELETE is set, that is a hint to the implementation that |
+** this cursor will only be used to seek to and delete entries of an index |
+** as part of a larger DELETE statement. The FORDELETE hint is not used by |
+** this implementation. But in a hypothetical alternative storage engine |
+** in which index entries are automatically deleted when corresponding table |
+** rows are deleted, the FORDELETE flag is a hint that all SEEK and DELETE |
+** operations on this cursor can be no-ops and all READ operations can |
+** return a null row (2-bytes: 0x01 0x00). |
+** |
** No checking is done to make sure that page iTable really is the |
** root page of a b-tree. If it is not, then the cursor acquired |
** will not work correctly. |
@@ -4107,7 +4134,7 @@ static int btreeCursor( |
if( wrFlag ){ |
allocateTempSpace(pBt); |
- if( pBt->pTmpSpace==0 ) return SQLITE_NOMEM; |
+ if( pBt->pTmpSpace==0 ) return SQLITE_NOMEM_BKPT; |
} |
if( iTable==1 && btreePagecount(pBt)==0 ){ |
assert( wrFlag==0 ); |
@@ -4252,48 +4279,39 @@ int sqlite3BtreeCursorIsValid(BtCursor *pCur){ |
return pCur && pCur->eState==CURSOR_VALID; |
} |
#endif /* NDEBUG */ |
+int sqlite3BtreeCursorIsValidNN(BtCursor *pCur){ |
+ assert( pCur!=0 ); |
+ return pCur->eState==CURSOR_VALID; |
+} |
/* |
-** Set *pSize to the size of the buffer needed to hold the value of |
-** the key for the current entry. If the cursor is not pointing |
-** to a valid entry, *pSize is set to 0. |
-** |
-** For a table with the INTKEY flag set, this routine returns the key |
-** itself, not the number of bytes in the key. |
-** |
-** The caller must position the cursor prior to invoking this routine. |
-** |
-** This routine cannot fail. It always returns SQLITE_OK. |
+** Return the value of the integer key or "rowid" for a table btree. |
+** This routine is only valid for a cursor that is pointing into a |
+** ordinary table btree. If the cursor points to an index btree or |
+** is invalid, the result of this routine is undefined. |
*/ |
-int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){ |
+i64 sqlite3BtreeIntegerKey(BtCursor *pCur){ |
assert( cursorHoldsMutex(pCur) ); |
assert( pCur->eState==CURSOR_VALID ); |
+ assert( pCur->curIntKey ); |
getCellInfo(pCur); |
- *pSize = pCur->info.nKey; |
- return SQLITE_OK; |
+ return pCur->info.nKey; |
} |
/* |
-** Set *pSize to the number of bytes of data in the entry the |
-** cursor currently points to. |
+** Return the number of bytes of payload for the entry that pCur is |
+** currently pointing to. For table btrees, this will be the amount |
+** of data. For index btrees, this will be the size of the key. |
** |
** The caller must guarantee that the cursor is pointing to a non-NULL |
** valid entry. In other words, the calling procedure must guarantee |
** that the cursor has Cursor.eState==CURSOR_VALID. |
-** |
-** Failure is not possible. This function always returns SQLITE_OK. |
-** It might just as well be a procedure (returning void) but we continue |
-** to return an integer result code for historical reasons. |
*/ |
-int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){ |
+u32 sqlite3BtreePayloadSize(BtCursor *pCur){ |
assert( cursorHoldsMutex(pCur) ); |
assert( pCur->eState==CURSOR_VALID ); |
- assert( pCur->iPage>=0 ); |
- assert( pCur->iPage<BTCURSOR_MAX_DEPTH ); |
- assert( pCur->apPage[pCur->iPage]->intKeyLeaf==1 ); |
getCellInfo(pCur); |
- *pSize = pCur->info.nPayload; |
- return SQLITE_OK; |
+ return pCur->info.nPayload; |
} |
/* |
@@ -4411,7 +4429,6 @@ static int copyPayload( |
** |
** 0: The operation is a read. Populate the overflow cache. |
** 1: The operation is a write. Populate the overflow cache. |
-** 2: The operation is a read. Do not populate the overflow cache. |
** |
** A total of "amt" bytes are read or written beginning at "offset". |
** Data is read to or from the buffer pBuf. |
@@ -4419,13 +4436,13 @@ static int copyPayload( |
** The content being read or written might appear on the main page |
** or be scattered out on multiple overflow pages. |
** |
-** If the current cursor entry uses one or more overflow pages and the |
-** eOp argument is not 2, this function may allocate space for and lazily |
-** populates the overflow page-list cache array (BtCursor.aOverflow). |
+** If the current cursor entry uses one or more overflow pages |
+** this function may allocate space for and lazily populate |
+** the overflow page-list cache array (BtCursor.aOverflow). |
** Subsequent calls use this cache to make seeking to the supplied offset |
** more efficient. |
** |
-** Once an overflow page-list cache has been allocated, it may be |
+** Once an overflow page-list cache has been allocated, it must be |
** invalidated if some other cursor writes to the same table, or if |
** the cursor is moved to a different row. Additionally, in auto-vacuum |
** mode, the following events may invalidate an overflow page-list cache. |
@@ -4447,25 +4464,26 @@ static int accessPayload( |
MemPage *pPage = pCur->apPage[pCur->iPage]; /* Btree page of current entry */ |
BtShared *pBt = pCur->pBt; /* Btree this cursor belongs to */ |
#ifdef SQLITE_DIRECT_OVERFLOW_READ |
- unsigned char * const pBufStart = pBuf; |
- int bEnd; /* True if reading to end of data */ |
+ unsigned char * const pBufStart = pBuf; /* Start of original out buffer */ |
#endif |
assert( pPage ); |
+ assert( eOp==0 || eOp==1 ); |
assert( pCur->eState==CURSOR_VALID ); |
assert( pCur->aiIdx[pCur->iPage]<pPage->nCell ); |
assert( cursorHoldsMutex(pCur) ); |
- assert( eOp!=2 || offset==0 ); /* Always start from beginning for eOp==2 */ |
getCellInfo(pCur); |
aPayload = pCur->info.pPayload; |
-#ifdef SQLITE_DIRECT_OVERFLOW_READ |
- bEnd = offset+amt==pCur->info.nPayload; |
-#endif |
assert( offset+amt <= pCur->info.nPayload ); |
- if( &aPayload[pCur->info.nLocal] > &pPage->aData[pBt->usableSize] ){ |
- /* Trying to read or write past the end of the data is an error */ |
+ assert( aPayload > pPage->aData ); |
+ if( (uptr)(aPayload - pPage->aData) > (pBt->usableSize - pCur->info.nLocal) ){ |
+ /* Trying to read or write past the end of the data is an error. The |
+ ** conditional above is really: |
+ ** &aPayload[pCur->info.nLocal] > &pPage->aData[pBt->usableSize] |
+ ** but is recast into its current form to avoid integer overflow problems |
+ */ |
return SQLITE_CORRUPT_BKPT; |
} |
@@ -4475,7 +4493,7 @@ static int accessPayload( |
if( a+offset>pCur->info.nLocal ){ |
a = pCur->info.nLocal - offset; |
} |
- rc = copyPayload(&aPayload[offset], pBuf, a, (eOp & 0x01), pPage->pDbPage); |
+ rc = copyPayload(&aPayload[offset], pBuf, a, eOp, pPage->pDbPage); |
offset = 0; |
pBuf += a; |
amt -= a; |
@@ -4491,53 +4509,46 @@ static int accessPayload( |
nextPage = get4byte(&aPayload[pCur->info.nLocal]); |
/* If the BtCursor.aOverflow[] has not been allocated, allocate it now. |
- ** Except, do not allocate aOverflow[] for eOp==2. |
** |
** The aOverflow[] array is sized at one entry for each overflow page |
** in the overflow chain. The page number of the first overflow page is |
** stored in aOverflow[0], etc. A value of 0 in the aOverflow[] array |
** means "not yet known" (the cache is lazily populated). |
*/ |
- if( eOp!=2 && (pCur->curFlags & BTCF_ValidOvfl)==0 ){ |
+ if( (pCur->curFlags & BTCF_ValidOvfl)==0 ){ |
int nOvfl = (pCur->info.nPayload-pCur->info.nLocal+ovflSize-1)/ovflSize; |
if( nOvfl>pCur->nOvflAlloc ){ |
Pgno *aNew = (Pgno*)sqlite3Realloc( |
pCur->aOverflow, nOvfl*2*sizeof(Pgno) |
); |
if( aNew==0 ){ |
- rc = SQLITE_NOMEM; |
+ return SQLITE_NOMEM_BKPT; |
}else{ |
pCur->nOvflAlloc = nOvfl*2; |
pCur->aOverflow = aNew; |
} |
} |
- if( rc==SQLITE_OK ){ |
- memset(pCur->aOverflow, 0, nOvfl*sizeof(Pgno)); |
- pCur->curFlags |= BTCF_ValidOvfl; |
+ memset(pCur->aOverflow, 0, nOvfl*sizeof(Pgno)); |
+ pCur->curFlags |= BTCF_ValidOvfl; |
+ }else{ |
+ /* If the overflow page-list cache has been allocated and the |
+ ** entry for the first required overflow page is valid, skip |
+ ** directly to it. |
+ */ |
+ if( pCur->aOverflow[offset/ovflSize] ){ |
+ iIdx = (offset/ovflSize); |
+ nextPage = pCur->aOverflow[iIdx]; |
+ offset = (offset%ovflSize); |
} |
} |
- /* If the overflow page-list cache has been allocated and the |
- ** entry for the first required overflow page is valid, skip |
- ** directly to it. |
- */ |
- if( (pCur->curFlags & BTCF_ValidOvfl)!=0 |
- && pCur->aOverflow[offset/ovflSize] |
- ){ |
- iIdx = (offset/ovflSize); |
- nextPage = pCur->aOverflow[iIdx]; |
- offset = (offset%ovflSize); |
- } |
- |
- for( ; rc==SQLITE_OK && amt>0 && nextPage; iIdx++){ |
- |
+ assert( rc==SQLITE_OK && amt>0 ); |
+ while( nextPage ){ |
/* If required, populate the overflow page-list cache. */ |
- if( (pCur->curFlags & BTCF_ValidOvfl)!=0 ){ |
- assert( pCur->aOverflow[iIdx]==0 |
- || pCur->aOverflow[iIdx]==nextPage |
- || CORRUPT_DB ); |
- pCur->aOverflow[iIdx] = nextPage; |
- } |
+ assert( pCur->aOverflow[iIdx]==0 |
+ || pCur->aOverflow[iIdx]==nextPage |
+ || CORRUPT_DB ); |
+ pCur->aOverflow[iIdx] = nextPage; |
if( offset>=ovflSize ){ |
/* The only reason to read this page is to obtain the page |
@@ -4545,11 +4556,7 @@ static int accessPayload( |
** data is not required. So first try to lookup the overflow |
** page-list cache, if any, then fall back to the getOverflowPage() |
** function. |
- ** |
- ** Note that the aOverflow[] array must be allocated because eOp!=2 |
- ** here. If eOp==2, then offset==0 and this branch is never taken. |
*/ |
- assert( eOp!=2 ); |
assert( pCur->curFlags & BTCF_ValidOvfl ); |
assert( pCur->pBtree->db==pBt->db ); |
if( pCur->aOverflow[iIdx+1] ){ |
@@ -4563,7 +4570,7 @@ static int accessPayload( |
** range of data that is being read (eOp==0) or written (eOp!=0). |
*/ |
#ifdef SQLITE_DIRECT_OVERFLOW_READ |
- sqlite3_file *fd; |
+ sqlite3_file *fd; /* File from which to do direct overflow read */ |
#endif |
int a = amt; |
if( a + offset > ovflSize ){ |
@@ -4575,27 +4582,25 @@ static int accessPayload( |
** |
** 1) this is a read operation, and |
** 2) data is required from the start of this overflow page, and |
- ** 3) the database is file-backed, and |
- ** 4) there is no open write-transaction, and |
- ** 5) the database is not a WAL database, |
- ** 6) all data from the page is being read. |
- ** 7) at least 4 bytes have already been read into the output buffer |
+ ** 3) there is no open write-transaction, and |
+ ** 4) the database is file-backed, and |
+ ** 5) the page is not in the WAL file |
+ ** 6) at least 4 bytes have already been read into the output buffer |
** |
** then data can be read directly from the database file into the |
** output buffer, bypassing the page-cache altogether. This speeds |
** up loading large records that span many overflow pages. |
*/ |
- if( (eOp&0x01)==0 /* (1) */ |
+ if( eOp==0 /* (1) */ |
&& offset==0 /* (2) */ |
- && (bEnd || a==ovflSize) /* (6) */ |
- && pBt->inTransaction==TRANS_READ /* (4) */ |
- && (fd = sqlite3PagerFile(pBt->pPager))->pMethods /* (3) */ |
- && pBt->pPage1->aData[19]==0x01 /* (5) */ |
- && &pBuf[-4]>=pBufStart /* (7) */ |
+ && pBt->inTransaction==TRANS_READ /* (3) */ |
+ && (fd = sqlite3PagerFile(pBt->pPager))->pMethods /* (4) */ |
+ && 0==sqlite3PagerUseWal(pBt->pPager, nextPage) /* (5) */ |
+ && &pBuf[-4]>=pBufStart /* (6) */ |
){ |
u8 aSave[4]; |
u8 *aWrite = &pBuf[-4]; |
- assert( aWrite>=pBufStart ); /* hence (7) */ |
+ assert( aWrite>=pBufStart ); /* due to (6) */ |
memcpy(aSave, aWrite, 4); |
rc = sqlite3OsRead(fd, aWrite, a+4, (i64)pBt->pageSize*(nextPage-1)); |
nextPage = get4byte(aWrite); |
@@ -4606,41 +4611,49 @@ static int accessPayload( |
{ |
DbPage *pDbPage; |
rc = sqlite3PagerGet(pBt->pPager, nextPage, &pDbPage, |
- ((eOp&0x01)==0 ? PAGER_GET_READONLY : 0) |
+ (eOp==0 ? PAGER_GET_READONLY : 0) |
); |
if( rc==SQLITE_OK ){ |
aPayload = sqlite3PagerGetData(pDbPage); |
nextPage = get4byte(aPayload); |
- rc = copyPayload(&aPayload[offset+4], pBuf, a, (eOp&0x01), pDbPage); |
+ rc = copyPayload(&aPayload[offset+4], pBuf, a, eOp, pDbPage); |
sqlite3PagerUnref(pDbPage); |
offset = 0; |
} |
} |
amt -= a; |
+ if( amt==0 ) return rc; |
pBuf += a; |
} |
+ if( rc ) break; |
+ iIdx++; |
} |
} |
if( rc==SQLITE_OK && amt>0 ){ |
- return SQLITE_CORRUPT_BKPT; |
+ return SQLITE_CORRUPT_BKPT; /* Overflow chain ends prematurely */ |
} |
return rc; |
} |
/* |
-** Read part of the key associated with cursor pCur. Exactly |
-** "amt" bytes will be transferred into pBuf[]. The transfer |
+** Read part of the payload for the row at which that cursor pCur is currently |
+** pointing. "amt" bytes will be transferred into pBuf[]. The transfer |
** begins at "offset". |
** |
-** The caller must ensure that pCur is pointing to a valid row |
-** in the table. |
+** pCur can be pointing to either a table or an index b-tree. |
+** If pointing to a table btree, then the content section is read. If |
+** pCur is pointing to an index b-tree then the key section is read. |
+** |
+** For sqlite3BtreePayload(), the caller must ensure that pCur is pointing |
+** to a valid row in the table. For sqlite3BtreePayloadChecked(), the |
+** cursor might be invalid or might need to be restored before being read. |
** |
** Return SQLITE_OK on success or an error code if anything goes |
** wrong. An error is returned if "offset+amt" is larger than |
** the available payload. |
*/ |
-int sqlite3BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){ |
+int sqlite3BtreePayload(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){ |
assert( cursorHoldsMutex(pCur) ); |
assert( pCur->eState==CURSOR_VALID ); |
assert( pCur->iPage>=0 && pCur->apPage[pCur->iPage] ); |
@@ -4649,33 +4662,34 @@ int sqlite3BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){ |
} |
/* |
-** Read part of the data associated with cursor pCur. Exactly |
-** "amt" bytes will be transfered into pBuf[]. The transfer |
-** begins at "offset". |
-** |
-** Return SQLITE_OK on success or an error code if anything goes |
-** wrong. An error is returned if "offset+amt" is larger than |
-** the available payload. |
+** This variant of sqlite3BtreePayload() works even if the cursor has not |
+** in the CURSOR_VALID state. It is only used by the sqlite3_blob_read() |
+** interface. |
*/ |
-int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){ |
- int rc; |
- |
#ifndef SQLITE_OMIT_INCRBLOB |
+static SQLITE_NOINLINE int accessPayloadChecked( |
+ BtCursor *pCur, |
+ u32 offset, |
+ u32 amt, |
+ void *pBuf |
+){ |
+ int rc; |
if ( pCur->eState==CURSOR_INVALID ){ |
return SQLITE_ABORT; |
} |
-#endif |
- |
- assert( cursorHoldsMutex(pCur) ); |
- rc = restoreCursorPosition(pCur); |
- if( rc==SQLITE_OK ){ |
- assert( pCur->eState==CURSOR_VALID ); |
- assert( pCur->iPage>=0 && pCur->apPage[pCur->iPage] ); |
- assert( pCur->aiIdx[pCur->iPage]<pCur->apPage[pCur->iPage]->nCell ); |
- rc = accessPayload(pCur, offset, amt, pBuf, 0); |
+ assert( cursorOwnsBtShared(pCur) ); |
+ rc = btreeRestoreCursorPosition(pCur); |
+ return rc ? rc : accessPayload(pCur, offset, amt, pBuf, 0); |
+} |
+int sqlite3BtreePayloadChecked(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){ |
+ if( pCur->eState==CURSOR_VALID ){ |
+ assert( cursorOwnsBtShared(pCur) ); |
+ return accessPayload(pCur, offset, amt, pBuf, 0); |
+ }else{ |
+ return accessPayloadChecked(pCur, offset, amt, pBuf); |
} |
- return rc; |
} |
+#endif /* SQLITE_OMIT_INCRBLOB */ |
/* |
** Return a pointer to payload information from the entry that the |
@@ -4704,7 +4718,7 @@ static const void *fetchPayload( |
assert( pCur!=0 && pCur->iPage>=0 && pCur->apPage[pCur->iPage]); |
assert( pCur->eState==CURSOR_VALID ); |
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); |
- assert( cursorHoldsMutex(pCur) ); |
+ assert( cursorOwnsBtShared(pCur) ); |
assert( pCur->aiIdx[pCur->iPage]<pCur->apPage[pCur->iPage]->nCell ); |
assert( pCur->info.nSize>0 ); |
assert( pCur->info.pPayload>pCur->apPage[pCur->iPage]->aData || CORRUPT_DB ); |
@@ -4730,10 +4744,7 @@ static const void *fetchPayload( |
** These routines is used to get quick access to key and data |
** in the common case where no overflow pages are used. |
*/ |
-const void *sqlite3BtreeKeyFetch(BtCursor *pCur, u32 *pAmt){ |
- return fetchPayload(pCur, pAmt); |
-} |
-const void *sqlite3BtreeDataFetch(BtCursor *pCur, u32 *pAmt){ |
+const void *sqlite3BtreePayloadFetch(BtCursor *pCur, u32 *pAmt){ |
return fetchPayload(pCur, pAmt); |
} |
@@ -4750,7 +4761,7 @@ const void *sqlite3BtreeDataFetch(BtCursor *pCur, u32 *pAmt){ |
static int moveToChild(BtCursor *pCur, u32 newPgno){ |
BtShared *pBt = pCur->pBt; |
- assert( cursorHoldsMutex(pCur) ); |
+ assert( cursorOwnsBtShared(pCur) ); |
assert( pCur->eState==CURSOR_VALID ); |
assert( pCur->iPage<BTCURSOR_MAX_DEPTH ); |
assert( pCur->iPage>=0 ); |
@@ -4796,7 +4807,7 @@ static void assertParentIndex(MemPage *pParent, int iIdx, Pgno iChild){ |
** the largest cell index. |
*/ |
static void moveToParent(BtCursor *pCur){ |
- assert( cursorHoldsMutex(pCur) ); |
+ assert( cursorOwnsBtShared(pCur) ); |
assert( pCur->eState==CURSOR_VALID ); |
assert( pCur->iPage>0 ); |
assert( pCur->apPage[pCur->iPage] ); |
@@ -4836,7 +4847,7 @@ static int moveToRoot(BtCursor *pCur){ |
MemPage *pRoot; |
int rc = SQLITE_OK; |
- assert( cursorHoldsMutex(pCur) ); |
+ assert( cursorOwnsBtShared(pCur) ); |
assert( CURSOR_INVALID < CURSOR_REQUIRESEEK ); |
assert( CURSOR_VALID < CURSOR_REQUIRESEEK ); |
assert( CURSOR_FAULT > CURSOR_REQUIRESEEK ); |
@@ -4849,9 +4860,12 @@ static int moveToRoot(BtCursor *pCur){ |
} |
if( pCur->iPage>=0 ){ |
- while( pCur->iPage ){ |
- assert( pCur->apPage[pCur->iPage]!=0 ); |
- releasePageNotNull(pCur->apPage[pCur->iPage--]); |
+ if( pCur->iPage ){ |
+ do{ |
+ assert( pCur->apPage[pCur->iPage]!=0 ); |
+ releasePageNotNull(pCur->apPage[pCur->iPage--]); |
+ }while( pCur->iPage); |
+ goto skip_init; |
} |
}else if( pCur->pgnoRoot==0 ){ |
pCur->eState = CURSOR_INVALID; |
@@ -4862,7 +4876,7 @@ static int moveToRoot(BtCursor *pCur){ |
0, pCur->curPagerFlags); |
if( rc!=SQLITE_OK ){ |
pCur->eState = CURSOR_INVALID; |
- return rc; |
+ return rc; |
} |
pCur->iPage = 0; |
pCur->curIntKey = pCur->apPage[0]->intKey; |
@@ -4885,10 +4899,12 @@ static int moveToRoot(BtCursor *pCur){ |
return SQLITE_CORRUPT_BKPT; |
} |
+skip_init: |
pCur->aiIdx[0] = 0; |
pCur->info.nSize = 0; |
pCur->curFlags &= ~(BTCF_AtLast|BTCF_ValidNKey|BTCF_ValidOvfl); |
+ pRoot = pCur->apPage[0]; |
if( pRoot->nCell>0 ){ |
pCur->eState = CURSOR_VALID; |
}else if( !pRoot->leaf ){ |
@@ -4915,7 +4931,7 @@ static int moveToLeftmost(BtCursor *pCur){ |
int rc = SQLITE_OK; |
MemPage *pPage; |
- assert( cursorHoldsMutex(pCur) ); |
+ assert( cursorOwnsBtShared(pCur) ); |
assert( pCur->eState==CURSOR_VALID ); |
while( rc==SQLITE_OK && !(pPage = pCur->apPage[pCur->iPage])->leaf ){ |
assert( pCur->aiIdx[pCur->iPage]<pPage->nCell ); |
@@ -4940,7 +4956,7 @@ static int moveToRightmost(BtCursor *pCur){ |
int rc = SQLITE_OK; |
MemPage *pPage = 0; |
- assert( cursorHoldsMutex(pCur) ); |
+ assert( cursorOwnsBtShared(pCur) ); |
assert( pCur->eState==CURSOR_VALID ); |
while( !(pPage = pCur->apPage[pCur->iPage])->leaf ){ |
pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]); |
@@ -4961,7 +4977,7 @@ static int moveToRightmost(BtCursor *pCur){ |
int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){ |
int rc; |
- assert( cursorHoldsMutex(pCur) ); |
+ assert( cursorOwnsBtShared(pCur) ); |
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); |
rc = moveToRoot(pCur); |
if( rc==SQLITE_OK ){ |
@@ -4984,7 +5000,7 @@ int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){ |
int sqlite3BtreeLast(BtCursor *pCur, int *pRes){ |
int rc; |
- assert( cursorHoldsMutex(pCur) ); |
+ assert( cursorOwnsBtShared(pCur) ); |
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); |
/* If the cursor already points to the last entry, this is a no-op. */ |
@@ -5062,23 +5078,41 @@ int sqlite3BtreeMovetoUnpacked( |
int rc; |
RecordCompare xRecordCompare; |
- assert( cursorHoldsMutex(pCur) ); |
+ assert( cursorOwnsBtShared(pCur) ); |
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); |
assert( pRes ); |
assert( (pIdxKey==0)==(pCur->pKeyInfo==0) ); |
+ assert( pCur->eState!=CURSOR_VALID || (pIdxKey==0)==(pCur->curIntKey!=0) ); |
/* If the cursor is already positioned at the point we are trying |
** to move to, then just return without doing any work */ |
- if( pCur->eState==CURSOR_VALID && (pCur->curFlags & BTCF_ValidNKey)!=0 |
- && pCur->curIntKey |
+ if( pIdxKey==0 |
+ && pCur->eState==CURSOR_VALID && (pCur->curFlags & BTCF_ValidNKey)!=0 |
){ |
if( pCur->info.nKey==intKey ){ |
*pRes = 0; |
return SQLITE_OK; |
} |
- if( (pCur->curFlags & BTCF_AtLast)!=0 && pCur->info.nKey<intKey ){ |
- *pRes = -1; |
- return SQLITE_OK; |
+ if( pCur->info.nKey<intKey ){ |
+ if( (pCur->curFlags & BTCF_AtLast)!=0 ){ |
+ *pRes = -1; |
+ return SQLITE_OK; |
+ } |
+ /* If the requested key is one more than the previous key, then |
+ ** try to get there using sqlite3BtreeNext() rather than a full |
+ ** binary search. This is an optimization only. The correct answer |
+ ** is still obtained without this ase, only a little more slowely */ |
+ if( pCur->info.nKey+1==intKey && !pCur->skipNext ){ |
+ *pRes = 0; |
+ rc = sqlite3BtreeNext(pCur, pRes); |
+ if( rc ) return rc; |
+ if( *pRes==0 ){ |
+ getCellInfo(pCur); |
+ if( pCur->info.nKey==intKey ){ |
+ return SQLITE_OK; |
+ } |
+ } |
+ } |
} |
} |
@@ -5144,16 +5178,16 @@ int sqlite3BtreeMovetoUnpacked( |
if( lwr>upr ){ c = +1; break; } |
}else{ |
assert( nCellKey==intKey ); |
- pCur->curFlags |= BTCF_ValidNKey; |
- pCur->info.nKey = nCellKey; |
pCur->aiIdx[pCur->iPage] = (u16)idx; |
if( !pPage->leaf ){ |
lwr = idx; |
goto moveto_next_layer; |
}else{ |
+ pCur->curFlags |= BTCF_ValidNKey; |
+ pCur->info.nKey = nCellKey; |
+ pCur->info.nSize = 0; |
*pRes = 0; |
- rc = SQLITE_OK; |
- goto moveto_finish; |
+ return SQLITE_OK; |
} |
} |
assert( lwr+upr>=0 ); |
@@ -5210,11 +5244,12 @@ int sqlite3BtreeMovetoUnpacked( |
} |
pCellKey = sqlite3Malloc( nCell+18 ); |
if( pCellKey==0 ){ |
- rc = SQLITE_NOMEM; |
+ rc = SQLITE_NOMEM_BKPT; |
goto moveto_finish; |
} |
pCur->aiIdx[pCur->iPage] = (u16)idx; |
- rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 2); |
+ rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 0); |
+ pCur->curFlags &= ~BTCF_ValidOvfl; |
if( rc ){ |
sqlite3_free(pCellKey); |
goto moveto_finish; |
@@ -5264,7 +5299,7 @@ moveto_next_layer: |
} |
moveto_finish: |
pCur->info.nSize = 0; |
- pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl); |
+ assert( (pCur->curFlags & BTCF_ValidOvfl)==0 ); |
return rc; |
} |
@@ -5310,7 +5345,7 @@ static SQLITE_NOINLINE int btreeNext(BtCursor *pCur, int *pRes){ |
int idx; |
MemPage *pPage; |
- assert( cursorHoldsMutex(pCur) ); |
+ assert( cursorOwnsBtShared(pCur) ); |
assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); |
assert( *pRes==0 ); |
if( pCur->eState!=CURSOR_VALID ){ |
@@ -5374,7 +5409,7 @@ static SQLITE_NOINLINE int btreeNext(BtCursor *pCur, int *pRes){ |
} |
int sqlite3BtreeNext(BtCursor *pCur, int *pRes){ |
MemPage *pPage; |
- assert( cursorHoldsMutex(pCur) ); |
+ assert( cursorOwnsBtShared(pCur) ); |
assert( pRes!=0 ); |
assert( *pRes==0 || *pRes==1 ); |
assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); |
@@ -5419,7 +5454,7 @@ static SQLITE_NOINLINE int btreePrevious(BtCursor *pCur, int *pRes){ |
int rc; |
MemPage *pPage; |
- assert( cursorHoldsMutex(pCur) ); |
+ assert( cursorOwnsBtShared(pCur) ); |
assert( pRes!=0 ); |
assert( *pRes==0 ); |
assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); |
@@ -5462,7 +5497,7 @@ static SQLITE_NOINLINE int btreePrevious(BtCursor *pCur, int *pRes){ |
moveToParent(pCur); |
} |
assert( pCur->info.nSize==0 ); |
- assert( (pCur->curFlags & (BTCF_ValidNKey|BTCF_ValidOvfl))==0 ); |
+ assert( (pCur->curFlags & (BTCF_ValidOvfl))==0 ); |
pCur->aiIdx[pCur->iPage]--; |
pPage = pCur->apPage[pCur->iPage]; |
@@ -5475,7 +5510,7 @@ static SQLITE_NOINLINE int btreePrevious(BtCursor *pCur, int *pRes){ |
return rc; |
} |
int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){ |
- assert( cursorHoldsMutex(pCur) ); |
+ assert( cursorOwnsBtShared(pCur) ); |
assert( pRes!=0 ); |
assert( *pRes==0 || *pRes==1 ); |
assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); |
@@ -5978,30 +6013,28 @@ static void freePage(MemPage *pPage, int *pRC){ |
static int clearCell( |
MemPage *pPage, /* The page that contains the Cell */ |
unsigned char *pCell, /* First byte of the Cell */ |
- u16 *pnSize /* Write the size of the Cell here */ |
+ CellInfo *pInfo /* Size information about the cell */ |
){ |
BtShared *pBt = pPage->pBt; |
- CellInfo info; |
Pgno ovflPgno; |
int rc; |
int nOvfl; |
u32 ovflPageSize; |
assert( sqlite3_mutex_held(pPage->pBt->mutex) ); |
- pPage->xParseCell(pPage, pCell, &info); |
- *pnSize = info.nSize; |
- if( info.nLocal==info.nPayload ){ |
+ pPage->xParseCell(pPage, pCell, pInfo); |
+ if( pInfo->nLocal==pInfo->nPayload ){ |
return SQLITE_OK; /* No overflow pages. Return without doing anything */ |
} |
- if( pCell+info.nSize-1 > pPage->aData+pPage->maskPage ){ |
+ if( pCell+pInfo->nSize-1 > pPage->aData+pPage->maskPage ){ |
return SQLITE_CORRUPT_BKPT; /* Cell extends past end of page */ |
} |
- ovflPgno = get4byte(pCell + info.nSize - 4); |
+ ovflPgno = get4byte(pCell + pInfo->nSize - 4); |
assert( pBt->usableSize > 4 ); |
ovflPageSize = pBt->usableSize - 4; |
- nOvfl = (info.nPayload - info.nLocal + ovflPageSize - 1)/ovflPageSize; |
+ nOvfl = (pInfo->nPayload - pInfo->nLocal + ovflPageSize - 1)/ovflPageSize; |
assert( nOvfl>0 || |
- (CORRUPT_DB && (info.nPayload + ovflPageSize)<ovflPageSize) |
+ (CORRUPT_DB && (pInfo->nPayload + ovflPageSize)<ovflPageSize) |
); |
while( nOvfl-- ){ |
Pgno iNext = 0; |
@@ -6059,9 +6092,7 @@ static int clearCell( |
static int fillInCell( |
MemPage *pPage, /* The page that contains the cell */ |
unsigned char *pCell, /* Complete text of the cell */ |
- const void *pKey, i64 nKey, /* The key */ |
- const void *pData,int nData, /* The data */ |
- int nZero, /* Extra zero bytes to append to pData */ |
+ const BtreePayload *pX, /* Payload with which to construct the cell */ |
int *pnSize /* Write cell size here */ |
){ |
int nPayload; |
@@ -6085,26 +6116,21 @@ static int fillInCell( |
/* Fill in the header. */ |
nHeader = pPage->childPtrSize; |
- nPayload = nData + nZero; |
- if( pPage->intKeyLeaf ){ |
+ if( pPage->intKey ){ |
+ nPayload = pX->nData + pX->nZero; |
+ pSrc = pX->pData; |
+ nSrc = pX->nData; |
+ assert( pPage->intKeyLeaf ); /* fillInCell() only called for leaves */ |
nHeader += putVarint32(&pCell[nHeader], nPayload); |
+ nHeader += putVarint(&pCell[nHeader], *(u64*)&pX->nKey); |
}else{ |
- assert( nData==0 ); |
- assert( nZero==0 ); |
+ assert( pX->nKey<=0x7fffffff && pX->pKey!=0 ); |
+ nSrc = nPayload = (int)pX->nKey; |
+ pSrc = pX->pKey; |
+ nHeader += putVarint32(&pCell[nHeader], nPayload); |
} |
- nHeader += putVarint(&pCell[nHeader], *(u64*)&nKey); |
- /* Fill in the payload size */ |
- if( pPage->intKey ){ |
- pSrc = pData; |
- nSrc = nData; |
- nData = 0; |
- }else{ |
- assert( nKey<=0x7fffffff && pKey!=0 ); |
- nPayload = (int)nKey; |
- pSrc = pKey; |
- nSrc = (int)nKey; |
- } |
+ /* Fill in the payload */ |
if( nPayload<=pPage->maxLocal ){ |
n = nHeader + nPayload; |
testcase( n==3 ); |
@@ -6141,8 +6167,8 @@ static int fillInCell( |
{ |
CellInfo info; |
pPage->xParseCell(pPage, pCell, &info); |
- assert( nHeader=(int)(info.pPayload - pCell) ); |
- assert( info.nKey==nKey ); |
+ assert( nHeader==(int)(info.pPayload - pCell) ); |
+ assert( info.nKey==pX->nKey ); |
assert( *pnSize == info.nSize ); |
assert( spaceLeft == info.nLocal ); |
} |
@@ -6227,10 +6253,6 @@ static int fillInCell( |
pSrc += n; |
nSrc -= n; |
spaceLeft -= n; |
- if( nSrc==0 ){ |
- nSrc = nData; |
- pSrc = pData; |
- } |
} |
releasePage(pToRelease); |
return SQLITE_OK; |
@@ -6252,7 +6274,6 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){ |
int hdr; /* Beginning of the header. 0 most pages. 100 page 1 */ |
if( *pRC ) return; |
- |
assert( idx>=0 && idx<pPage->nCell ); |
assert( CORRUPT_DB || sz==cellSize(pPage, idx) ); |
assert( sqlite3PagerIswriteable(pPage->pDbPage) ); |
@@ -6297,6 +6318,8 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){ |
** in pTemp or the original pCell) and also record its index. |
** Allocating a new entry in pPage->aCell[] implies that |
** pPage->nOverflow is incremented. |
+** |
+** *pRC must be SQLITE_OK when this routine is called. |
*/ |
static void insertCell( |
MemPage *pPage, /* Page into which we are copying */ |
@@ -6312,8 +6335,7 @@ static void insertCell( |
u8 *data; /* The content of the whole page */ |
u8 *pIns; /* The point in pPage->aCellIdx[] where no cell inserted */ |
- if( *pRC ) return; |
- |
+ assert( *pRC==SQLITE_OK ); |
assert( i>=0 && i<=pPage->nCell+pPage->nOverflow ); |
assert( MX_CELL(pPage->pBt)<=10921 ); |
assert( pPage->nCell<=MX_CELL(pPage->pBt) || CORRUPT_DB ); |
@@ -6335,7 +6357,10 @@ static void insertCell( |
put4byte(pCell, iChild); |
} |
j = pPage->nOverflow++; |
- assert( j<(int)(sizeof(pPage->apOvfl)/sizeof(pPage->apOvfl[0])) ); |
+ /* Comparison against ArraySize-1 since we hold back one extra slot |
+ ** as a contingency. In other words, never need more than 3 overflow |
+ ** slots but 4 are allocated, just to be safe. */ |
+ assert( j < ArraySize(pPage->apOvfl)-1 ); |
pPage->apOvfl[j] = pCell; |
pPage->aiOvfl[j] = (u16)i; |
@@ -6387,7 +6412,7 @@ static void insertCell( |
/* |
** A CellArray object contains a cache of pointers and sizes for a |
-** consecutive sequence of cells that might be held multiple pages. |
+** consecutive sequence of cells that might be held on multiple pages. |
*/ |
typedef struct CellArray CellArray; |
struct CellArray { |
@@ -6532,8 +6557,8 @@ static int pageInsertArray( |
u8 *pSlot; |
sz = cachedCellSize(pCArray, i); |
if( (aData[1]==0 && aData[2]==0) || (pSlot = pageFindSlot(pPg,sz,&rc))==0 ){ |
+ if( (pData - pBegin)<sz ) return 1; |
pData -= sz; |
- if( pData<pBegin ) return 1; |
pSlot = pData; |
} |
/* pSlot and pCArray->apCell[i] will never overlap on a well-formed |
@@ -6695,7 +6720,7 @@ static int editPage( |
for(i=0; i<nNew && !CORRUPT_DB; i++){ |
u8 *pCell = pCArray->apCell[i+iNew]; |
int iOff = get2byteAligned(&pPg->aCellIdx[i*2]); |
- if( pCell>=aData && pCell<&aData[pPg->pBt->usableSize] ){ |
+ if( SQLITE_WITHIN(pCell, aData, &aData[pPg->pBt->usableSize]) ){ |
pCell = &pTmp[pCell - aData]; |
} |
assert( 0==memcmp(pCell, &aData[iOff], |
@@ -6819,8 +6844,10 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){ |
while( ((*(pOut++) = *(pCell++))&0x80) && pCell<pStop ); |
/* Insert the new divider cell into pParent. */ |
- insertCell(pParent, pParent->nCell, pSpace, (int)(pOut-pSpace), |
- 0, pPage->pgno, &rc); |
+ if( rc==SQLITE_OK ){ |
+ insertCell(pParent, pParent->nCell, pSpace, (int)(pOut-pSpace), |
+ 0, pPage->pgno, &rc); |
+ } |
/* Set the right-child pointer of pParent to point to the new page. */ |
put4byte(&pParent->aData[pParent->hdrOffset+8], pgnoNew); |
@@ -7029,7 +7056,7 @@ static int balance_nonroot( |
assert( pParent->nOverflow==0 || pParent->aiOvfl[0]==iParentIdx ); |
if( !aOvflSpace ){ |
- return SQLITE_NOMEM; |
+ return SQLITE_NOMEM_BKPT; |
} |
/* Find the sibling pages to balance. Also locate the cells in pParent |
@@ -7073,7 +7100,7 @@ static int balance_nonroot( |
nMaxCells += 1+apOld[i]->nCell+apOld[i]->nOverflow; |
if( (i--)==0 ) break; |
- if( i+nxDiv==pParent->aiOvfl[0] && pParent->nOverflow ){ |
+ if( pParent->nOverflow && i+nxDiv==pParent->aiOvfl[0] ){ |
apDiv[i] = pParent->apOvfl[0]; |
pgno = get4byte(apDiv[i]); |
szNew[i] = pParent->xCellSize(pParent, apDiv[i]); |
@@ -7129,7 +7156,7 @@ static int balance_nonroot( |
assert( szScratch<=6*(int)pBt->pageSize ); |
b.apCell = sqlite3ScratchMalloc( szScratch ); |
if( b.apCell==0 ){ |
- rc = SQLITE_NOMEM; |
+ rc = SQLITE_NOMEM_BKPT; |
goto balance_cleanup; |
} |
b.szCell = (u16*)&b.apCell[nMaxCells]; |
@@ -7188,9 +7215,8 @@ static int balance_nonroot( |
** long be able to find the cells if a pointer to each cell is not saved |
** first. |
*/ |
- memset(&b.szCell[b.nCell], 0, sizeof(b.szCell[0])*limit); |
+ memset(&b.szCell[b.nCell], 0, sizeof(b.szCell[0])*(limit+pOld->nOverflow)); |
if( pOld->nOverflow>0 ){ |
- memset(&b.szCell[b.nCell+limit], 0, sizeof(b.szCell[0])*pOld->nOverflow); |
limit = pOld->aiOvfl[0]; |
for(j=0; j<limit; j++){ |
b.apCell[b.nCell] = aData + (maskPage & get2byteAligned(piCell)); |
@@ -7266,7 +7292,6 @@ static int balance_nonroot( |
for(i=0; i<nOld; i++){ |
MemPage *p = apOld[i]; |
szNew[i] = usableSpace - p->nFree; |
- if( szNew[i]<0 ){ rc = SQLITE_CORRUPT_BKPT; goto balance_cleanup; } |
for(j=0; j<p->nOverflow; j++){ |
szNew[i] += 2 + p->xCellSize(p, p->apOvfl[j]); |
} |
@@ -7341,7 +7366,7 @@ static int balance_nonroot( |
assert( r<nMaxCells ); |
(void)cachedCellSize(&b, r); |
if( szRight!=0 |
- && (bBulk || szRight+b.szCell[d]+2 > szLeft-(b.szCell[r]+2)) ){ |
+ && (bBulk || szRight+b.szCell[d]+2 > szLeft-(b.szCell[r]+(i==k-1?0:2)))){ |
break; |
} |
szRight += b.szCell[d] + 2; |
@@ -7565,9 +7590,9 @@ static int balance_nonroot( |
** any cell). But it is important to pass the correct size to |
** insertCell(), so reparse the cell now. |
** |
- ** Note that this can never happen in an SQLite data file, as all |
- ** cells are at least 4 bytes. It only happens in b-trees used |
- ** to evaluate "IN (SELECT ...)" and similar clauses. |
+ ** This can only happen for b-trees used to evaluate "IN (SELECT ...)" |
+ ** and WITHOUT ROWID tables with exactly one column which is the |
+ ** primary key. |
*/ |
if( b.szCell[j]==4 ){ |
assert(leafCorrection==4); |
@@ -7801,8 +7826,8 @@ static int balance(BtCursor *pCur){ |
u8 aBalanceQuickSpace[13]; |
u8 *pFree = 0; |
- TESTONLY( int balance_quick_called = 0 ); |
- TESTONLY( int balance_deeper_called = 0 ); |
+ VVA_ONLY( int balance_quick_called = 0 ); |
+ VVA_ONLY( int balance_deeper_called = 0 ); |
do { |
int iPage = pCur->iPage; |
@@ -7815,7 +7840,8 @@ static int balance(BtCursor *pCur){ |
** and copy the current contents of the root-page to it. The |
** next iteration of the do-loop will balance the child page. |
*/ |
- assert( (balance_deeper_called++)==0 ); |
+ assert( balance_deeper_called==0 ); |
+ VVA_ONLY( balance_deeper_called++ ); |
rc = balance_deeper(pPage, &pCur->apPage[1]); |
if( rc==SQLITE_OK ){ |
pCur->iPage = 1; |
@@ -7854,7 +7880,8 @@ static int balance(BtCursor *pCur){ |
** function. If this were not verified, a subtle bug involving reuse |
** of the aBalanceQuickSpace[] might sneak in. |
*/ |
- assert( (balance_quick_called++)==0 ); |
+ assert( balance_quick_called==0 ); |
+ VVA_ONLY( balance_quick_called++ ); |
rc = balance_quick(pParent, pPage, aBalanceQuickSpace); |
}else |
#endif |
@@ -7911,33 +7938,39 @@ static int balance(BtCursor *pCur){ |
/* |
-** Insert a new record into the BTree. The key is given by (pKey,nKey) |
-** and the data is given by (pData,nData). The cursor is used only to |
-** define what table the record should be inserted into. The cursor |
-** is left pointing at a random location. |
+** Insert a new record into the BTree. The content of the new record |
+** is described by the pX object. The pCur cursor is used only to |
+** define what table the record should be inserted into, and is left |
+** pointing at a random location. |
+** |
+** For a table btree (used for rowid tables), only the pX.nKey value of |
+** the key is used. The pX.pKey value must be NULL. The pX.nKey is the |
+** rowid or INTEGER PRIMARY KEY of the row. The pX.nData,pData,nZero fields |
+** hold the content of the row. |
** |
-** For an INTKEY table, only the nKey value of the key is used. pKey is |
-** ignored. For a ZERODATA table, the pData and nData are both ignored. |
+** For an index btree (used for indexes and WITHOUT ROWID tables), the |
+** key is an arbitrary byte sequence stored in pX.pKey,nKey. The |
+** pX.pData,nData,nZero fields must be zero. |
** |
** If the seekResult parameter is non-zero, then a successful call to |
-** MovetoUnpacked() to seek cursor pCur to (pKey, nKey) has already |
-** been performed. seekResult is the search result returned (a negative |
-** number if pCur points at an entry that is smaller than (pKey, nKey), or |
-** a positive value if pCur points at an entry that is larger than |
-** (pKey, nKey)). |
-** |
-** If the seekResult parameter is non-zero, then the caller guarantees that |
-** cursor pCur is pointing at the existing copy of a row that is to be |
-** overwritten. If the seekResult parameter is 0, then cursor pCur may |
-** point to any entry or to no entry at all and so this function has to seek |
-** the cursor before the new key can be inserted. |
+** MovetoUnpacked() to seek cursor pCur to (pKey,nKey) has already |
+** been performed. In other words, if seekResult!=0 then the cursor |
+** is currently pointing to a cell that will be adjacent to the cell |
+** to be inserted. If seekResult<0 then pCur points to a cell that is |
+** smaller then (pKey,nKey). If seekResult>0 then pCur points to a cell |
+** that is larger than (pKey,nKey). |
+** |
+** If seekResult==0, that means pCur is pointing at some unknown location. |
+** In that case, this routine must seek the cursor to the correct insertion |
+** point for (pKey,nKey) before doing the insertion. For index btrees, |
+** if pX->nMem is non-zero, then pX->aMem contains pointers to the unpacked |
+** key values and pX->aMem can be used instead of pX->pKey to avoid having |
+** to decode the key. |
*/ |
int sqlite3BtreeInsert( |
BtCursor *pCur, /* Insert data into the table of this cursor */ |
- const void *pKey, i64 nKey, /* The key of the new record */ |
- const void *pData, int nData, /* The data of the new record */ |
- int nZero, /* Number of extra 0 bytes to append to data */ |
- int appendBias, /* True if this is likely an append */ |
+ const BtreePayload *pX, /* Content of the row to be inserted */ |
+ int flags, /* True if this is likely an append */ |
int seekResult /* Result of prior MovetoUnpacked() call */ |
){ |
int rc; |
@@ -7950,12 +7983,14 @@ int sqlite3BtreeInsert( |
unsigned char *oldCell; |
unsigned char *newCell = 0; |
+ assert( (flags & (BTREE_SAVEPOSITION|BTREE_APPEND))==flags ); |
+ |
if( pCur->eState==CURSOR_FAULT ){ |
assert( pCur->skipNext!=SQLITE_OK ); |
return pCur->skipNext; |
} |
- assert( cursorHoldsMutex(pCur) ); |
+ assert( cursorOwnsBtShared(pCur) ); |
assert( (pCur->curFlags & BTCF_WriteFlag)!=0 |
&& pBt->inTransaction==TRANS_WRITE |
&& (pBt->btsFlags & BTS_READ_ONLY)==0 ); |
@@ -7966,7 +8001,7 @@ int sqlite3BtreeInsert( |
** keys with no associated data. If the cursor was opened expecting an |
** intkey table, the caller should be inserting integer keys with a |
** blob of associated data. */ |
- assert( (pKey==0)==(pCur->pKeyInfo==0) ); |
+ assert( (pX->pKey==0)==(pCur->pKeyInfo==0) ); |
/* Save the positions of any other cursors open on this table. |
** |
@@ -7985,44 +8020,64 @@ int sqlite3BtreeInsert( |
} |
if( pCur->pKeyInfo==0 ){ |
- assert( pKey==0 ); |
+ assert( pX->pKey==0 ); |
/* If this is an insert into a table b-tree, invalidate any incrblob |
** cursors open on the row being replaced */ |
- invalidateIncrblobCursors(p, nKey, 0); |
+ invalidateIncrblobCursors(p, pX->nKey, 0); |
+ |
+ /* If BTREE_SAVEPOSITION is set, the cursor must already be pointing |
+ ** to a row with the same key as the new entry being inserted. */ |
+ assert( (flags & BTREE_SAVEPOSITION)==0 || |
+ ((pCur->curFlags&BTCF_ValidNKey)!=0 && pX->nKey==pCur->info.nKey) ); |
/* If the cursor is currently on the last row and we are appending a |
** new row onto the end, set the "loc" to avoid an unnecessary |
** btreeMoveto() call */ |
- if( (pCur->curFlags&BTCF_ValidNKey)!=0 && nKey>0 |
- && pCur->info.nKey==nKey-1 ){ |
- loc = -1; |
+ if( (pCur->curFlags&BTCF_ValidNKey)!=0 && pX->nKey==pCur->info.nKey ){ |
+ loc = 0; |
+ }else if( (pCur->curFlags&BTCF_ValidNKey)!=0 && pX->nKey>0 |
+ && pCur->info.nKey==pX->nKey-1 ){ |
+ loc = -1; |
}else if( loc==0 ){ |
- rc = sqlite3BtreeMovetoUnpacked(pCur, 0, nKey, appendBias, &loc); |
+ rc = sqlite3BtreeMovetoUnpacked(pCur, 0, pX->nKey, flags!=0, &loc); |
if( rc ) return rc; |
} |
- }else if( loc==0 ){ |
- rc = btreeMoveto(pCur, pKey, nKey, appendBias, &loc); |
+ }else if( loc==0 && (flags & BTREE_SAVEPOSITION)==0 ){ |
+ if( pX->nMem ){ |
+ UnpackedRecord r; |
+ r.pKeyInfo = pCur->pKeyInfo; |
+ r.aMem = pX->aMem; |
+ r.nField = pX->nMem; |
+ r.default_rc = 0; |
+ r.errCode = 0; |
+ r.r1 = 0; |
+ r.r2 = 0; |
+ r.eqSeen = 0; |
+ rc = sqlite3BtreeMovetoUnpacked(pCur, &r, 0, flags!=0, &loc); |
+ }else{ |
+ rc = btreeMoveto(pCur, pX->pKey, pX->nKey, flags!=0, &loc); |
+ } |
if( rc ) return rc; |
} |
assert( pCur->eState==CURSOR_VALID || (pCur->eState==CURSOR_INVALID && loc) ); |
pPage = pCur->apPage[pCur->iPage]; |
- assert( pPage->intKey || nKey>=0 ); |
+ assert( pPage->intKey || pX->nKey>=0 ); |
assert( pPage->leaf || !pPage->intKey ); |
TRACE(("INSERT: table=%d nkey=%lld ndata=%d page=%d %s\n", |
- pCur->pgnoRoot, nKey, nData, pPage->pgno, |
+ pCur->pgnoRoot, pX->nKey, pX->nData, pPage->pgno, |
loc==0 ? "overwrite" : "new entry")); |
assert( pPage->isInit ); |
newCell = pBt->pTmpSpace; |
assert( newCell!=0 ); |
- rc = fillInCell(pPage, newCell, pKey, nKey, pData, nData, nZero, &szNew); |
+ rc = fillInCell(pPage, newCell, pX, &szNew); |
if( rc ) goto end_insert; |
assert( szNew==pPage->xCellSize(pPage, newCell) ); |
assert( szNew <= MX_CELL_SIZE(pBt) ); |
idx = pCur->aiIdx[pCur->iPage]; |
if( loc==0 ){ |
- u16 szOld; |
+ CellInfo info; |
assert( idx<pPage->nCell ); |
rc = sqlite3PagerWrite(pPage->pDbPage); |
if( rc ){ |
@@ -8032,8 +8087,19 @@ int sqlite3BtreeInsert( |
if( !pPage->leaf ){ |
memcpy(newCell, oldCell, 4); |
} |
- rc = clearCell(pPage, oldCell, &szOld); |
- dropCell(pPage, idx, szOld, &rc); |
+ rc = clearCell(pPage, oldCell, &info); |
+ if( info.nSize==szNew && info.nLocal==info.nPayload ){ |
+ /* Overwrite the old cell with the new if they are the same size. |
+ ** We could also try to do this if the old cell is smaller, then add |
+ ** the leftover space to the free list. But experiments show that |
+ ** doing that is no faster then skipping this optimization and just |
+ ** calling dropCell() and insertCell(). */ |
+ assert( rc==SQLITE_OK ); /* clearCell never fails when nLocal==nPayload */ |
+ if( oldCell+szNew > pPage->aDataEnd ) return SQLITE_CORRUPT_BKPT; |
+ memcpy(oldCell, newCell, szNew); |
+ return SQLITE_OK; |
+ } |
+ dropCell(pPage, idx, info.nSize, &rc); |
if( rc ) goto end_insert; |
}else if( loc<0 && pPage->nCell>0 ){ |
assert( pPage->leaf ); |
@@ -8042,6 +8108,7 @@ int sqlite3BtreeInsert( |
assert( pPage->leaf ); |
} |
insertCell(pPage, idx, newCell, szNew, 0, 0, &rc); |
+ assert( pPage->nOverflow==0 || rc==SQLITE_OK ); |
assert( rc!=SQLITE_OK || pPage->nCell>0 || pPage->nOverflow>0 ); |
/* If no error has occurred and pPage has an overflow cell, call balance() |
@@ -8065,7 +8132,8 @@ int sqlite3BtreeInsert( |
** row without seeking the cursor. This can be a big performance boost. |
*/ |
pCur->info.nSize = 0; |
- if( rc==SQLITE_OK && pPage->nOverflow ){ |
+ if( pPage->nOverflow ){ |
+ assert( rc==SQLITE_OK ); |
pCur->curFlags &= ~(BTCF_ValidNKey); |
rc = balance(pCur); |
@@ -8075,6 +8143,20 @@ int sqlite3BtreeInsert( |
** from trying to save the current position of the cursor. */ |
pCur->apPage[pCur->iPage]->nOverflow = 0; |
pCur->eState = CURSOR_INVALID; |
+ if( (flags & BTREE_SAVEPOSITION) && rc==SQLITE_OK ){ |
+ rc = moveToRoot(pCur); |
+ if( pCur->pKeyInfo ){ |
+ assert( pCur->pKey==0 ); |
+ pCur->pKey = sqlite3Malloc( pX->nKey ); |
+ if( pCur->pKey==0 ){ |
+ rc = SQLITE_NOMEM; |
+ }else{ |
+ memcpy(pCur->pKey, pX->pKey, pX->nKey); |
+ } |
+ } |
+ pCur->eState = CURSOR_REQUIRESEEK; |
+ pCur->nKey = pX->nKey; |
+ } |
} |
assert( pCur->apPage[pCur->iPage]->nOverflow==0 ); |
@@ -8085,13 +8167,21 @@ end_insert: |
/* |
** Delete the entry that the cursor is pointing to. |
** |
-** If the second parameter is zero, then the cursor is left pointing at an |
-** arbitrary location after the delete. If it is non-zero, then the cursor |
-** is left in a state such that the next call to BtreeNext() or BtreePrev() |
-** moves it to the same row as it would if the call to BtreeDelete() had |
-** been omitted. |
-*/ |
-int sqlite3BtreeDelete(BtCursor *pCur, int bPreserve){ |
+** If the BTREE_SAVEPOSITION bit of the flags parameter is zero, then |
+** the cursor is left pointing at an arbitrary location after the delete. |
+** But if that bit is set, then the cursor is left in a state such that |
+** the next call to BtreeNext() or BtreePrev() moves it to the same row |
+** as it would have been on if the call to BtreeDelete() had been omitted. |
+** |
+** The BTREE_AUXDELETE bit of flags indicates that is one of several deletes |
+** associated with a single table entry and its indexes. Only one of those |
+** deletes is considered the "primary" delete. The primary delete occurs |
+** on a cursor that is not a BTREE_FORDELETE cursor. All but one delete |
+** operation on non-FORDELETE cursors is tagged with the AUXDELETE flag. |
+** The BTREE_AUXDELETE bit is a hint that is not used by this implementation, |
+** but which might be used by alternative storage engines. |
+*/ |
+int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ |
Btree *p = pCur->pBtree; |
BtShared *pBt = p->pBt; |
int rc; /* Return code */ |
@@ -8099,10 +8189,11 @@ int sqlite3BtreeDelete(BtCursor *pCur, int bPreserve){ |
unsigned char *pCell; /* Pointer to cell to delete */ |
int iCellIdx; /* Index of cell to delete */ |
int iCellDepth; /* Depth of node containing pCell */ |
- u16 szCell; /* Size of the cell being deleted */ |
+ CellInfo info; /* Size of the cell being deleted */ |
int bSkipnext = 0; /* Leaf cursor in SKIPNEXT state */ |
+ u8 bPreserve = flags & BTREE_SAVEPOSITION; /* Keep cursor valid */ |
- assert( cursorHoldsMutex(pCur) ); |
+ assert( cursorOwnsBtShared(pCur) ); |
assert( pBt->inTransaction==TRANS_WRITE ); |
assert( (pBt->btsFlags & BTS_READ_ONLY)==0 ); |
assert( pCur->curFlags & BTCF_WriteFlag ); |
@@ -8110,12 +8201,35 @@ int sqlite3BtreeDelete(BtCursor *pCur, int bPreserve){ |
assert( !hasReadConflicts(p, pCur->pgnoRoot) ); |
assert( pCur->aiIdx[pCur->iPage]<pCur->apPage[pCur->iPage]->nCell ); |
assert( pCur->eState==CURSOR_VALID ); |
+ assert( (flags & ~(BTREE_SAVEPOSITION | BTREE_AUXDELETE))==0 ); |
iCellDepth = pCur->iPage; |
iCellIdx = pCur->aiIdx[iCellDepth]; |
pPage = pCur->apPage[iCellDepth]; |
pCell = findCell(pPage, iCellIdx); |
+ /* If the bPreserve flag is set to true, then the cursor position must |
+ ** be preserved following this delete operation. If the current delete |
+ ** will cause a b-tree rebalance, then this is done by saving the cursor |
+ ** key and leaving the cursor in CURSOR_REQUIRESEEK state before |
+ ** returning. |
+ ** |
+ ** Or, if the current delete will not cause a rebalance, then the cursor |
+ ** will be left in CURSOR_SKIPNEXT state pointing to the entry immediately |
+ ** before or after the deleted entry. In this case set bSkipnext to true. */ |
+ if( bPreserve ){ |
+ if( !pPage->leaf |
+ || (pPage->nFree+cellSizePtr(pPage,pCell)+2)>(int)(pBt->usableSize*2/3) |
+ ){ |
+ /* A b-tree rebalance will be required after deleting this entry. |
+ ** Save the cursor key. */ |
+ rc = saveCursorKey(pCur); |
+ if( rc ) return rc; |
+ }else{ |
+ bSkipnext = 1; |
+ } |
+ } |
+ |
/* If the page containing the entry to delete is not a leaf page, move |
** the cursor to the largest entry in the tree that is smaller than |
** the entry being deleted. This cell will replace the cell being deleted |
@@ -8142,35 +8256,13 @@ int sqlite3BtreeDelete(BtCursor *pCur, int bPreserve){ |
invalidateIncrblobCursors(p, pCur->info.nKey, 0); |
} |
- /* If the bPreserve flag is set to true, then the cursor position must |
- ** be preserved following this delete operation. If the current delete |
- ** will cause a b-tree rebalance, then this is done by saving the cursor |
- ** key and leaving the cursor in CURSOR_REQUIRESEEK state before |
- ** returning. |
- ** |
- ** Or, if the current delete will not cause a rebalance, then the cursor |
- ** will be left in CURSOR_SKIPNEXT state pointing to the entry immediately |
- ** before or after the deleted entry. In this case set bSkipnext to true. */ |
- if( bPreserve ){ |
- if( !pPage->leaf |
- || (pPage->nFree+cellSizePtr(pPage,pCell)+2)>(int)(pBt->usableSize*2/3) |
- ){ |
- /* A b-tree rebalance will be required after deleting this entry. |
- ** Save the cursor key. */ |
- rc = saveCursorKey(pCur); |
- if( rc ) return rc; |
- }else{ |
- bSkipnext = 1; |
- } |
- } |
- |
/* Make the page containing the entry to be deleted writable. Then free any |
** overflow pages associated with the entry and finally remove the cell |
** itself from within the page. */ |
rc = sqlite3PagerWrite(pPage->pDbPage); |
if( rc ) return rc; |
- rc = clearCell(pPage, pCell, &szCell); |
- dropCell(pPage, iCellIdx, szCell, &rc); |
+ rc = clearCell(pPage, pCell, &info); |
+ dropCell(pPage, iCellIdx, info.nSize, &rc); |
if( rc ) return rc; |
/* If the cell deleted was not located on a leaf page, then the cursor |
@@ -8191,7 +8283,9 @@ int sqlite3BtreeDelete(BtCursor *pCur, int bPreserve){ |
pTmp = pBt->pTmpSpace; |
assert( pTmp!=0 ); |
rc = sqlite3PagerWrite(pLeaf->pDbPage); |
- insertCell(pPage, iCellIdx, pCell-4, nCell+4, pTmp, n, &rc); |
+ if( rc==SQLITE_OK ){ |
+ insertCell(pPage, iCellIdx, pCell-4, nCell+4, pTmp, n, &rc); |
+ } |
dropCell(pLeaf, pLeaf->nCell-1, nCell, &rc); |
if( rc ) return rc; |
} |
@@ -8222,7 +8316,7 @@ int sqlite3BtreeDelete(BtCursor *pCur, int bPreserve){ |
if( rc==SQLITE_OK ){ |
if( bSkipnext ){ |
assert( bPreserve && (pCur->iPage==iCellDepth || CORRUPT_DB) ); |
- assert( pPage==pCur->apPage[pCur->iPage] ); |
+ assert( pPage==pCur->apPage[pCur->iPage] || CORRUPT_DB ); |
assert( (pPage->nCell>0 || CORRUPT_DB) && iCellIdx<=pPage->nCell ); |
pCur->eState = CURSOR_SKIPNEXT; |
if( iCellIdx>=pPage->nCell ){ |
@@ -8418,7 +8512,7 @@ static int clearDatabasePage( |
unsigned char *pCell; |
int i; |
int hdr; |
- u16 szCell; |
+ CellInfo info; |
assert( sqlite3_mutex_held(pBt->mutex) ); |
if( pgno>btreePagecount(pBt) ){ |
@@ -8438,7 +8532,7 @@ static int clearDatabasePage( |
rc = clearDatabasePage(pBt, get4byte(pCell), 1, pnChange); |
if( rc ) goto cleardatabasepage_out; |
} |
- rc = clearCell(pPage, pCell, &szCell); |
+ rc = clearCell(pPage, pCell, &info); |
if( rc ) goto cleardatabasepage_out; |
} |
if( !pPage->leaf ){ |
@@ -8529,19 +8623,7 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){ |
assert( sqlite3BtreeHoldsMutex(p) ); |
assert( p->inTrans==TRANS_WRITE ); |
- |
- /* It is illegal to drop a table if any cursors are open on the |
- ** database. This is because in auto-vacuum mode the backend may |
- ** need to move another root-page to fill a gap left by the deleted |
- ** root page. If an open cursor was using this page a problem would |
- ** occur. |
- ** |
- ** This error is caught long before control reaches this point. |
- */ |
- if( NEVER(pBt->pCursor) ){ |
- sqlite3ConnectionBlocked(p->db, pBt->pCursor->pBtree->db); |
- return SQLITE_LOCKED_SHAREDCACHE; |
- } |
+ assert( iTable>=2 ); |
rc = btreeGetPage(pBt, (Pgno)iTable, &pPage, 0); |
if( rc ) return rc; |
@@ -8553,76 +8635,67 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){ |
*piMoved = 0; |
- if( iTable>1 ){ |
#ifdef SQLITE_OMIT_AUTOVACUUM |
- freePage(pPage, &rc); |
- releasePage(pPage); |
+ freePage(pPage, &rc); |
+ releasePage(pPage); |
#else |
- if( pBt->autoVacuum ){ |
- Pgno maxRootPgno; |
- sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &maxRootPgno); |
- |
- if( iTable==maxRootPgno ){ |
- /* If the table being dropped is the table with the largest root-page |
- ** number in the database, put the root page on the free list. |
- */ |
- freePage(pPage, &rc); |
- releasePage(pPage); |
- if( rc!=SQLITE_OK ){ |
- return rc; |
- } |
- }else{ |
- /* The table being dropped does not have the largest root-page |
- ** number in the database. So move the page that does into the |
- ** gap left by the deleted root-page. |
- */ |
- MemPage *pMove; |
- releasePage(pPage); |
- rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0); |
- if( rc!=SQLITE_OK ){ |
- return rc; |
- } |
- rc = relocatePage(pBt, pMove, PTRMAP_ROOTPAGE, 0, iTable, 0); |
- releasePage(pMove); |
- if( rc!=SQLITE_OK ){ |
- return rc; |
- } |
- pMove = 0; |
- rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0); |
- freePage(pMove, &rc); |
- releasePage(pMove); |
- if( rc!=SQLITE_OK ){ |
- return rc; |
- } |
- *piMoved = maxRootPgno; |
- } |
+ if( pBt->autoVacuum ){ |
+ Pgno maxRootPgno; |
+ sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &maxRootPgno); |
- /* Set the new 'max-root-page' value in the database header. This |
- ** is the old value less one, less one more if that happens to |
- ** be a root-page number, less one again if that is the |
- ** PENDING_BYTE_PAGE. |
+ if( iTable==maxRootPgno ){ |
+ /* If the table being dropped is the table with the largest root-page |
+ ** number in the database, put the root page on the free list. |
*/ |
- maxRootPgno--; |
- while( maxRootPgno==PENDING_BYTE_PAGE(pBt) |
- || PTRMAP_ISPAGE(pBt, maxRootPgno) ){ |
- maxRootPgno--; |
+ freePage(pPage, &rc); |
+ releasePage(pPage); |
+ if( rc!=SQLITE_OK ){ |
+ return rc; |
} |
- assert( maxRootPgno!=PENDING_BYTE_PAGE(pBt) ); |
- |
- rc = sqlite3BtreeUpdateMeta(p, 4, maxRootPgno); |
}else{ |
- freePage(pPage, &rc); |
+ /* The table being dropped does not have the largest root-page |
+ ** number in the database. So move the page that does into the |
+ ** gap left by the deleted root-page. |
+ */ |
+ MemPage *pMove; |
releasePage(pPage); |
+ rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0); |
+ if( rc!=SQLITE_OK ){ |
+ return rc; |
+ } |
+ rc = relocatePage(pBt, pMove, PTRMAP_ROOTPAGE, 0, iTable, 0); |
+ releasePage(pMove); |
+ if( rc!=SQLITE_OK ){ |
+ return rc; |
+ } |
+ pMove = 0; |
+ rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0); |
+ freePage(pMove, &rc); |
+ releasePage(pMove); |
+ if( rc!=SQLITE_OK ){ |
+ return rc; |
+ } |
+ *piMoved = maxRootPgno; |
} |
-#endif |
- }else{ |
- /* If sqlite3BtreeDropTable was called on page 1. |
- ** This really never should happen except in a corrupt |
- ** database. |
+ |
+ /* Set the new 'max-root-page' value in the database header. This |
+ ** is the old value less one, less one more if that happens to |
+ ** be a root-page number, less one again if that is the |
+ ** PENDING_BYTE_PAGE. |
*/ |
- zeroPage(pPage, PTF_INTKEY|PTF_LEAF ); |
+ maxRootPgno--; |
+ while( maxRootPgno==PENDING_BYTE_PAGE(pBt) |
+ || PTRMAP_ISPAGE(pBt, maxRootPgno) ){ |
+ maxRootPgno--; |
+ } |
+ assert( maxRootPgno!=PENDING_BYTE_PAGE(pBt) ); |
+ |
+ rc = sqlite3BtreeUpdateMeta(p, 4, maxRootPgno); |
+ }else{ |
+ freePage(pPage, &rc); |
releasePage(pPage); |
} |
+#endif |
return rc; |
} |
int sqlite3BtreeDropTable(Btree *p, int iTable, int *piMoved){ |
@@ -8809,9 +8882,9 @@ static void checkAppendMsg( |
sqlite3StrAccumAppend(&pCheck->errMsg, "\n", 1); |
} |
if( pCheck->zPfx ){ |
- sqlite3XPrintf(&pCheck->errMsg, 0, pCheck->zPfx, pCheck->v1, pCheck->v2); |
+ sqlite3XPrintf(&pCheck->errMsg, pCheck->zPfx, pCheck->v1, pCheck->v2); |
} |
- sqlite3VXPrintf(&pCheck->errMsg, 1, zFormat, ap); |
+ sqlite3VXPrintf(&pCheck->errMsg, zFormat, ap); |
va_end(ap); |
if( pCheck->errMsg.accError==STRACCUM_NOMEM ){ |
pCheck->mallocFailed = 1; |
@@ -9312,7 +9385,8 @@ char *sqlite3BtreeIntegrityCheck( |
sqlite3BtreeEnter(p); |
assert( p->inTrans>TRANS_NONE && pBt->inTransaction>TRANS_NONE ); |
- assert( (nRef = sqlite3PagerRefcount(pBt->pPager))>=0 ); |
+ VVA_ONLY( nRef = sqlite3PagerRefcount(pBt->pPager) ); |
+ assert( nRef>=0 ); |
sCheck.pBt = pBt; |
sCheck.pPager = pBt->pPager; |
sCheck.nPage = btreePagecount(sCheck.pBt); |
@@ -9325,6 +9399,7 @@ char *sqlite3BtreeIntegrityCheck( |
sCheck.aPgRef = 0; |
sCheck.heap = 0; |
sqlite3StrAccumInit(&sCheck.errMsg, 0, zErr, sizeof(zErr), SQLITE_MAX_LENGTH); |
+ sCheck.errMsg.printfFlags = SQLITE_PRINTF_INTERNAL; |
if( sCheck.nPage==0 ){ |
goto integrity_ck_cleanup; |
} |
@@ -9456,7 +9531,7 @@ int sqlite3BtreeCheckpoint(Btree *p, int eMode, int *pnLog, int *pnCkpt){ |
if( pBt->inTransaction!=TRANS_NONE ){ |
rc = SQLITE_LOCKED; |
}else{ |
- rc = sqlite3PagerCheckpoint(pBt->pPager, eMode, pnLog, pnCkpt); |
+ rc = sqlite3PagerCheckpoint(pBt->pPager, p->db, eMode, pnLog, pnCkpt); |
} |
sqlite3BtreeLeave(p); |
} |
@@ -9564,7 +9639,7 @@ int sqlite3BtreeLockTable(Btree *p, int iTab, u8 isWriteLock){ |
*/ |
int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){ |
int rc; |
- assert( cursorHoldsMutex(pCsr) ); |
+ assert( cursorOwnsBtShared(pCsr) ); |
assert( sqlite3_mutex_held(pCsr->pBtree->db->mutex) ); |
assert( pCsr->curFlags & BTCF_Incrblob ); |
@@ -9671,3 +9746,22 @@ int sqlite3BtreeIsReadonly(Btree *p){ |
** Return the size of the header added to each page by this module. |
*/ |
int sqlite3HeaderSizeBtree(void){ return ROUND8(sizeof(MemPage)); } |
+ |
+#if !defined(SQLITE_OMIT_SHARED_CACHE) |
+/* |
+** Return true if the Btree passed as the only argument is sharable. |
+*/ |
+int sqlite3BtreeSharable(Btree *p){ |
+ return p->sharable; |
+} |
+ |
+/* |
+** Return the number of connections to the BtShared object accessed by |
+** the Btree handle passed as the only argument. For private caches |
+** this is always 1. For shared caches it may be 1 or greater. |
+*/ |
+int sqlite3BtreeConnectionCount(Btree *p){ |
+ testcase( p->sharable ); |
+ return p->pBt->nRef; |
+} |
+#endif |