]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Fix problems with using LIMIT and FROM clauses as part of single UPDATE statement.
authordan <dan@noemail.net>
Wed, 29 Apr 2020 20:11:01 +0000 (20:11 +0000)
committerdan <dan@noemail.net>
Wed, 29 Apr 2020 20:11:01 +0000 (20:11 +0000)
FossilOrigin-Name: b717dc3c5fafb9b86a141e7ecffc030fd9b36aa57a0b3e5200d64ad23a0aa13d

manifest
manifest.uuid
src/resolve.c
src/select.c
src/sqliteInt.h
src/update.c
test/upfrom2.test

index f4a1a2e9dc0183cb39d0acd91a7061d63d6a2b21..e0eba4c3f84ac4dd90d3a46b03570c69348eecb2 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\svarious\sbugs\sin\snew\sfeature\son\sthis\sbranch.
-D 2020-04-29T17:41:29.116
+C Fix\sproblems\swith\susing\sLIMIT\sand\sFROM\sclauses\sas\spart\sof\ssingle\sUPDATE\sstatement.
+D 2020-04-29T20:11:01.860
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -530,14 +530,14 @@ F src/pragma.h 9473160d220416456b40f27323bb4b316d4e4e08ffbf8bf88c5f7045d49c38e5
 F src/prepare.c 8d4d6c8aa6afefc48027c54b41cdf134b4d6bc2fc4badbe483ad7fd9e1728a28
 F src/printf.c 9be6945837c839ba57837b4bc3af349eba630920fa5532aa518816defe42a7d4
 F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
-F src/resolve.c d36a2b1639e1c33d7b508abfd3452a63e7fd81737f6f3940bfef085fca6f21f4
+F src/resolve.c 05471a183504f72aedf249851d94ad6feeac4d02be044fefdb4b42a8a6e13e42
 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
-F src/select.c 88ffd4e1a2b6bf221e3aaad026885485c7f2fe9291201f5e7d0656a3603b6bbe
+F src/select.c 51882f7b24503163414ddcdc33705ea4f74707da70d7d0545e52aa8dff9f8983
 F src/shell.c.in 1fc834b80c72dd37587ea87a4f4167cf5e6d98d12d143184ed2e732f529c0950
 F src/sqlite.h.in fd6fcfe173accab8d9cb9a843856d9e9fb475f893b60a455e01d8739b5076f0e
 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
 F src/sqlite3ext.h 2d1af80082edffd71c6f96f70ad1ce6a4fb46615ad10291fc77fe0dea9ff0197
-F src/sqliteInt.h 3caabf4700e4d98bf6d4d3d60fab71c808d43ca9a98ee9674342f99d1a96d382
+F src/sqliteInt.h cea0d39df23798779db3cd0a2aa9cecc769ba9b13f58d5958e4a9e9325fa1b58
 F src/sqliteLimit.h 95cb8479ca459496d9c1c6a9f76b38aee12203a56ce1092fe13e50ae2454c032
 F src/status.c 9ff2210207c6c3b4d9631a8241a7d45ab1b26a0e9c84cb07a9b5ce2de9a3b278
 F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
@@ -599,7 +599,7 @@ F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
 F src/tokenize.c eee7bae3ec0bc4abee951554bf46a8ba567c0f7752ac90c820ed8afff4c612dc
 F src/treeview.c 82c6391a3ba76215d4185fd4719a56ec4caf186a40c8a7b6e6ba4ae4467c2742
 F src/trigger.c 4ada1037cc99777f647a882cdacbd1a4deb6567b69daf02946286401b88cdc04
-F src/update.c 3dda0590ff4a87f29828cd21840f11e6ddc853061a94f4aebc2af8f37d0ba92c
+F src/update.c 15c1e9b6191be56d0752235f38e8c4e0eddf7b8a26e31992631a848cf3a87517
 F src/upsert.c 2920de71b20f04fe25eb00b655d086f0ba60ea133c59d7fa3325c49838818e78
 F src/utf.c 95fb6e03a5ca679045c5adccd05380f0addccabef5911abddcb06af069500ab7
 F src/util.c 3b6cedf7a0c69bd6e1acce832873952d416212d6293b18d03064e07d7a9b5118
@@ -1618,7 +1618,7 @@ F test/update.test e906ca7cb1dc6f52af1ea243e08f727edfa79f924c2691f2f9e72481f8473
 F test/update2.test 67455bc61fcbcf96923c45b3bc4f87bc72be7d67575ad35f134906148c7b06d3
 F test/upfrom1.tcl 62efddee869b3a6f3e076b2816793fec9422e38d10ea42b63da733cdd2b1ad8e
 F test/upfrom1.test 543389b4eef43c7a21079df018cf95e29d7c2a4efd09b2597e54a03bbdbd30b9
-F test/upfrom2.test aba21e9988809b05de336bc882ad7318a0ca275c735c9a9c6688ebc01053ce29
+F test/upfrom2.test 1c0f8d3b963304f0feb04ca172484942a15ffca975cd25463fdfd4614d1245d0
 F test/upsert1.test 88f9e258c6a0eeeb85937b08831e8daad440ba41f125af48439e9d33f266fb18
 F test/upsert2.test 9c3cdbb1a890227f6504ce4b0e3de68f4cdfa16bb21d8641208a9239896c5a09
 F test/upsert3.test 88d7d590a1948a9cb6eac1b54b0642f67a9f35a1fc0f19b200e97d5d39e3179c
@@ -1865,7 +1865,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P f353a1a613bb7ad8cedcda377a7fe6fd05ee03b1f50665b00b84a868a71c5bec
-R ff3249230b7ba7b12347209dea6f6d16
+P 823ba94e29dece1687e28711e503a1f56d392c306b0cbc0a20548180834530d1
+R c7060e5765cacf17cc72ccb6e64f7f11
 U dan
-Z 37eb2f9d556f45297467e2642dd5f97e
+Z 2cbbaf692ee6084a41961733ac9445a3
index 8f74ee97ef8bac918f2c494128f1382503650039..d8889b7ff8fb55fa1dd0d23b0fa25c8f87b72e54 100644 (file)
@@ -1 +1 @@
-823ba94e29dece1687e28711e503a1f56d392c306b0cbc0a20548180834530d1
\ No newline at end of file
+b717dc3c5fafb9b86a141e7ecffc030fd9b36aa57a0b3e5200d64ad23a0aa13d
\ No newline at end of file
index 60fed0b109bdeae7e509aad4e71f2e9f0ac893dc..76cdc8ccd7a3bc308a7aa6c5ddf0c9c85f691c12 100644 (file)
@@ -756,7 +756,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
     case TK_ROW: {
       SrcList *pSrcList = pNC->pSrcList;
       struct SrcList_item *pItem;
-      assert( pSrcList && pSrcList->nSrc==1 );
+      assert( pSrcList && pSrcList->nSrc>=1 );
       pItem = pSrcList->a;
       assert( HasRowid(pItem->pTab) && pItem->pTab->pSelect==0 );
       pExpr->op = TK_COLUMN;
index fdffe916344d945d566abb2a2c37b316f67eedc4..757fa3265af89706d11293c0cdd3ab3042763ab5 100644 (file)
@@ -1006,7 +1006,8 @@ static void selectInnerLoop(
       testcase( eDest==SRT_Coroutine );
       testcase( eDest==SRT_Output );
       assert( eDest==SRT_Set || eDest==SRT_Mem 
-           || eDest==SRT_Coroutine || eDest==SRT_Output );
+           || eDest==SRT_Coroutine || eDest==SRT_Output
+           || eDest==SRT_Upfrom );
     }
     sRowLoadInfo.regResult = regResult;
     sRowLoadInfo.ecelFlags = ecelFlags;
@@ -1156,14 +1157,18 @@ static void selectInnerLoop(
     }
 
     case SRT_Upfrom: {
-      assert( pSort==0 );
-      int i2 = pDest->iSDParm2;
-      int r1 = sqlite3GetTempReg(pParse);
-      sqlite3VdbeAddOp3(v, OP_MakeRecord,regResult+(i2<0),nResultCol-(i2<0),r1);
-      if( i2<0 ){
-        sqlite3VdbeAddOp3(v, OP_Insert, iParm, r1, regResult);
+      if( pSort ){
+        pushOntoSorter(
+            pParse, pSort, p, regResult, regOrig, nResultCol, nPrefixReg);
       }else{
-        sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1, regResult, i2);
+        int i2 = pDest->iSDParm2;
+        int r1 = sqlite3GetTempReg(pParse);
+        sqlite3VdbeAddOp3(v, OP_MakeRecord,regResult+(i2<0),nResultCol-(i2<0),r1);
+        if( i2<0 ){
+          sqlite3VdbeAddOp3(v, OP_Insert, iParm, r1, regResult);
+        }else{
+          sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1, regResult, i2);
+        }
       }
       break;
     }
@@ -1600,6 +1605,17 @@ static void generateSortTail(
       break;
     }
 #endif
+    case SRT_Upfrom: {
+      int i2 = pDest->iSDParm2;
+      int r1 = sqlite3GetTempReg(pParse);
+      sqlite3VdbeAddOp3(v, OP_MakeRecord,regRow+(i2<0),nColumn-(i2<0),r1);
+      if( i2<0 ){
+        sqlite3VdbeAddOp3(v, OP_Insert, iParm, r1, regRow);
+      }else{
+        sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1, regRow, i2);
+      }
+      break;
+    }
     default: {
       assert( eDest==SRT_Output || eDest==SRT_Coroutine ); 
       testcase( eDest==SRT_Output );
index 34d6fea7ec40c4f1248c982c4397dceec3d03e58..c668e6bee325441fd8ed2d2e6305515f5beac504 100644 (file)
@@ -3187,8 +3187,7 @@ struct Select {
 #define SRT_EphemTab    12  /* Create transient tab and store like SRT_Table */
 #define SRT_Coroutine   13  /* Generate a single row of result */
 #define SRT_Table       14  /* Store result as data with an automatic rowid */
-#define SRT_ISet        15  /* Store result as data with rowid */
-#define SRT_Upfrom      16  /* Store result as data with rowid */
+#define SRT_Upfrom      15  /* Store result as data with rowid */
 
 /*
 ** An instance of this object describes where to put of the results of
index 80f07d3de48efabd5dff64e14ca8a8ea6953a7f4..5d4e5ae7db8931ffa5b294fd8d95ba7b81e2e6c1 100644 (file)
@@ -164,7 +164,9 @@ static void updatePopulateEphTable(
   Index *pPk,                     /* PK if table 0 is WITHOUT ROWID */
   ExprList *pChanges,             /* List of expressions to return */
   SrcList *pTabList,              /* List of tables to select from */
-  Expr *pWhere                    /* WHERE clause for query */
+  Expr *pWhere,                   /* WHERE clause for query */
+  ExprList *pOrderBy,
+  Expr *pLimit
 ){
   int i;
   sqlite3 *db = pParse->db;
@@ -174,6 +176,9 @@ static void updatePopulateEphTable(
   Table *pTab = pTabList->a[0].pTab;
   SrcList *pSrc = sqlite3SrcListDup(db, pTabList, 0);
   Expr *pWhere2 = sqlite3ExprDup(db, pWhere, 0);
+  Expr *pLimit2 = sqlite3ExprDup(db, pLimit, 0);
+  ExprList *pOrderBy2 = sqlite3ExprListDup(db, pOrderBy, 0);
+  ExprList *pGroupBy = 0;
   int eDest;
 
   assert( pTabList->nSrc>1 );
@@ -184,11 +189,16 @@ static void updatePopulateEphTable(
   }
   if( pPk ){
     for(i=0; i<pPk->nKeyCol; i++){
-      pList = sqlite3ExprListAppend(pParse, pList, 
-          sqlite3PExpr(pParse, TK_DOT,
-            sqlite3Expr(db, TK_ID, pTab->zName),
-            sqlite3Expr(db, TK_ID, pTab->aCol[pPk->aiColumn[i]].zName)
-      ));
+      Expr *pNew = sqlite3PExpr(pParse, TK_DOT,
+          sqlite3Expr(db, TK_ID, pTab->zName),
+          sqlite3Expr(db, TK_ID, pTab->aCol[pPk->aiColumn[i]].zName)
+      );
+      if( pLimit ){
+        pGroupBy = sqlite3ExprListAppend(pParse, pGroupBy, 
+            sqlite3ExprDup(db, pNew, 0)
+        );
+      }
+      pList = sqlite3ExprListAppend(pParse, pList, pNew);
     }
     eDest = SRT_Upfrom;
   }else if( pTab->pSelect ){
@@ -199,19 +209,24 @@ static void updatePopulateEphTable(
     ));
     eDest = SRT_Table;
   }else{
-    pList = sqlite3ExprListAppend(pParse, pList, 
-        sqlite3PExpr(pParse, TK_DOT,
-          sqlite3Expr(db, TK_ID, pTab->zName),
-          sqlite3Expr(db, TK_ID, "_rowid_")
-    ));
     eDest = IsVirtual(pTab) ? SRT_Table : SRT_Upfrom;
+    pList = sqlite3ExprListAppend(pParse, pList, 
+        sqlite3PExpr(pParse, TK_ROW, 0, 0)
+    );
+    if( pLimit ){
+      pGroupBy = sqlite3ExprListAppend(pParse, pGroupBy, 
+          sqlite3PExpr(pParse, TK_ROW, 0, 0)
+      );
+    }
   }
   for(i=0; i<pChanges->nExpr; i++){
     pList = sqlite3ExprListAppend(pParse, pList, 
         sqlite3ExprDup(db, pChanges->a[i].pExpr, 0)
     );
   }
-  pSelect = sqlite3SelectNew(pParse, pList, pSrc, pWhere2, 0, 0, 0, 0, 0);
+  pSelect = sqlite3SelectNew(
+      pParse, pList, pSrc, pWhere2, pGroupBy, 0, pOrderBy2, 0, pLimit2
+  );
   sqlite3SelectDestInit(&dest, eDest, iEph);
   dest.iSDParm2 = (pPk ? pPk->nKeyCol : -1);
   sqlite3Select(pParse, pSelect, &dest);
@@ -323,7 +338,7 @@ void sqlite3Update(
   assert( nChangeFrom==0 || pUpsert==0 );
 
 #ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
-  if( !isView ){
+  if( !isView && nChangeFrom==0 ){
     pWhere = sqlite3LimitWhere(
         pParse, pTabList, pWhere, pOrderBy, pLimit, "UPDATE"
     );
@@ -608,7 +623,9 @@ void sqlite3Update(
       addrOpen = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEph, nEphCol);
       if( pPk ) sqlite3VdbeSetP4KeyInfo(pParse, pPk);
       if( nChangeFrom ){
-        updatePopulateEphTable(pParse, iEph, pPk, pChanges, pTabList, pWhere);
+        updatePopulateEphTable(
+            pParse, iEph, pPk, pChanges, pTabList, pWhere, pOrderBy, pLimit
+        );
 #ifndef SQLITE_OMIT_SUBQUERY
         if( isView ) iDataCur = iEph;
 #endif
@@ -1148,7 +1165,7 @@ static void updateVirtualTable(
       }
     }
 
-    updatePopulateEphTable(pParse, ephemTab, 0, pList, pSrc, pWhere);
+    updatePopulateEphTable(pParse, ephemTab, 0, pList, pSrc, pWhere, 0, 0);
     sqlite3ExprListDelete(db, pList);
     eOnePass = ONEPASS_OFF;
   }else{
index 0c3cf31efb7ac83dd39735a3cb2d53d685822d5e..2d8d51b82d058dcb670b23164161e52eea3d94f0 100644 (file)
@@ -14,26 +14,14 @@ set testdir [file dirname $argv0]
 source $testdir/tester.tcl
 set testprefix upfrom2
 
-if 0 {
-do_execsql_test 0.0 {
-  CREATE TABLE t1 (a PRIMARY KEY, b, c) WITHOUT ROWID;
-}
-explain_i { REPLACE INTO t1 VALUES('one', 'two', 'three'); }
-breakpoint
-execsql {
-  REPLACE INTO t1 VALUES('one', 'two', 'three');
-  REPLACE INTO t1 VALUES('one', 'two', 'four');
-}
-do_execsql_test x {
-  SELECT * FROM t1
-} {one two four}
-exit
-}
-
 # Test cases:
 #
 #   1.*: Test that triggers are fired correctly for UPDATE FROM statements,
-#        and only once for each row.
+#        and only once for each row. Except for INSTEAD OF triggers on 
+#        views - these are fired once for each row returned by the join, 
+#        including duplicates.
+#
+#   2.*: Test adding ORDER BY and LIMIT clauses with UPDATE FROM statements.
 #
 
 foreach {tn wo} {
@@ -110,9 +98,75 @@ foreach {tn wo} {
     ten->eight        i->i
     two->twelve       xii->xii
   }
+
+  do_test 1.%TN%.4 { db changes } {2}
+
+  do_execsql_test 1.%TN%.5 { 
+    CREATE VIEW v1 AS SELECT * FROM t1;
+    CREATE TRIGGER v1tr INSTEAD OF UPDATE ON v1 BEGIN
+      UPDATE t1 SET y=new.y, z=new.z WHERE x=new.x;
+    END;
+
+    DELETE FROM log;
+    WITH data(k, v) AS (
+      VALUES(3, 'thirteen'), (3, 'fourteen'), (4, 'fifteen'), (4, 'sixteen')
+    )
+    UPDATE v1 SET z=v FROM data WHERE x=k;
+  }
+
+  do_execsql_test 1.%TN%.6 {
+    SELECT * FROM v1;
+    SELECT * FROM log;
+  } {
+    1 i eight   2 xii twelve   3 v fourteen   4 iv sixteen
+    thirty->thirteen  v->v
+    thirteen->fourteen  v->v
+    four->fifteen  iv->iv
+    fifteen->sixteen  iv->iv
+  }
+
 }]
 }
 
+ifcapable update_delete_limit {
+foreach {tn wo} {
+  1 ""
+  2 "WITHOUT ROWID"
+} {
+  reset_db
+
+eval [string map [list %WO% $wo %TN% $tn] {
+  do_execsql_test 2.%TN%.1 {
+    CREATE TABLE x1(a INTEGER PRIMARY KEY, b) %WO%;
+    INSERT INTO x1 VALUES
+        (1, 'one'), (2, 'two'), (3, 'three'), (4, 'four'),
+        (5, 'five'), (6, 'six'), (7, 'seven'), (8, 'eight');
+  }
+
+  do_execsql_test 2.%TN%.2 {
+    CREATE TABLE data1(x, y);
+    INSERT INTO data1 VALUES
+    (1, 'eleven'), (1, 'twenty-one'), (2, 'twelve'), (2, 'twenty-two'),
+    (3, 'thirteen'), (3, 'twenty-three'), (4, 'fourteen'), (4, 'twenty-four');
+  }
+
+  do_execsql_test 2.%TN%.3 {
+    UPDATE x1 SET b=y FROM data1 WHERE a=x ORDER BY a LIMIT 3;
+    SELECT * FROM x1;
+  } {
+    1 eleven 2 twelve 3 thirteen 4 four 5 five 6 six 7 seven 8 eight
+  }
+
+  do_execsql_test 2.%TN%.4 {
+    UPDATE x1 SET b=b||y FROM data1 WHERE a=x ORDER BY b LIMIT 3;
+    SELECT * FROM x1;
+  } {
+    1 eleveneleven 2 twelve 3 thirteenthirteen 4 fourfourteen 
+    5 five 6 six 7 seven 8 eight
+  }
+
+}]
+}}
 
 
 finish_test