]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Fix the SQLITE_ENABLE_UPDATE_DELETE_LIMIT functionality so that it works with
authordan <dan@noemail.net>
Tue, 14 Nov 2017 18:26:22 +0000 (18:26 +0000)
committerdan <dan@noemail.net>
Tue, 14 Nov 2017 18:26:22 +0000 (18:26 +0000)
views and WITHOUT ROWID tables. This is a modified cherrypick of [dae4a97a].

FossilOrigin-Name: b2679d3b7ad090cbcb536317989f3ad1a9470e9f39daef561934d9d18dc69240

12 files changed:
manifest
manifest.uuid
src/delete.c
src/fkey.c
src/parse.y
src/resolve.c
src/sqliteInt.h
src/trigger.c
src/update.c
test/wherelfault.test [new file with mode: 0644]
test/wherelimit.test
test/wherelimit2.test [new file with mode: 0644]

index 88c44d39db96caadfde9960a4ef01d7bf1bdd484..022df6e1cf2092b8612762fd4eeddc45f4c1a0c4 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Disallow\sORDER\sBY\sand\sLIMIT\son\sUPDATE\sand\sDELETE\sof\sviews\sand\sWITHOUT\sROWID\ntables.\s\sThis\sis\sa\stemporary\sfix\sfor\sticket\s[d4beea1633f1b88f]\suntil\sa\sbetter\nsolution\scan\sbe\sfound.
-D 2017-11-09T04:13:54.494
+C Fix\sthe\sSQLITE_ENABLE_UPDATE_DELETE_LIMIT\sfunctionality\sso\sthat\sit\sworks\swith\nviews\sand\sWITHOUT\sROWID\stables.\sThis\sis\sa\smodified\scherrypick\sof\s[dae4a97a].
+D 2017-11-14T18:26:22.534
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in 00d12636df7a5b08af09116bcd6c7bfd49b8b3b4
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -181,10 +181,10 @@ F src/callback.c 7b44ce59674338ad48b0e84e7b72f935ea4f68b0
 F src/complete.c 198a0066ba60ab06fc00fba1998d870a4d575463
 F src/ctime.c 98f89724adc891a1a4c655bee04e33e716e05887
 F src/date.c e4d50b3283696836ec1036b695ead9a19e37a5ac
-F src/delete.c 37964e6c1d73ff49cbea9ff690c9605fb15f600e
+F src/delete.c 7a9df72134437c7f556c2002e3df6d9cba6488aa2a264d05e3ef2b5e49c308d4
 F src/expr.c d09dac67d53c78880ba31d56e8ba2be3a6490553
 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
-F src/fkey.c e0444b61bed271a76840cbe6182df93a9baa3f12
+F src/fkey.c f19ee86f39aac8069761b3c2acd65ace057d33f793b048734e5d203ef038be9e
 F src/func.c 8028a8f79becc879268a114729263a24733ecdb610e7b7ec6ddb228bc2c13cf1
 F src/global.c 4f77cadbc5427d00139ba43d0f3979804cbb700e
 F src/hash.c 4263fbc955f26c2e8cdc0cf214bc42435aa4e4f5
@@ -219,7 +219,7 @@ F src/os_win.c 03d27be3a20048ef52a648d5f0a15f5edda9f2a3
 F src/os_win.h eb7a47aa17b26b77eb97e4823f20a00b8bda12ca
 F src/pager.c 4120a49ecd37697e28f5ed807f470b9c0b88410c
 F src/pager.h c3476e7c89cdf1c6914e50a11f3714e30b4e0a77
-F src/parse.y 1299c66e7b1707322ccd8af43a359b8fb0d46d72
+F src/parse.y 69d085edcf0c9cdc92ae6eb2cec91ae9322f280d3c3989a3c5eeba394b754972
 F src/pcache.c 10539fb959849ad6efff80050541cab3d25089d4
 F src/pcache.h b44658c9c932d203510279439d891a2a83e12ba8
 F src/pcache1.c 69d137620a305f814398bd29a0c998038c0695e9
@@ -228,14 +228,14 @@ F src/pragma.h 09c89bca58e9a44de2116cc8272b8d454657129f
 F src/prepare.c 173a5a499138451b2561614ecb87d78f9f4644b9
 F src/printf.c 8ae1fa9d30c1200a9268a390ba9e9cea9197b27a
 F src/random.c ba2679f80ec82c4190062d756f22d0c358180696
-F src/resolve.c 741f5dcdcb1f6df30778af87119931639921ec957bd3f1a9a6c669b1cb7c6069
+F src/resolve.c 49f8e3709bc80bde91297007965cf6aa6f4f1e3d9c469aa3756247a5cffddb28
 F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e
 F src/select.c 7922b1e1aaceb8eea4b921d0e6d062e32cfef8d897c6b30015fdd546c9b9f57f
 F src/shell.c 84a1593bd86aaa14f4da8a8f9b16fbc239d262aa
 F src/sqlite.h.in 278602140d49575e8708e643161f4263e428a02a
 F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad
 F src/sqlite3ext.h 17d487c3c91b0b8c584a32fbeb393f6f795eea7d
-F src/sqliteInt.h 3b055fff776a257d0b2473edec5e121db8724c51c1034452c1be3b0faaf02f45
+F src/sqliteInt.h 3b807b71e7170f2c1e8859bd4663f1a758af8dc52677907318244e71c0ca0651
 F src/sqliteLimit.h 216557999cb45f2e3578ed53ebefe228d779cb46
 F src/status.c f266ad8a2892d659b74f0f50cb6a88b6e7c12179
 F src/table.c e7a09215315a978057fb42c640f890160dbcc45e
@@ -288,8 +288,8 @@ F src/test_vfstrace.c bab9594adc976cbe696ff3970728830b4c5ed698
 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
 F src/threads.c 6bbcc9fe50c917864d48287b4792d46d6e873481
 F src/tokenize.c a8d270b06e5f709930f7b67cf70a847969cb5bf3
-F src/trigger.c 25571661fdeae8c7f975ff40ffec205520a3f92f
-F src/update.c 3c4ecc282accf12d39edb8d524cf089645e55a13
+F src/trigger.c bdee9c6f068620908f1483e09be5941aaf532a158274ce13a181a8b39753e916
+F src/update.c 262a470e3aa82bf57648cc34eb8930d602fc3eeaf5c6039ac362a63b7c1f8072
 F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c
 F src/util.c 98a7627ca48ad3265b6940915a1d08355eb3fc7e
 F src/vacuum.c 9460b9de7b2d4e34b0d374894aa6c8a0632be8ec
@@ -1180,7 +1180,9 @@ F test/whereH.test e4b07f7a3c2f5d31195cd33710054c78667573b2
 F test/whereI.test 1d89199697919d4930be05a71e7fe620f114e622
 F test/whereJ.test 55a3221706a7ab706293f17cc8f96da563bf0767
 F test/whereK.test f8e3cf26a8513ecc7f514f54df9f0572c046c42b
-F test/wherelimit.test 035bac7669d5a22f3456d4d7e75cd5b11fd07f7a9b58b957cb860477cc5b3ddf
+F test/wherelfault.test c6627fcae6eda4defe3df1d74ec54634bad7121a8816f988f44c2068dccb5abc
+F test/wherelimit.test 5f147fb5a589c2ac6477d09e119d07bfd50fca4a36e7d40d83e204f22326bf6d
+F test/wherelimit2.test d01d10b9ee9eb6f00ea916866c5d026438eadd555387e3aecfb626eda33c2d73
 F test/wild001.test bca33f499866f04c24510d74baf1e578d4e44b1c
 F test/win32heap.test ea19770974795cff26e11575e12d422dbd16893c
 F test/win32lock.test fbf107c91d8f5512be5a5b87c4c42ab9fdd54972
@@ -1250,8 +1252,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P 2495acf71017fa2ffada18824590ead593c47dabe2312701a25adc517cbf72eb
-Q +62fe56b59270d9d7372b1bb8a53788a40d20d0f111fe38c61dd6269848592c70
-R 39f33bcdff1a181e0ad060a458e43b3e
-U drh
-Z 39845f6e7d1ca7e051c4b6ab3a1fe9f4
+P 30aa941fc16c1affe3821ff2d9a4955e14ab18cd4ece27037bd6bb532fcaa052
+R c0304b6b74e580ff6d98e75086a36094
+U dan
+Z 127d4fcc427982bc4413b5e864902384
index 6dd7a32cf09d37c70cd395bd37bc838a5997d412..15951044adcb7a1e851faa2de074ca7b7d4dcf0c 100644 (file)
@@ -1 +1 @@
-30aa941fc16c1affe3821ff2d9a4955e14ab18cd4ece27037bd6bb532fcaa052
\ No newline at end of file
+b2679d3b7ad090cbcb536317989f3ad1a9470e9f39daef561934d9d18dc69240
\ No newline at end of file
index ef6aace1c822b3a890dbb057bf98ff24c50430d6..7a971d32997569e364d06a89dc9fe6057ddccb6d 100644 (file)
@@ -90,6 +90,9 @@ void sqlite3MaterializeView(
   Parse *pParse,       /* Parsing context */
   Table *pView,        /* View definition */
   Expr *pWhere,        /* Optional WHERE clause to be added */
+  ExprList *pOrderBy,  /* Optional ORDER BY clause */
+  Expr *pLimit,        /* Optional LIMIT clause */
+  Expr *pOffset,       /* Optional OFFSET clause */
   int iCur             /* Cursor number for ephemeral table */
 ){
   SelectDest dest;
@@ -106,7 +109,8 @@ void sqlite3MaterializeView(
     assert( pFrom->a[0].pOn==0 );
     assert( pFrom->a[0].pUsing==0 );
   }
-  pSel = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, 0, 0, 0, 0);
+  pSel = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, pOrderBy, 0, 
+                          pLimit, pOffset);
   sqlite3SelectDestInit(&dest, SRT_EphemTab, iCur);
   sqlite3Select(pParse, pSel, &dest);
   sqlite3SelectDelete(db, pSel);
@@ -131,18 +135,19 @@ Expr *sqlite3LimitWhere(
   Expr *pOffset,               /* The OFFSET clause.  May be null */
   char *zStmtType              /* Either DELETE or UPDATE.  For err msgs. */
 ){
-  Expr *pWhereRowid = NULL;    /* WHERE rowid .. */
+  sqlite3 *db = pParse->db;
+  Expr *pLhs = NULL;           /* LHS of IN(SELECT...) operator */
   Expr *pInClause = NULL;      /* WHERE rowid IN ( select ) */
-  Expr *pSelectRowid = NULL;   /* SELECT rowid ... */
   ExprList *pEList = NULL;     /* Expression list contaning only pSelectRowid */
   SrcList *pSelectSrc = NULL;  /* SELECT rowid FROM x ... (dup of pSrc) */
   Select *pSelect = NULL;      /* Complete SELECT tree */
+  Table *pTab;
 
   /* Check that there isn't an ORDER BY without a LIMIT clause.
   */
-  if( pOrderBy && (pLimit == 0) ) {
+  if( pOrderBy && pLimit==0 ) {
     sqlite3ErrorMsg(pParse, "ORDER BY without LIMIT on %s", zStmtType);
-    goto limit_where_cleanup_2;
+    goto limit_where_cleanup;
   }
 
   /* We only need to generate a select expression if there
@@ -162,42 +167,50 @@ Expr *sqlite3LimitWhere(
   **     SELECT rowid FROM table_a WHERE col1=1 ORDER BY col2 LIMIT 1 OFFSET 1
   **   );
   */
-
-  pSelectRowid = sqlite3PExpr(pParse, TK_ROW, 0, 0, 0);
-  if( pSelectRowid == 0 ) goto limit_where_cleanup_2;
-  pEList = sqlite3ExprListAppend(pParse, 0, pSelectRowid);
-  if( pEList == 0 ) goto limit_where_cleanup_2;
+  pTab = pSrc->a[0].pTab;
+  if( HasRowid(pTab) ){
+    pLhs = sqlite3PExpr(pParse, TK_ROW, 0, 0, 0);
+    pEList = sqlite3ExprListAppend(
+        pParse, 0, sqlite3PExpr(pParse, TK_ROW, 0, 0, 0)
+    );
+  }else{
+    Index *pPk = sqlite3PrimaryKeyIndex(pTab);
+    if( pPk->nKeyCol==1 ){
+      const char *zName = pTab->aCol[pPk->aiColumn[0]].zName;
+      pLhs = sqlite3Expr(db, TK_ID, zName);
+      pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db, TK_ID, zName));
+    }else{
+      sqlite3ErrorMsg(pParse, 
+          "ORDER BY and LIMIT are not supported for table %s", pTab->zName
+      );
+      goto limit_where_cleanup;
+    }
+  }
 
   /* duplicate the FROM clause as it is needed by both the DELETE/UPDATE tree
   ** and the SELECT subtree. */
+  pSrc->a[0].pTab = 0;
   pSelectSrc = sqlite3SrcListDup(pParse->db, pSrc, 0);
-  if( pSelectSrc == 0 ) {
-    sqlite3ExprListDelete(pParse->db, pEList);
-    goto limit_where_cleanup_2;
-  }
+  pSrc->a[0].pTab = pTab;
+  pSrc->a[0].pIndex = 0;
 
   /* generate the SELECT expression tree. */
-  pSelect = sqlite3SelectNew(pParse,pEList,pSelectSrc,pWhere,0,0,
-                             pOrderBy,0,pLimit,pOffset);
-  if( pSelect == 0 ) return 0;
+  pSelect = sqlite3SelectNew(pParse, pEList, pSelectSrc, pWhere, 0 ,0, 
+      pOrderBy,0,pLimit,pOffset
+  );
 
   /* now generate the new WHERE rowid IN clause for the DELETE/UDPATE */
-  pWhereRowid = sqlite3PExpr(pParse, TK_ROW, 0, 0, 0);
-  if( pWhereRowid == 0 ) goto limit_where_cleanup_1;
-  pInClause = sqlite3PExpr(pParse, TK_IN, pWhereRowid, 0, 0);
-  if( pInClause == 0 ) goto limit_where_cleanup_1;
-
-  pInClause->x.pSelect = pSelect;
-  pInClause->flags |= EP_xIsSelect;
-  sqlite3ExprSetHeightAndFlags(pParse, pInClause);
+  pInClause = sqlite3PExpr(pParse, TK_IN, pLhs, 0, 0);
+  if( pInClause ){
+    pInClause->x.pSelect = pSelect;
+    ExprSetProperty(pInClause, EP_xIsSelect|EP_Subquery);
+    sqlite3ExprSetHeightAndFlags(pParse, pInClause);
+  }else{
+    sqlite3SelectDelete(pParse->db, pSelect);
+  }
   return pInClause;
 
-  /* something went wrong. clean up anything allocated. */
-limit_where_cleanup_1:
-  sqlite3SelectDelete(pParse->db, pSelect);
-  return 0;
-
-limit_where_cleanup_2:
+limit_where_cleanup:
   sqlite3ExprDelete(pParse->db, pWhere);
   sqlite3ExprListDelete(pParse->db, pOrderBy);
   sqlite3ExprDelete(pParse->db, pLimit);
@@ -217,7 +230,10 @@ limit_where_cleanup_2:
 void sqlite3DeleteFrom(
   Parse *pParse,         /* The parser context */
   SrcList *pTabList,     /* The table from which we should delete things */
-  Expr *pWhere           /* The WHERE clause.  May be null */
+  Expr *pWhere,          /* The WHERE clause.  May be null */
+  ExprList *pOrderBy,    /* ORDER BY clause. May be null */
+  Expr *pLimit,          /* LIMIT clause. May be null */
+  Expr *pOffset          /* OFFSET clause. May be null */
 ){
   Vdbe *v;               /* The virtual database engine */
   Table *pTab;           /* The table from which records will be deleted */
@@ -262,6 +278,7 @@ void sqlite3DeleteFrom(
   }
   assert( pTabList->nSrc==1 );
 
+
   /* Locate the table which we want to delete.  This table has to be
   ** put in an SrcList structure because some of the subroutines we
   ** will be calling are designed to work with multiple tables and expect
@@ -285,6 +302,16 @@ void sqlite3DeleteFrom(
 # define isView 0
 #endif
 
+#ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
+  if( !isView ){
+    pWhere = sqlite3LimitWhere(
+        pParse, pTabList, pWhere, pOrderBy, pLimit, pOffset, "DELETE"
+    );
+    pOrderBy = 0;
+    pLimit = pOffset = 0;
+  }
+#endif
+
   /* If pTab is really a view, make sure it has been initialized.
   */
   if( sqlite3ViewGetColumnNames(pParse, pTab) ){
@@ -332,8 +359,12 @@ void sqlite3DeleteFrom(
   */
 #if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)
   if( isView ){
-    sqlite3MaterializeView(pParse, pTab, pWhere, iTabCur);
+    sqlite3MaterializeView(pParse, pTab, 
+        pWhere, pOrderBy, pLimit, pOffset, iTabCur
+    );
     iDataCur = iIdxCur = iTabCur;
+    pOrderBy = 0;
+    pLimit = pOffset = 0;
   }
 #endif
 
@@ -553,6 +584,11 @@ delete_from_cleanup:
   sqlite3AuthContextPop(&sContext);
   sqlite3SrcListDelete(db, pTabList);
   sqlite3ExprDelete(db, pWhere);
+#if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) 
+  sqlite3ExprListDelete(db, pOrderBy);
+  sqlite3ExprDelete(db, pLimit);
+  sqlite3ExprDelete(db, pOffset);
+#endif
   sqlite3DbFree(db, aToOpen);
   return;
 }
index fa148ba6a3a306e3188635962c7c28285025d5f6..dba84b8aa59f35181537855301a043ede22ed58d 100644 (file)
@@ -721,7 +721,7 @@ void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTab){
     }
 
     pParse->disableTriggers = 1;
-    sqlite3DeleteFrom(pParse, sqlite3SrcListDup(db, pName, 0), 0);
+    sqlite3DeleteFrom(pParse, sqlite3SrcListDup(db, pName, 0), 0, 0, 0, 0);
     pParse->disableTriggers = 0;
 
     /* If the DELETE has generated immediate foreign key constraint 
index b8ef26810ca3ca2166ad15d385323896cb5234b0..3424fd7c385e41caf2b36e64c0a7a4a9a34b0e4b 100644 (file)
@@ -711,15 +711,14 @@ cmd ::= with(C) DELETE FROM fullname(X) indexed_opt(I) where_opt(W)
         orderby_opt(O) limit_opt(L). {
   sqlite3WithPush(pParse, C, 1);
   sqlite3SrcListIndexedBy(pParse, X, &I);
-  W = sqlite3LimitWhere(pParse, X, W, O, L.pLimit, L.pOffset, "DELETE");
-  sqlite3DeleteFrom(pParse,X,W);
+  sqlite3DeleteFrom(pParse,X,W,O,L.pLimit,L.pOffset); 
 }
 %endif
 %ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
 cmd ::= with(C) DELETE FROM fullname(X) indexed_opt(I) where_opt(W). {
   sqlite3WithPush(pParse, C, 1);
   sqlite3SrcListIndexedBy(pParse, X, &I);
-  sqlite3DeleteFrom(pParse,X,W);
+  sqlite3DeleteFrom(pParse,X,W,0,0,0);
 }
 %endif
 
@@ -737,8 +736,7 @@ cmd ::= with(C) UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y)
   sqlite3WithPush(pParse, C, 1);
   sqlite3SrcListIndexedBy(pParse, X, &I);
   sqlite3ExprListCheckLength(pParse,Y,"set list"); 
-  W = sqlite3LimitWhere(pParse, X, W, O, L.pLimit, L.pOffset, "UPDATE");
-  sqlite3Update(pParse,X,Y,W,R);
+  sqlite3Update(pParse,X,Y,W,R,O,L.pLimit,L.pOffset);
 }
 %endif
 %ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
@@ -747,7 +745,7 @@ cmd ::= with(C) UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y)
   sqlite3WithPush(pParse, C, 1);
   sqlite3SrcListIndexedBy(pParse, X, &I);
   sqlite3ExprListCheckLength(pParse,Y,"set list"); 
-  sqlite3Update(pParse,X,Y,W,R);
+  sqlite3Update(pParse,X,Y,W,R,0,0,0);
 }
 %endif
 
index 738dec9b1b34a299e322476f9babfe29e5522bf8..22de875ebbb26a3dfcb21b6da34390887b8144ba 100644 (file)
@@ -636,10 +636,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
       struct SrcList_item *pItem;
       assert( pSrcList && pSrcList->nSrc==1 );
       pItem = pSrcList->a;
-      if( !HasRowid(pItem->pTab) || pItem->pTab->pSelect!=0 ){
-         sqlite3ErrorMsg(pParse, "ORDER BY and LIMIT not support for table %s",
-                                 pItem->pTab->zName);
-      }
+      assert( HasRowid(pItem->pTab) && pItem->pTab->pSelect==0 );
       pExpr->op = TK_COLUMN;
       pExpr->pTab = pItem->pTab;
       pExpr->iTable = pItem->iCursor;
index 27f442ea92793cf65b81f9142c53a5ef35aaae31..cc0cc18e6d6f31f3b394971c626e6abad0a6e527 100644 (file)
@@ -3284,8 +3284,8 @@ void sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int);
 #if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY)
 Expr *sqlite3LimitWhere(Parse*,SrcList*,Expr*,ExprList*,Expr*,Expr*,char*);
 #endif
-void sqlite3DeleteFrom(Parse*, SrcList*, Expr*);
-void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int);
+void sqlite3DeleteFrom(Parse*, SrcList*, Expr*, ExprList*, Expr*, Expr*);
+void sqlite3Update(Parse*, SrcList*, ExprList*,Expr*,int,ExprList*,Expr*,Expr*);
 WhereInfo *sqlite3WhereBegin(Parse*,SrcList*,Expr*,ExprList*,ExprList*,u16,int);
 void sqlite3WhereEnd(WhereInfo*);
 u64 sqlite3WhereOutputRowCount(WhereInfo*);
@@ -3385,7 +3385,7 @@ int sqlite3SafetyCheckSickOrOk(sqlite3*);
 void sqlite3ChangeCookie(Parse*, int);
 
 #if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)
-void sqlite3MaterializeView(Parse*, Table*, Expr*, int);
+void sqlite3MaterializeView(Parse*, Table*, Expr*, ExprList*,Expr*,Expr*,int);
 #endif
 
 #ifndef SQLITE_OMIT_TRIGGER
index d2e7b5a1e6580b0b1be26ac5278dfad763b10fd0..42bcc13037e8c5439b2a437289827e705d781c64 100644 (file)
@@ -730,7 +730,7 @@ static int codeTriggerProgram(
           targetSrcList(pParse, pStep),
           sqlite3ExprListDup(db, pStep->pExprList, 0), 
           sqlite3ExprDup(db, pStep->pWhere, 0), 
-          pParse->eOrconf
+          pParse->eOrconf, 0, 0, 0
         );
         break;
       }
@@ -746,7 +746,7 @@ static int codeTriggerProgram(
       case TK_DELETE: {
         sqlite3DeleteFrom(pParse, 
           targetSrcList(pParse, pStep),
-          sqlite3ExprDup(db, pStep->pWhere, 0)
+          sqlite3ExprDup(db, pStep->pWhere, 0), 0, 0, 0
         );
         break;
       }
index 3af4017f1ba9a3143bbe2c9d0dd78b50aab3bd54..5c28747c5d1dc35483caf8a320b7aa4bb8b8b64d 100644 (file)
@@ -91,7 +91,10 @@ void sqlite3Update(
   SrcList *pTabList,     /* The table in which we should change things */
   ExprList *pChanges,    /* Things to be changed */
   Expr *pWhere,          /* The WHERE clause.  May be null */
-  int onError            /* How to handle constraint errors */
+  int onError,           /* How to handle constraint errors */
+  ExprList *pOrderBy,    /* ORDER BY clause. May be null */
+  Expr *pLimit,          /* LIMIT clause. May be null */
+  Expr *pOffset          /* OFFSET clause. May be null */
 ){
   int i, j;              /* Loop counters */
   Table *pTab;           /* The table to be updated */
@@ -171,6 +174,16 @@ void sqlite3Update(
 # define isView 0
 #endif
 
+#ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
+  if( !isView ){
+    pWhere = sqlite3LimitWhere(
+        pParse, pTabList, pWhere, pOrderBy, pLimit, pOffset, "UPDATE"
+    );
+    pOrderBy = 0;
+    pLimit = pOffset = 0;
+  }
+#endif
+
   if( sqlite3ViewGetColumnNames(pParse, pTab) ){
     goto update_cleanup;
   }
@@ -331,7 +344,11 @@ void sqlite3Update(
   */
 #if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)
   if( isView ){
-    sqlite3MaterializeView(pParse, pTab, pWhere, iDataCur);
+    sqlite3MaterializeView(pParse, pTab, 
+        pWhere, pOrderBy, pLimit, pOffset, iDataCur
+    );
+    pOrderBy = 0;
+    pLimit = pOffset = 0;
   }
 #endif
 
@@ -665,6 +682,11 @@ update_cleanup:
   sqlite3SrcListDelete(db, pTabList);
   sqlite3ExprListDelete(db, pChanges);
   sqlite3ExprDelete(db, pWhere);
+#if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) 
+  sqlite3ExprListDelete(db, pOrderBy);
+  sqlite3ExprDelete(db, pLimit);
+  sqlite3ExprDelete(db, pOffset);
+#endif
   return;
 }
 /* Make sure "isView" and other macros defined above are undefined. Otherwise
diff --git a/test/wherelfault.test b/test/wherelfault.test
new file mode 100644 (file)
index 0000000..5b2453e
--- /dev/null
@@ -0,0 +1,82 @@
+# 2008 October 6
+#
+# The author disclaims copyright to this source code.  In place of
+# a legal notice, here is a blessing:
+#
+#    May you do good and not evil.
+#    May you find forgiveness for yourself and forgive others.
+#    May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library.  The
+# focus of this file is testing fault-injection with the 
+# LIMIT ... OFFSET ... clause of UPDATE and DELETE statements.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/malloc_common.tcl
+set testprefix wherelfault
+
+ifcapable !update_delete_limit {
+  finish_test
+  return
+}
+
+do_execsql_test 1.0 {
+  CREATE TABLE t1(a, b);
+  INSERT INTO t1 VALUES(1, 'f');
+  INSERT INTO t1 VALUES(2, 'e');
+  INSERT INTO t1 VALUES(3, 'd');
+  INSERT INTO t1 VALUES(4, 'c');
+  INSERT INTO t1 VALUES(5, 'b');
+  INSERT INTO t1 VALUES(6, 'a');
+
+  CREATE VIEW v1 AS SELECT a,b FROM t1;
+  CREATE TABLE log(op, a);
+
+  CREATE TRIGGER v1del INSTEAD OF DELETE ON v1 BEGIN
+    INSERT INTO log VALUES('delete', old.a);
+  END;
+
+  CREATE TRIGGER v1upd INSTEAD OF UPDATE ON v1 BEGIN
+    INSERT INTO log VALUES('update', old.a);
+  END;
+}
+
+faultsim_save_and_close
+do_faultsim_test 1.1 -prep {
+  faultsim_restore_and_reopen
+  db eval {SELECT * FROM sqlite_master}
+} -body {
+  execsql { DELETE FROM v1 ORDER BY a LIMIT 3; }
+} -test {
+  faultsim_test_result {0 {}} 
+}
+
+do_faultsim_test 1.2 -prep {
+  faultsim_restore_and_reopen
+  db eval {SELECT * FROM sqlite_master}
+} -body {
+  execsql { UPDATE v1 SET b = 555 ORDER BY a LIMIT 3 }
+} -test {
+  faultsim_test_result {0 {}} 
+}
+
+#-------------------------------------------------------------------------
+sqlite3 db test.db
+do_execsql_test 2.1.0 {
+  CREATE TABLE t2(a, b, c, PRIMARY KEY(a)) WITHOUT ROWID;
+}
+faultsim_save_and_close
+
+do_faultsim_test 2.1 -prep {
+  faultsim_restore_and_reopen
+  db eval {SELECT * FROM sqlite_master}
+} -body {
+  execsql { DELETE FROM t2 WHERE c=? ORDER BY a DESC LIMIT 10 }
+} -test {
+  faultsim_test_result {0 {}} 
+}
+
+finish_test
index b2d072054630499d85819db26c2cab5fb79ec3fc..77890145aa1a9dab09571dea03734a77b36f9b55 100644 (file)
@@ -38,6 +38,8 @@ proc create_test_data {size} {
 
 ifcapable {update_delete_limit} {
 
+  execsql { CREATE TABLE t1(x, y) }
+
   # check syntax error support
   do_test wherelimit-0.1 {
     catchsql {DELETE FROM t1 ORDER BY x}
@@ -49,6 +51,8 @@ ifcapable {update_delete_limit} {
     catchsql {UPDATE t1 SET y=1 WHERE x=1 ORDER BY x}
   } {1 {ORDER BY without LIMIT on UPDATE}}
 
+  execsql { DROP TABLE t1 }
+
   # no AS on table sources
   do_test wherelimit-0.4 {
     catchsql {DELETE FROM t1 AS a WHERE x=1}
@@ -301,20 +305,20 @@ ifcapable {update_delete_limit} {
   } {}
   do_catchsql_test wherelimit-4.2 {
     DELETE FROM tv WHERE 1 LIMIT 2;
-  } {1 {ORDER BY and LIMIT not support for table tv}}
+  } {0 {}}
   do_catchsql_test wherelimit-4.3 {
     DELETE FROM tv WHERE 1 ORDER BY a LIMIT 2;
-  } {1 {ORDER BY and LIMIT not support for table tv}}
+  } {0 {}}
   do_execsql_test wherelimit-4.10 {
-    CREATE TABLE t3(a,b,c,d TEXT, PRIMARY KEY(a,b)) WITHOUT ROWID;
+    CREATE TABLE t3(a,b,c,d TEXT, PRIMARY KEY(a)) WITHOUT ROWID;
     INSERT INTO t3(a,b,c,d) VALUES(1,2,3,4),(5,6,7,8),(9,10,11,12);
   } {}
   do_catchsql_test wherelimit-4.11 {
     DELETE FROM t3 WHERE a=5 LIMIT 2;
-  } {1 {ORDER BY and LIMIT not support for table t3}}
+  } {0 {}}
   do_execsql_test wherelimit-4.12 {
     SELECT a,b,c,d FROM t3 ORDER BY 1;
-  } {1 2 3 4 5 6 7 8 9 10 11 12}
+  } {1 2 3 4 9 10 11 12}
 
 }
 
diff --git a/test/wherelimit2.test b/test/wherelimit2.test
new file mode 100644 (file)
index 0000000..6b77e78
--- /dev/null
@@ -0,0 +1,264 @@
+# 2008 October 6
+#
+# The author disclaims copyright to this source code.  In place of
+# a legal notice, here is a blessing:
+#
+#    May you do good and not evil.
+#    May you find forgiveness for yourself and forgive others.
+#    May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library.  The
+# focus of this file is testing the LIMIT ... OFFSET ... clause
+#  of UPDATE and DELETE statements.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix wherelimit2
+
+ifcapable !update_delete_limit {
+  finish_test
+  return
+}
+
+#-------------------------------------------------------------------------
+# Test with views and INSTEAD OF triggers.
+#
+do_execsql_test 1.0 {
+  CREATE TABLE t1(a, b);
+  INSERT INTO t1 VALUES(1, 'f');
+  INSERT INTO t1 VALUES(2, 'e');
+  INSERT INTO t1 VALUES(3, 'd');
+  INSERT INTO t1 VALUES(4, 'c');
+  INSERT INTO t1 VALUES(5, 'b');
+  INSERT INTO t1 VALUES(6, 'a');
+
+  CREATE VIEW v1 AS SELECT a,b FROM t1;
+  CREATE TABLE log(op, a);
+
+  CREATE TRIGGER v1del INSTEAD OF DELETE ON v1 BEGIN
+    INSERT INTO log VALUES('delete', old.a);
+  END;
+
+  CREATE TRIGGER v1upd INSTEAD OF UPDATE ON v1 BEGIN
+    INSERT INTO log VALUES('update', old.a);
+  END;
+}
+
+do_execsql_test 1.1 {
+  DELETE FROM v1 ORDER BY a LIMIT 3;
+  SELECT * FROM log; DELETE FROM log;
+} {
+  delete 1 delete 2 delete 3
+}
+do_execsql_test 1.2 {
+  DELETE FROM v1 ORDER BY b LIMIT 3;
+  SELECT * FROM log; DELETE FROM log;
+} {
+  delete 6 delete 5 delete 4
+}
+do_execsql_test 1.3 {
+  UPDATE v1 SET b = 555 ORDER BY a LIMIT 3;
+  SELECT * FROM log; DELETE FROM log;
+} {
+  update 1 update 2 update 3
+}
+do_execsql_test 1.4 {
+  UPDATE v1 SET b = 555 ORDER BY b LIMIT 3;
+  SELECT * FROM log; DELETE FROM log;
+} {
+  update 6 update 5 update 4
+}
+
+#-------------------------------------------------------------------------
+# Simple test using WITHOUT ROWID table.
+#
+do_execsql_test 2.2.0 {
+  CREATE TABLE t2(a INTEGER PRIMARY KEY, b, c) WITHOUT ROWID;
+  INSERT INTO t2 VALUES(1, 1, 'h');
+  INSERT INTO t2 VALUES(2, 2, 'g');
+  INSERT INTO t2 VALUES(3, 1, 'f');
+  INSERT INTO t2 VALUES(4, 2, 'e');
+  INSERT INTO t2 VALUES(5, 1, 'd');
+  INSERT INTO t2 VALUES(6, 2, 'c');
+  INSERT INTO t2 VALUES(7, 1, 'b');
+  INSERT INTO t2 VALUES(8, 2, 'a');
+}
+
+do_execsql_test 2.2.1 {
+  BEGIN;
+    DELETE FROM t2 WHERE b=1 ORDER BY c LIMIT 2;
+    SELECT c FROM t2 ORDER BY 1;
+  ROLLBACK;
+} {a c e f g h}
+
+do_execsql_test 2.2.2 {
+  BEGIN;
+    UPDATE t2 SET c=NULL ORDER BY a DESC LIMIT 3 OFFSET 1;
+    SELECT a, b, c FROM t2;
+  ROLLBACK;
+} {
+  1 1 h
+  2 2 g 
+  3 1 f
+  4 2 e
+  5 1 {}
+  6 2 {} 
+  7 1 {} 
+  8 2 a
+}
+
+#-------------------------------------------------------------------------
+# Test using a virtual table
+#
+ifcapable fts3 {
+  do_execsql_test 3.0 {
+    CREATE VIRTUAL TABLE ft USING fts3(x);
+    INSERT INTO ft(rowid, x) VALUES(-45,   'a a');
+    INSERT INTO ft(rowid, x) VALUES(12,    'a b');
+    INSERT INTO ft(rowid, x) VALUES(444,   'a c');
+    INSERT INTO ft(rowid, x) VALUES(12300, 'a d');
+    INSERT INTO ft(rowid, x) VALUES(25400, 'a c');
+    INSERT INTO ft(rowid, x) VALUES(25401, 'a b');
+    INSERT INTO ft(rowid, x) VALUES(50000, 'a a');
+  }
+
+  do_execsql_test 3.1.1 {
+    BEGIN;
+      DELETE FROM ft ORDER BY rowid LIMIT 3;
+      SELECT x FROM ft;
+    ROLLBACK;
+  } {{a d} {a c} {a b} {a a}}
+
+  do_execsql_test 3.1.2 {
+    BEGIN;
+      DELETE FROM ft WHERE ft MATCH 'a' ORDER BY rowid LIMIT 3;
+      SELECT x FROM ft;
+    ROLLBACK;
+  } {{a d} {a c} {a b} {a a}}
+  
+  do_execsql_test 3.1.3 {
+    BEGIN;
+      DELETE FROM ft WHERE ft MATCH 'b' ORDER BY rowid ASC LIMIT 1 OFFSET 1;
+      SELECT rowid FROM ft;
+    ROLLBACK;
+  } {-45 12 444 12300 25400 50000}
+
+  do_execsql_test 3.2.1 {
+    BEGIN;
+      UPDATE ft SET x='hello' ORDER BY rowid LIMIT 2 OFFSET 2;
+      SELECT x FROM ft;
+    ROLLBACK;
+  } {{a a} {a b} hello hello {a c} {a b} {a a}}
+
+  do_execsql_test 3.2.2 {
+    BEGIN;
+      UPDATE ft SET x='hello' WHERE ft MATCH 'a' 
+          ORDER BY rowid DESC LIMIT 2 OFFSET 2;
+      SELECT x FROM ft;
+    ROLLBACK;
+  } {{a a} {a b} {a c} hello hello {a b} {a a}}
+} ;# fts5
+
+#-------------------------------------------------------------------------
+# Test using INDEXED BY clauses.
+#
+do_execsql_test 4.0 {
+  CREATE TABLE x1(a INTEGER PRIMARY KEY, b, c, d);
+  CREATE INDEX x1bc ON x1(b, c);
+  INSERT INTO x1 VALUES(1,1,1,1);
+  INSERT INTO x1 VALUES(2,1,2,2);
+  INSERT INTO x1 VALUES(3,2,1,3);
+  INSERT INTO x1 VALUES(4,2,2,3);
+  INSERT INTO x1 VALUES(5,3,1,2);
+  INSERT INTO x1 VALUES(6,3,2,1);
+}
+
+do_execsql_test 4.1 {
+  BEGIN;
+    DELETE FROM x1 ORDER BY a LIMIT 2;
+    SELECT a FROM x1;
+  ROLLBACK;
+} {3 4 5 6}
+
+do_catchsql_test 4.2 {
+  DELETE FROM x1 INDEXED BY x1bc WHERE d=3 LIMIT 1;
+} {1 {no query solution}}
+
+do_execsql_test 4.3 {
+  DELETE FROM x1 INDEXED BY x1bc WHERE b=3 LIMIT 1;
+  SELECT a FROM x1;
+} {1 2 3 4 6}
+
+do_catchsql_test 4.4 {
+  UPDATE x1 INDEXED BY x1bc SET d=5 WHERE d=3 LIMIT 1;
+} {1 {no query solution}}
+
+do_execsql_test 4.5 {
+  UPDATE x1 INDEXED BY x1bc SET d=5 WHERE b=2 LIMIT 1;
+  SELECT a, d FROM x1;
+} {1 1 2 2 3 5 4 3 6 1}
+
+#-------------------------------------------------------------------------
+# Test using object names that require quoting.
+#
+do_execsql_test 5.0 {
+  CREATE TABLE "x y"("a b" PRIMARY KEY, "c d") WITHOUT ROWID;
+  CREATE INDEX xycd ON "x y"("c d");
+
+  INSERT INTO "x y" VALUES('a', 'a');
+  INSERT INTO "x y" VALUES('b', 'b');
+  INSERT INTO "x y" VALUES('c', 'c');
+  INSERT INTO "x y" VALUES('d', 'd');
+  INSERT INTO "x y" VALUES('e', 'a');
+  INSERT INTO "x y" VALUES('f', 'b');
+  INSERT INTO "x y" VALUES('g', 'c');
+  INSERT INTO "x y" VALUES('h', 'd');
+}
+
+do_execsql_test 5.1 {
+  BEGIN;
+    DELETE FROM "x y" WHERE "c d"!='e' ORDER BY "c d" LIMIT 2 OFFSET 2;
+    SELECT * FROM "x y" ORDER BY 1;
+  ROLLBACK;
+} {
+  a a c c d d e a g c h d
+}
+
+do_execsql_test 5.2 {
+  BEGIN;
+    UPDATE "x y" SET "c d"='e' WHERE "c d"!='e' ORDER BY "c d" LIMIT 2 OFFSET 2;
+    SELECT * FROM "x y" ORDER BY 1;
+  ROLLBACK;
+} {
+  a a b e c c d d e a f e g c h d
+}
+
+proc log {args} { lappend ::log {*}$args }
+db func log log
+do_execsql_test 5.3 {
+  CREATE VIEW "v w" AS SELECT * FROM "x y";
+  CREATE TRIGGER tr1 INSTEAD OF DELETE ON "v w" BEGIN
+    SELECT log(old."a b", old."c d");
+  END;
+  CREATE TRIGGER tr2 INSTEAD OF UPDATE ON "v w" BEGIN
+    SELECT log(new."a b", new."c d");
+  END;
+}
+
+do_test 5.4 {
+  set ::log {}
+  execsql { DELETE FROM "v w" ORDER BY "a b" LIMIT 3 }
+  set ::log
+} {a a b b c c}
+
+do_test 5.5 {
+  set ::log {}
+  execsql { UPDATE "v w" SET "a b" = "a b" || 'x' ORDER BY "a b" LIMIT 5; }
+  set ::log
+} {ax a bx b cx c dx d ex a}
+
+
+finish_test
+