]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Detect and omit cases where an index key is being replaced with an identical key.
authordan <Dan Kennedy>
Wed, 22 Apr 2026 16:58:36 +0000 (16:58 +0000)
committerdan <Dan Kennedy>
Wed, 22 Apr 2026 16:58:36 +0000 (16:58 +0000)
FossilOrigin-Name: 7a01caf0d0864a462eb81b7643dcf89048f4ae56a29d762f0cfc38066218504f

manifest
manifest.tags
manifest.uuid
src/delete.c
src/vdbe.c

index 8fa43adb3091a1c4356d4fd80a85f003dcfea2d9..df6ce7afdcdd0897c3d67cd49d668c64f2b5f300 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\sa\sinteger\stype\sbug\sin\sthe\sprevious\scheck-in.
-D 2026-04-22T13:41:20.203
+C Detect\sand\somit\scases\swhere\san\sindex\skey\sis\sbeing\sreplaced\swith\san\sidentical\skey.
+D 2026-04-22T16:58:36.117
 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
@@ -685,7 +685,7 @@ F src/complete.c f216b970ce99c5a657556cf1f17e7ddd494515d3beb63df426bf59ff43bd3d9
 F src/date.c 61e92f1f7e2e88e1cd91e91dc69eb2b2854e7877254470f9fabd776bfac922b8
 F src/dbpage.c c9ea81c11727f27e02874611e92773e68e2a90a875ef2404b084564c235fd91f
 F src/dbstat.c 73362c0df0f40ad5523a6f5501224959d0976757b511299bf892313e79d14f5c
-F src/delete.c 1f2268d6fe3c78fc1bf794ba65d7026498b78e2342ffaf85825dedae546e6fde
+F src/delete.c dbde329841d214a85b1fa9f4c7ce9f53e4fc3066d2f5d491b3364cd43ec3bc14
 F src/expr.c 68400681c5f6e41231d2c85abf6bb432aeeb2e36c4abdf90eb7b78551a5ce0f3
 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
 F src/fkey.c 931f74cec1dc8038a0217ef340c91ce147dd1bbed08dc40c47ee0ec6edfffb08
@@ -802,7 +802,7 @@ F src/upsert.c 215328c3f91623c520ec8672c44323553f12caeb4f01b1090ebdca99fdf7b4f1
 F src/utf.c 7267c3fb9e2467020507601af3354c2446c61f444387e094c779dccd5ca62165
 F src/util.c 377af5da226519a0f374dc3c6d408c9d303a92943e3ae5986432c7d52e6679a2
 F src/vacuum.c d3d35d8ae893d419ade5fa196d761a83bddcbb62137a1a157ae751ef38b26e82
-F src/vdbe.c 6c57525d7db0232d52687d30da1093db0c152f14206c2ef1adf0c19a09d863e3
+F src/vdbe.c aeb80f9f9e3b7750b14dcaa439bbf026a4c71cd1881da86d04dfa1348ca8a474
 F src/vdbe.h 70e862ac8a11b590f8c1eaac17a0078429d42bc4ea3f757a9af0f451dd966a71
 F src/vdbeInt.h c31ba4dc8d280c2b1dc89c6fcee68f2555e3813ab34279552c20b964c0e338b1
 F src/vdbeapi.c 6cdcbe5c7afa754c998e73d2d5d2805556268362914b952811bdfb9c78a37cf1
@@ -2202,8 +2202,11 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee
 F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
 F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c
-P dbc1d6f0a3452607e4e92155df0fdb1c5ea4ea5ad1654664f86383faf6c105ef
-R feff29d9635a2e79cc3708641148218b
-U drh
-Z 064078e48b7f7788e91c44276a045e81
+P 5e1b5ff1c170e94956bf77bd38afcad6369cb9f9333864711212af045281ad0e
+R 6a9d04ec755fb9001a47ce242326fc64
+T *branch * index-detect-noop
+T *sym-index-detect-noop *
+T -sym-trunk *
+U dan
+Z bd48131fa723540fccfa324eb37cb0d1
 # Remove this line to create a well-formed Fossil manifest.
index bec971799ff1b8ee641c166c7aeb22d12c785393..0a396c097d11ca7aff867db57435585ba8609fed 100644 (file)
@@ -1,2 +1,2 @@
-branch trunk
-tag trunk
+branch index-detect-noop
+tag index-detect-noop
index 493050eeb16cca66dc8fdd2c1b01e035e08858dc..d88f4113444281c1c87b3d40df2130d478cc7871 100644 (file)
@@ -1 +1 @@
-5e1b5ff1c170e94956bf77bd38afcad6369cb9f9333864711212af045281ad0e
+7a01caf0d0864a462eb81b7643dcf89048f4ae56a29d762f0cfc38066218504f
index 95020c4df92f6f3e0c570e17b8dc59d10cde00a9..e450c2ad6468702e5b89348e20ff1b693a9400cd 100644 (file)
@@ -922,10 +922,9 @@ void sqlite3GenerateRowIndexDelete(
     VdbeModuleComment((v, "GenRowIdxDel for %s", pIdx->zName));
     r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 1,
         &iPartIdxLabel, pPrior, r1);
-    sqlite3VdbeAddOp3(v, OP_IdxDelete, iIdxCur+i, r1,
-        pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn
-    );
+    sqlite3VdbeAddOp3(v, OP_IdxDelete, iIdxCur+i, r1, aRegIdx ? aRegIdx[i] : 0);
     sqlite3VdbeChangeP4(v, -1, (const char*)pIdx, P4_INDEX);
+    sqlite3VdbeChangeP5(v, pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn);
     sqlite3ResolvePartIdxLabel(pParse, iPartIdxLabel);
     pPrior = pIdx;
   }
index 20b96d4721a487bbb7132bea4a34f027292491d0..e28b61d01eefcc151789ee143a8e799f76393944 100644 (file)
@@ -708,7 +708,6 @@ static u64 filterHash(const Mem *aMem, const Op *pOp){
   return h;
 }
 
-
 /*
 ** For OP_Column, factor out the case where content is loaded from
 ** overflow pages, so that the code to implement this case is separate
@@ -794,6 +793,40 @@ static SQLITE_NOINLINE int vdbeColumnFromOverflow(
   return rc;
 }
 
+/*
+** Memory cell pMem may contain a blob or a NULL value. Cursor pCsr is
+** open on an index. If the current index entry matches the blob value in
+** pMem byte-for-byte, set pMem to NULL and return 1. Otherwise, return 0.
+**
+** If an error occurs, set (*pRc) to an SQLite error code. Return 1 in this
+** case as well.
+*/
+static SQLITE_NOINLINE int vdbeIndexKeyCompare(
+  BtCursor *pCsr,                 /* Cursor to compare key to */
+  Mem *pMem,
+  int *pRc
+){
+  int ret = 0;
+  u32 nKey = 0;
+
+  assert( pMem->flags & (MEM_Blob|MEM_Null) );
+  nKey = sqlite3BtreePayloadSize(pCsr);
+  if( nKey==pMem->n && (pMem->flags & MEM_Blob) ){
+    /* This code could just use sqlite3BtreePayloadFetch(). But calling that
+    ** function here apparently prevents compilers from inlining it in other,
+    ** more performance critical, places. So this code uses
+    ** MemFromBtreeZeroOffset(), which is just as fast in most cases, but also
+    ** handles the case where the index record uses overflow pages. */
+    Mem m;
+    memset(&m, 0, sizeof(m));
+    *pRc = sqlite3VdbeMemFromBtreeZeroOffset(pCsr, nKey, &m);
+    ret = (*pRc!=SQLITE_OK || 0==memcmp(pMem->z, m.z, nKey));
+    sqlite3VdbeMemReleaseMalloc(&m);
+  }
+
+  return ret;
+}
+
 /*
 ** Send a "statement aborts" message to the error log.
 */
@@ -6577,6 +6610,7 @@ case OP_IdxInsert: {        /* in2 */
   assert( pC!=0 );
   assert( !isSorter(pC) );
   pIn2 = &aMem[pOp->p2];
+  if( (pIn2->flags & MEM_Null) && (pOp->p5 & OPFLAG_PREFORMAT)==0 ) break;
   assert( (pIn2->flags & MEM_Blob) || (pOp->p5 & OPFLAG_PREFORMAT) );
   if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++;
   assert( pC->eCurType==CURTYPE_BTREE );
@@ -6622,8 +6656,8 @@ case OP_SorterInsert: {     /* in2 */
   break;
 }
 
-/* Opcode: IdxDelete P1 P2 P3 P4 *
-** Synopsis: key=r[P2@P3]
+/* Opcode: IdxDelete P1 P2 P3 P4 P5
+** Synopsis: key=r[P2@P5]
 **
 ** The content of P3 registers starting at register P2 form
 ** an unpacked index key. This opcode removes that entry from the
@@ -6631,6 +6665,11 @@ case OP_SorterInsert: {     /* in2 */
 **
 ** P4 is a pointer to an Index structure.
 **
+** If P3 is non-zero, it is the register number of a register holding
+** a record that will be inserted into this index. If that record is
+** identical to the one that would be deleted by this instruction, 
+** skip the delete and set register P3 to NULL.
+**
 ** Raise an SQLITE_CORRUPT_INDEX error if no matching index entry is found
 ** and not in writable_schema mode.
 */
@@ -6640,8 +6679,8 @@ case OP_IdxDelete: {
   int res;
   UnpackedRecord r;
 
-  assert( pOp->p3>0 );
-  assert( pOp->p2>0 && pOp->p2+pOp->p3<=(p->nMem+1 - p->nCursor)+1 );
+  assert( pOp->p5>0 );
+  assert( pOp->p2>0 && pOp->p2+pOp->p5<=(p->nMem+1 - p->nCursor)+1 );
   assert( pOp->p1>=0 && pOp->p1<p->nCursor );
   pC = p->apCsr[pOp->p1];
   assert( pC!=0 );
@@ -6650,7 +6689,7 @@ case OP_IdxDelete: {
   pCrsr = pC->uc.pCursor;
   assert( pCrsr!=0 );
   r.pKeyInfo = pC->pKeyInfo;
-  r.nField = (u16)pOp->p3;
+  r.nField = pOp->p5;
   r.default_rc = 0;
   r.aMem = &aMem[pOp->p2];
   rc = sqlite3BtreeIndexMoveto(pCrsr, &r, &res);
@@ -6669,6 +6708,13 @@ case OP_IdxDelete: {
       break;
     }
   }
+
+  if( pOp->p3 && vdbeIndexKeyCompare(pCrsr, &aMem[pOp->p3], &rc) ){
+    if( rc ) goto abort_due_to_error;
+    sqlite3VdbeMemSetNull(&aMem[pOp->p3]);
+    break;
+  }
+
   rc = sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE);
   if( rc ) goto abort_due_to_error;
   assert( pC->deferredMoveto==0 );