-C Properly\sfix\stemp\striggers\screated\sas\spart\sof\sFK\sprocessing\sto\stheir\sschemas.\sOtherwise\sthey\smay\sbecome\sconfused\sby\ssimilarly\snamed\schild\stables\sin\sother\sattached\sdatabases.\sFix\sfor\sforum\spost\s[forum:636bd0180a\s|\s636bd0180a].
-D 2026-03-16T11:14:26.814
+C Make\sOP_IdxDelete\stolerant\sof\ssmall\svariations\sin\sindex\skey\svalues.
+D 2026-03-16T15:16:13.147
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F src/utf.c 7267c3fb9e2467020507601af3354c2446c61f444387e094c779dccd5ca62165
F src/util.c 0cb2e590e9dcac6807352017fcbf5a52e0f836d74a338cb8c02ee3162bcf6508
F src/vacuum.c d3d35d8ae893d419ade5fa196d761a83bddcbb62137a1a157ae751ef38b26e82
-F src/vdbe.c 5328c99dd256ee8132383565a86e253543a85daccfd7477c52f20bac6b385a7f
+F src/vdbe.c 303e8070a9d77509c754c2948cf1b61944930f1136001733e2bd5daaadefb8d1
F src/vdbe.h 966d0677a540b7ea6549b7c4e1312fc0d830fce3a235a58c801f2cc31cf5ecf9
-F src/vdbeInt.h 42488247a80cd9d300627833c6c85ace067ae5011a99e7614e2358130d62feea
+F src/vdbeInt.h e2ac1de191ba71b98daee4757950548f00b8bb169a794cf8cde51587cb147fe9
F src/vdbeapi.c 6cdcbe5c7afa754c998e73d2d5d2805556268362914b952811bdfb9c78a37cf1
-F src/vdbeaux.c 396d38a62a357b807eabae0cae441fc89d2767a57ab08026b7072bf7aa2dd00c
+F src/vdbeaux.c 4885cfc6a58b8bf7a8a367673649057dc4a076c0c8797edb6bf1470bb082a31d
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 dc78d258745a1350685b37da56f0a2ac2e437b422415d634522dc61d340d06eb
-R f4cdffcb37cefef0e7ac6ef7ca48fea4
+P 80bc5bc07e221f837c28066f0a438f11c8ab6be4c8ba93615439eb1667967003
+R 5a3b7bcea17f97d45cd400c7992de61b
+T *branch * idxdelete-tolerance
+T *sym-idxdelete-tolerance *
+T -sym-trunk *
U dan
-Z d215803567bcaa7157c0e213d8c093a7
+Z 7bca9a15e7fe96dadf1d72ae49f0c8d2
# Remove this line to create a well-formed Fossil manifest.
r.aMem = &aMem[pOp->p2];
rc = sqlite3BtreeIndexMoveto(pCrsr, &r, &res);
if( rc ) goto abort_due_to_error;
- if( res==0 ){
- rc = sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE);
- if( rc ) goto abort_due_to_error;
- }else if( !sqlite3WritableSchema(db) ){
- rc = sqlite3ReportError(SQLITE_CORRUPT_INDEX, __LINE__, "index corruption");
- goto abort_due_to_error;
+ if( res!=0 ){
+ rc = sqlite3VdbeFindDeleteKey(pCrsr, &r, &res);
+ if( rc!=SQLITE_OK ) goto abort_due_to_error;
+ if( res!=0 ){
+ if( !sqlite3WritableSchema(db) ){
+ rc = sqlite3ReportError(
+ SQLITE_CORRUPT_INDEX, __LINE__, "index corruption");
+ goto abort_due_to_error;
+ }
+ pC->cacheStatus = CACHE_STALE;
+ pC->seekResult = 0;
+ break;
+ }
}
+ rc = sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE);
+ if( rc ) goto abort_due_to_error;
assert( pC->deferredMoveto==0 );
pC->cacheStatus = CACHE_STALE;
pC->seekResult = 0;
}
}
+/*
+** 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.
+*/
+static int vdbeIsDeleteKey(
+ BtCursor *pCur, /* Cursor open on index */
+ UnpackedRecord *p, /* Index key being deleted */
+ int *piRes /* 0 for a match, non-zero for not a match */
+){
+ u8 *aRec = 0;
+ u32 nRec = 0;
+ Mem mem;
+ int rc = SQLITE_OK;
+
+ memset(&mem, 0, sizeof(mem));
+ mem.enc = p->pKeyInfo->enc;
+ mem.db = p->pKeyInfo->db;
+ nRec = sqlite3BtreePayloadSize(pCur);
+ aRec = sqlite3MallocZero(nRec);
+ if( aRec==0 ){
+ rc = SQLITE_NOMEM_BKPT;
+ }else{
+ rc = sqlite3BtreePayload(pCur, 0, nRec, aRec);
+ }
+
+ 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 */
+
+ idxRec = szHdr;
+ nCol = p->pKeyInfo->nAllField;
+ for(ii=0; ii<nCol && rc==SQLITE_OK; ii++){
+ u32 iSerial = 0;
+ int nSerial = 0;
+ int r2 = 0;
+
+ idxHdr += getVarint32(&aRec[idxHdr], iSerial);
+ nSerial = sqlite3VdbeSerialTypeLen(iSerial);
+ if( (idxRec+nSerial)>nRec ){
+ rc = SQLITE_CORRUPT_BKPT;
+ }else{
+ sqlite3VdbeSerialGet(&aRec[idxRec], iSerial, &mem);
+ idxRec += sqlite3VdbeSerialTypeLen(iSerial);
+ r2 = sqlite3MemCompare(&mem, &p->aMem[ii], p->pKeyInfo->aColl[ii]);
+ if( r2!=0 ){
+ if( ii<p->pKeyInfo->nKeyField ){
+ if( recres==0 ) recres = r2;
+ }else{
+ res = recres;
+ if( res==0 ) res = r2;
+ break;
+ }
+ }
+ }
+ }
+
+ *piRes = res;
+ }
+ }
+
+ sqlite3_free(aRec);
+ return rc;
+}
+
+/*
+** This is called when the record in (*p) is to be deleted from the index
+** opened by cursor pCur, but an exact match for the record was not found
+** in the index. The result of searching for it was (*pRes) - if (*pRes)
+** is -1, then the cursor points at a record that is smaller than (*p),
+** if it is +1, then it points to a record greater than (*p).
+**
+** One reason that an exact match was not found may be the EIIB bug - that
+** a text-to-float conversion may have caused a real value in record (*p)
+** to be slightly different from its counterpart on disk. This function
+** attempts to find the right record to delete. If it does find the right
+** record, it leaves *pCur pointing to it and sets (*pRes) to 0 before
+** returning. Otherwise, (*pRes) is set to non-zero and an SQLite error
+** code returned.
+**
+** 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.
+**
+** * 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).
+*/
+int sqlite3VdbeFindDeleteKey(BtCursor *pCur, UnpackedRecord *p, int *pRes){
+ int resCaller = *pRes;
+ int res = resCaller;
+ int rc = SQLITE_OK;
+
+ assert( resCaller==-1 || resCaller==0 || resCaller==+1 );
+ while( sqlite3BtreeEof(pCur)==0 && rc==SQLITE_OK ){
+ rc = vdbeIsDeleteKey(pCur, 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);
+ }
+ }
+ }
+
+ if( rc==SQLITE_DONE ) rc = SQLITE_OK;
+ *pRes = res;
+ return rc;
+}
+
#ifndef SQLITE_OMIT_DATETIME_FUNCS
/*
** Cause a function to throw an error if it was call from OP_PureFunc