| 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 2db3c90aab8069df781f35bcb1cf00ed1a7611f9..b5c3ba8fca9a535f2dd28889de57c92de2315c43 100644
|
| --- a/third_party/sqlite/src/src/btree.c
|
| +++ b/third_party/sqlite/src/src/btree.c
|
| @@ -9,8 +9,6 @@
|
| ** May you share freely, never taking more than you give.
|
| **
|
| *************************************************************************
|
| -** $Id: btree.c,v 1.705 2009/08/10 03:57:58 shane Exp $
|
| -**
|
| ** This file implements a external (disk-based) database using BTrees.
|
| ** See the header comment on "btreeInt.h" for additional information.
|
| ** Including a description of file format and an overview of operation.
|
| @@ -40,7 +38,16 @@ int sqlite3BtreeTrace=1; /* True to enable tracing */
|
| # define TRACE(X)
|
| #endif
|
|
|
| -
|
| +/*
|
| +** Extract a 2-byte big-endian integer from an array of unsigned bytes.
|
| +** But if the value is zero, make it 65536.
|
| +**
|
| +** This routine is used to extract the "offset to cell content area" value
|
| +** from the header of a btree page. If the page size is 65536 and the page
|
| +** is empty, the offset should be 65536, but the 2-byte value stores zero.
|
| +** This routine makes the necessary adjustment to 65536.
|
| +*/
|
| +#define get2byteNotZero(X) (((((int)get2byte(X))-1)&0xffff)+1)
|
|
|
| #ifndef SQLITE_OMIT_SHARED_CACHE
|
| /*
|
| @@ -96,22 +103,24 @@ int sqlite3_enable_shared_cache(int enable){
|
|
|
| #ifdef SQLITE_DEBUG
|
| /*
|
| -** This function is only used as part of an assert() statement. It checks
|
| -** that connection p holds the required locks to read or write to the
|
| -** b-tree with root page iRoot. If so, true is returned. Otherwise, false.
|
| -** For example, when writing to a table b-tree with root-page iRoot via
|
| +**** This function is only used as part of an assert() statement. ***
|
| +**
|
| +** Check to see if pBtree holds the required locks to read or write to the
|
| +** table with root page iRoot. Return 1 if it does and 0 if not.
|
| +**
|
| +** For example, when writing to a table with root-page iRoot via
|
| ** Btree connection pBtree:
|
| **
|
| ** assert( hasSharedCacheTableLock(pBtree, iRoot, 0, WRITE_LOCK) );
|
| **
|
| -** When writing to an index b-tree that resides in a sharable database, the
|
| +** When writing to an index that resides in a sharable database, the
|
| ** caller should have first obtained a lock specifying the root page of
|
| -** the corresponding table b-tree. This makes things a bit more complicated,
|
| -** as this module treats each b-tree as a separate structure. To determine
|
| -** the table b-tree corresponding to the index b-tree being written, this
|
| +** the corresponding table. This makes things a bit more complicated,
|
| +** as this module treats each table as a separate structure. To determine
|
| +** the table corresponding to the index being written, this
|
| ** function has to search through the database schema.
|
| **
|
| -** Instead of a lock on the b-tree rooted at page iRoot, the caller may
|
| +** Instead of a lock on the table/index rooted at page iRoot, the caller may
|
| ** hold a write-lock on the schema table (root page 1). This is also
|
| ** acceptable.
|
| */
|
| @@ -125,20 +134,25 @@ static int hasSharedCacheTableLock(
|
| Pgno iTab = 0;
|
| BtLock *pLock;
|
|
|
| - /* If this b-tree database is not shareable, or if the client is reading
|
| + /* If this database is not shareable, or if the client is reading
|
| ** and has the read-uncommitted flag set, then no lock is required.
|
| - ** In these cases return true immediately. If the client is reading
|
| - ** or writing an index b-tree, but the schema is not loaded, then return
|
| - ** true also. In this case the lock is required, but it is too difficult
|
| - ** to check if the client actually holds it. This doesn't happen very
|
| - ** often. */
|
| + ** Return true immediately.
|
| + */
|
| if( (pBtree->sharable==0)
|
| || (eLockType==READ_LOCK && (pBtree->db->flags & SQLITE_ReadUncommitted))
|
| - || (isIndex && (!pSchema || (pSchema->flags&DB_SchemaLoaded)==0 ))
|
| ){
|
| return 1;
|
| }
|
|
|
| + /* If the client is reading or writing an index and the schema is
|
| + ** not loaded, then it is too difficult to actually check to see if
|
| + ** the correct locks are held. So do not bother - just return true.
|
| + ** This case does not come up very often anyhow.
|
| + */
|
| + if( isIndex && (!pSchema || (pSchema->flags&DB_SchemaLoaded)==0) ){
|
| + return 1;
|
| + }
|
| +
|
| /* Figure out the root-page that the lock should be held on. For table
|
| ** b-trees, this is just the root page of the b-tree being read or
|
| ** written. For index b-trees, it is the root page of the associated
|
| @@ -170,14 +184,24 @@ static int hasSharedCacheTableLock(
|
| /* Failed to find the required lock. */
|
| return 0;
|
| }
|
| +#endif /* SQLITE_DEBUG */
|
|
|
| +#ifdef SQLITE_DEBUG
|
| /*
|
| -** This function is also used as part of assert() statements only. It
|
| -** returns true if there exist one or more cursors open on the table
|
| -** with root page iRoot that do not belong to either connection pBtree
|
| -** or some other connection that has the read-uncommitted flag set.
|
| +**** This function may be used as part of assert() statements only. ****
|
| +**
|
| +** Return true if it would be illegal for pBtree to write into the
|
| +** table or index rooted at iRoot because other shared connections are
|
| +** simultaneously reading that same table or index.
|
| **
|
| -** For example, before writing to page iRoot:
|
| +** It is illegal for pBtree to write if some other Btree object that
|
| +** shares the same BtShared object is currently reading or writing
|
| +** the iRoot table. Except, if the other Btree object has the
|
| +** read-uncommitted flag set, then it is OK for the other object to
|
| +** have a read cursor.
|
| +**
|
| +** For example, before writing to any part of the table or index
|
| +** rooted at page iRoot, one should call:
|
| **
|
| ** assert( !hasReadConflicts(pBtree, iRoot) );
|
| */
|
| @@ -196,7 +220,7 @@ static int hasReadConflicts(Btree *pBtree, Pgno iRoot){
|
| #endif /* #ifdef SQLITE_DEBUG */
|
|
|
| /*
|
| -** Query to see if btree handle p may obtain a lock of type eLock
|
| +** Query to see if Btree handle p may obtain a lock of type eLock
|
| ** (READ_LOCK or WRITE_LOCK) on the table with root-page iTab. Return
|
| ** SQLITE_OK if the lock may be obtained (by calling
|
| ** setSharedCacheTableLock()), or SQLITE_LOCKED if not.
|
| @@ -217,7 +241,7 @@ static int querySharedCacheTableLock(Btree *p, Pgno iTab, u8 eLock){
|
| assert( eLock==READ_LOCK || (p==pBt->pWriter && p->inTrans==TRANS_WRITE) );
|
| assert( eLock==READ_LOCK || pBt->inTransaction==TRANS_WRITE );
|
|
|
| - /* This is a no-op if the shared-cache is not enabled */
|
| + /* This routine is a no-op if the shared-cache is not enabled */
|
| if( !p->sharable ){
|
| return SQLITE_OK;
|
| }
|
| @@ -263,10 +287,10 @@ static int querySharedCacheTableLock(Btree *p, Pgno iTab, u8 eLock){
|
| **
|
| ** This function assumes the following:
|
| **
|
| -** (a) The specified b-tree connection handle is connected to a sharable
|
| -** b-tree database (one with the BtShared.sharable) flag set, and
|
| +** (a) The specified Btree object p is connected to a sharable
|
| +** database (one with the BtShared.sharable flag set), and
|
| **
|
| -** (b) No other b-tree connection handle holds a lock that conflicts
|
| +** (b) No other Btree objects hold a lock that conflicts
|
| ** with the requested lock (i.e. querySharedCacheTableLock() has
|
| ** already been called and returned SQLITE_OK).
|
| **
|
| @@ -331,9 +355,9 @@ static int setSharedCacheTableLock(Btree *p, Pgno iTable, u8 eLock){
|
| #ifndef SQLITE_OMIT_SHARED_CACHE
|
| /*
|
| ** Release all the table locks (locks obtained via calls to
|
| -** the setSharedCacheTableLock() procedure) held by Btree handle p.
|
| +** the setSharedCacheTableLock() procedure) held by Btree object p.
|
| **
|
| -** This function assumes that handle p has an open read or write
|
| +** This function assumes that Btree p has an open read or write
|
| ** transaction. If it does not, then the BtShared.isPending variable
|
| ** may be incorrectly cleared.
|
| */
|
| @@ -366,7 +390,7 @@ static void clearAllSharedCacheTableLocks(Btree *p){
|
| pBt->isExclusive = 0;
|
| pBt->isPending = 0;
|
| }else if( pBt->nTransaction==2 ){
|
| - /* This function is called when connection p is concluding its
|
| + /* 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
|
| @@ -380,7 +404,7 @@ static void clearAllSharedCacheTableLocks(Btree *p){
|
| }
|
|
|
| /*
|
| -** This function changes all write-locks held by connection p to read-locks.
|
| +** This function changes all write-locks held by Btree p into read-locks.
|
| */
|
| static void downgradeAllSharedCacheTableLocks(Btree *p){
|
| BtShared *pBt = p->pBt;
|
| @@ -401,9 +425,11 @@ static void downgradeAllSharedCacheTableLocks(Btree *p){
|
| static void releasePage(MemPage *pPage); /* Forward reference */
|
|
|
| /*
|
| -** Verify that the cursor holds a mutex on the BtShared
|
| +***** This routine is used inside of assert() only ****
|
| +**
|
| +** Verify that the cursor holds the mutex on its BtShared
|
| */
|
| -#ifndef NDEBUG
|
| +#ifdef SQLITE_DEBUG
|
| static int cursorHoldsMutex(BtCursor *p){
|
| return sqlite3_mutex_held(p->pBt->mutex);
|
| }
|
| @@ -434,7 +460,7 @@ static void invalidateAllOverflowCache(BtShared *pBt){
|
|
|
| /*
|
| ** This function is called before modifying the contents of a table
|
| -** b-tree to invalidate any incrblob cursors that are open on the
|
| +** to invalidate any incrblob cursors that are open on the
|
| ** row or one of the rows being modified.
|
| **
|
| ** If argument isClearTable is true, then the entire contents of the
|
| @@ -443,7 +469,7 @@ static void invalidateAllOverflowCache(BtShared *pBt){
|
| **
|
| ** Otherwise, if argument isClearTable is false, then the row with
|
| ** rowid iRow is being replaced or deleted. In this case invalidate
|
| -** only those incrblob cursors open on this specific row.
|
| +** only those incrblob cursors open on that specific row.
|
| */
|
| static void invalidateIncrblobCursors(
|
| Btree *pBtree, /* The database file to check */
|
| @@ -461,10 +487,11 @@ static void invalidateIncrblobCursors(
|
| }
|
|
|
| #else
|
| + /* Stub functions when INCRBLOB is omitted */
|
| #define invalidateOverflowCache(x)
|
| #define invalidateAllOverflowCache(x)
|
| #define invalidateIncrblobCursors(x,y,z)
|
| -#endif
|
| +#endif /* SQLITE_OMIT_INCRBLOB */
|
|
|
| /*
|
| ** Set bit pgno of the BtShared.pHasContent bitvec. This is called
|
| @@ -497,18 +524,15 @@ static void invalidateIncrblobCursors(
|
| ** The solution is the BtShared.pHasContent bitvec. Whenever a page is
|
| ** moved to become a free-list leaf page, the corresponding bit is
|
| ** set in the bitvec. Whenever a leaf page is extracted from the free-list,
|
| -** optimization 2 above is ommitted if the corresponding bit is already
|
| +** optimization 2 above is omitted if the corresponding bit is already
|
| ** set in BtShared.pHasContent. The contents of the bitvec are cleared
|
| ** at the end of every transaction.
|
| */
|
| static int btreeSetHasContent(BtShared *pBt, Pgno pgno){
|
| int rc = SQLITE_OK;
|
| if( !pBt->pHasContent ){
|
| - int nPage = 100;
|
| - sqlite3PagerPagecount(pBt->pPager, &nPage);
|
| - /* If sqlite3PagerPagecount() fails there is no harm because the
|
| - ** nPage variable is unchanged from its default value of 100 */
|
| - pBt->pHasContent = sqlite3BitvecCreate((u32)nPage);
|
| + assert( pgno<=pBt->nPage );
|
| + pBt->pHasContent = sqlite3BitvecCreate(pBt->nPage);
|
| if( !pBt->pHasContent ){
|
| rc = SQLITE_NOMEM;
|
| }
|
| @@ -593,8 +617,8 @@ static int saveCursorPosition(BtCursor *pCur){
|
| }
|
|
|
| /*
|
| -** Save the positions of all cursors except pExcept open on the table
|
| -** with root-page iRoot. Usually, this is called just before cursor
|
| +** Save the positions of all cursors (except pExcept) that are 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){
|
| @@ -712,11 +736,16 @@ int sqlite3BtreeCursorHasMoved(BtCursor *pCur, int *pHasMoved){
|
| ** Given a page number of a regular database page, return the page
|
| ** number for the pointer-map page that contains the entry for the
|
| ** input page number.
|
| +**
|
| +** Return 0 (not a valid page) for pgno==1 since there is
|
| +** no pointer map associated with page 1. The integrity_check logic
|
| +** requires that ptrmapPageno(*,1)!=1.
|
| */
|
| static Pgno ptrmapPageno(BtShared *pBt, Pgno pgno){
|
| int nPagesPerMapPage;
|
| Pgno iPtrMap, ret;
|
| assert( sqlite3_mutex_held(pBt->mutex) );
|
| + if( pgno<2 ) return 0;
|
| nPagesPerMapPage = (pBt->usableSize/5)+1;
|
| iPtrMap = (pgno-2)/nPagesPerMapPage;
|
| ret = (iPtrMap*nPagesPerMapPage) + 2;
|
| @@ -999,7 +1028,10 @@ static u16 cellSizePtr(MemPage *pPage, u8 *pCell){
|
| assert( nSize==debuginfo.nSize );
|
| return (u16)nSize;
|
| }
|
| -#ifndef NDEBUG
|
| +
|
| +#ifdef SQLITE_DEBUG
|
| +/* This variation on cellSizePtr() is used inside of assert() statements
|
| +** only. */
|
| static u16 cellSize(MemPage *pPage, int iCell){
|
| return cellSizePtr(pPage, findCell(pPage, iCell));
|
| }
|
| @@ -1128,6 +1160,7 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
|
| int top; /* First byte of cell content area */
|
| int gap; /* First byte of gap between cell pointers and cell content */
|
| int rc; /* Integer return code */
|
| + int usableSize; /* Usable size of the page */
|
|
|
| assert( sqlite3PagerIswriteable(pPage->pDbPage) );
|
| assert( pPage->pBt );
|
| @@ -1135,12 +1168,13 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
|
| assert( nByte>=0 ); /* Minimum cell size is 4 */
|
| assert( pPage->nFree>=nByte );
|
| assert( pPage->nOverflow==0 );
|
| - assert( nByte<pPage->pBt->usableSize-8 );
|
| + usableSize = pPage->pBt->usableSize;
|
| + assert( nByte < usableSize-8 );
|
|
|
| nFrag = data[hdr+7];
|
| assert( pPage->cellOffset == hdr + 12 - 4*pPage->leaf );
|
| gap = pPage->cellOffset + 2*pPage->nCell;
|
| - top = get2byte(&data[hdr+5]);
|
| + top = get2byteNotZero(&data[hdr+5]);
|
| if( gap>top ) return SQLITE_CORRUPT_BKPT;
|
| testcase( gap+2==top );
|
| testcase( gap+1==top );
|
| @@ -1150,7 +1184,7 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
|
| /* Always defragment highly fragmented pages */
|
| rc = defragmentPage(pPage);
|
| if( rc ) return rc;
|
| - top = get2byte(&data[hdr+5]);
|
| + top = get2byteNotZero(&data[hdr+5]);
|
| }else if( gap+2<=top ){
|
| /* Search the freelist looking for a free slot big enough to satisfy
|
| ** the request. The allocation is made from the first free slot in
|
| @@ -1158,7 +1192,11 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
|
| */
|
| int pc, addr;
|
| for(addr=hdr+1; (pc = get2byte(&data[addr]))>0; addr=pc){
|
| - int size = get2byte(&data[pc+2]); /* Size of free slot */
|
| + int size; /* Size of the free slot */
|
| + if( pc>usableSize-4 || pc<addr+4 ){
|
| + return SQLITE_CORRUPT_BKPT;
|
| + }
|
| + size = get2byte(&data[pc+2]);
|
| if( size>=nByte ){
|
| int x = size - nByte;
|
| testcase( x==4 );
|
| @@ -1168,6 +1206,8 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
|
| ** fragmented bytes within the page. */
|
| memcpy(&data[addr], &data[pc], 2);
|
| data[hdr+7] = (u8)(nFrag + x);
|
| + }else if( size+pc > usableSize ){
|
| + return SQLITE_CORRUPT_BKPT;
|
| }else{
|
| /* The slot remains on the free-list. Reduce its size to account
|
| ** for the portion used by the new allocation. */
|
| @@ -1186,7 +1226,7 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
|
| if( gap+2+nByte>top ){
|
| rc = defragmentPage(pPage);
|
| if( rc ) return rc;
|
| - top = get2byte(&data[hdr+5]);
|
| + top = get2byteNotZero(&data[hdr+5]);
|
| assert( gap+nByte<=top );
|
| }
|
|
|
| @@ -1224,11 +1264,11 @@ static int freeSpace(MemPage *pPage, int start, int size){
|
| assert( sqlite3_mutex_held(pPage->pBt->mutex) );
|
| assert( size>=0 ); /* Minimum cell size is 4 */
|
|
|
| -#ifdef SQLITE_SECURE_DELETE
|
| - /* Overwrite deleted information with zeros when the SECURE_DELETE
|
| - ** option is enabled at compile-time */
|
| - memset(&data[start], 0, size);
|
| -#endif
|
| + if( pPage->pBt->secureDelete ){
|
| + /* Overwrite deleted information with zeros when the secure_delete
|
| + ** option is enabled */
|
| + memset(&data[start], 0, size);
|
| + }
|
|
|
| /* Add the space back into the linked list of freeblocks. Note that
|
| ** even though the freeblock list was checked by btreeInitPage(),
|
| @@ -1352,10 +1392,10 @@ static int btreeInitPage(MemPage *pPage){
|
| u8 hdr; /* Offset to beginning of page header */
|
| u8 *data; /* Equal to pPage->aData */
|
| BtShared *pBt; /* The main btree structure */
|
| - u16 usableSize; /* Amount of usable space on each page */
|
| + int usableSize; /* Amount of usable space on each page */
|
| u16 cellOffset; /* Offset from start of page to first cell pointer */
|
| - u16 nFree; /* Number of unused bytes on the page */
|
| - u16 top; /* First byte of the cell content area */
|
| + int nFree; /* Number of unused bytes on the page */
|
| + int top; /* First byte of the cell content area */
|
| int iCellFirst; /* First allowable cell or freeblock offset */
|
| int iCellLast; /* Last possible cell or freeblock offset */
|
|
|
| @@ -1364,12 +1404,12 @@ static int btreeInitPage(MemPage *pPage){
|
| hdr = pPage->hdrOffset;
|
| data = pPage->aData;
|
| if( decodeFlags(pPage, data[hdr]) ) return SQLITE_CORRUPT_BKPT;
|
| - assert( pBt->pageSize>=512 && pBt->pageSize<=32768 );
|
| - pPage->maskPage = pBt->pageSize - 1;
|
| + assert( pBt->pageSize>=512 && pBt->pageSize<=65536 );
|
| + pPage->maskPage = (u16)(pBt->pageSize - 1);
|
| pPage->nOverflow = 0;
|
| usableSize = pBt->usableSize;
|
| pPage->cellOffset = cellOffset = hdr + 12 - 4*pPage->leaf;
|
| - top = get2byte(&data[hdr+5]);
|
| + top = get2byteNotZero(&data[hdr+5]);
|
| pPage->nCell = get2byte(&data[hdr+3]);
|
| if( pPage->nCell>MX_CELL(pBt) ){
|
| /* To many cells for a single page. The page must be corrupt */
|
| @@ -1460,19 +1500,21 @@ static void zeroPage(MemPage *pPage, int flags){
|
| assert( sqlite3PagerGetData(pPage->pDbPage) == data );
|
| assert( sqlite3PagerIswriteable(pPage->pDbPage) );
|
| assert( sqlite3_mutex_held(pBt->mutex) );
|
| - /*memset(&data[hdr], 0, pBt->usableSize - hdr);*/
|
| + if( pBt->secureDelete ){
|
| + memset(&data[hdr], 0, pBt->usableSize - hdr);
|
| + }
|
| data[hdr] = (char)flags;
|
| first = hdr + 8 + 4*((flags&PTF_LEAF)==0 ?1:0);
|
| memset(&data[hdr+1], 0, 4);
|
| data[hdr+7] = 0;
|
| put2byte(&data[hdr+5], pBt->usableSize);
|
| - pPage->nFree = pBt->usableSize - first;
|
| + pPage->nFree = (u16)(pBt->usableSize - first);
|
| decodeFlags(pPage, flags);
|
| pPage->hdrOffset = hdr;
|
| pPage->cellOffset = first;
|
| pPage->nOverflow = 0;
|
| - assert( pBt->pageSize>=512 && pBt->pageSize<=32768 );
|
| - pPage->maskPage = pBt->pageSize - 1;
|
| + assert( pBt->pageSize>=512 && pBt->pageSize<=65536 );
|
| + pPage->maskPage = (u16)(pBt->pageSize - 1);
|
| pPage->nCell = 0;
|
| pPage->isInit = 1;
|
| }
|
| @@ -1538,13 +1580,13 @@ static MemPage *btreePageLookup(BtShared *pBt, Pgno pgno){
|
| ** Return the size of the database file in pages. If there is any kind of
|
| ** error, return ((unsigned int)-1).
|
| */
|
| -static Pgno pagerPagecount(BtShared *pBt){
|
| - int nPage = -1;
|
| - int rc;
|
| - assert( pBt->pPage1 );
|
| - rc = sqlite3PagerPagecount(pBt->pPager, &nPage);
|
| - assert( rc==SQLITE_OK || nPage==-1 );
|
| - return (Pgno)nPage;
|
| +static Pgno btreePagecount(BtShared *pBt){
|
| + return pBt->nPage;
|
| +}
|
| +u32 sqlite3BtreeLastPage(Btree *p){
|
| + assert( sqlite3BtreeHoldsMutex(p) );
|
| + assert( ((p->pBt->nPage)&0x8000000)==0 );
|
| + return (int)btreePagecount(p->pBt);
|
| }
|
|
|
| /*
|
| @@ -1561,25 +1603,22 @@ static int getAndInitPage(
|
| MemPage **ppPage /* Write the page pointer here */
|
| ){
|
| int rc;
|
| - TESTONLY( Pgno iLastPg = pagerPagecount(pBt); )
|
| assert( sqlite3_mutex_held(pBt->mutex) );
|
|
|
| - rc = btreeGetPage(pBt, pgno, ppPage, 0);
|
| - if( rc==SQLITE_OK ){
|
| - rc = btreeInitPage(*ppPage);
|
| - if( rc!=SQLITE_OK ){
|
| - releasePage(*ppPage);
|
| + if( pgno>btreePagecount(pBt) ){
|
| + rc = SQLITE_CORRUPT_BKPT;
|
| + }else{
|
| + rc = btreeGetPage(pBt, pgno, ppPage, 0);
|
| + if( rc==SQLITE_OK ){
|
| + rc = btreeInitPage(*ppPage);
|
| + if( rc!=SQLITE_OK ){
|
| + releasePage(*ppPage);
|
| + }
|
| }
|
| }
|
|
|
| - /* If the requested page number was either 0 or greater than the page
|
| - ** number of the last page in the database, this function should return
|
| - ** SQLITE_CORRUPT or some other error (i.e. SQLITE_FULL). Check that this
|
| - ** is the case. */
|
| - assert( (pgno>0 && pgno<=iLastPg) || rc!=SQLITE_OK );
|
| testcase( pgno==0 );
|
| - testcase( pgno==iLastPg );
|
| -
|
| + assert( pgno!=0 || rc==SQLITE_CORRUPT );
|
| return rc;
|
| }
|
|
|
| @@ -1589,7 +1628,6 @@ static int getAndInitPage(
|
| */
|
| static void releasePage(MemPage *pPage){
|
| if( pPage ){
|
| - assert( pPage->nOverflow==0 || sqlite3PagerPageRefcount(pPage->pDbPage)>1 );
|
| assert( pPage->aData );
|
| assert( pPage->pBt );
|
| assert( sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage );
|
| @@ -1640,11 +1678,20 @@ static int btreeInvokeBusyHandler(void *pArg){
|
| ** Open a database file.
|
| **
|
| ** zFilename is the name of the database file. If zFilename is NULL
|
| -** a new database with a random name is created. This randomly named
|
| -** database file will be deleted when sqlite3BtreeClose() is called.
|
| +** then an ephemeral database is created. The ephemeral database might
|
| +** be exclusively in memory, or it might use a disk-based memory cache.
|
| +** Either way, the ephemeral database will be automatically deleted
|
| +** when sqlite3BtreeClose() is called.
|
| +**
|
| ** 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.
|
| +**
|
| ** 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
|
| ** SQLITE_CONSTRAINT error. We cannot allow two or more BtShared
|
| @@ -1666,6 +1713,9 @@ int sqlite3BtreeOpen(
|
| u8 nReserve; /* Byte of unused space on each page */
|
| unsigned char zDbHeader[100]; /* Database header content */
|
|
|
| + /* True if opening an ephemeral, temporary database */
|
| + const int isTempDb = zFilename==0 || zFilename[0]==0;
|
| +
|
| /* Set the variable isMemdb to true for an in-memory database, or
|
| ** false for a file-based database. This symbol is only required if
|
| ** either of the shared-data or autovacuum features are compiled
|
| @@ -1675,13 +1725,30 @@ int sqlite3BtreeOpen(
|
| #ifdef SQLITE_OMIT_MEMORYDB
|
| const int isMemdb = 0;
|
| #else
|
| - const int isMemdb = zFilename && !strcmp(zFilename, ":memory:");
|
| + const int isMemdb = (zFilename && strcmp(zFilename, ":memory:")==0)
|
| + || (isTempDb && sqlite3TempInMemory(db));
|
| #endif
|
| #endif
|
|
|
| assert( db!=0 );
|
| assert( sqlite3_mutex_held(db->mutex) );
|
| + assert( (flags&0xff)==flags ); /* flags fit in 8 bits */
|
|
|
| + /* Only a BTREE_SINGLE database can be BTREE_UNORDERED */
|
| + assert( (flags & BTREE_UNORDERED)==0 || (flags & BTREE_SINGLE)!=0 );
|
| +
|
| + /* 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;
|
| + }
|
| + if( (vfsFlags & SQLITE_OPEN_MAIN_DB)!=0 && (isMemdb || isTempDb) ){
|
| + vfsFlags = (vfsFlags & ~SQLITE_OPEN_MAIN_DB) | SQLITE_OPEN_TEMP_DB;
|
| + }
|
| pVfs = db->pVfs;
|
| p = sqlite3MallocZero(sizeof(Btree));
|
| if( !p ){
|
| @@ -1699,7 +1766,7 @@ int sqlite3BtreeOpen(
|
| ** If this Btree is a candidate for shared cache, try to find an
|
| ** existing BtShared object that we can share with
|
| */
|
| - if( isMemdb==0 && zFilename && zFilename[0] ){
|
| + if( isMemdb==0 && isTempDb==0 ){
|
| if( vfsFlags & SQLITE_OPEN_SHAREDCACHE ){
|
| int nFullPathname = pVfs->mxPathname+1;
|
| char *zFullPathname = sqlite3Malloc(nFullPathname);
|
| @@ -1774,6 +1841,7 @@ int sqlite3BtreeOpen(
|
| if( rc!=SQLITE_OK ){
|
| goto btree_open_out;
|
| }
|
| + pBt->openFlags = (u8)flags;
|
| pBt->db = db;
|
| sqlite3PagerSetBusyhandler(pBt->pPager, btreeInvokeBusyHandler, pBt);
|
| p->pBt = pBt;
|
| @@ -1781,7 +1849,10 @@ int sqlite3BtreeOpen(
|
| pBt->pCursor = 0;
|
| pBt->pPage1 = 0;
|
| pBt->readOnly = sqlite3PagerIsreadonly(pBt->pPager);
|
| - pBt->pageSize = get2byte(&zDbHeader[16]);
|
| +#ifdef SQLITE_SECURE_DELETE
|
| + pBt->secureDelete = 1;
|
| +#endif
|
| + pBt->pageSize = (zDbHeader[16]<<8) | (zDbHeader[17]<<16);
|
| if( pBt->pageSize<512 || pBt->pageSize>SQLITE_MAX_PAGE_SIZE
|
| || ((pBt->pageSize-1)&pBt->pageSize)!=0 ){
|
| pBt->pageSize = 0;
|
| @@ -1875,6 +1946,14 @@ btree_open_out:
|
| sqlite3_free(pBt);
|
| sqlite3_free(p);
|
| *ppBtree = 0;
|
| + }else{
|
| + /* 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.
|
| + */
|
| + if( sqlite3BtreeSchema(p, 0, 0)==0 ){
|
| + sqlite3PagerSetCachesize(p->pBt->pPager, SQLITE_DEFAULT_CACHE_SIZE);
|
| + }
|
| }
|
| if( mutexOpen ){
|
| assert( sqlite3_mutex_held(mutexOpen) );
|
| @@ -1983,7 +2062,7 @@ int sqlite3BtreeClose(Btree *p){
|
| if( pBt->xFreeSchema && pBt->pSchema ){
|
| pBt->xFreeSchema(pBt->pSchema);
|
| }
|
| - sqlite3_free(pBt->pSchema);
|
| + sqlite3DbFree(0, pBt->pSchema);
|
| freeTempSpace(pBt);
|
| sqlite3_free(pBt);
|
| }
|
| @@ -2095,7 +2174,7 @@ int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, int iFix){
|
| ((pageSize-1)&pageSize)==0 ){
|
| assert( (pageSize & 7)==0 );
|
| assert( !pBt->pPage1 && !pBt->pCursor );
|
| - pBt->pageSize = (u16)pageSize;
|
| + pBt->pageSize = (u32)pageSize;
|
| freeTempSpace(pBt);
|
| }
|
| rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize, nReserve);
|
| @@ -2137,6 +2216,23 @@ int sqlite3BtreeMaxPageCount(Btree *p, int mxPage){
|
| sqlite3BtreeLeave(p);
|
| return n;
|
| }
|
| +
|
| +/*
|
| +** 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
|
| +** setting after the change.
|
| +*/
|
| +int sqlite3BtreeSecureDelete(Btree *p, int newFlag){
|
| + int b;
|
| + if( p==0 ) return 0;
|
| + sqlite3BtreeEnter(p);
|
| + if( newFlag>=0 ){
|
| + p->pBt->secureDelete = (newFlag!=0) ? 1 : 0;
|
| + }
|
| + b = p->pBt->secureDelete;
|
| + sqlite3BtreeLeave(p);
|
| + return b;
|
| +}
|
| #endif /* !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM) */
|
|
|
| /*
|
| @@ -2196,9 +2292,11 @@ int sqlite3BtreeGetAutoVacuum(Btree *p){
|
| ** is returned if we run out of memory.
|
| */
|
| static int lockBtree(BtShared *pBt){
|
| - int rc;
|
| - MemPage *pPage1;
|
| - int nPage;
|
| + int rc; /* Result code from subfunctions */
|
| + MemPage *pPage1; /* Page 1 of the database file */
|
| + int nPage; /* Number of pages in the database */
|
| + int nPageFile = 0; /* Number of pages in the database file */
|
| + int nPageHeader; /* Number of pages in the database according to hdr */
|
|
|
| assert( sqlite3_mutex_held(pBt->mutex) );
|
| assert( pBt->pPage1==0 );
|
| @@ -2210,23 +2308,56 @@ static int lockBtree(BtShared *pBt){
|
| /* Do some checking to help insure the file we opened really is
|
| ** a valid database file.
|
| */
|
| - rc = sqlite3PagerPagecount(pBt->pPager, &nPage);
|
| - if( rc!=SQLITE_OK ){
|
| - goto page1_init_failed;
|
| - }else if( nPage>0 ){
|
| - int pageSize;
|
| - int usableSize;
|
| + nPage = nPageHeader = get4byte(28+(u8*)pPage1->aData);
|
| + sqlite3PagerPagecount(pBt->pPager, &nPageFile);
|
| + if( nPage==0 || memcmp(24+(u8*)pPage1->aData, 92+(u8*)pPage1->aData,4)!=0 ){
|
| + nPage = nPageFile;
|
| + }
|
| + if( nPage>0 ){
|
| + u32 pageSize;
|
| + u32 usableSize;
|
| u8 *page1 = pPage1->aData;
|
| rc = SQLITE_NOTADB;
|
| if( memcmp(page1, zMagicHeader, 16)!=0 ){
|
| goto page1_init_failed;
|
| }
|
| +
|
| +#ifdef SQLITE_OMIT_WAL
|
| if( page1[18]>1 ){
|
| pBt->readOnly = 1;
|
| }
|
| if( page1[19]>1 ){
|
| goto page1_init_failed;
|
| }
|
| +#else
|
| + if( page1[18]>2 ){
|
| + pBt->readOnly = 1;
|
| + }
|
| + if( page1[19]>2 ){
|
| + goto page1_init_failed;
|
| + }
|
| +
|
| + /* If the write version is set to 2, this database should be accessed
|
| + ** in WAL mode. If the log is not already open, open it now. Then
|
| + ** return SQLITE_OK and return without populating BtShared.pPage1.
|
| + ** The caller detects this and calls this function again. This is
|
| + assert( sizeof(zMagicHeader)==sizeof(zPoisonHeader) );
|
| + ** required as the version of page 1 currently in the page1 buffer
|
| + ** may not be the latest version - there may be a newer one in the log
|
| + ** file.
|
| + */
|
| + if( page1[19]==2 && pBt->doNotUseWAL==0 ){
|
| + int isOpen = 0;
|
| + rc = sqlite3PagerOpenWal(pBt->pPager, &isOpen);
|
| + if( rc!=SQLITE_OK ){
|
| + goto page1_init_failed;
|
| + }else if( isOpen==0 ){
|
| + releasePage(pPage1);
|
| + return SQLITE_OK;
|
| + }
|
| + rc = SQLITE_NOTADB;
|
| + }
|
| +#endif
|
|
|
| /* The maximum embedded fraction must be exactly 25%. And the minimum
|
| ** embedded fraction must be 12.5% for both leaf-data and non-leaf-data.
|
| @@ -2236,15 +2367,16 @@ static int lockBtree(BtShared *pBt){
|
| if( memcmp(&page1[21], "\100\040\040",3)!=0 ){
|
| goto page1_init_failed;
|
| }
|
| - pageSize = get2byte(&page1[16]);
|
| - if( ((pageSize-1)&pageSize)!=0 || pageSize<512 ||
|
| - (SQLITE_MAX_PAGE_SIZE<32768 && pageSize>SQLITE_MAX_PAGE_SIZE)
|
| + pageSize = (page1[16]<<8) | (page1[17]<<16);
|
| + if( ((pageSize-1)&pageSize)!=0
|
| + || pageSize>SQLITE_MAX_PAGE_SIZE
|
| + || pageSize<=256
|
| ){
|
| goto page1_init_failed;
|
| }
|
| assert( (pageSize & 7)==0 );
|
| usableSize = pageSize - page1[20];
|
| - if( pageSize!=pBt->pageSize ){
|
| + if( (u32)pageSize!=pBt->pageSize ){
|
| /* After reading the first page of the database assuming a page size
|
| ** of BtShared.pageSize, we have discovered that the page-size is
|
| ** actually pageSize. Unlock the database, leave pBt->pPage1 at
|
| @@ -2252,18 +2384,22 @@ static int lockBtree(BtShared *pBt){
|
| ** again with the correct page-size.
|
| */
|
| releasePage(pPage1);
|
| - pBt->usableSize = (u16)usableSize;
|
| - pBt->pageSize = (u16)pageSize;
|
| + pBt->usableSize = usableSize;
|
| + pBt->pageSize = pageSize;
|
| freeTempSpace(pBt);
|
| rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize,
|
| pageSize-usableSize);
|
| return rc;
|
| }
|
| + if( nPageHeader>nPageFile ){
|
| + rc = SQLITE_CORRUPT_BKPT;
|
| + goto page1_init_failed;
|
| + }
|
| if( usableSize<480 ){
|
| goto page1_init_failed;
|
| }
|
| - pBt->pageSize = (u16)pageSize;
|
| - pBt->usableSize = (u16)usableSize;
|
| + pBt->pageSize = pageSize;
|
| + pBt->usableSize = usableSize;
|
| #ifndef SQLITE_OMIT_AUTOVACUUM
|
| pBt->autoVacuum = (get4byte(&page1[36 + 4*4])?1:0);
|
| pBt->incrVacuum = (get4byte(&page1[36 + 7*4])?1:0);
|
| @@ -2279,16 +2415,17 @@ static int lockBtree(BtShared *pBt){
|
| ** 9-byte nKey value
|
| ** 4-byte nData value
|
| ** 4-byte overflow page pointer
|
| - ** So a cell consists of a 2-byte poiner, a header which is as much as
|
| + ** So a cell consists of a 2-byte pointer, a header which is as much as
|
| ** 17 bytes long, 0 to N bytes of payload, and an optional 4 byte overflow
|
| ** page pointer.
|
| */
|
| - pBt->maxLocal = (pBt->usableSize-12)*64/255 - 23;
|
| - pBt->minLocal = (pBt->usableSize-12)*32/255 - 23;
|
| - pBt->maxLeaf = pBt->usableSize - 35;
|
| - pBt->minLeaf = (pBt->usableSize-12)*32/255 - 23;
|
| + pBt->maxLocal = (u16)((pBt->usableSize-12)*64/255 - 23);
|
| + pBt->minLocal = (u16)((pBt->usableSize-12)*32/255 - 23);
|
| + pBt->maxLeaf = (u16)(pBt->usableSize - 35);
|
| + pBt->minLeaf = (u16)((pBt->usableSize-12)*32/255 - 23);
|
| assert( pBt->maxLeaf + 23 <= MX_CELL_SIZE(pBt) );
|
| pBt->pPage1 = pPage1;
|
| + pBt->nPage = nPage;
|
| return SQLITE_OK;
|
|
|
| page1_init_failed:
|
| @@ -2326,15 +2463,10 @@ static int newDatabase(BtShared *pBt){
|
| MemPage *pP1;
|
| unsigned char *data;
|
| int rc;
|
| - int nPage;
|
|
|
| assert( sqlite3_mutex_held(pBt->mutex) );
|
| - /* The database size has already been measured and cached, so failure
|
| - ** is impossible here. If the original size measurement failed, then
|
| - ** processing aborts before entering this routine. */
|
| - rc = sqlite3PagerPagecount(pBt->pPager, &nPage);
|
| - if( NEVER(rc!=SQLITE_OK) || nPage>0 ){
|
| - return rc;
|
| + if( pBt->nPage>0 ){
|
| + return SQLITE_OK;
|
| }
|
| pP1 = pBt->pPage1;
|
| assert( pP1!=0 );
|
| @@ -2343,8 +2475,8 @@ static int newDatabase(BtShared *pBt){
|
| if( rc ) return rc;
|
| memcpy(data, zMagicHeader, sizeof(zMagicHeader));
|
| assert( sizeof(zMagicHeader)==16 );
|
| - assert( sizeof(zMagicHeader)==sizeof(zPoisonHeader) );
|
| - put2byte(&data[16], pBt->pageSize);
|
| + data[16] = (u8)((pBt->pageSize>>8)&0xff);
|
| + data[17] = (u8)((pBt->pageSize>>16)&0xff);
|
| data[18] = 1;
|
| data[19] = 1;
|
| assert( pBt->usableSize<=pBt->pageSize && pBt->usableSize+255>=pBt->pageSize);
|
| @@ -2361,6 +2493,8 @@ static int newDatabase(BtShared *pBt){
|
| put4byte(&data[36 + 4*4], pBt->autoVacuum);
|
| put4byte(&data[36 + 7*4], pBt->incrVacuum);
|
| #endif
|
| + pBt->nPage = 1;
|
| + data[31] = 1;
|
| return SQLITE_OK;
|
| }
|
|
|
| @@ -2450,6 +2584,7 @@ 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);
|
| do {
|
| /* Call lockBtree() until either pBt->pPage1 is populated or
|
| ** lockBtree() returns something other than SQLITE_OK. lockBtree()
|
| @@ -2474,7 +2609,7 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
|
| if( rc!=SQLITE_OK ){
|
| unlockBtreeIfUnused(pBt);
|
| }
|
| - }while( rc==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE &&
|
| + }while( (rc&0xFF)==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE &&
|
| btreeInvokeBusyHandler(pBt) );
|
|
|
| if( rc==SQLITE_OK ){
|
| @@ -2493,13 +2628,27 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
|
| if( p->inTrans>pBt->inTransaction ){
|
| pBt->inTransaction = p->inTrans;
|
| }
|
| -#ifndef SQLITE_OMIT_SHARED_CACHE
|
| if( wrflag ){
|
| + MemPage *pPage1 = pBt->pPage1;
|
| +#ifndef SQLITE_OMIT_SHARED_CACHE
|
| assert( !pBt->pWriter );
|
| pBt->pWriter = p;
|
| pBt->isExclusive = (u8)(wrflag>1);
|
| - }
|
| #endif
|
| +
|
| + /* If the db-size header field is incorrect (as it may be if an old
|
| + ** client has been writing the database file), update it now. Doing
|
| + ** this sooner rather than later means the database size can safely
|
| + ** re-read the database size from page 1 if a savepoint or transaction
|
| + ** rollback occurs within the transaction.
|
| + */
|
| + if( pBt->nPage!=get4byte(&pPage1->aData[28]) ){
|
| + rc = sqlite3PagerWrite(pPage1->pDbPage);
|
| + if( rc==SQLITE_OK ){
|
| + put4byte(&pPage1->aData[28], pBt->nPage);
|
| + }
|
| + }
|
| + }
|
| }
|
|
|
|
|
| @@ -2729,12 +2878,12 @@ static int allocateBtreePage(BtShared *, MemPage **, Pgno *, Pgno, u8);
|
| */
|
| static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){
|
| Pgno nFreeList; /* Number of pages still on the free-list */
|
| + int rc;
|
|
|
| assert( sqlite3_mutex_held(pBt->mutex) );
|
| assert( iLastPg>nFin );
|
|
|
| if( !PTRMAP_ISPAGE(pBt, iLastPg) && iLastPg!=PENDING_BYTE_PAGE(pBt) ){
|
| - int rc;
|
| u8 eType;
|
| Pgno iPtrPage;
|
|
|
| @@ -2810,7 +2959,7 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){
|
| while( iLastPg==PENDING_BYTE_PAGE(pBt)||PTRMAP_ISPAGE(pBt, iLastPg) ){
|
| if( PTRMAP_ISPAGE(pBt, iLastPg) ){
|
| MemPage *pPg;
|
| - int rc = btreeGetPage(pBt, iLastPg, &pPg, 0);
|
| + rc = btreeGetPage(pBt, iLastPg, &pPg, 0);
|
| if( rc!=SQLITE_OK ){
|
| return rc;
|
| }
|
| @@ -2823,6 +2972,7 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){
|
| iLastPg--;
|
| }
|
| sqlite3PagerTruncateImage(pBt->pPager, iLastPg);
|
| + pBt->nPage = iLastPg;
|
| }
|
| return SQLITE_OK;
|
| }
|
| @@ -2845,7 +2995,11 @@ int sqlite3BtreeIncrVacuum(Btree *p){
|
| rc = SQLITE_DONE;
|
| }else{
|
| invalidateAllOverflowCache(pBt);
|
| - rc = incrVacuumStep(pBt, 0, pagerPagecount(pBt));
|
| + rc = incrVacuumStep(pBt, 0, btreePagecount(pBt));
|
| + if( rc==SQLITE_OK ){
|
| + rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
|
| + put4byte(&pBt->pPage1->aData[28], pBt->nPage);
|
| + }
|
| }
|
| sqlite3BtreeLeave(p);
|
| return rc;
|
| @@ -2876,7 +3030,7 @@ static int autoVacuumCommit(BtShared *pBt){
|
| int nEntry; /* Number of entries on one ptrmap page */
|
| Pgno nOrig; /* Database size before freeing */
|
|
|
| - nOrig = pagerPagecount(pBt);
|
| + nOrig = btreePagecount(pBt);
|
| if( PTRMAP_ISPAGE(pBt, nOrig) || nOrig==PENDING_BYTE_PAGE(pBt) ){
|
| /* It is not possible to create a database for which the final page
|
| ** is either a pointer-map page or the pending-byte page. If one
|
| @@ -2901,11 +3055,12 @@ static int autoVacuumCommit(BtShared *pBt){
|
| rc = incrVacuumStep(pBt, nFin, iFree);
|
| }
|
| if( (rc==SQLITE_DONE || rc==SQLITE_OK) && nFree>0 ){
|
| - rc = SQLITE_OK;
|
| rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
|
| put4byte(&pBt->pPage1->aData[32], 0);
|
| put4byte(&pBt->pPage1->aData[36], 0);
|
| + put4byte(&pBt->pPage1->aData[28], nFin);
|
| sqlite3PagerTruncateImage(pBt->pPager, nFin);
|
| + pBt->nPage = nFin;
|
| }
|
| if( rc!=SQLITE_OK ){
|
| sqlite3PagerRollback(pPager);
|
| @@ -2972,18 +3127,13 @@ int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zMaster){
|
| */
|
| static void btreeEndTransaction(Btree *p){
|
| BtShared *pBt = p->pBt;
|
| - BtCursor *pCsr;
|
| assert( sqlite3BtreeHoldsMutex(p) );
|
|
|
| - /* Search for a cursor held open by this b-tree connection. If one exists,
|
| - ** then the transaction will be downgraded to a read-only transaction
|
| - ** instead of actually concluded. A subsequent call to CommitPhaseTwo()
|
| - ** or Rollback() will finish the transaction and unlock the database. */
|
| - for(pCsr=pBt->pCursor; pCsr && pCsr->pBtree!=p; pCsr=pCsr->pNext);
|
| - assert( pCsr==0 || p->inTrans>TRANS_NONE );
|
| -
|
| btreeClearHasContent(pBt);
|
| - if( pCsr ){
|
| + if( p->inTrans>TRANS_NONE && p->db->activeVdbeCnt>1 ){
|
| + /* If there are other active statements that belong to this database
|
| + ** handle, downgrade to a read-only transaction. The other statements
|
| + ** may still be reading from the database. */
|
| downgradeAllSharedCacheTableLocks(p);
|
| p->inTrans = TRANS_READ;
|
| }else{
|
| @@ -3160,6 +3310,11 @@ int sqlite3BtreeRollback(Btree *p){
|
| ** call btreeGetPage() on page 1 again to make
|
| ** sure pPage1->aData is set correctly. */
|
| if( btreeGetPage(pBt, 1, &pPage1, 0)==SQLITE_OK ){
|
| + int nPage = get4byte(28+(u8*)pPage1->aData);
|
| + testcase( nPage==0 );
|
| + if( nPage==0 ) sqlite3PagerPagecount(pBt->pPager, &nPage);
|
| + testcase( pBt->nPage!=nPage );
|
| + pBt->nPage = nPage;
|
| releasePage(pPage1);
|
| }
|
| assert( countWriteCursors(pBt)==0 );
|
| @@ -3197,17 +3352,13 @@ int sqlite3BtreeBeginStmt(Btree *p, int iStatement){
|
| assert( pBt->readOnly==0 );
|
| assert( iStatement>0 );
|
| assert( iStatement>p->db->nSavepoint );
|
| - if( NEVER(p->inTrans!=TRANS_WRITE || pBt->readOnly) ){
|
| - rc = SQLITE_INTERNAL;
|
| - }else{
|
| - assert( pBt->inTransaction==TRANS_WRITE );
|
| - /* At the pager level, a statement transaction is a savepoint with
|
| - ** an index greater than all savepoints created explicitly using
|
| - ** SQL statements. It is illegal to open, release or rollback any
|
| - ** such savepoints while the statement transaction savepoint is active.
|
| - */
|
| - rc = sqlite3PagerOpenSavepoint(pBt->pPager, iStatement);
|
| - }
|
| + assert( pBt->inTransaction==TRANS_WRITE );
|
| + /* At the pager level, a statement transaction is a savepoint with
|
| + ** an index greater than all savepoints created explicitly using
|
| + ** SQL statements. It is illegal to open, release or rollback any
|
| + ** such savepoints while the statement transaction savepoint is active.
|
| + */
|
| + rc = sqlite3PagerOpenSavepoint(pBt->pPager, iStatement);
|
| sqlite3BtreeLeave(p);
|
| return rc;
|
| }
|
| @@ -3233,7 +3384,14 @@ 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;
|
| rc = newDatabase(pBt);
|
| + pBt->nPage = get4byte(28 + pBt->pPage1->aData);
|
| +
|
| + /* The database size was written into the offset 28 of the header
|
| + ** when the transaction started, so we know that the value at offset
|
| + ** 28 is nonzero. */
|
| + assert( pBt->nPage>0 );
|
| }
|
| sqlite3BtreeLeave(p);
|
| }
|
| @@ -3269,8 +3427,8 @@ int sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){
|
| ** root page of a b-tree. If it is not, then the cursor acquired
|
| ** will not work correctly.
|
| **
|
| -** It is assumed that the sqlite3BtreeCursorSize() bytes of memory
|
| -** pointed to by pCur have been zeroed by the caller.
|
| +** It is assumed that the sqlite3BtreeCursorZero() has been called
|
| +** on pCur to initialize the memory space prior to invoking this routine.
|
| */
|
| static int btreeCursor(
|
| Btree *p, /* The btree */
|
| @@ -3299,7 +3457,7 @@ static int btreeCursor(
|
| if( NEVER(wrFlag && pBt->readOnly) ){
|
| return SQLITE_READONLY;
|
| }
|
| - if( iTable==1 && pagerPagecount(pBt)==0 ){
|
| + if( iTable==1 && btreePagecount(pBt)==0 ){
|
| return SQLITE_EMPTY;
|
| }
|
|
|
| @@ -3343,7 +3501,19 @@ int sqlite3BtreeCursor(
|
| ** this routine.
|
| */
|
| int sqlite3BtreeCursorSize(void){
|
| - return sizeof(BtCursor);
|
| + return ROUND8(sizeof(BtCursor));
|
| +}
|
| +
|
| +/*
|
| +** Initialize memory that will be converted into a BtCursor object.
|
| +**
|
| +** The simple approach here would be to memset() the entire object
|
| +** to zero. But it turns out that the apPage[] and aiIdx[] arrays
|
| +** do not need to be zeroed and they are large, so we can save a lot
|
| +** of run-time by skipping the initialization of those elements.
|
| +*/
|
| +void sqlite3BtreeCursorZero(BtCursor *p){
|
| + memset(p, 0, offsetof(BtCursor, iPage));
|
| }
|
|
|
| /*
|
| @@ -3558,7 +3728,7 @@ static int getOverflowPage(
|
| iGuess++;
|
| }
|
|
|
| - if( iGuess<=pagerPagecount(pBt) ){
|
| + if( iGuess<=btreePagecount(pBt) ){
|
| rc = ptrmapGet(pBt, iGuess, &eType, &pgno);
|
| if( rc==SQLITE_OK && eType==PTRMAP_OVERFLOW2 && pgno==ovfl ){
|
| next = iGuess;
|
| @@ -4153,7 +4323,6 @@ int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
|
| if( pCur->eState==CURSOR_INVALID ){
|
| assert( pCur->apPage[pCur->iPage]->nCell==0 );
|
| *pRes = 1;
|
| - rc = SQLITE_OK;
|
| }else{
|
| assert( pCur->apPage[pCur->iPage]->nCell>0 );
|
| *pRes = 0;
|
| @@ -4318,9 +4487,9 @@ int sqlite3BtreeMovetoUnpacked(
|
| pCur->validNKey = 1;
|
| pCur->info.nKey = nCellKey;
|
| }else{
|
| - /* The maximum supported page-size is 32768 bytes. This means that
|
| + /* The maximum supported page-size is 65536 bytes. This means that
|
| ** the maximum number of record bytes stored on an index B-Tree
|
| - ** page is at most 8198 bytes, which may be stored as a 2-byte
|
| + ** page is less than 16384 bytes and may be stored as a 2-byte
|
| ** varint. This information is used to attempt to avoid parsing
|
| ** the entire cell by checking for the cases where the record is
|
| ** stored entirely within the b-tree page by inspecting the first
|
| @@ -4590,7 +4759,7 @@ static int allocateBtreePage(
|
|
|
| assert( sqlite3_mutex_held(pBt->mutex) );
|
| pPage1 = pBt->pPage1;
|
| - mxPage = pagerPagecount(pBt);
|
| + mxPage = btreePagecount(pBt);
|
| n = get4byte(&pPage1->aData[36]);
|
| testcase( n==mxPage-1 );
|
| if( n>=mxPage ){
|
| @@ -4683,6 +4852,10 @@ static int allocateBtreePage(
|
| if( !pPrevTrunk ){
|
| memcpy(&pPage1->aData[32], &pTrunk->aData[0], 4);
|
| }else{
|
| + rc = sqlite3PagerWrite(pPrevTrunk->pDbPage);
|
| + if( rc!=SQLITE_OK ){
|
| + goto end_allocate_page;
|
| + }
|
| memcpy(&pPrevTrunk->aData[0], &pTrunk->aData[0], 4);
|
| }
|
| }else{
|
| @@ -4786,35 +4959,35 @@ static int allocateBtreePage(
|
| }else{
|
| /* There are no pages on the freelist, so create a new page at the
|
| ** end of the file */
|
| - int nPage = pagerPagecount(pBt);
|
| - *pPgno = nPage + 1;
|
| -
|
| - if( *pPgno==PENDING_BYTE_PAGE(pBt) ){
|
| - (*pPgno)++;
|
| - }
|
| + rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
|
| + if( rc ) return rc;
|
| + pBt->nPage++;
|
| + if( pBt->nPage==PENDING_BYTE_PAGE(pBt) ) pBt->nPage++;
|
|
|
| #ifndef SQLITE_OMIT_AUTOVACUUM
|
| - if( pBt->autoVacuum && PTRMAP_ISPAGE(pBt, *pPgno) ){
|
| + if( pBt->autoVacuum && PTRMAP_ISPAGE(pBt, pBt->nPage) ){
|
| /* If *pPgno refers to a pointer-map page, allocate two new pages
|
| ** at the end of the file instead of one. The first allocated page
|
| ** becomes a new pointer-map page, the second is used by the caller.
|
| */
|
| MemPage *pPg = 0;
|
| - TRACE(("ALLOCATE: %d from end of file (pointer-map page)\n", *pPgno));
|
| - assert( *pPgno!=PENDING_BYTE_PAGE(pBt) );
|
| - rc = btreeGetPage(pBt, *pPgno, &pPg, 0);
|
| + TRACE(("ALLOCATE: %d from end of file (pointer-map page)\n", pBt->nPage));
|
| + assert( pBt->nPage!=PENDING_BYTE_PAGE(pBt) );
|
| + rc = btreeGetPage(pBt, pBt->nPage, &pPg, 1);
|
| if( rc==SQLITE_OK ){
|
| rc = sqlite3PagerWrite(pPg->pDbPage);
|
| releasePage(pPg);
|
| }
|
| if( rc ) return rc;
|
| - (*pPgno)++;
|
| - if( *pPgno==PENDING_BYTE_PAGE(pBt) ){ (*pPgno)++; }
|
| + pBt->nPage++;
|
| + if( pBt->nPage==PENDING_BYTE_PAGE(pBt) ){ pBt->nPage++; }
|
| }
|
| #endif
|
| + put4byte(28 + (u8*)pBt->pPage1->aData, pBt->nPage);
|
| + *pPgno = pBt->nPage;
|
|
|
| assert( *pPgno!=PENDING_BYTE_PAGE(pBt) );
|
| - rc = btreeGetPage(pBt, *pPgno, ppPage, 0);
|
| + rc = btreeGetPage(pBt, *pPgno, ppPage, 1);
|
| if( rc ) return rc;
|
| rc = sqlite3PagerWrite((*ppPage)->pDbPage);
|
| if( rc!=SQLITE_OK ){
|
| @@ -4877,17 +5050,17 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){
|
| nFree = get4byte(&pPage1->aData[36]);
|
| put4byte(&pPage1->aData[36], nFree+1);
|
|
|
| -#ifdef SQLITE_SECURE_DELETE
|
| - /* If the SQLITE_SECURE_DELETE compile-time option is enabled, then
|
| - ** always fully overwrite deleted information with zeros.
|
| - */
|
| - if( (!pPage && (rc = btreeGetPage(pBt, iPage, &pPage, 0)))
|
| - || (rc = sqlite3PagerWrite(pPage->pDbPage))
|
| - ){
|
| - goto freepage_out;
|
| + if( pBt->secureDelete ){
|
| + /* If the secure_delete option is enabled, then
|
| + ** always fully overwrite deleted information with zeros.
|
| + */
|
| + if( (!pPage && ((rc = btreeGetPage(pBt, iPage, &pPage, 0))!=0) )
|
| + || ((rc = sqlite3PagerWrite(pPage->pDbPage))!=0)
|
| + ){
|
| + goto freepage_out;
|
| + }
|
| + memset(pPage->aData, 0, pPage->pBt->pageSize);
|
| }
|
| - memset(pPage->aData, 0, pPage->pBt->pageSize);
|
| -#endif
|
|
|
| /* If the database supports auto-vacuum, write an entry in the pointer-map
|
| ** to indicate that the page is free.
|
| @@ -4938,11 +5111,9 @@ 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);
|
| -#ifndef SQLITE_SECURE_DELETE
|
| - if( pPage ){
|
| + if( pPage && !pBt->secureDelete ){
|
| sqlite3PagerDontWrite(pPage->pDbPage);
|
| }
|
| -#endif
|
| rc = btreeSetHasContent(pBt, iPage);
|
| }
|
| TRACE(("FREE-PAGE: %d leaf on trunk page %d\n",pPage->pgno,pTrunk->pgno));
|
| @@ -4991,7 +5162,7 @@ static int clearCell(MemPage *pPage, unsigned char *pCell){
|
| Pgno ovflPgno;
|
| int rc;
|
| int nOvfl;
|
| - u16 ovflPageSize;
|
| + u32 ovflPageSize;
|
|
|
| assert( sqlite3_mutex_held(pPage->pBt->mutex) );
|
| btreeParseCellPtr(pPage, pCell, &info);
|
| @@ -5006,7 +5177,7 @@ static int clearCell(MemPage *pPage, unsigned char *pCell){
|
| while( nOvfl-- ){
|
| Pgno iNext = 0;
|
| MemPage *pOvfl = 0;
|
| - if( ovflPgno<2 || ovflPgno>pagerPagecount(pBt) ){
|
| + if( ovflPgno<2 || ovflPgno>btreePagecount(pBt) ){
|
| /* 0 is not a legal page number and page 1 cannot be an
|
| ** overflow page. Therefore if ovflPgno<2 or past the end of the
|
| ** file the database must be corrupt. */
|
| @@ -5016,7 +5187,25 @@ static int clearCell(MemPage *pPage, unsigned char *pCell){
|
| rc = getOverflowPage(pBt, ovflPgno, &pOvfl, &iNext);
|
| if( rc ) return rc;
|
| }
|
| - rc = freePage2(pBt, pOvfl, ovflPgno);
|
| +
|
| + if( ( pOvfl || ((pOvfl = btreePageLookup(pBt, ovflPgno))!=0) )
|
| + && sqlite3PagerPageRefcount(pOvfl->pDbPage)!=1
|
| + ){
|
| + /* There is no reason any cursor should have an outstanding reference
|
| + ** to an overflow page belonging to a cell that is being deleted/updated.
|
| + ** So if there exists more than one reference to this page, then it
|
| + ** must not really be an overflow page and the database must be corrupt.
|
| + ** It is helpful to detect this before calling freePage2(), as
|
| + ** freePage2() may zero the page contents if secure-delete mode is
|
| + ** enabled. If this 'overflow' page happens to be a page that the
|
| + ** caller is iterating through or using in some other way, this
|
| + ** can be problematic.
|
| + */
|
| + rc = SQLITE_CORRUPT_BKPT;
|
| + }else{
|
| + rc = freePage2(pBt, pOvfl, ovflPgno);
|
| + }
|
| +
|
| if( pOvfl ){
|
| sqlite3PagerUnref(pOvfl->pDbPage);
|
| }
|
| @@ -5198,7 +5387,7 @@ static int fillInCell(
|
| */
|
| static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
|
| int i; /* Loop counter */
|
| - int pc; /* Offset to cell content of cell being deleted */
|
| + u32 pc; /* Offset to cell content of cell being deleted */
|
| u8 *data; /* pPage->aData */
|
| u8 *ptr; /* Used to move bytes around within data[] */
|
| int rc; /* The return code */
|
| @@ -5216,7 +5405,7 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
|
| hdr = pPage->hdrOffset;
|
| testcase( pc==get2byte(&data[hdr+5]) );
|
| testcase( pc+sz==pPage->pBt->usableSize );
|
| - if( pc < get2byte(&data[hdr+5]) || pc+sz > pPage->pBt->usableSize ){
|
| + if( pc < (u32)get2byte(&data[hdr+5]) || pc+sz > pPage->pBt->usableSize ){
|
| *pRC = SQLITE_CORRUPT_BKPT;
|
| return;
|
| }
|
| @@ -5260,7 +5449,7 @@ static void insertCell(
|
| Pgno iChild, /* If non-zero, replace first 4 bytes with this value */
|
| int *pRC /* Read and write return code from here */
|
| ){
|
| - int idx; /* Where to write new cell content in data[] */
|
| + int idx = 0; /* Where to write new cell content in data[] */
|
| int j; /* Loop counter */
|
| int end; /* First byte past the last cell pointer in data[] */
|
| int ins; /* Index in data[] where new cell pointer is inserted */
|
| @@ -5273,10 +5462,15 @@ static void insertCell(
|
| if( *pRC ) return;
|
|
|
| assert( i>=0 && i<=pPage->nCell+pPage->nOverflow );
|
| - assert( pPage->nCell<=MX_CELL(pPage->pBt) && MX_CELL(pPage->pBt)<=5460 );
|
| + assert( pPage->nCell<=MX_CELL(pPage->pBt) && MX_CELL(pPage->pBt)<=10921 );
|
| assert( pPage->nOverflow<=ArraySize(pPage->aOvfl) );
|
| - assert( sz==cellSizePtr(pPage, pCell) );
|
| 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
|
| + ** wanted to be less than 4 but got rounded up to 4 on the leaf, then size
|
| + ** might be less than 8 (leaf-size + pointer) on the interior node. Hence
|
| + ** the term after the || in the following assert(). */
|
| + assert( sz==cellSizePtr(pPage, pCell) || (sz==8 && iChild>0) );
|
| if( pPage->nOverflow || sz+2>pPage->nFree ){
|
| if( pTemp ){
|
| memcpy(pTemp+nSkip, pCell+nSkip, sz-nSkip);
|
| @@ -5348,12 +5542,12 @@ static void assemblePage(
|
|
|
| assert( pPage->nOverflow==0 );
|
| assert( sqlite3_mutex_held(pPage->pBt->mutex) );
|
| - assert( nCell>=0 && nCell<=MX_CELL(pPage->pBt) && MX_CELL(pPage->pBt)<=5460 );
|
| + assert( nCell>=0 && nCell<=MX_CELL(pPage->pBt) && MX_CELL(pPage->pBt)<=10921);
|
| assert( sqlite3PagerIswriteable(pPage->pDbPage) );
|
|
|
| /* Check that the page has just been zeroed by zeroPage() */
|
| assert( pPage->nCell==0 );
|
| - assert( get2byte(&data[hdr+5])==nUsable );
|
| + assert( get2byteNotZero(&data[hdr+5])==nUsable );
|
|
|
| pCellptr = &data[pPage->cellOffset + nCell*2];
|
| cellbody = nUsable;
|
| @@ -5419,6 +5613,7 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){
|
| assert( sqlite3PagerIswriteable(pParent->pDbPage) );
|
| assert( pPage->nOverflow==1 );
|
|
|
| + /* This error condition is now caught prior to reaching this function */
|
| if( pPage->nCell<=0 ) return SQLITE_CORRUPT_BKPT;
|
|
|
| /* Allocate a new page. This page will become the right-sibling of
|
| @@ -5555,7 +5750,7 @@ static void copyNodeContent(MemPage *pFrom, MemPage *pTo, int *pRC){
|
| u8 * const aTo = pTo->aData;
|
| int const iFromHdr = pFrom->hdrOffset;
|
| int const iToHdr = ((pTo->pgno==1) ? 100 : 0);
|
| - TESTONLY(int rc;)
|
| + int rc;
|
| int iData;
|
|
|
|
|
| @@ -5569,11 +5764,16 @@ static void copyNodeContent(MemPage *pFrom, MemPage *pTo, int *pRC){
|
| memcpy(&aTo[iToHdr], &aFrom[iFromHdr], pFrom->cellOffset + 2*pFrom->nCell);
|
|
|
| /* Reinitialize page pTo so that the contents of the MemPage structure
|
| - ** match the new data. The initialization of pTo "cannot" fail, as the
|
| - ** data copied from pFrom is known to be valid. */
|
| + ** match the new data. The initialization of pTo can actually fail under
|
| + ** fairly obscure circumstances, even though it is a copy of initialized
|
| + ** page pFrom.
|
| + */
|
| pTo->isInit = 0;
|
| - TESTONLY(rc = ) btreeInitPage(pTo);
|
| - assert( rc==SQLITE_OK );
|
| + rc = btreeInitPage(pTo);
|
| + if( rc!=SQLITE_OK ){
|
| + *pRC = rc;
|
| + return;
|
| + }
|
|
|
| /* If this is an auto-vacuum database, update the pointer-map entries
|
| ** for any b-tree or overflow pages that pTo now contains the pointers to.
|
| @@ -5741,10 +5941,17 @@ 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. */
|
| -#ifdef SQLITE_SECURE_DELETE
|
| - memcpy(&aOvflSpace[apDiv[i]-pParent->aData], apDiv[i], szNew[i]);
|
| - apDiv[i] = &aOvflSpace[apDiv[i]-pParent->aData];
|
| -#endif
|
| + if( pBt->secureDelete ){
|
| + int iOff = SQLITE_PTR_TO_INT(apDiv[i]) - SQLITE_PTR_TO_INT(pParent->aData);
|
| + if( (iOff+szNew[i])>(int)pBt->usableSize ){
|
| + rc = SQLITE_CORRUPT_BKPT;
|
| + memset(apOld, 0, (i+1)*sizeof(MemPage*));
|
| + goto balance_cleanup;
|
| + }else{
|
| + memcpy(&aOvflSpace[iOff], apDiv[i], szNew[i]);
|
| + apDiv[i] = &aOvflSpace[apDiv[i]-pParent->aData];
|
| + }
|
| + }
|
| dropCell(pParent, i+nxDiv-pParent->nOverflow, szNew[i], &rc);
|
| }
|
| }
|
| @@ -5815,7 +6022,7 @@ static int balance_nonroot(
|
| szCell[nCell] = sz;
|
| pTemp = &aSpace1[iSpace1];
|
| iSpace1 += sz;
|
| - assert( sz<=pBt->pageSize/4 );
|
| + assert( sz<=pBt->maxLocal+23 );
|
| assert( iSpace1<=pBt->pageSize );
|
| memcpy(pTemp, apDiv[i], sz);
|
| apCell[nCell] = pTemp+leafCorrection;
|
| @@ -5864,7 +6071,7 @@ static int balance_nonroot(
|
| if( leafData ){ i--; }
|
| subtotal = 0;
|
| k++;
|
| - if( k>NB+1 ){ rc = SQLITE_CORRUPT; goto balance_cleanup; }
|
| + if( k>NB+1 ){ rc = SQLITE_CORRUPT_BKPT; goto balance_cleanup; }
|
| }
|
| }
|
| szNew[k] = subtotal;
|
| @@ -5918,7 +6125,7 @@ static int balance_nonroot(
|
| ** Allocate k new pages. Reuse old pages where possible.
|
| */
|
| if( apOld[0]->pgno<=1 ){
|
| - rc = SQLITE_CORRUPT;
|
| + rc = SQLITE_CORRUPT_BKPT;
|
| goto balance_cleanup;
|
| }
|
| pageFlags = apOld[0]->aData[0];
|
| @@ -6061,7 +6268,7 @@ static int balance_nonroot(
|
| }
|
| }
|
| iOvflSpace += sz;
|
| - assert( sz<=pBt->pageSize/4 );
|
| + assert( sz<=pBt->maxLocal+23 );
|
| assert( iOvflSpace<=pBt->pageSize );
|
| insertCell(pParent, nxDiv, pCell, sz, pTemp, pNew->pgno, &rc);
|
| if( rc!=SQLITE_OK ) goto balance_cleanup;
|
| @@ -6443,7 +6650,7 @@ int sqlite3BtreeInsert(
|
| ){
|
| int rc;
|
| int loc = seekResult; /* -1: before desired location +1: after */
|
| - int szNew;
|
| + int szNew = 0;
|
| int idx;
|
| MemPage *pPage;
|
| Btree *p = pCur->pBtree;
|
| @@ -6698,11 +6905,12 @@ int sqlite3BtreeDelete(BtCursor *pCur){
|
| ** BTREE_INTKEY|BTREE_LEAFDATA Used for SQL tables with rowid keys
|
| ** BTREE_ZERODATA Used for SQL indices
|
| */
|
| -static int btreeCreateTable(Btree *p, int *piTable, int flags){
|
| +static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){
|
| BtShared *pBt = p->pBt;
|
| MemPage *pRoot;
|
| Pgno pgnoRoot;
|
| int rc;
|
| + int ptfFlags; /* Page-type flage for the root page of new table */
|
|
|
| assert( sqlite3BtreeHoldsMutex(p) );
|
| assert( pBt->inTransaction==TRANS_WRITE );
|
| @@ -6803,8 +7011,14 @@ static int btreeCreateTable(Btree *p, int *piTable, int flags){
|
| releasePage(pRoot);
|
| return rc;
|
| }
|
| +
|
| + /* When the new root page was allocated, page 1 was made writable in
|
| + ** order either to increase the database filesize, or to decrement the
|
| + ** freelist count. Hence, the sqlite3BtreeUpdateMeta() call cannot fail.
|
| + */
|
| + assert( sqlite3PagerIswriteable(pBt->pPage1->pDbPage) );
|
| rc = sqlite3BtreeUpdateMeta(p, 4, pgnoRoot);
|
| - if( rc ){
|
| + if( NEVER(rc) ){
|
| releasePage(pRoot);
|
| return rc;
|
| }
|
| @@ -6815,8 +7029,14 @@ static int btreeCreateTable(Btree *p, int *piTable, int flags){
|
| }
|
| #endif
|
| assert( sqlite3PagerIswriteable(pRoot->pDbPage) );
|
| - zeroPage(pRoot, flags | PTF_LEAF);
|
| + if( createTabFlags & BTREE_INTKEY ){
|
| + ptfFlags = PTF_INTKEY | PTF_LEAFDATA | PTF_LEAF;
|
| + }else{
|
| + ptfFlags = PTF_ZERODATA | PTF_LEAF;
|
| + }
|
| + zeroPage(pRoot, ptfFlags);
|
| sqlite3PagerUnref(pRoot->pDbPage);
|
| + assert( (pBt->openFlags & BTREE_SINGLE)==0 || pgnoRoot==2 );
|
| *piTable = (int)pgnoRoot;
|
| return SQLITE_OK;
|
| }
|
| @@ -6834,9 +7054,9 @@ int sqlite3BtreeCreateTable(Btree *p, int *piTable, int flags){
|
| */
|
| static int clearDatabasePage(
|
| BtShared *pBt, /* The BTree that contains the table */
|
| - Pgno pgno, /* Page number to clear */
|
| - int freePageFlag, /* Deallocate page if true */
|
| - int *pnChange
|
| + Pgno pgno, /* Page number to clear */
|
| + int freePageFlag, /* Deallocate page if true */
|
| + int *pnChange /* Add number of Cells freed to this counter */
|
| ){
|
| MemPage *pPage;
|
| int rc;
|
| @@ -6844,7 +7064,7 @@ static int clearDatabasePage(
|
| int i;
|
|
|
| assert( sqlite3_mutex_held(pBt->mutex) );
|
| - if( pgno>pagerPagecount(pBt) ){
|
| + if( pgno>btreePagecount(pBt) ){
|
| return SQLITE_CORRUPT_BKPT;
|
| }
|
|
|
| @@ -7299,7 +7519,7 @@ static void checkList(
|
| checkPtrmap(pCheck, iPage, PTRMAP_FREEPAGE, 0, zContext);
|
| }
|
| #endif
|
| - if( n>pCheck->pBt->usableSize/4-2 ){
|
| + if( n>(int)pCheck->pBt->usableSize/4-2 ){
|
| checkAppendMsg(pCheck, zContext,
|
| "freelist leaf count too big on page %d", iPage);
|
| N--;
|
| @@ -7356,7 +7576,9 @@ static void checkList(
|
| static int checkTreePage(
|
| IntegrityCk *pCheck, /* Context for the sanity check */
|
| int iPage, /* Page number of the page to check */
|
| - char *zParentContext /* Parent context */
|
| + char *zParentContext, /* Parent context */
|
| + i64 *pnParentMinKey,
|
| + i64 *pnParentMaxKey
|
| ){
|
| MemPage *pPage;
|
| int i, rc, depth, d2, pgno, cnt;
|
| @@ -7367,6 +7589,8 @@ static int checkTreePage(
|
| int usableSize;
|
| char zContext[100];
|
| char *hit = 0;
|
| + i64 nMinKey = 0;
|
| + i64 nMaxKey = 0;
|
|
|
| sqlite3_snprintf(sizeof(zContext), zContext, "Page %d: ", iPage);
|
|
|
| @@ -7409,6 +7633,16 @@ static int checkTreePage(
|
| btreeParseCellPtr(pPage, pCell, &info);
|
| sz = info.nData;
|
| if( !pPage->intKey ) sz += (int)info.nKey;
|
| + /* For intKey pages, check that the keys are in order.
|
| + */
|
| + else if( i==0 ) nMinKey = nMaxKey = info.nKey;
|
| + else{
|
| + if( info.nKey <= nMaxKey ){
|
| + checkAppendMsg(pCheck, zContext,
|
| + "Rowid %lld out of order (previous was %lld)", info.nKey, nMaxKey);
|
| + }
|
| + nMaxKey = info.nKey;
|
| + }
|
| assert( sz==info.nPayload );
|
| if( (sz>info.nLocal)
|
| && (&pCell[info.iOverflow]<=&pPage->aData[pBt->usableSize])
|
| @@ -7432,25 +7666,62 @@ static int checkTreePage(
|
| checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage, zContext);
|
| }
|
| #endif
|
| - d2 = checkTreePage(pCheck, pgno, zContext);
|
| + d2 = checkTreePage(pCheck, pgno, zContext, &nMinKey, i==0 ? NULL : &nMaxKey);
|
| if( i>0 && d2!=depth ){
|
| checkAppendMsg(pCheck, zContext, "Child page depth differs");
|
| }
|
| depth = d2;
|
| }
|
| }
|
| +
|
| if( !pPage->leaf ){
|
| pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]);
|
| sqlite3_snprintf(sizeof(zContext), zContext,
|
| "On page %d at right child: ", iPage);
|
| #ifndef SQLITE_OMIT_AUTOVACUUM
|
| if( pBt->autoVacuum ){
|
| - checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage, 0);
|
| + checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage, zContext);
|
| }
|
| #endif
|
| - checkTreePage(pCheck, pgno, zContext);
|
| + checkTreePage(pCheck, pgno, zContext, NULL, !pPage->nCell ? NULL : &nMaxKey);
|
| }
|
|
|
| + /* For intKey leaf pages, check that the min/max keys are in order
|
| + ** with any left/parent/right pages.
|
| + */
|
| + if( pPage->leaf && pPage->intKey ){
|
| + /* if we are a left child page */
|
| + if( pnParentMinKey ){
|
| + /* if we are the left most child page */
|
| + if( !pnParentMaxKey ){
|
| + if( nMaxKey > *pnParentMinKey ){
|
| + checkAppendMsg(pCheck, zContext,
|
| + "Rowid %lld out of order (max larger than parent min of %lld)",
|
| + nMaxKey, *pnParentMinKey);
|
| + }
|
| + }else{
|
| + if( nMinKey <= *pnParentMinKey ){
|
| + checkAppendMsg(pCheck, zContext,
|
| + "Rowid %lld out of order (min less than parent min of %lld)",
|
| + nMinKey, *pnParentMinKey);
|
| + }
|
| + if( nMaxKey > *pnParentMaxKey ){
|
| + checkAppendMsg(pCheck, zContext,
|
| + "Rowid %lld out of order (max larger than parent max of %lld)",
|
| + nMaxKey, *pnParentMaxKey);
|
| + }
|
| + *pnParentMinKey = nMaxKey;
|
| + }
|
| + /* else if we're a right child page */
|
| + } else if( pnParentMaxKey ){
|
| + if( nMinKey <= *pnParentMaxKey ){
|
| + checkAppendMsg(pCheck, zContext,
|
| + "Rowid %lld out of order (min less than parent max of %lld)",
|
| + nMinKey, *pnParentMaxKey);
|
| + }
|
| + }
|
| + }
|
| +
|
| /* Check for complete coverage of the page
|
| */
|
| data = pPage->aData;
|
| @@ -7459,7 +7730,7 @@ static int checkTreePage(
|
| if( hit==0 ){
|
| pCheck->mallocFailed = 1;
|
| }else{
|
| - u16 contentOffset = get2byte(&data[hdr+5]);
|
| + int contentOffset = get2byteNotZero(&data[hdr+5]);
|
| assert( contentOffset<=usableSize ); /* Enforced by btreeInitPage() */
|
| memset(hit+contentOffset, 0, usableSize-contentOffset);
|
| memset(hit, 1, contentOffset);
|
| @@ -7467,14 +7738,14 @@ static int checkTreePage(
|
| cellStart = hdr + 12 - 4*pPage->leaf;
|
| for(i=0; i<nCell; i++){
|
| int pc = get2byte(&data[cellStart+i*2]);
|
| - u16 size = 1024;
|
| + u32 size = 65536;
|
| int j;
|
| if( pc<=usableSize-4 ){
|
| size = cellSizePtr(pPage, &data[pc]);
|
| }
|
| - if( (pc+size-1)>=usableSize ){
|
| + if( (int)(pc+size-1)>=usableSize ){
|
| checkAppendMsg(pCheck, 0,
|
| - "Corruption detected in cell %d on page %d",i,iPage,0);
|
| + "Corruption detected in cell %d on page %d",i,iPage);
|
| }else{
|
| for(j=pc+size-1; j>=pc; j--) hit[j]++;
|
| }
|
| @@ -7544,7 +7815,7 @@ char *sqlite3BtreeIntegrityCheck(
|
| nRef = sqlite3PagerRefcount(pBt->pPager);
|
| sCheck.pBt = pBt;
|
| sCheck.pPager = pBt->pPager;
|
| - sCheck.nPage = pagerPagecount(sCheck.pBt);
|
| + sCheck.nPage = btreePagecount(sCheck.pBt);
|
| sCheck.mxErr = mxErr;
|
| sCheck.nErr = 0;
|
| sCheck.mallocFailed = 0;
|
| @@ -7565,6 +7836,7 @@ char *sqlite3BtreeIntegrityCheck(
|
| sCheck.anRef[i] = 1;
|
| }
|
| sqlite3StrAccumInit(&sCheck.errMsg, zErr, sizeof(zErr), 20000);
|
| + sCheck.errMsg.useMalloc = 2;
|
|
|
| /* Check the integrity of the freelist
|
| */
|
| @@ -7580,7 +7852,7 @@ char *sqlite3BtreeIntegrityCheck(
|
| checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0, 0);
|
| }
|
| #endif
|
| - checkTreePage(&sCheck, aRoot[i], "List of tree roots: ");
|
| + checkTreePage(&sCheck, aRoot[i], "List of tree roots: ", NULL, NULL);
|
| }
|
|
|
| /* Make sure every page in the file is referenced
|
| @@ -7663,6 +7935,97 @@ int sqlite3BtreeIsInTrans(Btree *p){
|
| return (p && (p->inTrans==TRANS_WRITE));
|
| }
|
|
|
| +#ifndef SQLITE_OMIT_WAL
|
| +/*
|
| +** Run a checkpoint on the Btree passed as the first argument.
|
| +**
|
| +** Return SQLITE_LOCKED if this or any other connection has an open
|
| +** transaction on the shared-cache the argument Btree is connected to.
|
| +*/
|
| +int sqlite3BtreeCheckpoint(Btree *p){
|
| + int rc = SQLITE_OK;
|
| + if( p ){
|
| + BtShared *pBt = p->pBt;
|
| + sqlite3BtreeEnter(p);
|
| + if( pBt->inTransaction!=TRANS_NONE ){
|
| + rc = SQLITE_LOCKED;
|
| + }else{
|
| + rc = sqlite3PagerCheckpoint(pBt->pPager);
|
| + }
|
| + sqlite3BtreeLeave(p);
|
| + }
|
| + return rc;
|
| +}
|
| +
|
| +/* Poison the db so that other clients error out as quickly as
|
| +** possible.
|
| +*/
|
| +int sqlite3Poison(sqlite3 *db){
|
| + int rc;
|
| + Btree *p;
|
| + BtShared *pBt;
|
| + unsigned char *pP1;
|
| +
|
| + if( db == NULL) return SQLITE_OK;
|
| +
|
| + /* Database 0 corrosponds to the main database. */
|
| + if( db->nDb<1 ) return SQLITE_OK;
|
| + p = db->aDb[0].pBt;
|
| + pBt = p->pBt;
|
| +
|
| + /* If in a transaction, roll it back. Committing any changes to a
|
| + ** corrupt database may mess up evidence, we definitely don't want
|
| + ** to allow poisoning to be rolled back, and the database is anyhow
|
| + ** going bye-bye RSN.
|
| + */
|
| + /* TODO(shess): Figure out if this might release the lock and let
|
| + ** someone else get in there, which might deny us the lock a couple
|
| + ** lines down.
|
| + */
|
| + if( sqlite3BtreeIsInTrans(p) ) sqlite3BtreeRollback(p);
|
| +
|
| + /* Start an exclusive transaction. This will check the headers, so
|
| + ** if someone else poisoned the database we should get an error.
|
| + */
|
| + rc = sqlite3BtreeBeginTrans(p, 2);
|
| + /* TODO(shess): Handle SQLITE_BUSY? */
|
| + if( rc!=SQLITE_OK ) return rc;
|
| +
|
| + /* Copied from sqlite3BtreeUpdateMeta(). Writing the old version of
|
| + ** the page to the journal may be overkill, but it probably won't
|
| + ** hurt.
|
| + */
|
| + assert( pBt->inTrans==TRANS_WRITE );
|
| + assert( pBt->pPage1!=0 );
|
| + rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
|
| + if( rc ) goto err;
|
| +
|
| + /* "SQLite format 3" changes to
|
| + ** "SQLite poison 3". Be extra paranoid about making this change.
|
| + */
|
| + if( sizeof(zMagicHeader)!=16 ||
|
| + sizeof(zPoisonHeader)!=sizeof(zMagicHeader) ){
|
| + rc = SQLITE_ERROR;
|
| + goto err;
|
| + }
|
| + pP1 = pBt->pPage1->aData;
|
| + if( memcmp(pP1, zMagicHeader, 16)!=0 ){
|
| + rc = SQLITE_CORRUPT;
|
| + goto err;
|
| + }
|
| + memcpy(pP1, zPoisonHeader, 16);
|
| +
|
| + /* Push it to the database file. */
|
| + return sqlite3BtreeCommit(p);
|
| +
|
| + err:
|
| + /* TODO(shess): What about errors, here? */
|
| + sqlite3BtreeRollback(p);
|
| + return rc;
|
| +}
|
| +
|
| +#endif
|
| +
|
| /*
|
| ** Return non-zero if a read (or write) transaction is active.
|
| */
|
| @@ -7702,7 +8065,7 @@ void *sqlite3BtreeSchema(Btree *p, int nBytes, void(*xFree)(void *)){
|
| BtShared *pBt = p->pBt;
|
| sqlite3BtreeEnter(p);
|
| if( !pBt->pSchema && nBytes ){
|
| - pBt->pSchema = sqlite3MallocZero(nBytes);
|
| + pBt->pSchema = sqlite3DbMallocZero(0, nBytes);
|
| pBt->xFreeSchema = xFree;
|
| }
|
| sqlite3BtreeLeave(p);
|
| @@ -7811,72 +8174,40 @@ void sqlite3BtreeCacheOverflow(BtCursor *pCur){
|
| assert(!pCur->aOverflow);
|
| pCur->isIncrblobHandle = 1;
|
| }
|
| +#endif
|
|
|
| -/* Poison the db so that other clients error out as quickly as
|
| -** possible.
|
| +/*
|
| +** Set both the "read version" (single byte at byte offset 18) and
|
| +** "write version" (single byte at byte offset 19) fields in the database
|
| +** header to iVersion.
|
| */
|
| -int sqlite3Poison(sqlite3 *db){
|
| - int rc;
|
| - Btree *p;
|
| - BtShared *pBt;
|
| - unsigned char *pP1;
|
| -
|
| - if( db == NULL) return SQLITE_OK;
|
| -
|
| - /* Database 0 corrosponds to the main database. */
|
| - if( db->nDb<1 ) return SQLITE_OK;
|
| - p = db->aDb[0].pBt;
|
| - pBt = p->pBt;
|
| -
|
| - /* If in a transaction, roll it back. Committing any changes to a
|
| - ** corrupt database may mess up evidence, we definitely don't want
|
| - ** to allow poisoning to be rolled back, and the database is anyhow
|
| - ** going bye-bye RSN.
|
| - */
|
| - /* TODO(shess): Figure out if this might release the lock and let
|
| - ** someone else get in there, which might deny us the lock a couple
|
| - ** lines down.
|
| - */
|
| - if( sqlite3BtreeIsInTrans(p) ) sqlite3BtreeRollback(p);
|
| -
|
| - /* Start an exclusive transaction. This will check the headers, so
|
| - ** if someone else poisoned the database we should get an error.
|
| - */
|
| - rc = sqlite3BtreeBeginTrans(p, 2);
|
| - /* TODO(shess): Handle SQLITE_BUSY? */
|
| - if( rc!=SQLITE_OK ) return rc;
|
| +int sqlite3BtreeSetVersion(Btree *pBtree, int iVersion){
|
| + BtShared *pBt = pBtree->pBt;
|
| + int rc; /* Return code */
|
| +
|
| + assert( pBtree->inTrans==TRANS_NONE );
|
| + assert( iVersion==1 || iVersion==2 );
|
|
|
| - /* Copied from sqlite3BtreeUpdateMeta(). Writing the old version of
|
| - ** the page to the journal may be overkill, but it probably won't
|
| - ** hurt.
|
| + /* If setting the version fields to 1, do not automatically open the
|
| + ** WAL connection, even if the version fields are currently set to 2.
|
| */
|
| - assert( pBt->inTrans==TRANS_WRITE );
|
| - assert( pBt->pPage1!=0 );
|
| - rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
|
| - if( rc ) goto err;
|
| + pBt->doNotUseWAL = (u8)(iVersion==1);
|
|
|
| - /* "SQLite format 3" changes to
|
| - ** "SQLite poison 3". Be extra paranoid about making this change.
|
| - */
|
| - if( sizeof(zMagicHeader)!=16 ||
|
| - sizeof(zPoisonHeader)!=sizeof(zMagicHeader) ){
|
| - rc = SQLITE_ERROR;
|
| - goto err;
|
| - }
|
| - pP1 = pBt->pPage1->aData;
|
| - if( memcmp(pP1, zMagicHeader, 16)!=0 ){
|
| - rc = SQLITE_CORRUPT;
|
| - goto err;
|
| + rc = sqlite3BtreeBeginTrans(pBtree, 0);
|
| + if( rc==SQLITE_OK ){
|
| + u8 *aData = pBt->pPage1->aData;
|
| + if( aData[18]!=(u8)iVersion || aData[19]!=(u8)iVersion ){
|
| + rc = sqlite3BtreeBeginTrans(pBtree, 2);
|
| + if( rc==SQLITE_OK ){
|
| + rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
|
| + if( rc==SQLITE_OK ){
|
| + aData[18] = (u8)iVersion;
|
| + aData[19] = (u8)iVersion;
|
| + }
|
| + }
|
| + }
|
| }
|
| - memcpy(pP1, zPoisonHeader, 16);
|
| -
|
| - /* Push it to the database file. */
|
| - return sqlite3BtreeCommit(p);
|
|
|
| - err:
|
| - /* TODO(shess): What about errors, here? */
|
| - sqlite3BtreeRollback(p);
|
| + pBt->doNotUseWAL = 0;
|
| return rc;
|
| }
|
| -
|
| -#endif
|
|
|