diff options
Diffstat (limited to 'ext/pdo_sqlite/sqlite/src/btree.c')
-rw-r--r-- | ext/pdo_sqlite/sqlite/src/btree.c | 473 |
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++){ |