diff options
| author | Hans-Christoph Steiner <hans@eds.org> | 2012-09-20 18:34:38 -0400 | 
|---|---|---|
| committer | Hans-Christoph Steiner <hans@eds.org> | 2012-09-20 18:34:38 -0400 | 
| commit | 487e15dc239ccdb3344d1c99ce120e872bab4a74 (patch) | |
| tree | c986d492f6092ca7b4401d91515f74daed17fae2 /src/btree.c | |
| parent | 7bb481fda9ecb134804b49c2ce77ca28f7eea583 (diff) | |
Imported Upstream version 2.0.6
Diffstat (limited to 'src/btree.c')
| -rw-r--r-- | src/btree.c | 320 | 
1 files changed, 192 insertions, 128 deletions
| diff --git a/src/btree.c b/src/btree.c index d64e172..2876526 100644 --- a/src/btree.c +++ b/src/btree.c @@ -243,7 +243,7 @@ static int querySharedCacheTableLock(Btree *p, Pgno iTab, u8 eLock){    /* If some other connection is holding an exclusive lock, the    ** requested lock may not be obtained.    */ -  if( pBt->pWriter!=p && pBt->isExclusive ){ +  if( pBt->pWriter!=p && (pBt->btsFlags & BTS_EXCLUSIVE)!=0 ){      sqlite3ConnectionBlocked(p->db, pBt->pWriter->db);      return SQLITE_LOCKED_SHAREDCACHE;    } @@ -264,7 +264,7 @@ static int querySharedCacheTableLock(Btree *p, Pgno iTab, u8 eLock){        sqlite3ConnectionBlocked(p->db, pIter->pBtree->db);        if( eLock==WRITE_LOCK ){          assert( p==pBt->pWriter ); -        pBt->isPending = 1; +        pBt->btsFlags |= BTS_PENDING;        }        return SQLITE_LOCKED_SHAREDCACHE;      } @@ -352,7 +352,7 @@ static int setSharedCacheTableLock(Btree *p, Pgno iTable, u8 eLock){  ** the setSharedCacheTableLock() procedure) held by Btree object p.  **  ** This function assumes that Btree p has an open read or write  -** transaction. If it does not, then the BtShared.isPending variable +** transaction. If it does not, then the BTS_PENDING flag  ** may be incorrectly cleared.  */  static void clearAllSharedCacheTableLocks(Btree *p){ @@ -365,7 +365,7 @@ static void clearAllSharedCacheTableLocks(Btree *p){    while( *ppIter ){      BtLock *pLock = *ppIter; -    assert( pBt->isExclusive==0 || pBt->pWriter==pLock->pBtree ); +    assert( (pBt->btsFlags & BTS_EXCLUSIVE)==0 || pBt->pWriter==pLock->pBtree );      assert( pLock->pBtree->inTrans>=pLock->eLock );      if( pLock->pBtree==p ){        *ppIter = pLock->pNext; @@ -378,22 +378,21 @@ static void clearAllSharedCacheTableLocks(Btree *p){      }    } -  assert( pBt->isPending==0 || pBt->pWriter ); +  assert( (pBt->btsFlags & BTS_PENDING)==0 || pBt->pWriter );    if( pBt->pWriter==p ){      pBt->pWriter = 0; -    pBt->isExclusive = 0; -    pBt->isPending = 0; +    pBt->btsFlags &= ~(BTS_EXCLUSIVE|BTS_PENDING);    }else if( pBt->nTransaction==2 ){      /* This function is called when Btree p is concluding its       ** transaction. If there currently exists a writer, and p is not      ** that writer, then the number of locks held by connections other      ** than the writer must be about to drop to zero. In this case -    ** set the isPending flag to 0. +    ** set the BTS_PENDING flag to 0.      ** -    ** If there is not currently a writer, then BtShared.isPending must +    ** If there is not currently a writer, then BTS_PENDING must      ** be zero already. So this next line is harmless in that case.      */ -    pBt->isPending = 0; +    pBt->btsFlags &= ~BTS_PENDING;    }  } @@ -405,8 +404,7 @@ static void downgradeAllSharedCacheTableLocks(Btree *p){    if( pBt->pWriter==p ){      BtLock *pLock;      pBt->pWriter = 0; -    pBt->isExclusive = 0; -    pBt->isPending = 0; +    pBt->btsFlags &= ~(BTS_EXCLUSIVE|BTS_PENDING);      for(pLock=pBt->pLock; pLock; pLock=pLock->pNext){        assert( pLock->eLock==READ_LOCK || pLock->pBtree==p );        pLock->eLock = READ_LOCK; @@ -859,7 +857,7 @@ static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){  ** This routine works only for pages that do not contain overflow cells.  */  #define findCell(P,I) \ -  ((P)->aData + ((P)->maskPage & get2byte(&(P)->aData[(P)->cellOffset+2*(I)]))) +  ((P)->aData + ((P)->maskPage & get2byte(&(P)->aCellIdx[2*(I)])))  #define findCellv2(D,M,O,I) (D+(M&get2byte(D+(O+2*(I))))) @@ -872,12 +870,10 @@ static u8 *findOverflowCell(MemPage *pPage, int iCell){    assert( sqlite3_mutex_held(pPage->pBt->mutex) );    for(i=pPage->nOverflow-1; i>=0; i--){      int k; -    struct _OvflCell *pOvfl; -    pOvfl = &pPage->aOvfl[i]; -    k = pOvfl->idx; +    k = pPage->aiOvfl[i];      if( k<=iCell ){        if( k==iCell ){ -        return pOvfl->pCell; +        return pPage->apOvfl[i];        }        iCell--;      } @@ -1264,7 +1260,7 @@ static int freeSpace(MemPage *pPage, int start, int size){    assert( sqlite3_mutex_held(pPage->pBt->mutex) );    assert( size>=0 );   /* Minimum cell size is 4 */ -  if( pPage->pBt->secureDelete ){ +  if( pPage->pBt->btsFlags & BTS_SECURE_DELETE ){      /* Overwrite deleted information with zeros when the secure_delete      ** option is enabled */      memset(&data[start], 0, size); @@ -1367,6 +1363,7 @@ static int decodeFlags(MemPage *pPage, int flagByte){    }else{      return SQLITE_CORRUPT_BKPT;    } +  pPage->max1bytePayload = pBt->max1bytePayload;    return SQLITE_OK;  } @@ -1409,6 +1406,8 @@ static int btreeInitPage(MemPage *pPage){      pPage->nOverflow = 0;      usableSize = pBt->usableSize;      pPage->cellOffset = cellOffset = hdr + 12 - 4*pPage->leaf; +    pPage->aDataEnd = &data[usableSize]; +    pPage->aCellIdx = &data[cellOffset];      top = get2byteNotZero(&data[hdr+5]);      pPage->nCell = get2byte(&data[hdr+3]);      if( pPage->nCell>MX_CELL(pBt) ){ @@ -1500,7 +1499,7 @@ static void zeroPage(MemPage *pPage, int flags){    assert( sqlite3PagerGetData(pPage->pDbPage) == data );    assert( sqlite3PagerIswriteable(pPage->pDbPage) );    assert( sqlite3_mutex_held(pBt->mutex) ); -  if( pBt->secureDelete ){ +  if( pBt->btsFlags & BTS_SECURE_DELETE ){      memset(&data[hdr], 0, pBt->usableSize - hdr);    }    data[hdr] = (char)flags; @@ -1512,6 +1511,8 @@ static void zeroPage(MemPage *pPage, int flags){    decodeFlags(pPage, flags);    pPage->hdrOffset = hdr;    pPage->cellOffset = first; +  pPage->aDataEnd = &data[pBt->usableSize]; +  pPage->aCellIdx = &data[first];    pPage->nOverflow = 0;    assert( pBt->pageSize>=512 && pBt->pageSize<=65536 );    pPage->maskPage = (u16)(pBt->pageSize - 1); @@ -1686,11 +1687,8 @@ static int btreeInvokeBusyHandler(void *pArg){  ** If zFilename is ":memory:" then an in-memory database is created  ** that is automatically destroyed when it is closed.  ** -** The "flags" parameter is a bitmask that might contain bits -** BTREE_OMIT_JOURNAL and/or BTREE_NO_READLOCK.  The BTREE_NO_READLOCK -** bit is also set if the SQLITE_NoReadlock flags is set in db->flags. -** These flags are passed through into sqlite3PagerOpen() and must -** be the same values as PAGER_OMIT_JOURNAL and PAGER_NO_READLOCK. +** The "flags" parameter is a bitmask that might contain bits like +** BTREE_OMIT_JOURNAL and/or BTREE_MEMORY.  **  ** If the database is already opened in the same database connection  ** and we are in shared cache mode, then the open will fail with an @@ -1737,9 +1735,6 @@ int sqlite3BtreeOpen(    /* A BTREE_SINGLE database is always a temporary and/or ephemeral */    assert( (flags & BTREE_SINGLE)==0 || isTempDb ); -  if( db->flags & SQLITE_NoReadlock ){ -    flags |= BTREE_NO_READLOCK; -  }    if( isMemdb ){      flags |= BTREE_MEMORY;    } @@ -1772,7 +1767,12 @@ int sqlite3BtreeOpen(          sqlite3_free(p);          return SQLITE_NOMEM;        } -      sqlite3OsFullPathname(pVfs, zFilename, nFullPathname, zFullPathname); +      rc = sqlite3OsFullPathname(pVfs, zFilename, nFullPathname, zFullPathname); +      if( rc ){ +        sqlite3_free(zFullPathname); +        sqlite3_free(p); +        return rc; +      }  #if SQLITE_THREADSAFE        mutexOpen = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_OPEN);        sqlite3_mutex_enter(mutexOpen); @@ -1846,9 +1846,9 @@ int sqlite3BtreeOpen(      pBt->pCursor = 0;      pBt->pPage1 = 0; -    pBt->readOnly = sqlite3PagerIsreadonly(pBt->pPager); +    if( sqlite3PagerIsreadonly(pBt->pPager) ) pBt->btsFlags |= BTS_READ_ONLY;  #ifdef SQLITE_SECURE_DELETE -    pBt->secureDelete = 1; +    pBt->btsFlags |= BTS_SECURE_DELETE;  #endif      pBt->pageSize = (zDbHeader[16]<<8) | (zDbHeader[17]<<16);      if( pBt->pageSize<512 || pBt->pageSize>SQLITE_MAX_PAGE_SIZE @@ -1869,7 +1869,7 @@ int sqlite3BtreeOpen(        nReserve = 0;      }else{        nReserve = zDbHeader[20]; -      pBt->pageSizeFixed = 1; +      pBt->btsFlags |= BTS_PAGESIZE_FIXED;  #ifndef SQLITE_OMIT_AUTOVACUUM        pBt->autoVacuum = (get4byte(&zDbHeader[36 + 4*4])?1:0);        pBt->incrVacuum = (get4byte(&zDbHeader[36 + 7*4])?1:0); @@ -2041,7 +2041,7 @@ int sqlite3BtreeClose(Btree *p){    ** The call to sqlite3BtreeRollback() drops any table-locks held by    ** this handle.    */ -  sqlite3BtreeRollback(p); +  sqlite3BtreeRollback(p, SQLITE_OK);    sqlite3BtreeLeave(p);    /* If there are still other outstanding references to the shared-btree @@ -2157,7 +2157,7 @@ int sqlite3BtreeSyncDisabled(Btree *p){  ** If parameter nReserve is less than zero, then the number of reserved  ** bytes per page is left unchanged.  ** -** If the iFix!=0 then the pageSizeFixed flag is set so that the page size +** If the iFix!=0 then the BTS_PAGESIZE_FIXED flag is set so that the page size  ** and autovacuum mode can no longer be changed.  */  int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, int iFix){ @@ -2165,7 +2165,7 @@ int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, int iFix){    BtShared *pBt = p->pBt;    assert( nReserve>=-1 && nReserve<=255 );    sqlite3BtreeEnter(p); -  if( pBt->pageSizeFixed ){ +  if( pBt->btsFlags & BTS_PAGESIZE_FIXED ){      sqlite3BtreeLeave(p);      return SQLITE_READONLY;    } @@ -2182,7 +2182,7 @@ int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, int iFix){    }    rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize, nReserve);    pBt->usableSize = pBt->pageSize - (u16)nReserve; -  if( iFix ) pBt->pageSizeFixed = 1; +  if( iFix ) pBt->btsFlags |= BTS_PAGESIZE_FIXED;    sqlite3BtreeLeave(p);    return rc;  } @@ -2222,8 +2222,8 @@ int sqlite3BtreeMaxPageCount(Btree *p, int mxPage){  }  /* -** Set the secureDelete flag if newFlag is 0 or 1.  If newFlag is -1, -** then make no changes.  Always return the value of the secureDelete +** Set the BTS_SECURE_DELETE flag if newFlag is 0 or 1.  If newFlag is -1, +** then make no changes.  Always return the value of the BTS_SECURE_DELETE  ** setting after the change.  */  int sqlite3BtreeSecureDelete(Btree *p, int newFlag){ @@ -2231,9 +2231,10 @@ int sqlite3BtreeSecureDelete(Btree *p, int newFlag){    if( p==0 ) return 0;    sqlite3BtreeEnter(p);    if( newFlag>=0 ){ -    p->pBt->secureDelete = (newFlag!=0) ? 1 : 0; +    p->pBt->btsFlags &= ~BTS_SECURE_DELETE; +    if( newFlag ) p->pBt->btsFlags |= BTS_SECURE_DELETE;    }  -  b = p->pBt->secureDelete; +  b = (p->pBt->btsFlags & BTS_SECURE_DELETE)!=0;    sqlite3BtreeLeave(p);    return b;  } @@ -2254,7 +2255,7 @@ int sqlite3BtreeSetAutoVacuum(Btree *p, int autoVacuum){    u8 av = (u8)autoVacuum;    sqlite3BtreeEnter(p); -  if( pBt->pageSizeFixed && (av ?1:0)!=pBt->autoVacuum ){ +  if( (pBt->btsFlags & BTS_PAGESIZE_FIXED)!=0 && (av ?1:0)!=pBt->autoVacuum ){      rc = SQLITE_READONLY;    }else{      pBt->autoVacuum = av ?1:0; @@ -2328,14 +2329,14 @@ static int lockBtree(BtShared *pBt){  #ifdef SQLITE_OMIT_WAL      if( page1[18]>1 ){ -      pBt->readOnly = 1; +      pBt->btsFlags |= BTS_READ_ONLY;      }      if( page1[19]>1 ){        goto page1_init_failed;      }  #else      if( page1[18]>2 ){ -      pBt->readOnly = 1; +      pBt->btsFlags |= BTS_READ_ONLY;      }      if( page1[19]>2 ){        goto page1_init_failed; @@ -2349,7 +2350,7 @@ static int lockBtree(BtShared *pBt){      ** may not be the latest version - there may be a newer one in the log      ** file.      */ -    if( page1[19]==2 && pBt->doNotUseWAL==0 ){ +    if( page1[19]==2 && (pBt->btsFlags & BTS_NO_WAL)==0 ){        int isOpen = 0;        rc = sqlite3PagerOpenWal(pBt->pPager, &isOpen);        if( rc!=SQLITE_OK ){ @@ -2426,6 +2427,11 @@ static int lockBtree(BtShared *pBt){    pBt->minLocal = (u16)((pBt->usableSize-12)*32/255 - 23);    pBt->maxLeaf = (u16)(pBt->usableSize - 35);    pBt->minLeaf = (u16)((pBt->usableSize-12)*32/255 - 23); +  if( pBt->maxLocal>127 ){ +    pBt->max1bytePayload = 127; +  }else{ +    pBt->max1bytePayload = (u8)pBt->maxLocal; +  }    assert( pBt->maxLeaf + 23 <= MX_CELL_SIZE(pBt) );    pBt->pPage1 = pPage1;    pBt->nPage = nPage; @@ -2489,7 +2495,7 @@ static int newDatabase(BtShared *pBt){    data[23] = 32;    memset(&data[24], 0, 100-24);    zeroPage(pP1, PTF_INTKEY|PTF_LEAF|PTF_LEAFDATA ); -  pBt->pageSizeFixed = 1; +  pBt->btsFlags |= BTS_PAGESIZE_FIXED;  #ifndef SQLITE_OMIT_AUTOVACUUM    assert( pBt->autoVacuum==1 || pBt->autoVacuum==0 );    assert( pBt->incrVacuum==1 || pBt->incrVacuum==0 ); @@ -2553,7 +2559,7 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){    }    /* Write transactions are not possible on a read-only database */ -  if( pBt->readOnly && wrflag ){ +  if( (pBt->btsFlags & BTS_READ_ONLY)!=0 && wrflag ){      rc = SQLITE_READONLY;      goto trans_begun;    } @@ -2563,7 +2569,9 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){    ** on this shared-btree structure and a second write transaction is    ** requested, return SQLITE_LOCKED.    */ -  if( (wrflag && pBt->inTransaction==TRANS_WRITE) || pBt->isPending ){ +  if( (wrflag && pBt->inTransaction==TRANS_WRITE) +   || (pBt->btsFlags & BTS_PENDING)!=0 +  ){      pBlock = pBt->pWriter->db;    }else if( wrflag>1 ){      BtLock *pIter; @@ -2587,7 +2595,8 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){    rc = querySharedCacheTableLock(p, MASTER_ROOT, READ_LOCK);    if( SQLITE_OK!=rc ) goto trans_begun; -  pBt->initiallyEmpty = (u8)(pBt->nPage==0); +  pBt->btsFlags &= ~BTS_INITIALLY_EMPTY; +  if( pBt->nPage==0 ) pBt->btsFlags |= BTS_INITIALLY_EMPTY;    do {      /* Call lockBtree() until either pBt->pPage1 is populated or      ** lockBtree() returns something other than SQLITE_OK. lockBtree() @@ -2599,7 +2608,7 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){      while( pBt->pPage1==0 && SQLITE_OK==(rc = lockBtree(pBt)) );      if( rc==SQLITE_OK && wrflag ){ -      if( pBt->readOnly ){ +      if( (pBt->btsFlags & BTS_READ_ONLY)!=0 ){          rc = SQLITE_READONLY;        }else{          rc = sqlite3PagerBegin(pBt->pPager,wrflag>1,sqlite3TempInMemory(p->db)); @@ -2636,7 +2645,8 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){  #ifndef SQLITE_OMIT_SHARED_CACHE        assert( !pBt->pWriter );        pBt->pWriter = p; -      pBt->isExclusive = (u8)(wrflag>1); +      pBt->btsFlags &= ~BTS_EXCLUSIVE; +      if( wrflag>1 ) pBt->btsFlags |= BTS_EXCLUSIVE;  #endif        /* If the db-size header field is incorrect (as it may be if an old @@ -3269,6 +3279,7 @@ static int countWriteCursors(BtShared *pBt){  */  void sqlite3BtreeTripAllCursors(Btree *pBtree, int errCode){    BtCursor *p; +  if( pBtree==0 ) return;    sqlite3BtreeEnter(pBtree);    for(p=pBtree->pBt->pCursor; p; p=p->pNext){      int i; @@ -3292,25 +3303,20 @@ void sqlite3BtreeTripAllCursors(Btree *pBtree, int errCode){  ** This will release the write lock on the database file.  If there  ** are no active cursors, it also releases the read lock.  */ -int sqlite3BtreeRollback(Btree *p){ +int sqlite3BtreeRollback(Btree *p, int tripCode){    int rc;    BtShared *pBt = p->pBt;    MemPage *pPage1;    sqlite3BtreeEnter(p); -  rc = saveAllCursors(pBt, 0, 0); -#ifndef SQLITE_OMIT_SHARED_CACHE -  if( rc!=SQLITE_OK ){ -    /* This is a horrible situation. An IO or malloc() error occurred whilst -    ** trying to save cursor positions. If this is an automatic rollback (as -    ** the result of a constraint, malloc() failure or IO error) then  -    ** the cache may be internally inconsistent (not contain valid trees) so -    ** we cannot simply return the error to the caller. Instead, abort  -    ** all queries that may be using any of the cursors that failed to save. -    */ -    sqlite3BtreeTripAllCursors(p, rc); +  if( tripCode==SQLITE_OK ){ +    rc = tripCode = saveAllCursors(pBt, 0, 0); +  }else{ +    rc = SQLITE_OK; +  } +  if( tripCode ){ +    sqlite3BtreeTripAllCursors(p, tripCode);    } -#endif    btreeIntegrity(p);    if( p->inTrans==TRANS_WRITE ){ @@ -3365,7 +3371,7 @@ int sqlite3BtreeBeginStmt(Btree *p, int iStatement){    BtShared *pBt = p->pBt;    sqlite3BtreeEnter(p);    assert( p->inTrans==TRANS_WRITE ); -  assert( pBt->readOnly==0 ); +  assert( (pBt->btsFlags & BTS_READ_ONLY)==0 );    assert( iStatement>0 );    assert( iStatement>p->db->nSavepoint );    assert( pBt->inTransaction==TRANS_WRITE ); @@ -3400,7 +3406,9 @@ int sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){      sqlite3BtreeEnter(p);      rc = sqlite3PagerSavepoint(pBt->pPager, op, iSavepoint);      if( rc==SQLITE_OK ){ -      if( iSavepoint<0 && pBt->initiallyEmpty ) pBt->nPage = 0; +      if( iSavepoint<0 && (pBt->btsFlags & BTS_INITIALLY_EMPTY)!=0 ){ +        pBt->nPage = 0; +      }        rc = newDatabase(pBt);        pBt->nPage = get4byte(28 + pBt->pPage1->aData); @@ -3470,7 +3478,7 @@ static int btreeCursor(    assert( wrFlag==0 || p->inTrans==TRANS_WRITE );    assert( pBt->pPage1 && pBt->pPage1->aData ); -  if( NEVER(wrFlag && pBt->readOnly) ){ +  if( NEVER(wrFlag && (pBt->btsFlags & BTS_READ_ONLY)!=0) ){      return SQLITE_READONLY;    }    if( iTable==1 && btreePagecount(pBt)==0 ){ @@ -3970,7 +3978,7 @@ static int accessPayload(            u8 aSave[4];            u8 *aWrite = &pBuf[-4];            memcpy(aSave, aWrite, 4); -          rc = sqlite3OsRead(fd, aWrite, a+4, pBt->pageSize * (nextPage-1)); +          rc = sqlite3OsRead(fd, aWrite, a+4, (i64)pBt->pageSize*(nextPage-1));            nextPage = get4byte(aWrite);            memcpy(aWrite, aSave, 4);          }else @@ -4174,7 +4182,7 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){    return SQLITE_OK;  } -#ifndef NDEBUG +#if 0  /*  ** Page pParent is an internal (non-leaf) tree page. This function   ** asserts that page number iChild is the left-child if the iIdx'th @@ -4207,11 +4215,21 @@ static void moveToParent(BtCursor *pCur){    assert( pCur->eState==CURSOR_VALID );    assert( pCur->iPage>0 );    assert( pCur->apPage[pCur->iPage] ); + +  /* UPDATE: It is actually possible for the condition tested by the assert +  ** below to be untrue if the database file is corrupt. This can occur if +  ** one cursor has modified page pParent while a reference to it is held  +  ** by a second cursor. Which can only happen if a single page is linked +  ** into more than one b-tree structure in a corrupt database.  */ +#if 0    assertParentIndex(      pCur->apPage[pCur->iPage-1],       pCur->aiIdx[pCur->iPage-1],       pCur->apPage[pCur->iPage]->pgno    ); +#endif +  testcase( pCur->aiIdx[pCur->iPage-1] > pCur->apPage[pCur->iPage-1]->nCell ); +    releasePage(pCur->apPage[pCur->iPage]);    pCur->iPage--;    pCur->info.nSize = 0; @@ -4550,16 +4568,21 @@ int sqlite3BtreeMovetoUnpacked(          ** 2 bytes of the cell.          */          int nCell = pCell[0]; -        if( !(nCell & 0x80) && nCell<=pPage->maxLocal ){ +        if( nCell<=pPage->max1bytePayload +         /* && (pCell+nCell)<pPage->aDataEnd */ +        ){            /* This branch runs if the record-size field of the cell is a            ** single byte varint and the record fits entirely on the main            ** b-tree page.  */ +          testcase( pCell+nCell+1==pPage->aDataEnd );            c = sqlite3VdbeRecordCompare(nCell, (void*)&pCell[1], pIdxKey);          }else if( !(pCell[1] & 0x80)             && (nCell = ((nCell&0x7f)<<7) + pCell[1])<=pPage->maxLocal +          /* && (pCell+nCell+2)<=pPage->aDataEnd */          ){            /* The record-size field is a 2 byte varint and the record             ** fits entirely on the main b-tree page.  */ +          testcase( pCell+nCell+2==pPage->aDataEnd );            c = sqlite3VdbeRecordCompare(nCell, (void*)&pCell[2], pIdxKey);          }else{            /* The record flows over onto one or more overflow pages. In @@ -4676,7 +4699,13 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){    pPage = pCur->apPage[pCur->iPage];    idx = ++pCur->aiIdx[pCur->iPage];    assert( pPage->isInit ); -  assert( idx<=pPage->nCell ); + +  /* If the database file is corrupt, it is possible for the value of idx  +  ** to be invalid here. This can only occur if a second cursor modifies +  ** the page while cursor pCur is holding a reference to it. Which can +  ** only happen if the database is corrupt in such a way as to link the +  ** page into more than one b-tree structure. */ +  testcase( idx>pPage->nCell );    pCur->info.nSize = 0;    pCur->validNKey = 0; @@ -5101,7 +5130,7 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){    nFree = get4byte(&pPage1->aData[36]);    put4byte(&pPage1->aData[36], nFree+1); -  if( pBt->secureDelete ){ +  if( pBt->btsFlags & BTS_SECURE_DELETE ){      /* If the secure_delete option is enabled, then      ** always fully overwrite deleted information with zeros.      */ @@ -5162,7 +5191,7 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){        if( rc==SQLITE_OK ){          put4byte(&pTrunk->aData[4], nLeaf+1);          put4byte(&pTrunk->aData[8+nLeaf*4], iPage); -        if( pPage && !pBt->secureDelete ){ +        if( pPage && (pBt->btsFlags & BTS_SECURE_DELETE)==0 ){            sqlite3PagerDontWrite(pPage->pDbPage);          }          rc = btreeSetHasContent(pBt, iPage); @@ -5454,7 +5483,7 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){    assert( sqlite3PagerIswriteable(pPage->pDbPage) );    assert( sqlite3_mutex_held(pPage->pBt->mutex) );    data = pPage->aData; -  ptr = &data[pPage->cellOffset + 2*idx]; +  ptr = &pPage->aCellIdx[2*idx];    pc = get2byte(ptr);    hdr = pPage->hdrOffset;    testcase( pc==get2byte(&data[hdr+5]) ); @@ -5468,7 +5497,7 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){      *pRC = rc;      return;    } -  endPtr = &data[pPage->cellOffset + 2*pPage->nCell - 2]; +  endPtr = &pPage->aCellIdx[2*pPage->nCell - 2];    assert( (SQLITE_PTR_TO_INT(ptr)&1)==0 );  /* ptr is always 2-byte aligned */    while( ptr<endPtr ){      *(u16*)ptr = *(u16*)&ptr[2]; @@ -5486,7 +5515,7 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){  ** If the cell content will fit on the page, then put it there.  If it  ** will not fit, then make a copy of the cell content into pTemp if  ** pTemp is not null.  Regardless of pTemp, allocate a new entry -** in pPage->aOvfl[] and make it point to the cell content (either +** in pPage->apOvfl[] and make it point to the cell content (either  ** in pTemp or the original pCell) and also record its index.   ** Allocating a new entry in pPage->aCell[] implies that   ** pPage->nOverflow is incremented. @@ -5520,7 +5549,8 @@ static void insertCell(    assert( i>=0 && i<=pPage->nCell+pPage->nOverflow );    assert( pPage->nCell<=MX_CELL(pPage->pBt) && MX_CELL(pPage->pBt)<=10921 ); -  assert( pPage->nOverflow<=ArraySize(pPage->aOvfl) ); +  assert( pPage->nOverflow<=ArraySize(pPage->apOvfl) ); +  assert( ArraySize(pPage->apOvfl)==ArraySize(pPage->aiOvfl) );    assert( sqlite3_mutex_held(pPage->pBt->mutex) );    /* The cell should normally be sized correctly.  However, when moving a    ** malformed cell from a leaf page to an interior page, if the cell size @@ -5537,9 +5567,9 @@ static void insertCell(        put4byte(pCell, iChild);      }      j = pPage->nOverflow++; -    assert( j<(int)(sizeof(pPage->aOvfl)/sizeof(pPage->aOvfl[0])) ); -    pPage->aOvfl[j].pCell = pCell; -    pPage->aOvfl[j].idx = (u16)i; +    assert( j<(int)(sizeof(pPage->apOvfl)/sizeof(pPage->apOvfl[0])) ); +    pPage->apOvfl[j] = pCell; +    pPage->aiOvfl[j] = (u16)i;    }else{      int rc = sqlite3PagerWrite(pPage->pDbPage);      if( rc!=SQLITE_OK ){ @@ -5610,7 +5640,7 @@ static void assemblePage(    assert( pPage->nCell==0 );    assert( get2byteNotZero(&data[hdr+5])==nUsable ); -  pCellptr = &data[pPage->cellOffset + nCell*2]; +  pCellptr = &pPage->aCellIdx[nCell*2];    cellbody = nUsable;    for(i=nCell-1; i>=0; i--){      u16 sz = aSize[i]; @@ -5687,7 +5717,7 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){    if( rc==SQLITE_OK ){      u8 *pOut = &pSpace[4]; -    u8 *pCell = pPage->aOvfl[0].pCell; +    u8 *pCell = pPage->apOvfl[0];      u16 szCell = cellSizePtr(pPage, pCell);      u8 *pStop; @@ -5797,7 +5827,7 @@ static int ptrmapCheckPages(MemPage **apPage, int nPage){  ** map entries are also updated so that the parent page is page pTo.  **  ** If pFrom is currently carrying any overflow cells (entries in the -** MemPage.aOvfl[] array), they are not copied to pTo.  +** MemPage.apOvfl[] array), they are not copied to pTo.   **  ** Before returning, page pTo is reinitialized using btreeInitPage().  ** @@ -5934,7 +5964,7 @@ static int balance_nonroot(    ** is called (indirectly) from sqlite3BtreeDelete().    */    assert( pParent->nOverflow==0 || pParent->nOverflow==1 ); -  assert( pParent->nOverflow==0 || pParent->aOvfl[0].idx==iParentIdx ); +  assert( pParent->nOverflow==0 || pParent->aiOvfl[0]==iParentIdx );    if( !aOvflSpace ){      return SQLITE_NOMEM; @@ -5981,8 +6011,8 @@ static int balance_nonroot(      nMaxCells += 1+apOld[i]->nCell+apOld[i]->nOverflow;      if( (i--)==0 ) break; -    if( i+nxDiv==pParent->aOvfl[0].idx && pParent->nOverflow ){ -      apDiv[i] = pParent->aOvfl[0].pCell; +    if( i+nxDiv==pParent->aiOvfl[0] && pParent->nOverflow ){ +      apDiv[i] = pParent->apOvfl[0];        pgno = get4byte(apDiv[i]);        szNew[i] = cellSizePtr(pParent, apDiv[i]);        pParent->nOverflow = 0; @@ -6003,7 +6033,7 @@ static int balance_nonroot(        ** In this case, temporarily copy the cell into the aOvflSpace[]        ** buffer. It will be copied out again as soon as the aSpace[] buffer        ** is allocated.  */ -      if( pBt->secureDelete ){ +      if( pBt->btsFlags & BTS_SECURE_DELETE ){          int iOff;          iOff = SQLITE_PTR_TO_INT(apDiv[i]) - SQLITE_PTR_TO_INT(pParent->aData); @@ -6188,8 +6218,14 @@ static int balance_nonroot(    /* Either we found one or more cells (cntnew[0])>0) or pPage is    ** a virtual root page.  A virtual root page is when the real root    ** page is page 1 and we are the only child of that page. +  ** +  ** UPDATE:  The assert() below is not necessarily true if the database +  ** file is corrupt.  The corruption will be detected and reported later +  ** in this procedure so there is no need to act upon it now.    */ +#if 0    assert( cntNew[0]>0 || (pParent->pgno==1 && pParent->nCell==0) ); +#endif    TRACE(("BALANCE: old: %d %d %d  ",      apOld[0]->pgno,  @@ -6417,7 +6453,7 @@ static int balance_nonroot(      MemPage *pOld = apCopy[0];      int nOverflow = pOld->nOverflow;      int iNextOld = pOld->nCell + nOverflow; -    int iOverflow = (nOverflow ? pOld->aOvfl[0].idx : -1); +    int iOverflow = (nOverflow ? pOld->aiOvfl[0] : -1);      j = 0;                             /* Current 'old' sibling page */      k = 0;                             /* Current 'new' sibling page */      for(i=0; i<nCell; i++){ @@ -6431,14 +6467,14 @@ static int balance_nonroot(          iNextOld = i + !leafData + pOld->nCell + pOld->nOverflow;          if( pOld->nOverflow ){            nOverflow = pOld->nOverflow; -          iOverflow = i + !leafData + pOld->aOvfl[0].idx; +          iOverflow = i + !leafData + pOld->aiOvfl[0];          }          isDivider = !leafData;          }        assert(nOverflow>0 || iOverflow<i ); -      assert(nOverflow<2 || pOld->aOvfl[0].idx==pOld->aOvfl[1].idx-1); -      assert(nOverflow<3 || pOld->aOvfl[1].idx==pOld->aOvfl[2].idx-1); +      assert(nOverflow<2 || pOld->aiOvfl[0]==pOld->aiOvfl[1]-1); +      assert(nOverflow<3 || pOld->aiOvfl[1]==pOld->aiOvfl[2]-1);        if( i==iOverflow ){          isDivider = 1;          if( (--nOverflow)>0 ){ @@ -6559,7 +6595,10 @@ static int balance_deeper(MemPage *pRoot, MemPage **ppChild){    TRACE(("BALANCE: copy root %d into %d\n", pRoot->pgno, pChild->pgno));    /* Copy the overflow cells from pRoot to pChild */ -  memcpy(pChild->aOvfl, pRoot->aOvfl, pRoot->nOverflow*sizeof(pRoot->aOvfl[0])); +  memcpy(pChild->aiOvfl, pRoot->aiOvfl, +         pRoot->nOverflow*sizeof(pRoot->aiOvfl[0])); +  memcpy(pChild->apOvfl, pRoot->apOvfl, +         pRoot->nOverflow*sizeof(pRoot->apOvfl[0]));    pChild->nOverflow = pRoot->nOverflow;    /* Zero the contents of pRoot. Then install pChild as the right-child. */ @@ -6622,7 +6661,7 @@ static int balance(BtCursor *pCur){  #ifndef SQLITE_OMIT_QUICKBALANCE          if( pPage->hasData           && pPage->nOverflow==1 -         && pPage->aOvfl[0].idx==pPage->nCell +         && pPage->aiOvfl[0]==pPage->nCell           && pParent->pgno!=1           && pParent->nCell==iIdx          ){ @@ -6739,7 +6778,8 @@ int sqlite3BtreeInsert(    }    assert( cursorHoldsMutex(pCur) ); -  assert( pCur->wrFlag && pBt->inTransaction==TRANS_WRITE && !pBt->readOnly ); +  assert( pCur->wrFlag && pBt->inTransaction==TRANS_WRITE +              && (pBt->btsFlags & BTS_READ_ONLY)==0 );    assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) );    /* Assert that the caller has been consistent. If this cursor was opened @@ -6749,13 +6789,6 @@ int sqlite3BtreeInsert(    ** blob of associated data.  */    assert( (pKey==0)==(pCur->pKeyInfo==0) ); -  /* If this is an insert into a table b-tree, invalidate any incrblob  -  ** cursors open on the row being replaced (assuming this is a replace -  ** operation - if it is not, the following is a no-op).  */ -  if( pCur->pKeyInfo==0 ){ -    invalidateIncrblobCursors(p, nKey, 0); -  } -    /* Save the positions of any other cursors open on this table.    **    ** In some cases, the call to btreeMoveto() below is a no-op. For @@ -6769,6 +6802,14 @@ int sqlite3BtreeInsert(    */    rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur);    if( rc ) return rc; + +  /* If this is an insert into a table b-tree, invalidate any incrblob  +  ** cursors open on the row being replaced (assuming this is a replace +  ** operation - if it is not, the following is a no-op).  */ +  if( pCur->pKeyInfo==0 ){ +    invalidateIncrblobCursors(p, nKey, 0); +  } +    if( !loc ){      rc = btreeMoveto(pCur, pKey, nKey, appendBias, &loc);      if( rc ) return rc; @@ -6868,7 +6909,7 @@ int sqlite3BtreeDelete(BtCursor *pCur){    assert( cursorHoldsMutex(pCur) );    assert( pBt->inTransaction==TRANS_WRITE ); -  assert( !pBt->readOnly ); +  assert( (pBt->btsFlags & BTS_READ_ONLY)==0 );    assert( pCur->wrFlag );    assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) );    assert( !hasReadConflicts(p, pCur->pgnoRoot) ); @@ -6879,12 +6920,6 @@ int sqlite3BtreeDelete(BtCursor *pCur){      return SQLITE_ERROR;  /* Something has gone awry. */    } -  /* If this is a delete operation to remove a row from a table b-tree, -  ** invalidate any incrblob cursors open on the row being deleted.  */ -  if( pCur->pKeyInfo==0 ){ -    invalidateIncrblobCursors(p, pCur->info.nKey, 0); -  } -    iCellDepth = pCur->iPage;    iCellIdx = pCur->aiIdx[iCellDepth];    pPage = pCur->apPage[iCellDepth]; @@ -6910,6 +6945,13 @@ int sqlite3BtreeDelete(BtCursor *pCur){    */    rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur);    if( rc ) return rc; + +  /* If this is a delete operation to remove a row from a table b-tree, +  ** invalidate any incrblob cursors open on the row being deleted.  */ +  if( pCur->pKeyInfo==0 ){ +    invalidateIncrblobCursors(p, pCur->info.nKey, 0); +  } +    rc = sqlite3PagerWrite(pPage->pDbPage);    if( rc ) return rc;    rc = clearCell(pPage, pCell); @@ -6989,7 +7031,7 @@ static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){    assert( sqlite3BtreeHoldsMutex(p) );    assert( pBt->inTransaction==TRANS_WRITE ); -  assert( !pBt->readOnly ); +  assert( (pBt->btsFlags & BTS_READ_ONLY)==0 );  #ifdef SQLITE_OMIT_AUTOVACUUM    rc = allocateBtreePage(pBt, &pRoot, &pgnoRoot, 1, 0); @@ -7191,13 +7233,13 @@ int sqlite3BtreeClearTable(Btree *p, int iTable, int *pnChange){    sqlite3BtreeEnter(p);    assert( p->inTrans==TRANS_WRITE ); -  /* Invalidate all incrblob cursors open on table iTable (assuming iTable -  ** is the root of a table b-tree - if it is not, the following call is -  ** a no-op).  */ -  invalidateIncrblobCursors(p, 0, 1); -    rc = saveAllCursors(pBt, (Pgno)iTable, 0); +    if( SQLITE_OK==rc ){ +    /* Invalidate all incrblob cursors open on table iTable (assuming iTable +    ** is the root of a table b-tree - if it is not, the following call is +    ** a no-op).  */ +    invalidateIncrblobCursors(p, 0, 1);      rc = clearDatabasePage(pBt, (Pgno)iTable, 0, pnChange);    }    sqlite3BtreeLeave(p); @@ -7363,7 +7405,9 @@ void sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){    /* If auto-vacuum is disabled in this build and this is an auto-vacuum    ** database, mark the database as read-only.  */  #ifdef SQLITE_OMIT_AUTOVACUUM -  if( idx==BTREE_LARGEST_ROOT_PAGE && *pMeta>0 ) pBt->readOnly = 1; +  if( idx==BTREE_LARGEST_ROOT_PAGE && *pMeta>0 ){ +    pBt->btsFlags |= BTS_READ_ONLY; +  }  #endif    sqlite3BtreeLeave(p); @@ -7510,6 +7554,25 @@ static void checkAppendMsg(  #endif /* SQLITE_OMIT_INTEGRITY_CHECK */  #ifndef SQLITE_OMIT_INTEGRITY_CHECK + +/* +** Return non-zero if the bit in the IntegrityCk.aPgRef[] array that +** corresponds to page iPg is already set. +*/ +static int getPageReferenced(IntegrityCk *pCheck, Pgno iPg){ +  assert( iPg<=pCheck->nPage && sizeof(pCheck->aPgRef[0])==1 ); +  return (pCheck->aPgRef[iPg/8] & (1 << (iPg & 0x07))); +} + +/* +** Set the bit in the IntegrityCk.aPgRef[] array that corresponds to page iPg. +*/ +static void setPageReferenced(IntegrityCk *pCheck, Pgno iPg){ +  assert( iPg<=pCheck->nPage && sizeof(pCheck->aPgRef[0])==1 ); +  pCheck->aPgRef[iPg/8] |= (1 << (iPg & 0x07)); +} + +  /*  ** Add 1 to the reference count for page iPage.  If this is the second  ** reference to the page, add an error message to pCheck->zErrMsg. @@ -7524,11 +7587,12 @@ static int checkRef(IntegrityCk *pCheck, Pgno iPage, char *zContext){      checkAppendMsg(pCheck, zContext, "invalid page number %d", iPage);      return 1;    } -  if( pCheck->anRef[iPage]==1 ){ +  if( getPageReferenced(pCheck, iPage) ){      checkAppendMsg(pCheck, zContext, "2nd reference to page %d", iPage);      return 1;    } -  return  (pCheck->anRef[iPage]++)>1; +  setPageReferenced(pCheck, iPage); +  return 0;  }  #ifndef SQLITE_OMIT_AUTOVACUUM @@ -7904,17 +7968,15 @@ char *sqlite3BtreeIntegrityCheck(      sqlite3BtreeLeave(p);      return 0;    } -  sCheck.anRef = sqlite3Malloc( (sCheck.nPage+1)*sizeof(sCheck.anRef[0]) ); -  if( !sCheck.anRef ){ + +  sCheck.aPgRef = sqlite3MallocZero((sCheck.nPage / 8)+ 1); +  if( !sCheck.aPgRef ){      *pnErr = 1;      sqlite3BtreeLeave(p);      return 0;    } -  for(i=0; i<=sCheck.nPage; i++){ sCheck.anRef[i] = 0; }    i = PENDING_BYTE_PAGE(pBt); -  if( i<=sCheck.nPage ){ -    sCheck.anRef[i] = 1; -  } +  if( i<=sCheck.nPage ) setPageReferenced(&sCheck, i);    sqlite3StrAccumInit(&sCheck.errMsg, zErr, sizeof(zErr), 20000);    sCheck.errMsg.useMalloc = 2; @@ -7939,18 +8001,18 @@ char *sqlite3BtreeIntegrityCheck(    */    for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){  #ifdef SQLITE_OMIT_AUTOVACUUM -    if( sCheck.anRef[i]==0 ){ +    if( getPageReferenced(&sCheck, i)==0 ){        checkAppendMsg(&sCheck, 0, "Page %d is never used", i);      }  #else      /* If the database supports auto-vacuum, make sure no tables contain      ** references to pointer-map pages.      */ -    if( sCheck.anRef[i]==0 &&  +    if( getPageReferenced(&sCheck, i)==0 &&          (PTRMAP_PAGENO(pBt, i)!=i || !pBt->autoVacuum) ){        checkAppendMsg(&sCheck, 0, "Page %d is never used", i);      } -    if( sCheck.anRef[i]!=0 &&  +    if( getPageReferenced(&sCheck, i)!=0 &&          (PTRMAP_PAGENO(pBt, i)==i && pBt->autoVacuum) ){        checkAppendMsg(&sCheck, 0, "Pointer map page %d is referenced", i);      } @@ -7971,7 +8033,7 @@ char *sqlite3BtreeIntegrityCheck(    /* Clean  up and report errors.    */    sqlite3BtreeLeave(p); -  sqlite3_free(sCheck.anRef); +  sqlite3_free(sCheck.aPgRef);    if( sCheck.mallocFailed ){      sqlite3StrAccumReset(&sCheck.errMsg);      *pnErr = sCheck.nErr+1; @@ -8163,7 +8225,8 @@ int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){    if( !pCsr->wrFlag ){      return SQLITE_READONLY;    } -  assert( !pCsr->pBt->readOnly && pCsr->pBt->inTransaction==TRANS_WRITE ); +  assert( (pCsr->pBt->btsFlags & BTS_READ_ONLY)==0 +              && pCsr->pBt->inTransaction==TRANS_WRITE );    assert( hasSharedCacheTableLock(pCsr->pBtree, pCsr->pgnoRoot, 0, 2) );    assert( !hasReadConflicts(pCsr->pBtree, pCsr->pgnoRoot) );    assert( pCsr->apPage[pCsr->iPage]->intKey ); @@ -8203,7 +8266,8 @@ int sqlite3BtreeSetVersion(Btree *pBtree, int iVersion){    /* If setting the version fields to 1, do not automatically open the    ** WAL connection, even if the version fields are currently set to 2.    */ -  pBt->doNotUseWAL = (u8)(iVersion==1); +  pBt->btsFlags &= ~BTS_NO_WAL; +  if( iVersion==1 ) pBt->btsFlags |= BTS_NO_WAL;    rc = sqlite3BtreeBeginTrans(pBtree, 0);    if( rc==SQLITE_OK ){ @@ -8220,6 +8284,6 @@ int sqlite3BtreeSetVersion(Btree *pBtree, int iVersion){      }    } -  pBt->doNotUseWAL = 0; +  pBt->btsFlags &= ~BTS_NO_WAL;    return rc;  } | 
