From: drh Date: Wed, 20 Aug 2014 17:56:27 +0000 (+0000) Subject: Reimplement the freeSpace() routine in btree.c so that it runs faster. X-Git-Tag: version-3.8.7~181 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=5f5c753efbbe23df1e52a0560c2b7abbc0f3c334;p=thirdparty%2Fsqlite.git Reimplement the freeSpace() routine in btree.c so that it runs faster. FossilOrigin-Name: fe4fd014b42b7b158ca968f1535b5636c67769f6 --- diff --git a/manifest b/manifest index c1cbe5415f..4fe8e5ec20 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Refactor\slocal\svariable\snames\sin\sthe\sfreeSpace()\sroutine\sof\sbtree.c\sfor\nimproved\sunderstandability. -D 2014-08-20T14:37:09.167 +C Reimplement\sthe\sfreeSpace()\sroutine\sin\sbtree.c\sso\sthat\sit\sruns\sfaster. +D 2014-08-20T17:56:27.585 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 5eb79e334a5de69c87740edd56af6527dd219308 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -167,7 +167,7 @@ F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34 F src/backup.c a729e63cf5cd1829507cb7b8e89f99b95141bb53 F src/bitvec.c 19a4ba637bd85f8f63fc8c9bae5ade9fb05ec1cb F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7 -F src/btree.c c1235eacb8d4de12850daccb1636763218da3381 +F src/btree.c fe2d11c8c0d99e324614141ad012b37d0601cf59 F src/btree.h 4245a349bfe09611d7ff887dbc3a80cee8b7955a F src/btreeInt.h cf180d86b2e9e418f638d65baa425c4c69c0e0e3 F src/build.c 5abf794fe8a605f2005b422e98a3cedad9b9ef5b @@ -1186,7 +1186,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 121308fa869ad490a6924798d276c0ff32759acc -R 332118792b6ef82f7eceb02b0d545622 +P 7e63089a191f29aefde05e89bb612f3036cfa034 +R cbff1ae81d05ca3b78b420deaaa6083f U drh -Z accc32db7c4714707b5e7cc22312c49c +Z d684bbbc1bfe7cdee532398c2232ddf8 diff --git a/manifest.uuid b/manifest.uuid index 1b60255c36..238682a66d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7e63089a191f29aefde05e89bb612f3036cfa034 \ No newline at end of file +fe4fd014b42b7b158ca968f1535b5636c67769f6 \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index ac13b0f3da..c10e2f1c32 100644 --- a/src/btree.c +++ b/src/btree.c @@ -1293,92 +1293,93 @@ defragment_page: ** The first byte of the new free block is pPage->aData[iStart] ** and the size of the block is iSize bytes. ** -** Most of the effort here is involved in coalesing adjacent -** free blocks into a single big free block. -*/ -static int freeSpace(MemPage *pPage, int iStart, int iSize){ - int iPtr; /* Address of pointer to next freeblock */ - int iFreeBlk; /* Address of the next freeblock */ - int hdr; /* Page header size. 0 or 100 */ - int iLast; /* Largest possible freeblock offset */ +** Adjacent freeblocks are coalesced. +** +** Note that even though the freeblock list was checked by btreeInitPage(), +** that routine will not detect overlap between cells or freeblocks. Nor +** does it detect cells or freeblocks that encrouch into the reserved bytes +** at the end of the page. So do additional corruption checks inside this +** routine and return SQLITE_CORRUPT if any problems are found. +*/ +static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ + u16 iPtr; /* Address of pointer to next freeblock */ + u16 iFreeBlk; /* Address of the next freeblock */ + u8 hdr; /* Page header size. 0 or 100 */ + u8 nFrag = 0; /* Reduction in fragmentation */ + u16 iOrigSize = iSize; /* Original value of iSize */ + u32 iLast = pPage->pBt->usableSize-4; /* Largest possible freeblock offset */ + u32 iEnd = iStart + iSize; /* First byte past the iStart buffer */ unsigned char *data = pPage->aData; /* Page content */ assert( pPage->pBt!=0 ); assert( sqlite3PagerIswriteable(pPage->pDbPage) ); assert( iStart>=pPage->hdrOffset+6+pPage->childPtrSize ); - assert( (iStart + iSize) <= (int)pPage->pBt->usableSize ); + assert( iEnd <= pPage->pBt->usableSize ); assert( sqlite3_mutex_held(pPage->pBt->mutex) ); assert( iSize>=4 ); /* Minimum cell size is 4 */ + assert( iStart<=iLast ); + /* Overwrite deleted information with zeros when the secure_delete + ** option is enabled */ if( pPage->pBt->btsFlags & BTS_SECURE_DELETE ){ - /* Overwrite deleted information with zeros when the secure_delete - ** option is enabled */ memset(&data[iStart], 0, iSize); } - /* Add the space back into the linked list of freeblocks. Note that - ** even though the freeblock list was checked by btreeInitPage(), - ** btreeInitPage() did not detect overlapping cells or - ** freeblocks that overlapped cells. Nor does it detect when the - ** cell content area exceeds the value in the page header. If these - ** situations arise, then subsequent insert operations might corrupt - ** the freelist. So we do need to check for corruption while scanning - ** the freelist. + /* The list of freeblocks must be in ascending order. Find the + ** spot on the list where iStart should be inserted. */ hdr = pPage->hdrOffset; iPtr = hdr + 1; - iLast = pPage->pBt->usableSize - 4; - assert( iStart<=iLast ); - while( (iFreeBlk = get2byte(&data[iPtr]))0 ){ - if( iFreeBlk0 && iFreeBlkiLast ){ - return SQLITE_CORRUPT_BKPT; - } + if( iFreeBlk>iLast ) return SQLITE_CORRUPT_BKPT; assert( iFreeBlk>iPtr || iFreeBlk==0 ); - put2byte(&data[iPtr], iStart); - put2byte(&data[iStart], iFreeBlk); - put2byte(&data[iStart+2], iSize); - pPage->nFree = pPage->nFree + (u16)iSize; - /* Coalesce adjacent free blocks */ - iPtr = hdr + 1; - while( (iFreeBlk = get2byte(&data[iPtr]))>0 ){ - int iNextBlk; /* Next freeblock after iFreeBlk */ - int szFreeBlk; /* Size of iFreeBlk */ - assert( iFreeBlk>iPtr ); - assert( iFreeBlk <= (int)pPage->pBt->usableSize-4 ); - iNextBlk = get2byte(&data[iFreeBlk]); - szFreeBlk = get2byte(&data[iFreeBlk+2]); - if( iFreeBlk + szFreeBlk + 3 >= iNextBlk && iNextBlk>0 ){ - int nFrag; /* Fragment bytes in between iFreeBlk and iNextBlk */ - int x; /* Temp value */ - nFrag = iNextBlk - (iFreeBlk+szFreeBlk); - if( (nFrag<0) || (nFrag>(int)data[hdr+7]) ){ - return SQLITE_CORRUPT_BKPT; - } - data[hdr+7] -= (u8)nFrag; - x = get2byte(&data[iNextBlk]); - put2byte(&data[iFreeBlk], x); - x = iNextBlk + get2byte(&data[iNextBlk+2]) - iFreeBlk; - put2byte(&data[iFreeBlk+2], x); - }else{ - iPtr = iFreeBlk; - } + /* At this point: + ** iFreeBlk: First freeblock after iStart, or zero if none + ** iPtr: The address of a pointer iFreeBlk + ** + ** Check to see if iFreeBlk should be coalesced onto the end of iStart. + */ + if( iFreeBlk && iEnd+3>=iFreeBlk ){ + nFrag = iFreeBlk - iEnd; + if( iEnd>iFreeBlk ) return SQLITE_CORRUPT_BKPT; + iEnd = iFreeBlk + get2byte(&data[iFreeBlk+2]); + iSize = iEnd - iStart; + iFreeBlk = get2byte(&data[iFreeBlk]); } - /* If the cell content area begins with a freeblock, remove it. */ - if( data[hdr+1]==data[hdr+5] && data[hdr+2]==data[hdr+6] ){ - int top; - iFreeBlk = get2byte(&data[hdr+1]); - memcpy(&data[hdr+1], &data[iFreeBlk], 2); - top = get2byte(&data[hdr+5]) + get2byte(&data[iFreeBlk+2]); - put2byte(&data[hdr+5], top); + /* If iPtr is another freeblock (that is, if iPtr is not the freelist pointer + ** in the page header) then check to see if iStart should be coalesced + ** onto the end of iPtr. + */ + if( iPtr>hdr+1 ){ + int iPtrEnd = iPtr + get2byte(&data[iPtr+2]); + if( iPtrEnd+3>=iStart ){ + if( iPtrEnd>iStart ) return SQLITE_CORRUPT_BKPT; + nFrag += iStart - iPtrEnd; + iSize = iEnd - iPtr; + iStart = iPtr; + } + } + if( nFrag>data[hdr+7] ) return SQLITE_CORRUPT_BKPT; + + data[hdr+7] -= nFrag; + if( iPtr==hdr+1 && iStart==get2byte(&data[hdr+5]) ){ + /* The new freeblock is at the beginning of the cell content area, + ** so just extend the cell content area rather than create another + ** freelist entry */ + put2byte(&data[hdr+1], iFreeBlk); + put2byte(&data[hdr+5], iEnd); + }else{ + /* Insert the new freeblock into the freelist */ + put2byte(&data[iPtr], iStart); + put2byte(&data[iStart], iFreeBlk); + put2byte(&data[iStart+2], iSize); } - assert( sqlite3PagerIswriteable(pPage->pDbPage) ); + pPage->nFree += iOrigSize; return SQLITE_OK; }