summaryrefslogtreecommitdiff
path: root/ext/pdo_sqlite/sqlite/src/btree.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/pdo_sqlite/sqlite/src/btree.c')
-rw-r--r--ext/pdo_sqlite/sqlite/src/btree.c473
1 files changed, 196 insertions, 277 deletions
diff --git a/ext/pdo_sqlite/sqlite/src/btree.c b/ext/pdo_sqlite/sqlite/src/btree.c
index 52bc749a32..595f19ffa1 100644
--- a/ext/pdo_sqlite/sqlite/src/btree.c
+++ b/ext/pdo_sqlite/sqlite/src/btree.c
@@ -387,17 +387,13 @@ struct BtCursor {
CellInfo info; /* A parse of the cell we are pointing at */
u8 wrFlag; /* True if writable */
u8 eState; /* One of the CURSOR_XXX constants (see below) */
-#ifndef SQLITE_OMIT_SHARED_CACHE
void *pKey; /* Saved key that was cursor's last known position */
i64 nKey; /* Size of pKey, or last integer key */
int skip; /* (skip<0) -> Prev() is a no-op. (skip>0) -> Next() is */
-#endif
};
/*
-** Potential values for BtCursor.eState. The first two values (VALID and
-** INVALID) may occur in any build. The third (REQUIRESEEK) may only occur
-** if sqlite was compiled without the OMIT_SHARED_CACHE symbol defined.
+** Potential values for BtCursor.eState.
**
** CURSOR_VALID:
** Cursor points to a valid entry. getPayload() etc. may be called.
@@ -425,7 +421,8 @@ struct BtCursor {
*/
#if SQLITE_TEST
# define TRACE(X) if( sqlite3_btree_trace )\
- { sqlite3DebugPrintf X; fflush(stdout); }
+/* { sqlite3DebugPrintf X; fflush(stdout); } */ \
+{ printf X; fflush(stdout); }
int sqlite3_btree_trace=0; /* True to enable tracing */
#else
# define TRACE(X)
@@ -434,7 +431,7 @@ int sqlite3_btree_trace=0; /* True to enable tracing */
/*
** Forward declaration
*/
-static int checkReadLocks(BtShared*,Pgno,BtCursor*);
+static int checkReadLocks(Btree*,Pgno,BtCursor*);
/*
** Read or write a two- and four-byte big-endian integer values.
@@ -509,105 +506,8 @@ struct BtLock {
#define queryTableLock(a,b,c) SQLITE_OK
#define lockTable(a,b,c) SQLITE_OK
#define unlockAllTables(a)
- #define restoreOrClearCursorPosition(a,b) SQLITE_OK
- #define saveAllCursors(a,b,c) SQLITE_OK
-
#else
-static void releasePage(MemPage *pPage);
-
-/*
-** Save the current cursor position in the variables BtCursor.nKey
-** and BtCursor.pKey. The cursor's state is set to CURSOR_REQUIRESEEK.
-*/
-static int saveCursorPosition(BtCursor *pCur){
- int rc;
-
- assert( CURSOR_VALID==pCur->eState );
- assert( 0==pCur->pKey );
-
- rc = sqlite3BtreeKeySize(pCur, &pCur->nKey);
-
- /* 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( rc==SQLITE_OK && 0==pCur->pPage->intKey){
- void *pKey = sqliteMalloc(pCur->nKey);
- if( pKey ){
- rc = sqlite3BtreeKey(pCur, 0, pCur->nKey, pKey);
- if( rc==SQLITE_OK ){
- pCur->pKey = pKey;
- }else{
- sqliteFree(pKey);
- }
- }else{
- rc = SQLITE_NOMEM;
- }
- }
- assert( !pCur->pPage->intKey || !pCur->pKey );
-
- if( rc==SQLITE_OK ){
- releasePage(pCur->pPage);
- pCur->pPage = 0;
- pCur->eState = CURSOR_REQUIRESEEK;
- }
-
- return rc;
-}
-
-/*
-** Save the positions of all cursors except pExcept open on the table
-** with root-page iRoot. Usually, this is called just before cursor
-** pExcept is used to modify the table (BtreeDelete() or BtreeInsert()).
-*/
-static int saveAllCursors(BtShared *pBt, Pgno iRoot, BtCursor *pExcept){
- BtCursor *p;
- if( sqlite3ThreadDataReadOnly()->useSharedData ){
- for(p=pBt->pCursor; p; p=p->pNext){
- if( p!=pExcept && (0==iRoot || p->pgnoRoot==iRoot) &&
- p->eState==CURSOR_VALID ){
- int rc = saveCursorPosition(p);
- if( SQLITE_OK!=rc ){
- return rc;
- }
- }
- }
- }
- return SQLITE_OK;
-}
-
-/*
-** Restore the cursor to the position it was in (or as close to as possible)
-** when saveCursorPosition() was called. Note that this call deletes the
-** saved position info stored by saveCursorPosition(), so there can be
-** at most one effective restoreOrClearCursorPosition() call after each
-** saveCursorPosition().
-**
-** If the second argument argument - doSeek - is false, then instead of
-** returning the cursor to it's saved position, any saved position is deleted
-** and the cursor state set to CURSOR_INVALID.
-*/
-static int restoreOrClearCursorPositionX(BtCursor *pCur, int doSeek){
- int rc = SQLITE_OK;
- assert( sqlite3ThreadDataReadOnly()->useSharedData );
- assert( pCur->eState==CURSOR_REQUIRESEEK );
- pCur->eState = CURSOR_INVALID;
- if( doSeek ){
- rc = sqlite3BtreeMoveto(pCur, pCur->pKey, pCur->nKey, &pCur->skip);
- }
- if( rc==SQLITE_OK ){
- sqliteFree(pCur->pKey);
- pCur->pKey = 0;
- assert( CURSOR_VALID==pCur->eState || CURSOR_INVALID==pCur->eState );
- }
- return rc;
-}
-
-#define restoreOrClearCursorPosition(p,x) \
- (p->eState==CURSOR_REQUIRESEEK?restoreOrClearCursorPositionX(p,x):SQLITE_OK)
/*
** Query to see if btree handle p may obtain a lock of type eLock
@@ -747,6 +647,98 @@ static void unlockAllTables(Btree *p){
}
#endif /* SQLITE_OMIT_SHARED_CACHE */
+static void releasePage(MemPage *pPage); /* Forward reference */
+
+/*
+** Save the current cursor position in the variables BtCursor.nKey
+** and BtCursor.pKey. The cursor's state is set to CURSOR_REQUIRESEEK.
+*/
+static int saveCursorPosition(BtCursor *pCur){
+ int rc;
+
+ assert( CURSOR_VALID==pCur->eState );
+ assert( 0==pCur->pKey );
+
+ rc = sqlite3BtreeKeySize(pCur, &pCur->nKey);
+
+ /* 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( rc==SQLITE_OK && 0==pCur->pPage->intKey){
+ void *pKey = sqliteMalloc(pCur->nKey);
+ if( pKey ){
+ rc = sqlite3BtreeKey(pCur, 0, pCur->nKey, pKey);
+ if( rc==SQLITE_OK ){
+ pCur->pKey = pKey;
+ }else{
+ sqliteFree(pKey);
+ }
+ }else{
+ rc = SQLITE_NOMEM;
+ }
+ }
+ assert( !pCur->pPage->intKey || !pCur->pKey );
+
+ if( rc==SQLITE_OK ){
+ releasePage(pCur->pPage);
+ pCur->pPage = 0;
+ pCur->eState = CURSOR_REQUIRESEEK;
+ }
+
+ return rc;
+}
+
+/*
+** Save the positions of all cursors except pExcept open on the table
+** with root-page iRoot. Usually, this is called just before cursor
+** pExcept is used to modify the table (BtreeDelete() or BtreeInsert()).
+*/
+static int saveAllCursors(BtShared *pBt, Pgno iRoot, BtCursor *pExcept){
+ BtCursor *p;
+ for(p=pBt->pCursor; p; p=p->pNext){
+ if( p!=pExcept && (0==iRoot || p->pgnoRoot==iRoot) &&
+ p->eState==CURSOR_VALID ){
+ int rc = saveCursorPosition(p);
+ if( SQLITE_OK!=rc ){
+ return rc;
+ }
+ }
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Restore the cursor to the position it was in (or as close to as possible)
+** when saveCursorPosition() was called. Note that this call deletes the
+** saved position info stored by saveCursorPosition(), so there can be
+** at most one effective restoreOrClearCursorPosition() call after each
+** saveCursorPosition().
+**
+** If the second argument argument - doSeek - is false, then instead of
+** returning the cursor to it's saved position, any saved position is deleted
+** and the cursor state set to CURSOR_INVALID.
+*/
+static int restoreOrClearCursorPositionX(BtCursor *pCur, int doSeek){
+ int rc = SQLITE_OK;
+ assert( pCur->eState==CURSOR_REQUIRESEEK );
+ pCur->eState = CURSOR_INVALID;
+ if( doSeek ){
+ rc = sqlite3BtreeMoveto(pCur, pCur->pKey, pCur->nKey, &pCur->skip);
+ }
+ if( rc==SQLITE_OK ){
+ sqliteFree(pCur->pKey);
+ pCur->pKey = 0;
+ assert( CURSOR_VALID==pCur->eState || CURSOR_INVALID==pCur->eState );
+ }
+ return rc;
+}
+
+#define restoreOrClearCursorPosition(p,x) \
+ (p->eState==CURSOR_REQUIRESEEK?restoreOrClearCursorPositionX(p,x):SQLITE_OK)
+
#ifndef SQLITE_OMIT_AUTOVACUUM
/*
** These macros define the location of the pointer-map entry for a
@@ -1048,91 +1040,6 @@ static int ptrmapPutOvfl(MemPage *pPage, int iCell){
#endif
-/*
-** Do sanity checking on a page. Throw an exception if anything is
-** not right.
-**
-** This routine is used for internal error checking only. It is omitted
-** from most builds.
-*/
-#if defined(BTREE_DEBUG) && !defined(NDEBUG) && 0
-static void _pageIntegrity(MemPage *pPage){
- int usableSize;
- u8 *data;
- int i, j, idx, c, pc, hdr, nFree;
- int cellOffset;
- int nCell, cellLimit;
- u8 *used;
-
- used = sqliteMallocRaw( pPage->pBt->pageSize );
- if( used==0 ) return;
- usableSize = pPage->pBt->usableSize;
- assert( pPage->aData==&((unsigned char*)pPage)[-pPage->pBt->pageSize] );
- hdr = pPage->hdrOffset;
- assert( hdr==(pPage->pgno==1 ? 100 : 0) );
- assert( pPage->pgno==sqlite3pager_pagenumber(pPage->aData) );
- c = pPage->aData[hdr];
- if( pPage->isInit ){
- assert( pPage->leaf == ((c & PTF_LEAF)!=0) );
- assert( pPage->zeroData == ((c & PTF_ZERODATA)!=0) );
- assert( pPage->leafData == ((c & PTF_LEAFDATA)!=0) );
- assert( pPage->intKey == ((c & (PTF_INTKEY|PTF_LEAFDATA))!=0) );
- assert( pPage->hasData ==
- !(pPage->zeroData || (!pPage->leaf && pPage->leafData)) );
- assert( pPage->cellOffset==pPage->hdrOffset+12-4*pPage->leaf );
- assert( pPage->nCell = get2byte(&pPage->aData[hdr+3]) );
- }
- data = pPage->aData;
- memset(used, 0, usableSize);
- for(i=0; i<hdr+10-pPage->leaf*4; i++) used[i] = 1;
- nFree = 0;
- pc = get2byte(&data[hdr+1]);
- while( pc ){
- int size;
- assert( pc>0 && pc<usableSize-4 );
- size = get2byte(&data[pc+2]);
- assert( pc+size<=usableSize );
- nFree += size;
- for(i=pc; i<pc+size; i++){
- assert( used[i]==0 );
- used[i] = 1;
- }
- pc = get2byte(&data[pc]);
- }
- idx = 0;
- nCell = get2byte(&data[hdr+3]);
- cellLimit = get2byte(&data[hdr+5]);
- assert( pPage->isInit==0
- || pPage->nFree==nFree+data[hdr+7]+cellLimit-(cellOffset+2*nCell) );
- cellOffset = pPage->cellOffset;
- for(i=0; i<nCell; i++){
- int size;
- pc = get2byte(&data[cellOffset+2*i]);
- assert( pc>0 && pc<usableSize-4 );
- size = cellSize(pPage, &data[pc]);
- assert( pc+size<=usableSize );
- for(j=pc; j<pc+size; j++){
- assert( used[j]==0 );
- used[j] = 1;
- }
- }
- for(i=cellOffset+2*nCell; i<cellimit; i++){
- assert( used[i]==0 );
- used[i] = 1;
- }
- nFree = 0;
- for(i=0; i<usableSize; i++){
- assert( used[i]<=1 );
- if( used[i]==0 ) nFree++;
- }
- assert( nFree==data[hdr+7] );
- sqliteFree(used);
-}
-#define pageIntegrity(X) _pageIntegrity(X)
-#else
-# define pageIntegrity(X)
-#endif
-
/* A bunch of assert() statements to check the transaction state variables
** of handle p (type Btree*) are internally consistent.
*/
@@ -1439,7 +1346,6 @@ static int initPage(
}
pPage->isInit = 1;
- pageIntegrity(pPage);
return SQLITE_OK;
}
@@ -1470,7 +1376,6 @@ static void zeroPage(MemPage *pPage, int flags){
pPage->idxShift = 0;
pPage->nCell = 0;
pPage->isInit = 1;
- pageIntegrity(pPage);
}
/*
@@ -1591,9 +1496,9 @@ int sqlite3BtreeOpen(
*/
#if !defined(SQLITE_OMIT_SHARED_CACHE) || !defined(SQLITE_OMIT_AUTOVACUUM)
#ifdef SQLITE_OMIT_MEMORYDB
- const int isMemdb = !zFilename;
+ const int isMemdb = 0;
#else
- const int isMemdb = !zFilename || (strcmp(zFilename, ":memory:")?0:1);
+ const int isMemdb = zFilename && !strcmp(zFilename, ":memory:");
#endif
#endif
@@ -1645,8 +1550,13 @@ int sqlite3BtreeOpen(
return SQLITE_NOMEM;
}
rc = sqlite3pager_open(&pBt->pPager, zFilename, EXTRA_SIZE, flags);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3pager_read_fileheader(pBt->pPager,sizeof(zDbHeader),zDbHeader);
+ }
if( rc!=SQLITE_OK ){
- if( pBt->pPager ) sqlite3pager_close(pBt->pPager);
+ if( pBt->pPager ){
+ sqlite3pager_close(pBt->pPager);
+ }
sqliteFree(pBt);
sqliteFree(p);
*ppBtree = 0;
@@ -1659,7 +1569,6 @@ int sqlite3BtreeOpen(
pBt->pCursor = 0;
pBt->pPage1 = 0;
pBt->readOnly = sqlite3pager_isreadonly(pBt->pPager);
- sqlite3pager_read_fileheader(pBt->pPager, sizeof(zDbHeader), zDbHeader);
pBt->pageSize = get2byte(&zDbHeader[16]);
if( pBt->pageSize<512 || pBt->pageSize>SQLITE_MAX_PAGE_SIZE
|| ((pBt->pageSize-1)&pBt->pageSize)!=0 ){
@@ -2022,13 +1931,15 @@ static int lockBtreeWithRetry(Btree *pRef){
*/
static void unlockBtreeIfUnused(BtShared *pBt){
if( pBt->inTransaction==TRANS_NONE && pBt->pCursor==0 && pBt->pPage1!=0 ){
- if( pBt->pPage1->aData==0 ){
- MemPage *pPage = pBt->pPage1;
- pPage->aData = &((u8*)pPage)[-pBt->pageSize];
- pPage->pBt = pBt;
- pPage->pgno = 1;
+ if( sqlite3pager_refcount(pBt->pPager)>=1 ){
+ if( pBt->pPage1->aData==0 ){
+ MemPage *pPage = pBt->pPage1;
+ pPage->aData = &((u8*)pPage)[-pBt->pageSize];
+ pPage->pBt = pBt;
+ pPage->pgno = 1;
+ }
+ releasePage(pBt->pPage1);
}
- releasePage(pBt->pPage1);
pBt->pPage1 = 0;
pBt->inStmt = 0;
}
@@ -2548,7 +2459,7 @@ static int countWriteCursors(BtShared *pBt){
}
#endif
-#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
+#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG)
/*
** Print debugging information about all cursors to standard output.
*/
@@ -2778,7 +2689,7 @@ int sqlite3BtreeCursor(
if( pBt->readOnly ){
return SQLITE_READONLY;
}
- if( checkReadLocks(pBt, iTable, 0) ){
+ if( checkReadLocks(p, iTable, 0) ){
return SQLITE_LOCKED;
}
}
@@ -2980,7 +2891,6 @@ static int getPayload(
assert( pCur->eState==CURSOR_VALID );
pBt = pCur->pBtree->pBt;
pPage = pCur->pPage;
- pageIntegrity(pPage);
assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
getCellInfo(pCur);
aPayload = pCur->info.pCell + pCur->info.nHeader;
@@ -3118,7 +3028,6 @@ static const unsigned char *fetchPayload(
assert( pCur!=0 && pCur->pPage!=0 );
assert( pCur->eState==CURSOR_VALID );
pPage = pCur->pPage;
- pageIntegrity(pPage);
assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
getCellInfo(pCur);
aPayload = pCur->info.pCell;
@@ -3180,7 +3089,6 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){
assert( pCur->eState==CURSOR_VALID );
rc = getAndInitPage(pBt, newPgno, &pNewPage, pCur->pPage);
if( rc ) return rc;
- pageIntegrity(pNewPage);
pNewPage->idxParent = pCur->idx;
pOldPage = pCur->pPage;
pOldPage->idxShift = 0;
@@ -3228,10 +3136,8 @@ static void moveToParent(BtCursor *pCur){
pPage = pCur->pPage;
assert( pPage!=0 );
assert( !isRootPage(pPage) );
- pageIntegrity(pPage);
pParent = pPage->pParent;
assert( pParent!=0 );
- pageIntegrity(pParent);
idxParent = pPage->idxParent;
sqlite3pager_ref(pParent->aData);
releasePage(pPage);
@@ -3261,7 +3167,6 @@ static int moveToRoot(BtCursor *pCur){
return rc;
}
releasePage(pCur->pPage);
- pageIntegrity(pRoot);
pCur->pPage = pRoot;
}
pCur->idx = 0;
@@ -3405,7 +3310,7 @@ int sqlite3BtreeMoveto(BtCursor *pCur, const void *pKey, i64 nKey, int *pRes){
assert( pCur->pPage->nCell==0 );
return SQLITE_OK;
}
- for(;;){
+ for(;;){
int lwr, upr;
Pgno chldPg;
MemPage *pPage = pCur->pPage;
@@ -3415,7 +3320,6 @@ int sqlite3BtreeMoveto(BtCursor *pCur, const void *pKey, i64 nKey, int *pRes){
if( !pPage->intKey && pKey==0 ){
return SQLITE_CORRUPT_BKPT;
}
- pageIntegrity(pPage);
while( lwr<=upr ){
void *pCellKey;
i64 nCellKey;
@@ -3668,14 +3572,14 @@ static int allocatePage(
int rc;
int n; /* Number of pages on the freelist */
int k; /* Number of leaves on the trunk of the freelist */
+ MemPage *pTrunk = 0;
+ MemPage *pPrevTrunk = 0;
pPage1 = pBt->pPage1;
n = get4byte(&pPage1->aData[36]);
if( n>0 ){
/* There are pages on the freelist. Reuse one of those pages. */
- MemPage *pTrunk = 0;
Pgno iTrunk;
- MemPage *pPrevTrunk = 0;
u8 searchList = 0; /* If the free-list must be searched for 'nearby' */
/* If the 'exact' parameter was true and a query of the pointer-map
@@ -3716,16 +3620,8 @@ static int allocatePage(
}
rc = getPage(pBt, iTrunk, &pTrunk);
if( rc ){
- releasePage(pPrevTrunk);
- return rc;
- }
-
- /* TODO: This should move to after the loop? */
- rc = sqlite3pager_write(pTrunk->aData);
- if( rc ){
- releasePage(pTrunk);
- releasePage(pPrevTrunk);
- return rc;
+ pTrunk = 0;
+ goto end_allocate_page;
}
k = get4byte(&pTrunk->aData[4]);
@@ -3734,6 +3630,10 @@ static int allocatePage(
** So extract the trunk page itself and use it as the newly
** allocated page */
assert( pPrevTrunk==0 );
+ rc = sqlite3pager_write(pTrunk->aData);
+ if( rc ){
+ goto end_allocate_page;
+ }
*pPgno = iTrunk;
memcpy(&pPage1->aData[32], &pTrunk->aData[0], 4);
*ppPage = pTrunk;
@@ -3741,7 +3641,8 @@ static int allocatePage(
TRACE(("ALLOCATE: %d trunk - %d free pages left\n", *pPgno, n-1));
}else if( k>pBt->usableSize/4 - 8 ){
/* Value of k is out of range. Database corruption */
- return SQLITE_CORRUPT_BKPT;
+ rc = SQLITE_CORRUPT_BKPT;
+ goto end_allocate_page;
#ifndef SQLITE_OMIT_AUTOVACUUM
}else if( searchList && nearby==iTrunk ){
/* The list is being searched and this trunk page is the page
@@ -3750,6 +3651,10 @@ static int allocatePage(
assert( *pPgno==iTrunk );
*ppPage = pTrunk;
searchList = 0;
+ rc = sqlite3pager_write(pTrunk->aData);
+ if( rc ){
+ goto end_allocate_page;
+ }
if( k==0 ){
if( !pPrevTrunk ){
memcpy(&pPage1->aData[32], &pTrunk->aData[0], 4);
@@ -3765,26 +3670,26 @@ static int allocatePage(
Pgno iNewTrunk = get4byte(&pTrunk->aData[8]);
rc = getPage(pBt, iNewTrunk, &pNewTrunk);
if( rc!=SQLITE_OK ){
- releasePage(pTrunk);
- releasePage(pPrevTrunk);
- return rc;
+ goto end_allocate_page;
}
rc = sqlite3pager_write(pNewTrunk->aData);
if( rc!=SQLITE_OK ){
releasePage(pNewTrunk);
- releasePage(pTrunk);
- releasePage(pPrevTrunk);
- return rc;
+ goto end_allocate_page;
}
memcpy(&pNewTrunk->aData[0], &pTrunk->aData[0], 4);
put4byte(&pNewTrunk->aData[4], k-1);
memcpy(&pNewTrunk->aData[8], &pTrunk->aData[12], (k-1)*4);
+ releasePage(pNewTrunk);
if( !pPrevTrunk ){
put4byte(&pPage1->aData[32], iNewTrunk);
}else{
+ rc = sqlite3pager_write(pPrevTrunk->aData);
+ if( rc ){
+ goto end_allocate_page;
+ }
put4byte(&pPrevTrunk->aData[0], iNewTrunk);
}
- releasePage(pNewTrunk);
}
pTrunk = 0;
TRACE(("ALLOCATE: %d trunk - %d free pages left\n", *pPgno, n-1));
@@ -3794,6 +3699,10 @@ static int allocatePage(
int closest;
Pgno iPage;
unsigned char *aData = pTrunk->aData;
+ rc = sqlite3pager_write(aData);
+ if( rc ){
+ goto end_allocate_page;
+ }
if( nearby>0 ){
int i, dist;
closest = 0;
@@ -3837,8 +3746,8 @@ static int allocatePage(
}
}
releasePage(pPrevTrunk);
+ pPrevTrunk = 0;
}while( searchList );
- releasePage(pTrunk);
}else{
/* There are no pages on the freelist, so create a new page at the
** end of the file */
@@ -3867,6 +3776,10 @@ static int allocatePage(
}
assert( *pPgno!=PENDING_BYTE_PAGE(pBt) );
+
+end_allocate_page:
+ releasePage(pTrunk);
+ releasePage(pPrevTrunk);
return rc;
}
@@ -4267,7 +4180,6 @@ static int insertCell(
put2byte(&data[ins], idx);
put2byte(&data[hdr+3], pPage->nCell);
pPage->idxShift = 1;
- pageIntegrity(pPage);
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pPage->pBt->autoVacuum ){
/* The cell may contain a pointer to an overflow page. If so, write
@@ -5007,8 +4919,6 @@ static int balance_nonroot(MemPage *pPage){
** But the parent page will always be initialized.
*/
assert( pParent->isInit );
- /* assert( pPage->isInit ); // No! pPage might have been added to freelist */
- /* pageIntegrity(pPage); // No! pPage might have been added to freelist */
rc = balance(pParent, 0);
/*
@@ -5215,27 +5125,35 @@ static int balance(MemPage *pPage, int insert){
/*
** This routine checks all cursors that point to table pgnoRoot.
-** If any of those cursors other than pExclude were opened with
-** wrFlag==0 then this routine returns SQLITE_LOCKED. If all
-** cursors that point to pgnoRoot were opened with wrFlag==1
-** then this routine returns SQLITE_OK.
+** If any of those cursors were opened with wrFlag==0 in a different
+** database connection (a database connection that shares the pager
+** cache with the current connection) and that other connection
+** is not in the ReadUncommmitted state, then this routine returns
+** SQLITE_LOCKED.
**
** In addition to checking for read-locks (where a read-lock
** means a cursor opened with wrFlag==0) this routine also moves
-** all cursors other than pExclude so that they are pointing to the
-** first Cell on root page. This is necessary because an insert
+** all cursors write cursors so that they are pointing to the
+** first Cell on the root page. This is necessary because an insert
** or delete might change the number of cells on a page or delete
** a page entirely and we do not want to leave any cursors
** pointing to non-existant pages or cells.
*/
-static int checkReadLocks(BtShared *pBt, Pgno pgnoRoot, BtCursor *pExclude){
+static int checkReadLocks(Btree *pBtree, Pgno pgnoRoot, BtCursor *pExclude){
BtCursor *p;
+ BtShared *pBt = pBtree->pBt;
+ sqlite3 *db = pBtree->pSqlite;
for(p=pBt->pCursor; p; p=p->pNext){
- u32 flags = (p->pBtree->pSqlite ? p->pBtree->pSqlite->flags : 0);
- if( p->pgnoRoot!=pgnoRoot || p==pExclude ) continue;
- if( p->wrFlag==0 && flags&SQLITE_ReadUncommitted ) continue;
- if( p->wrFlag==0 ) return SQLITE_LOCKED;
- if( p->pPage->pgno!=p->pgnoRoot ){
+ if( p==pExclude ) continue;
+ if( p->eState!=CURSOR_VALID ) continue;
+ if( p->pgnoRoot!=pgnoRoot ) continue;
+ if( p->wrFlag==0 ){
+ sqlite3 *dbOther = p->pBtree->pSqlite;
+ if( dbOther==0 ||
+ (dbOther!=db && (dbOther->flags & SQLITE_ReadUncommitted)==0) ){
+ return SQLITE_LOCKED;
+ }
+ }else if( p->pPage->pgno!=p->pgnoRoot ){
moveToRoot(p);
}
}
@@ -5272,7 +5190,7 @@ int sqlite3BtreeInsert(
if( !pCur->wrFlag ){
return SQLITE_PERM; /* Cursor not open for writing */
}
- if( checkReadLocks(pBt, pCur->pgnoRoot, pCur) ){
+ if( checkReadLocks(pCur->pBtree, pCur->pgnoRoot, pCur) ){
return SQLITE_LOCKED; /* The table pCur points to has a read lock */
}
@@ -5354,7 +5272,7 @@ int sqlite3BtreeDelete(BtCursor *pCur){
if( !pCur->wrFlag ){
return SQLITE_PERM; /* Did not open this cursor for writing */
}
- if( checkReadLocks(pBt, pCur->pgnoRoot, pCur) ){
+ if( checkReadLocks(pCur->pBtree, pCur->pgnoRoot, pCur) ){
return SQLITE_LOCKED; /* The table pCur points to has a read lock */
}
@@ -5631,25 +5549,13 @@ cleardatabasepage_out:
*/
int sqlite3BtreeClearTable(Btree *p, int iTable){
int rc;
- BtCursor *pCur;
BtShared *pBt = p->pBt;
- sqlite3 *db = p->pSqlite;
if( p->inTrans!=TRANS_WRITE ){
return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
}
-
- /* If this connection is not in read-uncommitted mode and currently has
- ** a read-cursor open on the table being cleared, return SQLITE_LOCKED.
- */
- if( 0==db || 0==(db->flags&SQLITE_ReadUncommitted) ){
- for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
- if( pCur->pBtree==p && pCur->pgnoRoot==(Pgno)iTable ){
- if( 0==pCur->wrFlag ){
- return SQLITE_LOCKED;
- }
- moveToRoot(pCur);
- }
- }
+ rc = checkReadLocks(p, iTable, 0);
+ if( rc ){
+ return rc;
}
/* Save the position of all cursors open on this table */
@@ -5969,7 +5875,7 @@ int sqlite3BtreePageDump(Btree *p, int pgno, int recursive){
}
#endif
-#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
+#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG)
/*
** Fill aResult[] with information about the entry and page that the
** cursor is pointing to.
@@ -5997,14 +5903,12 @@ int sqlite3BtreeCursorInfo(BtCursor *pCur, int *aResult, int upCnt){
return rc;
}
- pageIntegrity(pPage);
assert( pPage->isInit );
getTempCursor(pCur, &tmpCur);
while( upCnt-- ){
moveToParent(&tmpCur);
}
pPage = tmpCur.pPage;
- pageIntegrity(pPage);
aResult[0] = sqlite3pager_pagenumber(pPage->aData);
assert( aResult[0]==pPage->pgno );
aResult[1] = tmpCur.idx;
@@ -6054,10 +5958,12 @@ Pager *sqlite3BtreePager(Btree *p){
typedef struct IntegrityCk IntegrityCk;
struct IntegrityCk {
BtShared *pBt; /* The tree being checked out */
- Pager *pPager; /* The associated pager. Also accessible by pBt->pPager */
- int nPage; /* Number of pages in the database */
- int *anRef; /* Number of times each page is referenced */
- char *zErrMsg; /* An error message. NULL of no errors seen. */
+ Pager *pPager; /* The associated pager. Also accessible by pBt->pPager */
+ int nPage; /* Number of pages in the database */
+ int *anRef; /* Number of times each page is referenced */
+ int mxErr; /* Stop accumulating errors when this reaches zero */
+ char *zErrMsg; /* An error message. NULL if no errors seen. */
+ int nErr; /* Number of messages written to zErrMsg so far */
};
#ifndef SQLITE_OMIT_INTEGRITY_CHECK
@@ -6072,6 +5978,9 @@ static void checkAppendMsg(
){
va_list ap;
char *zMsg2;
+ if( !pCheck->mxErr ) return;
+ pCheck->mxErr--;
+ pCheck->nErr++;
va_start(ap, zFormat);
zMsg2 = sqlite3VMPrintf(zFormat, ap);
va_end(ap);
@@ -6155,7 +6064,7 @@ static void checkList(
int i;
int expected = N;
int iFirst = iPage;
- while( N-- > 0 ){
+ while( N-- > 0 && pCheck->mxErr ){
unsigned char *pOvfl;
if( iPage<1 ){
checkAppendMsg(pCheck, zContext,
@@ -6267,7 +6176,7 @@ static int checkTreePage(
/* Check out all the cells.
*/
depth = 0;
- for(i=0; i<pPage->nCell; i++){
+ for(i=0; i<pPage->nCell && pCheck->mxErr; i++){
u8 *pCell;
int sz;
CellInfo info;
@@ -6382,7 +6291,13 @@ static int checkTreePage(
** and a pointer to that error message is returned. The calling function
** is responsible for freeing the error message when it is done.
*/
-char *sqlite3BtreeIntegrityCheck(Btree *p, int *aRoot, int nRoot){
+char *sqlite3BtreeIntegrityCheck(
+ Btree *p, /* The btree to be checked */
+ int *aRoot, /* An array of root pages numbers for individual trees */
+ int nRoot, /* Number of entries in aRoot[] */
+ int mxErr, /* Stop reporting errors after this many */
+ int *pnErr /* Write number of errors seen to this variable */
+){
int i;
int nRef;
IntegrityCk sCheck;
@@ -6395,6 +6310,9 @@ char *sqlite3BtreeIntegrityCheck(Btree *p, int *aRoot, int nRoot){
sCheck.pBt = pBt;
sCheck.pPager = pBt->pPager;
sCheck.nPage = sqlite3pager_pagecount(sCheck.pPager);
+ sCheck.mxErr = mxErr;
+ sCheck.nErr = 0;
+ *pnErr = 0;
if( sCheck.nPage==0 ){
unlockBtreeIfUnused(pBt);
return 0;
@@ -6402,6 +6320,7 @@ char *sqlite3BtreeIntegrityCheck(Btree *p, int *aRoot, int nRoot){
sCheck.anRef = sqliteMallocRaw( (sCheck.nPage+1)*sizeof(sCheck.anRef[0]) );
if( !sCheck.anRef ){
unlockBtreeIfUnused(pBt);
+ *pnErr = 1;
return sqlite3MPrintf("Unable to malloc %d bytes",
(sCheck.nPage+1)*sizeof(sCheck.anRef[0]));
}
@@ -6419,7 +6338,7 @@ char *sqlite3BtreeIntegrityCheck(Btree *p, int *aRoot, int nRoot){
/* Check all the tables.
*/
- for(i=0; i<nRoot; i++){
+ for(i=0; i<nRoot && sCheck.mxErr; i++){
if( aRoot[i]==0 ) continue;
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pBt->autoVacuum && aRoot[i]>1 ){
@@ -6431,7 +6350,7 @@ char *sqlite3BtreeIntegrityCheck(Btree *p, int *aRoot, int nRoot){
/* Make sure every page in the file is referenced
*/
- for(i=1; i<=sCheck.nPage; i++){
+ for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){
#ifdef SQLITE_OMIT_AUTOVACUUM
if( sCheck.anRef[i]==0 ){
checkAppendMsg(&sCheck, 0, "Page %d is never used", i);
@@ -6464,6 +6383,7 @@ char *sqlite3BtreeIntegrityCheck(Btree *p, int *aRoot, int nRoot){
/* Clean up and report errors.
*/
sqliteFree(sCheck.anRef);
+ *pnErr = sCheck.nErr;
return sCheck.zErrMsg;
}
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
@@ -6522,7 +6442,6 @@ int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
rc = sqlite3pager_get(pBtFrom->pPager, i, &pPage);
if( rc ) break;
rc = sqlite3pager_overwrite(pBtTo->pPager, i, pPage);
- if( rc ) break;
sqlite3pager_unref(pPage);
}
for(i=nPage+1; rc==SQLITE_OK && i<=nToPage; i++){