-C Fix\sa\sproblem\swith\sthe\sfind-delete-key\smethod\son\sthis\sbranch.
-D 2026-03-16T15:49:58.396
+C If\sOP_IdxDelete\scannot\sfind\san\sindex\sentry\sto\sdelete,\sfirst\ssearch\s10\sentries\sin\seither\sdirection\sof\swhere\sthe\sentry\sshould\sbe\sin\sthe\sindex.\sIf\sthat\sfails,\ssearch\sthe\sentire\sindex\sfor\san\sentry\swith\smatching\sPK\sfields.
+D 2026-03-16T16:21:43.376
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F src/vdbe.h 966d0677a540b7ea6549b7c4e1312fc0d830fce3a235a58c801f2cc31cf5ecf9
F src/vdbeInt.h 68d4adddb1eb48cdbaff8cce1305ac6bc5ad9115f1a37087a13d01a045303e7a
F src/vdbeapi.c 6cdcbe5c7afa754c998e73d2d5d2805556268362914b952811bdfb9c78a37cf1
-F src/vdbeaux.c 6af3a26cd0ecde496eb3a0ad5ac90da098ce923a5c93f48c8d4f0bbc768863bc
+F src/vdbeaux.c df278c8a46a1e8ff9610f166167966de20b216016550d0d4fec26503c4e55006
F src/vdbeblob.c b3f0640db9642fbdc88bd6ebcc83d6009514cafc98f062f675f2c8d505d82692
F src/vdbemem.c 317ec5e870ddb16951b606c9fe8be22baef22ecbe46f58fdefc259662238afb7
F src/vdbesort.c b69220f4ea9ffea5fdef34d968c60305444eea909252a81933b54c296d9cca70
F tool/warnings.sh d924598cf2f55a4ecbc2aeb055c10bd5f48114793e7ba25f9585435da29e7e98
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c
-P c4b0081f08bd0458dbcf269b43a4051941eec8067393aa8e6810d4b8422ce44a
-R a18915b4118c3d28a02387e1bb9fac1b
+P 6f49b5ecad5d924999d045a1ade93d95e731182be1781aefa5bc763b6202e9c3
+R 07aad91547589b98e374a31de7dba336
U dan
-Z ddfe823bdcd5bb4257cbd5344ccd28d9
+Z da974e9658962ed0763a3b684d15cc9b
# Remove this line to create a well-formed Fossil manifest.
/*
** This function compares the unpacked record with the current key that
-** cursor pCur points to. It returns the usual less than zero, zero, or
-** greater than zero if the cursor key is less than, equal to or greater
-** than p. i.e.
-**
-** (pCur->pKey) - (p)
-**
-** Except that:
-**
-** * if the PK fields of the keys match, zero is always returned, even
-** if the preceding fields do not match.
-**
-** * otherwise, if the preceding fields are not identical, the result
-** of comparing them is returned.
-**
-** * finally, if the preceding fields all match but the PK fields do
-** not, the result of comparing the PK fields is returned.
-**
-** This function is not optimized. It is not expected to be called often.
+** cursor pCur points to, ignoring the first nKeyCol fields. It returns
+** the usual less than zero, zero, or greater than zero if the remaining
+** fields of the cursor cursor key are less than, equal to or greater
+** than those in (*p).
*/
static int vdbeIsDeleteKey(
BtCursor *pCur, /* Cursor open on index */
if( rc==SQLITE_OK ){
int szHdr = 0; /* Size of record header in bytes */
int idxHdr = 0; /* Current index in header */
- int idxRec = 0; /* Current index in record */
- int ii = 0;
- int nCol = 0;
- int res = 0;
idxHdr = getVarint32(aRec, szHdr);
if( szHdr>98307 ){
rc = SQLITE_CORRUPT;
}else{
- int recres = 0; /* Result of comparing record fields */
int res = 0; /* Result of this function call */
+ int idxRec = szHdr; /* Index of next field in record body */
+ int ii = 0; /* Iterator variable */
- idxRec = szHdr;
- nCol = p->pKeyInfo->nAllField;
+ int nCol = p->pKeyInfo->nAllField;
for(ii=0; ii<nCol && rc==SQLITE_OK; ii++){
u32 iSerial = 0;
int nSerial = 0;
nSerial = sqlite3VdbeSerialTypeLen(iSerial);
if( (idxRec+nSerial)>nRec ){
rc = SQLITE_CORRUPT_BKPT;
- }else{
+ }else if( ii>=nKeyCol ){
sqlite3VdbeSerialGet(&aRec[idxRec], iSerial, &mem);
- idxRec += sqlite3VdbeSerialTypeLen(iSerial);
- r2 = sqlite3MemCompare(&mem, &p->aMem[ii], p->pKeyInfo->aColl[ii]);
- if( r2!=0 ){
- if( ii<nKeyCol ){
- if( recres==0 ) recres = r2;
- }else{
- res = recres;
- if( res==0 ) res = r2;
- break;
- }
- }
+ res = sqlite3MemCompare(&mem, &p->aMem[ii], p->pKeyInfo->aColl[ii]);
+ if( res!=0 ) break;
}
+ idxRec += sqlite3VdbeSerialTypeLen(iSerial);
}
*piRes = res;
**
** The algorithm used to find the correct record is:
**
-** * Test the PK columns of the current record to see if they match (*p).
-** If so, delete the current record.
+** * Scan up to BTREE_FDK_RANGE entries either side of the current entry.
**
-** * If the caller's (*pRes) value was -ve, advance the cursor forward one
-** entry. Then test the PK fields again. Repeat until the cursor points
-** to an entry larger than (*p).
-**
-** * Or, if the caller's (*pRes) value was +ve, move the cursor backwards
-** one entry. Then test the PK fields again. Repeat until the cursor
-** points to an entry larger than (*p).
+** * If the above fails to find an entry to delete, search the entire index.
*/
int sqlite3VdbeFindDeleteKey(
BtCursor *pCur,
UnpackedRecord *p,
int *pRes
){
- int resCaller = *pRes;
- int res = resCaller;
+#define BTREE_FDK_RANGE 10
+ int nStep = 0;
+ int res = 1;
int rc = SQLITE_OK;
+ int ii = 0;
+
+ /* Move the cursor back BTREE_FDK_RANGE entries. If this hits an EOF,
+ ** position the cursor at the first entry in the index and set nStep
+ ** to -1 so that the first loop below scans the entire index. Otherwise,
+ ** set nStep to BTREE_FDK_RANGE*2 so that the first loop below scans
+ ** just that many entries. */
+ for(ii=0; sqlite3BtreeEof(pCur)==0 && ii<BTREE_FDK_RANGE; ii++){
+ rc = sqlite3BtreePrevious(pCur, 0);
+ }
+ if( rc==SQLITE_DONE ){
+ rc = sqlite3BtreeFirst(pCur, &res);
+ nStep = -1;
+ }else{
+ nStep = BTREE_FDK_RANGE*2;
+ }
- assert( resCaller==-1 || resCaller==0 || resCaller==+1 );
- while( sqlite3BtreeEof(pCur)==0 && rc==SQLITE_OK ){
- rc = vdbeIsDeleteKey(pCur, nKeyCol, p, &res);
- assert( res==-1 || res==0 || res==+1 );
- if( res!=resCaller ) break;
-
- if( rc==SQLITE_OK ){
- if( resCaller<0 ){
- rc = sqlite3BtreeNext(pCur, 0);
- }else{
- rc = sqlite3BtreePrevious(pCur, 0);
- }
+ /* This loop runs at most twice to search for a key with matching PK
+ ** fields in the index. The second iteration always searches the entire
+ ** index. The first iteration searches nStep entries starting with the
+ ** current cursor entry if (nStep>=0), or the entire index if (nStep<0). */
+ while( 1 ){
+ for(ii=0; rc==SQLITE_OK && (ii<nStep || nStep<0); ii++){
+ rc = vdbeIsDeleteKey(pCur, nKeyCol, p, &res);
+ if( res==0 || rc!=SQLITE_OK ) break;
+ rc = sqlite3BtreeNext(pCur, 0);
}
+ if( rc==SQLITE_DONE ){
+ rc = SQLITE_OK;
+ assert( res!=0 );
+ }
+ if( nStep<0 || rc!=SQLITE_OK || res==0 ) break;
+
+ /* The first, non-exhaustive, search failed to find an entry with
+ ** matching PK fields. So restart for an exhaustive search of the
+ ** entire index. */
+ nStep = -1;
+ rc = sqlite3BtreeFirst(pCur, &res);
}
- if( rc==SQLITE_DONE ) rc = SQLITE_OK;
*pRes = res;
return rc;
}