-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
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
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
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
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
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
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
-30aa941fc16c1affe3821ff2d9a4955e14ab18cd4ece27037bd6bb532fcaa052
\ No newline at end of file
+b2679d3b7ad090cbcb536317989f3ad1a9470e9f39daef561934d9d18dc69240
\ No newline at end of file
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;
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);
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
** 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);
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 */
}
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
# 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) ){
*/
#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
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;
}
}
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
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
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
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
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;
#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*);
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
targetSrcList(pParse, pStep),
sqlite3ExprListDup(db, pStep->pExprList, 0),
sqlite3ExprDup(db, pStep->pWhere, 0),
- pParse->eOrconf
+ pParse->eOrconf, 0, 0, 0
);
break;
}
case TK_DELETE: {
sqlite3DeleteFrom(pParse,
targetSrcList(pParse, pStep),
- sqlite3ExprDup(db, pStep->pWhere, 0)
+ sqlite3ExprDup(db, pStep->pWhere, 0), 0, 0, 0
);
break;
}
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 */
# 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;
}
*/
#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
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
--- /dev/null
+# 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
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}
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}
} {}
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}
}
--- /dev/null
+# 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
+