]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Change the sqlite3VdbeFindDeleteKey() routine so that all index fields that are not...
authordan <Dan Kennedy>
Tue, 17 Mar 2026 11:17:54 +0000 (11:17 +0000)
committerdan <Dan Kennedy>
Tue, 17 Mar 2026 11:17:54 +0000 (11:17 +0000)
FossilOrigin-Name: eccd9fcfa7e34ddcdeb671544c6afac7aa0d4665c5e1df7c8e53c41387ed0386

manifest
manifest.uuid
src/delete.c
src/vdbe.c
src/vdbe.h
src/vdbeInt.h
src/vdbeaux.c
test/eiib1.test

index c7c3cd89c7fd4434c156c5803ec5e0fc58ea049a..8a8f30db66d8dc19380c2ecd40ec267e4cfd554f 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Merge\strunk\senhancements\sinto\sthe\sidxdelete-tolerance\sbranch
-D 2026-03-17T09:53:02.101
+C Change\sthe\ssqlite3VdbeFindDeleteKey()\sroutine\sso\sthat\sall\sindex\sfields\sthat\sare\snot\sexpressions\sor\svirtual\scolumns\smust\smatch\sthe\stable\sfor\sthe\sdelete\sto\ssucceed.
+D 2026-03-17T11:17:54.759
 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
@@ -682,7 +682,7 @@ F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
 F src/date.c 61e92f1f7e2e88e1cd91e91dc69eb2b2854e7877254470f9fabd776bfac922b8
 F src/dbpage.c c9ea81c11727f27e02874611e92773e68e2a90a875ef2404b084564c235fd91f
 F src/dbstat.c 73362c0df0f40ad5523a6f5501224959d0976757b511299bf892313e79d14f5c
-F src/delete.c cc318f7e5e9d9606c3600c7af96e836597f0f4d77ff7914ef03c5f906f4a43b4
+F src/delete.c 39258c930aabe3e4949cfa634f27684a1a4dd27fbd3c0e151bc968d007d1f84f
 F src/expr.c 51e9c77ff5d9a21439e611fe6571a3cd50387e526e13c5614fd407e5b8571930
 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
 F src/fkey.c 931f74cec1dc8038a0217ef340c91ce147dd1bbed08dc40c47ee0ec6edfffb08
@@ -799,11 +799,11 @@ F src/upsert.c 215328c3f91623c520ec8672c44323553f12caeb4f01b1090ebdca99fdf7b4f1
 F src/utf.c 7267c3fb9e2467020507601af3354c2446c61f444387e094c779dccd5ca62165
 F src/util.c cf91389b58590edfb5978199ef59488ef8e3723e1ba1aa0ff15c62f8a658b95f
 F src/vacuum.c d3d35d8ae893d419ade5fa196d761a83bddcbb62137a1a157ae751ef38b26e82
-F src/vdbe.c 31ca4aaad332c3b973c5fb74bc76972a856fcb99c581e68efc35b6e9299a6d6c
-F src/vdbe.h 966d0677a540b7ea6549b7c4e1312fc0d830fce3a235a58c801f2cc31cf5ecf9
-F src/vdbeInt.h 68d4adddb1eb48cdbaff8cce1305ac6bc5ad9115f1a37087a13d01a045303e7a
+F src/vdbe.c 8442ec7a77cca0b8c465bfcc57b1f548865aec44e8cf16aa21adb6772a2803b5
+F src/vdbe.h fd7a85ab0d538db5c036b06c9c2e6493d6df4b22ce6f7c3ef64799d71fbbaa1c
+F src/vdbeInt.h e876d6c5fd5f2eee4d818d49dabd615c88147a131f773edf5bd329e5b66cd365
 F src/vdbeapi.c 6cdcbe5c7afa754c998e73d2d5d2805556268362914b952811bdfb9c78a37cf1
-F src/vdbeaux.c f2f97545ace70c7d3171b3a38c6313e59c317916370a74fc82fadf0c924e6e79
+F src/vdbeaux.c 34641d8268fcf86434eb8fe7d0afb4bcd858e0107c2df3f341e7e76c25eb3bc9
 F src/vdbeblob.c b3f0640db9642fbdc88bd6ebcc83d6009514cafc98f062f675f2c8d505d82692
 F src/vdbemem.c 317ec5e870ddb16951b606c9fe8be22baef22ecbe46f58fdefc259662238afb7
 F src/vdbesort.c b69220f4ea9ffea5fdef34d968c60305444eea909252a81933b54c296d9cca70
@@ -1081,7 +1081,7 @@ F test/e_wal.test db7c33642711cf3c7959714b5f012aca08cacfa78da0382f95e849eb3ba66a
 F test/e_walauto.test 248af31e73c98df23476a22bdb815524c9dc3ba8
 F test/e_walckpt.test 16e7d006e8687654ee59e7ad5a6d285ba23f0fe0eeb87f790afd6bc9cf1d1924
 F test/e_walhook.test 01b494287ba9e60b70f6ebf3c6c62e0ffe01788e344a4846b08e5de0b344cb66
-F test/eiib1.test b1e4361242f6215dd82ca3903bbec7832f1085f68bbf9d4d773ab0fbc71e924c
+F test/eiib1.test 92df1db7db9ba2024e4151019caf0d2f00b7855b548e8df4e773ec963b6fbe0c
 F test/emptytable.test a38110becbdfa6325cd65cb588dca658cd885f62
 F test/enc.test b5503a87b31cea8a5084c6e447383f9ca08933bd2f29d97b6b6201081b2343eb
 F test/enc2.test 872afe58db772e7dfa1ad8e0759f8cc820e9efc8172d460fae83023101c2e435
@@ -2194,8 +2194,8 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee
 F tool/warnings.sh d924598cf2f55a4ecbc2aeb055c10bd5f48114793e7ba25f9585435da29e7e98
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
 F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c
-P f66cb3dc275d3f31e57576466e697a6c7c5179144babab08d75b624e9846ecc3 704ddfd0df57c2b137af88264df20a540e3f8f114a58ae5e31b40ab8259bb63e
-R 1a6d1a6d80f42677ecead1da6c93305a
-U drh
-Z 4d0938105db2727fa5b7a43131c4525b
+P 5f5ab3eb3618b6a2799355df50bfdf94121161f7cadba6253963036d9f284f08
+R 3a87a584039b1feb9335e171078664f3
+U dan
+Z 85992f44adf014214f0cee4e807ed494
 # Remove this line to create a well-formed Fossil manifest.
index 841286647660dd873d6269dd47696b02ad445bf6..537c8aa23c9b60938429516ecea07f994734ce78 100644 (file)
@@ -1 +1 @@
-5f5ab3eb3618b6a2799355df50bfdf94121161f7cadba6253963036d9f284f08
+eccd9fcfa7e34ddcdeb671544c6afac7aa0d4665c5e1df7c8e53c41387ed0386
index 8507da9dab570760f6e6664f0a1eaab7aa766f7a..e89a8c8e9620baf42e744a9400cd043dec21aab8 100644 (file)
@@ -922,8 +922,10 @@ void sqlite3GenerateRowIndexDelete(
     VdbeModuleComment((v, "GenRowIdxDel for %s", pIdx->zName));
     r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 1,
         &iPartIdxLabel, pPrior, r1);
-    sqlite3VdbeAddOp4Int(v, OP_IdxDelete, iIdxCur+i, r1,
-        pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn, pIdx->nKeyCol);
+    sqlite3VdbeAddOp3(v, OP_IdxDelete, iIdxCur+i, r1,
+        pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn
+    );
+    sqlite3VdbeChangeP4(v, -1, pIdx, P4_INDEX);
     sqlite3ResolvePartIdxLabel(pParse, iPartIdxLabel);
     pPrior = pIdx;
   }
index 34e325eec2391b02cf8a613b7c42c71746d95569..b643f1e1e949342f90930ce5af628e6072401c52 100644 (file)
@@ -6629,7 +6629,7 @@ case OP_SorterInsert: {     /* in2 */
 ** an unpacked index key. This opcode removes that entry from the
 ** index opened by cursor P1.
 **
-** P4 is the number of non-PK columns in the index entry.
+** P4 is a pointer to an Index structure.
 **
 ** Raise an SQLITE_CORRUPT_INDEX error if no matching index entry is found
 ** and not in writable_schema mode.
@@ -6656,7 +6656,7 @@ case OP_IdxDelete: {
   rc = sqlite3BtreeIndexMoveto(pCrsr, &r, &res);
   if( rc ) goto abort_due_to_error;
   if( res!=0 ){
-    rc = sqlite3VdbeFindDeleteKey(pCrsr, pOp->p4.i, &r, &res);
+    rc = sqlite3VdbeFindDeleteKey(pCrsr, pOp->p4.pIdx, &r, &res);
     if( rc!=SQLITE_OK ) goto abort_due_to_error;
     if( res!=0 ){
       if( !sqlite3WritableSchema(db) ){
index a2905eae4d382657bb3a9da1182f0ed080385593..312feb3dcce1ec1df909879a17e070caa2c2951c 100644 (file)
@@ -75,6 +75,7 @@ struct VdbeOp {
     SubProgram *pProgram;  /* Used when p4type is P4_SUBPROGRAM */
     Table *pTab;           /* Used when p4type is P4_TABLE */
     SubrtnSig *pSubrtnSig; /* Used when p4type is P4_SUBRTNSIG */
+    Index *pIdx;           /* Used when p4type is P4_INDEX */
 #ifdef SQLITE_ENABLE_CURSOR_HINTS
     Expr *pExpr;           /* Used when p4type is P4_EXPR */
 #endif
@@ -129,20 +130,21 @@ typedef struct VdbeOpList VdbeOpList;
 #define P4_INT32      (-3)  /* P4 is a 32-bit signed integer */
 #define P4_SUBPROGRAM (-4)  /* P4 is a pointer to a SubProgram structure */
 #define P4_TABLE      (-5)  /* P4 is a pointer to a Table structure */
+#define P4_INDEX      (-6)  /* P4 is a pointer to an Index structure */
 /* Above do not own any resources.  Must free those below */
 #define P4_FREE_IF_LE (-6)
-#define P4_DYNAMIC    (-6)  /* Pointer to memory from sqliteMalloc() */
-#define P4_FUNCDEF    (-7)  /* P4 is a pointer to a FuncDef structure */
-#define P4_KEYINFO    (-8)  /* P4 is a pointer to a KeyInfo structure */
-#define P4_EXPR       (-9) /* P4 is a pointer to an Expr tree */
-#define P4_MEM        (-10) /* P4 is a pointer to a Mem*    structure */
-#define P4_VTAB       (-11) /* P4 is a pointer to an sqlite3_vtab structure */
-#define P4_REAL       (-12) /* P4 is a 64-bit floating point value */
-#define P4_INT64      (-13) /* P4 is a 64-bit signed integer */
-#define P4_INTARRAY   (-14) /* P4 is a vector of 32-bit integers */
-#define P4_FUNCCTX    (-15) /* P4 is a pointer to an sqlite3_context object */
-#define P4_TABLEREF   (-16) /* Like P4_TABLE, but reference counted */
-#define P4_SUBRTNSIG  (-17) /* P4 is a SubrtnSig pointer */
+#define P4_DYNAMIC    (-7)  /* Pointer to memory from sqliteMalloc() */
+#define P4_FUNCDEF    (-8)  /* P4 is a pointer to a FuncDef structure */
+#define P4_KEYINFO    (-9)  /* P4 is a pointer to a KeyInfo structure */
+#define P4_EXPR       (-10) /* P4 is a pointer to an Expr tree */
+#define P4_MEM        (-11) /* P4 is a pointer to a Mem*    structure */
+#define P4_VTAB       (-12) /* P4 is a pointer to an sqlite3_vtab structure */
+#define P4_REAL       (-13) /* P4 is a 64-bit floating point value */
+#define P4_INT64      (-14) /* P4 is a 64-bit signed integer */
+#define P4_INTARRAY   (-15) /* P4 is a vector of 32-bit integers */
+#define P4_FUNCCTX    (-16) /* P4 is a pointer to an sqlite3_context object */
+#define P4_TABLEREF   (-17) /* Like P4_TABLE, but reference counted */
+#define P4_SUBRTNSIG  (-18) /* P4 is a SubrtnSig pointer */
 
 /* Error message codes for OP_Halt */
 #define P5_ConstraintNotNull 1
index eb296318011d551c1da3b3cfa4bb81890b2be749..57f4714155c5de7ff6728f7f2cbdef3d09a66b17 100644 (file)
@@ -687,7 +687,7 @@ void sqlite3VdbePreUpdateHook(
     Vdbe*,VdbeCursor*,int,const char*,Table*,i64,int,int);
 #endif
 int sqlite3VdbeTransferError(Vdbe *p);
-int sqlite3VdbeFindDeleteKey(BtCursor*, int, UnpackedRecord*, int*);
+int sqlite3VdbeFindDeleteKey(BtCursor*, Index*, UnpackedRecord*, int*);
 
 int sqlite3VdbeSorterInit(sqlite3 *, int, VdbeCursor *);
 void sqlite3VdbeSorterReset(sqlite3 *, VdbeSorter *);
index 839d59981ac77dcdea753c0a4924ac8129f69fc0..09e18790af06b86db044795e59063be918eb838b 100644 (file)
@@ -5395,14 +5395,14 @@ void sqlite3VdbeSetVarmask(Vdbe *v, int iVar){
 
 /*
 ** This function compares the unpacked record with the current key that
-** 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).
+** cursor pCur points to, ignoring any fields for which the corresponding
+** bit in parameter "mask" is set.  Return 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 */
-  int nKeyCol,
+  Bitmask mask,
   UnpackedRecord *p,              /* Index key being deleted */
   int *piRes                      /* 0 for a match, non-zero for not a match */
 ){
@@ -5443,7 +5443,7 @@ static int vdbeIsDeleteKey(
         nSerial = sqlite3VdbeSerialTypeLen(iSerial);
         if( (idxRec+nSerial)>nRec ){
           rc = SQLITE_CORRUPT_BKPT;
-        }else if( ii>=nKeyCol ){
+        }else if( ii>=BMS || (mask & MASKBIT(ii))==0 ){
           sqlite3VdbeSerialGet(&aRec[idxRec], iSerial, &mem);
           res = sqlite3MemCompare(&mem, &p->aMem[ii], p->pKeyInfo->aColl[ii]);
           if( res!=0 ) break;
@@ -5482,7 +5482,7 @@ static int vdbeIsDeleteKey(
 */
 int sqlite3VdbeFindDeleteKey(
   BtCursor *pCur, 
-  int nKeyCol, 
+  Index *pIdx,
   UnpackedRecord *p, 
   int *pRes
 ){
@@ -5492,42 +5492,62 @@ int sqlite3VdbeFindDeleteKey(
   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;
+  /* Calculate a mask based on the first 64 columns of the index. The mask
+  ** bit is set if the corresponding index field is either an expression
+  ** or a virtual column of the table.  */
+  Bitmask mask = 0;
+  for(ii=0; ii<MIN(pIdx->nColumn, BMS); ii++){
+    int iCol = pIdx->aiColumn[ii];
+    if( (iCol==XN_EXPR)
+     || (iCol>=0 && (pIdx->pTable->aCol[iCol].colFlags & COLFLAG_VIRTUAL))
+    ){
+      mask |= MASKBIT(ii);
+    }
   }
 
-  /* 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 the mask is 0 at this point, then the index contains no expressions
+  ** or virtual columns. So do not search for a match - return so that the
+  ** caller may declare the db corrupt immediately. Or, if mask is non-zero,
+  ** proceed.  */
+  if( mask!=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 = SQLITE_OK;
-      assert( res!=0 );
+      rc = sqlite3BtreeFirst(pCur, &res);
+      nStep = -1;
+    }else{
+      nStep = BTREE_FDK_RANGE*2;
+    }
+  
+    /* 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, mask, 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( 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);
   }
 
   *pRes = res;
index f80b2bc29c9476fc17b49596ee081e30b41cb0a8..c1a0d2a2fbef09844b0d8fa341cad870383494d1 100644 (file)
@@ -28,7 +28,7 @@ do_execsql_test 1.0.1 {
   INSERT INTO t1 VALUES(20, 20.0);
   INSERT INTO t1 VALUES(25, 25.0);
   INSERT INTO t1 VALUES(30, 30.0);
-  CREATE INDEX i1 ON t1(b);
+  CREATE INDEX i1 ON t1((b+0.0));
 }
 
 set root [db one {SELECT rootpage FROM sqlite_schema WHERE name='i1'}]
@@ -110,7 +110,7 @@ do_execsql_test 2.0 {
     SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<$nRow
   )
   INSERT INTO t1 SELECT i, random(), hex(randomblob(50)) FROM s;
-  CREATE INDEX t1c ON t1(c);
+  CREATE INDEX t1c ON t1(+c);
 }
 
 set root [db one {SELECT rootpage FROM sqlite_schema WHERE name='t1c'}]
@@ -134,5 +134,30 @@ do_test 2.3 {
   execsql {PRAGMA integrity_check}
 } {ok}
 
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 3.0 {
+  CREATE TABLE y1(a, b, c GENERATED ALWAYS AS (a*b) VIRTUAL);
+  CREATE INDEX i1 ON y1(c);
+  INSERT INTO y1 VALUES(2, 3);
+  INSERT INTO y1 VALUES(4, 5);
+}
+
+set root [db one {SELECT rootpage FROM sqlite_schema WHERE name='i1'}]
+
+do_test 3.1 { 
+  sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 1 $root
+  db eval { CREATE TABLE x1(c, rowid, PRIMARY KEY(c, rowid)) WITHOUT ROWID; }
+  sqlite3_test_control SQLITE_TESTCTRL_IMPOSTER db main 0 0
+} {}
+
+do_execsql_test 3.2 {
+  UPDATE x1 SET c=19 WHERE rowid=2;
+}
+
+breakpoint
+do_execsql_test 3.3 {
+  DELETE FROM y1 WHERE a=4;
+}
 
 finish_test