1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-07 02:42:48 +03:00

Split the sqlite3BtreeMovetoUnpacked() routine into two separate routines

sqlite3BtreeTableMoveto() and sqlite3BtreeIndexMoveto(), since we usually
know the type of btree in advance.  This results in less branching and
better performance.

FossilOrigin-Name: 3b0d34e5e5f9a16c3397e4551f3b534729b1b375770f05f6ed5847818b1f4c0b
This commit is contained in:
drh
2021-06-19 18:32:20 +00:00
parent 6d72858e79
commit 42a410dcad
6 changed files with 266 additions and 181 deletions

View File

@@ -827,15 +827,13 @@ static int btreeMoveto(
sqlite3VdbeRecordUnpack(pKeyInfo, (int)nKey, pKey, pIdxKey);
if( pIdxKey->nField==0 || pIdxKey->nField>pKeyInfo->nAllField ){
rc = SQLITE_CORRUPT_BKPT;
goto moveto_done;
}else{
rc = sqlite3BtreeIndexMoveto(pCur, pIdxKey, pRes);
}
sqlite3DbFree(pCur->pKeyInfo->db, pIdxKey);
}else{
pIdxKey = 0;
}
rc = sqlite3BtreeMovetoUnpacked(pCur, pIdxKey, nKey, bias, pRes);
moveto_done:
if( pIdxKey ){
sqlite3DbFree(pCur->pKeyInfo->db, pIdxKey);
rc = sqlite3BtreeTableMoveto(pCur, nKey, bias, pRes);
}
return rc;
}
@@ -5421,12 +5419,8 @@ int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
return rc;
}
/* Move the cursor so that it points to an entry near the key
** specified by pIdxKey or intKey. Return a success code.
**
** For INTKEY tables, the intKey parameter is used. pIdxKey
** must be NULL. For index tables, pIdxKey is used and intKey
** is ignored.
/* Move the cursor so that it points to an entry in a table (a.k.a INTKEY)
** table near the key intKey. Return a success code.
**
** If an exact match is not found, then the cursor is always
** left pointing at a leaf page which would hold the entry if it
@@ -5439,39 +5433,32 @@ int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
** *pRes is as follows:
**
** *pRes<0 The cursor is left pointing at an entry that
** is smaller than intKey/pIdxKey or if the table is empty
** is smaller than intKey or if the table is empty
** and the cursor is therefore left point to nothing.
**
** *pRes==0 The cursor is left pointing at an entry that
** exactly matches intKey/pIdxKey.
** exactly matches intKey.
**
** *pRes>0 The cursor is left pointing at an entry that
** is larger than intKey/pIdxKey.
**
** For index tables, the pIdxKey->eqSeen field is set to 1 if there
** exists an entry in the table that exactly matches pIdxKey.
** is larger than intKey.
*/
int sqlite3BtreeMovetoUnpacked(
int sqlite3BtreeTableMoveto(
BtCursor *pCur, /* The cursor to be moved */
UnpackedRecord *pIdxKey, /* Unpacked index key */
i64 intKey, /* The table key */
int biasRight, /* If true, bias the search to the high end */
int *pRes /* Write search results here */
){
int rc;
RecordCompare xRecordCompare;
assert( cursorOwnsBtShared(pCur) );
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
assert( pRes );
assert( (pIdxKey==0)==(pCur->pKeyInfo==0) );
assert( pCur->eState!=CURSOR_VALID || (pIdxKey==0)==(pCur->curIntKey!=0) );
assert( pCur->pKeyInfo==0 );
assert( pCur->eState!=CURSOR_VALID || pCur->curIntKey!=0 );
/* If the cursor is already positioned at the point we are trying
** to move to, then just return without doing any work */
if( pIdxKey==0
&& pCur->eState==CURSOR_VALID && (pCur->curFlags & BTCF_ValidNKey)!=0
){
if( pCur->eState==CURSOR_VALID && (pCur->curFlags & BTCF_ValidNKey)!=0 ){
if( pCur->info.nKey==intKey ){
*pRes = 0;
return SQLITE_OK;
@@ -5506,16 +5493,149 @@ int sqlite3BtreeMovetoUnpacked(
pCur->pBtree->nSeek++; /* Performance measurement during testing */
#endif
if( pIdxKey ){
xRecordCompare = sqlite3VdbeFindCompare(pIdxKey);
pIdxKey->errCode = 0;
assert( pIdxKey->default_rc==1
|| pIdxKey->default_rc==0
|| pIdxKey->default_rc==-1
);
}else{
xRecordCompare = 0; /* All keys are integers */
rc = moveToRoot(pCur);
if( rc ){
if( rc==SQLITE_EMPTY ){
assert( pCur->pgnoRoot==0 || pCur->pPage->nCell==0 );
*pRes = -1;
return SQLITE_OK;
}
return rc;
}
assert( pCur->pPage );
assert( pCur->pPage->isInit );
assert( pCur->eState==CURSOR_VALID );
assert( pCur->pPage->nCell > 0 );
assert( pCur->iPage==0 || pCur->apPage[0]->intKey==pCur->curIntKey );
assert( pCur->curIntKey );
for(;;){
int lwr, upr, idx, c;
Pgno chldPg;
MemPage *pPage = pCur->pPage;
u8 *pCell; /* Pointer to current cell in pPage */
/* pPage->nCell must be greater than zero. If this is the root-page
** the cursor would have been INVALID above and this for(;;) loop
** not run. If this is not the root-page, then the moveToChild() routine
** would have already detected db corruption. Similarly, pPage must
** be the right kind (index or table) of b-tree page. Otherwise
** a moveToChild() or moveToRoot() call would have detected corruption. */
assert( pPage->nCell>0 );
assert( pPage->intKey );
lwr = 0;
upr = pPage->nCell-1;
assert( biasRight==0 || biasRight==1 );
idx = upr>>(1-biasRight); /* idx = biasRight ? upr : (lwr+upr)/2; */
pCur->ix = (u16)idx;
for(;;){
i64 nCellKey;
pCell = findCellPastPtr(pPage, idx);
if( pPage->intKeyLeaf ){
while( 0x80 <= *(pCell++) ){
if( pCell>=pPage->aDataEnd ){
return SQLITE_CORRUPT_PAGE(pPage);
}
}
}
getVarint(pCell, (u64*)&nCellKey);
if( nCellKey<intKey ){
lwr = idx+1;
if( lwr>upr ){ c = -1; break; }
}else if( nCellKey>intKey ){
upr = idx-1;
if( lwr>upr ){ c = +1; break; }
}else{
assert( nCellKey==intKey );
pCur->ix = (u16)idx;
if( !pPage->leaf ){
lwr = idx;
goto moveto_table_next_layer;
}else{
pCur->curFlags |= BTCF_ValidNKey;
pCur->info.nKey = nCellKey;
pCur->info.nSize = 0;
*pRes = 0;
return SQLITE_OK;
}
}
assert( lwr+upr>=0 );
idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2; */
}
assert( lwr==upr+1 || !pPage->leaf );
assert( pPage->isInit );
if( pPage->leaf ){
assert( pCur->ix<pCur->pPage->nCell );
pCur->ix = (u16)idx;
*pRes = c;
rc = SQLITE_OK;
goto moveto_table_finish;
}
moveto_table_next_layer:
if( lwr>=pPage->nCell ){
chldPg = get4byte(&pPage->aData[pPage->hdrOffset+8]);
}else{
chldPg = get4byte(findCell(pPage, lwr));
}
pCur->ix = (u16)lwr;
rc = moveToChild(pCur, chldPg);
if( rc ) break;
}
moveto_table_finish:
pCur->info.nSize = 0;
assert( (pCur->curFlags & BTCF_ValidOvfl)==0 );
return rc;
}
/* Move the cursor so that it points to an entry in an index table
** near the key pIdxKey. Return a success code.
**
** If an exact match is not found, then the cursor is always
** left pointing at a leaf page which would hold the entry if it
** were present. The cursor might point to an entry that comes
** before or after the key.
**
** An integer is written into *pRes which is the result of
** comparing the key with the entry to which the cursor is
** pointing. The meaning of the integer written into
** *pRes is as follows:
**
** *pRes<0 The cursor is left pointing at an entry that
** is smaller than pIdxKey or if the table is empty
** and the cursor is therefore left point to nothing.
**
** *pRes==0 The cursor is left pointing at an entry that
** exactly matches pIdxKey.
**
** *pRes>0 The cursor is left pointing at an entry that
** is larger than pIdxKey.
**
** The pIdxKey->eqSeen field is set to 1 if there
** exists an entry in the table that exactly matches pIdxKey.
*/
int sqlite3BtreeIndexMoveto(
BtCursor *pCur, /* The cursor to be moved */
UnpackedRecord *pIdxKey, /* Unpacked index key */
int *pRes /* Write search results here */
){
int rc;
RecordCompare xRecordCompare;
assert( cursorOwnsBtShared(pCur) );
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
assert( pRes );
assert( pCur->pKeyInfo!=0 );
#ifdef SQLITE_DEBUG
pCur->pBtree->nSeek++; /* Performance measurement during testing */
#endif
xRecordCompare = sqlite3VdbeFindCompare(pIdxKey);
pIdxKey->errCode = 0;
assert( pIdxKey->default_rc==1
|| pIdxKey->default_rc==0
|| pIdxKey->default_rc==-1
);
rc = moveToRoot(pCur);
if( rc ){
@@ -5548,130 +5668,92 @@ int sqlite3BtreeMovetoUnpacked(
assert( pPage->intKey==(pIdxKey==0) );
lwr = 0;
upr = pPage->nCell-1;
assert( biasRight==0 || biasRight==1 );
idx = upr>>(1-biasRight); /* idx = biasRight ? upr : (lwr+upr)/2; */
idx = upr>>1; /* idx = (lwr+upr)/2; */
pCur->ix = (u16)idx;
if( xRecordCompare==0 ){
for(;;){
i64 nCellKey;
pCell = findCellPastPtr(pPage, idx);
if( pPage->intKeyLeaf ){
while( 0x80 <= *(pCell++) ){
if( pCell>=pPage->aDataEnd ){
return SQLITE_CORRUPT_PAGE(pPage);
}
}
}
getVarint(pCell, (u64*)&nCellKey);
if( nCellKey<intKey ){
lwr = idx+1;
if( lwr>upr ){ c = -1; break; }
}else if( nCellKey>intKey ){
upr = idx-1;
if( lwr>upr ){ c = +1; break; }
}else{
assert( nCellKey==intKey );
pCur->ix = (u16)idx;
if( !pPage->leaf ){
lwr = idx;
goto moveto_next_layer;
}else{
pCur->curFlags |= BTCF_ValidNKey;
pCur->info.nKey = nCellKey;
pCur->info.nSize = 0;
*pRes = 0;
return SQLITE_OK;
}
}
assert( lwr+upr>=0 );
idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2; */
}
}else{
for(;;){
int nCell; /* Size of the pCell cell in bytes */
pCell = findCellPastPtr(pPage, idx);
for(;;){
int nCell; /* Size of the pCell cell in bytes */
pCell = findCellPastPtr(pPage, idx);
/* 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 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
** 2 bytes of the cell.
*/
nCell = pCell[0];
if( nCell<=pPage->max1bytePayload ){
/* 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 = xRecordCompare(nCell, (void*)&pCell[1], pIdxKey);
}else if( !(pCell[1] & 0x80)
&& (nCell = ((nCell&0x7f)<<7) + pCell[1])<=pPage->maxLocal
){
/* 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 = xRecordCompare(nCell, (void*)&pCell[2], pIdxKey);
}else{
/* The record flows over onto one or more overflow pages. In
** this case the whole cell needs to be parsed, a buffer allocated
** and accessPayload() used to retrieve the record into the
** buffer before VdbeRecordCompare() can be called.
**
** If the record is corrupt, the xRecordCompare routine may read
** up to two varints past the end of the buffer. An extra 18
** bytes of padding is allocated at the end of the buffer in
** case this happens. */
void *pCellKey;
u8 * const pCellBody = pCell - pPage->childPtrSize;
const int nOverrun = 18; /* Size of the overrun padding */
pPage->xParseCell(pPage, pCellBody, &pCur->info);
nCell = (int)pCur->info.nKey;
testcase( nCell<0 ); /* True if key size is 2^32 or more */
testcase( nCell==0 ); /* Invalid key size: 0x80 0x80 0x00 */
testcase( nCell==1 ); /* Invalid key size: 0x80 0x80 0x01 */
testcase( nCell==2 ); /* Minimum legal index key size */
if( nCell<2 || nCell/pCur->pBt->usableSize>pCur->pBt->nPage ){
rc = SQLITE_CORRUPT_PAGE(pPage);
goto moveto_finish;
}
pCellKey = sqlite3Malloc( nCell+nOverrun );
if( pCellKey==0 ){
rc = SQLITE_NOMEM_BKPT;
goto moveto_finish;
}
pCur->ix = (u16)idx;
rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 0);
memset(((u8*)pCellKey)+nCell,0,nOverrun); /* Fix uninit warnings */
pCur->curFlags &= ~BTCF_ValidOvfl;
if( rc ){
sqlite3_free(pCellKey);
goto moveto_finish;
}
c = sqlite3VdbeRecordCompare(nCell, pCellKey, pIdxKey);
/* 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 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
** 2 bytes of the cell.
*/
nCell = pCell[0];
if( nCell<=pPage->max1bytePayload ){
/* 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 = xRecordCompare(nCell, (void*)&pCell[1], pIdxKey);
}else if( !(pCell[1] & 0x80)
&& (nCell = ((nCell&0x7f)<<7) + pCell[1])<=pPage->maxLocal
){
/* 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 = xRecordCompare(nCell, (void*)&pCell[2], pIdxKey);
}else{
/* The record flows over onto one or more overflow pages. In
** this case the whole cell needs to be parsed, a buffer allocated
** and accessPayload() used to retrieve the record into the
** buffer before VdbeRecordCompare() can be called.
**
** If the record is corrupt, the xRecordCompare routine may read
** up to two varints past the end of the buffer. An extra 18
** bytes of padding is allocated at the end of the buffer in
** case this happens. */
void *pCellKey;
u8 * const pCellBody = pCell - pPage->childPtrSize;
const int nOverrun = 18; /* Size of the overrun padding */
pPage->xParseCell(pPage, pCellBody, &pCur->info);
nCell = (int)pCur->info.nKey;
testcase( nCell<0 ); /* True if key size is 2^32 or more */
testcase( nCell==0 ); /* Invalid key size: 0x80 0x80 0x00 */
testcase( nCell==1 ); /* Invalid key size: 0x80 0x80 0x01 */
testcase( nCell==2 ); /* Minimum legal index key size */
if( nCell<2 || nCell/pCur->pBt->usableSize>pCur->pBt->nPage ){
rc = SQLITE_CORRUPT_PAGE(pPage);
goto moveto_index_finish;
}
pCellKey = sqlite3Malloc( nCell+nOverrun );
if( pCellKey==0 ){
rc = SQLITE_NOMEM_BKPT;
goto moveto_index_finish;
}
pCur->ix = (u16)idx;
rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 0);
memset(((u8*)pCellKey)+nCell,0,nOverrun); /* Fix uninit warnings */
pCur->curFlags &= ~BTCF_ValidOvfl;
if( rc ){
sqlite3_free(pCellKey);
goto moveto_index_finish;
}
assert(
(pIdxKey->errCode!=SQLITE_CORRUPT || c==0)
&& (pIdxKey->errCode!=SQLITE_NOMEM || pCur->pBtree->db->mallocFailed)
);
if( c<0 ){
lwr = idx+1;
}else if( c>0 ){
upr = idx-1;
}else{
assert( c==0 );
*pRes = 0;
rc = SQLITE_OK;
pCur->ix = (u16)idx;
if( pIdxKey->errCode ) rc = SQLITE_CORRUPT_BKPT;
goto moveto_finish;
}
if( lwr>upr ) break;
assert( lwr+upr>=0 );
idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2 */
c = sqlite3VdbeRecordCompare(nCell, pCellKey, pIdxKey);
sqlite3_free(pCellKey);
}
assert(
(pIdxKey->errCode!=SQLITE_CORRUPT || c==0)
&& (pIdxKey->errCode!=SQLITE_NOMEM || pCur->pBtree->db->mallocFailed)
);
if( c<0 ){
lwr = idx+1;
}else if( c>0 ){
upr = idx-1;
}else{
assert( c==0 );
*pRes = 0;
rc = SQLITE_OK;
pCur->ix = (u16)idx;
if( pIdxKey->errCode ) rc = SQLITE_CORRUPT_BKPT;
goto moveto_index_finish;
}
if( lwr>upr ) break;
assert( lwr+upr>=0 );
idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2 */
}
assert( lwr==upr+1 || (pPage->intKey && !pPage->leaf) );
assert( pPage->isInit );
@@ -5680,9 +5762,8 @@ int sqlite3BtreeMovetoUnpacked(
pCur->ix = (u16)idx;
*pRes = c;
rc = SQLITE_OK;
goto moveto_finish;
goto moveto_index_finish;
}
moveto_next_layer:
if( lwr>=pPage->nCell ){
chldPg = get4byte(&pPage->aData[pPage->hdrOffset+8]);
}else{
@@ -5692,7 +5773,7 @@ moveto_next_layer:
rc = moveToChild(pCur, chldPg);
if( rc ) break;
}
moveto_finish:
moveto_index_finish:
pCur->info.nSize = 0;
assert( (pCur->curFlags & BTCF_ValidOvfl)==0 );
return rc;
@@ -8798,7 +8879,8 @@ int sqlite3BtreeInsert(
** to an adjacent cell. Move the cursor so that it is pointing either
** to the cell to be overwritten or an adjacent cell.
*/
rc = sqlite3BtreeMovetoUnpacked(pCur, 0, pX->nKey, flags!=0, &loc);
rc = sqlite3BtreeTableMoveto(pCur, pX->nKey,
(flags & BTREE_APPEND)!=0, &loc);
if( rc ) return rc;
}
}else{
@@ -8825,9 +8907,10 @@ int sqlite3BtreeInsert(
r.r1 = 0;
r.r2 = 0;
r.eqSeen = 0;
rc = sqlite3BtreeMovetoUnpacked(pCur, &r, 0, flags!=0, &loc);
rc = sqlite3BtreeIndexMoveto(pCur, &r, &loc);
}else{
rc = btreeMoveto(pCur, pX->pKey, pX->nKey, flags!=0, &loc);
rc = btreeMoveto(pCur, pX->pKey, pX->nKey,
(flags & BTREE_APPEND)!=0, &loc);
}
if( rc ) return rc;
}