-C ALTER\sTABLE\suses\sdouble-quotes\sfor\squoting\stable\snames.\s(CVS\s4781)
-D 2008-02-09T14:30:30
+C When\smaterializing\sa\sview\sfor\san\sUPDATE\sor\sDELETE\smake\suse\sof\sthe\sWHERE\nclause\sto\slimit\sthe\snumber\sof\srows\smaterialized.\s\sTicket\s#2938.\s(CVS\s4782)
+D 2008-02-12T16:52:14
F Makefile.arm-wince-mingw32ce-gcc ac5f7b2cef0cd850d6f755ba6ee4ab961b1fadf7
F Makefile.in bc2b5df3e3d0d4b801b824b7ef6dec43812b049b
F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
F src/callback.c 77b302b0d41468dcda78c70e706e5b84577f0fa0
F src/complete.c 4cf68fd75d60257524cbe74f87351b9848399131
F src/date.c 8ce763c68143b1e8fb6f79dcfc8b801853c97017
-F src/delete.c 220570cc99f1461b00a8e1b417d9254ec3588f94
+F src/delete.c fa13c296262e89c32d28949f15be275e52d7f524
F src/experimental.c 1b2d1a6cd62ecc39610e97670332ca073c50792b
F src/expr.c e6fb42c6e55fd9526174b1cb8296b69a60a6688a
F src/fault.c 049b88b8ba0a1db3240aeaf9695cd08b9a3ba9e1
F src/shell.c ca06cb687c40a8bff6307b5fad41a0e86a0f8558
F src/sqlite.h.in 690736613958e0f462e08ae2a9136fa335214edc
F src/sqlite3ext.h a93f59cdee3638dc0c9c086f80df743a4e68c3cb
-F src/sqliteInt.h 9dabb5a68e1952a556b78558c87e26af2fdb5ddb
+F src/sqliteInt.h e4ca11fff0cdac38551b75a2a278edb8ad9e1f00
F src/sqliteLimit.h ee4430f88f69bf63527967bb35ca52af7b0ccb1e
F src/table.c 46ccf9b7892a86f57420ae7bac69ecd5e72d26b5
F src/tclsqlite.c 9923abeffc9b3d7dad58e92b319661521f60debf
F src/test_thread.c e297dd41db0b249646e69f97d36ec13e56e8b730
F src/tokenize.c c4b79fd48ddb709b2b8522b7d93a5a3d98168ca4
F src/trigger.c 9bd3b6fa0beff4a02d262c96466f752ec15a7fc3
-F src/update.c 31edd9c9764e80753930bd5f9b43e0edb404636f
+F src/update.c 9b3be169cd2a0b065717164aa0f90aa48f34aed1
F src/utf.c ef4b7d83bae533b76c3e1bf635b113fdad86a736
F src/util.c c56e41ed4769c1f2b8af9ffde4757a7b4fb08ed1
F src/vacuum.c 3f34f278809bf3eb0b62ec46ff779e9c385b28f0
F test/attach2.test a295d2d7061adcee5884ef4a93c7c96a82765437
F test/attach3.test 7b92dc8e40c1ebca9732ca6f2d3fefbd46f196df
F test/attachmalloc.test 56c5e55563dba6d64641ef2f70ce06900df16912
-F test/auth.test be181f70ced0c84ecb5e2515d9b620f89f6a7b87
+F test/auth.test d5896499f901eeda88c9f441b13aaccf22d5ceee
F test/auth2.test 65ac294b8d52cbdd463f61e77ad0165268373126
F test/autoinc.test 0555aa5c789520f16d86a39c6c49b87998e01bea
F test/autovacuum.test 4339e66003b9cf813dd667a83aed2dee27c4c36d
F test/trigger7.test 0afa870be2ce1b132cdb85b17a4a4ef45aa8cece
F test/trigger8.test 3a09275aa2214fdff56f731b1e775d8dfee4408a
F test/trigger9.test b42703c378916d52a5e240ba98b25b155d3927a3
+F test/triggerA.test 8dbf5bffa3190bd513785a24a573a166a885fc1b
F test/types.test 98e7a631bddf0806204358b452b02d0e319318a6
F test/types2.test 3555aacf8ed8dc883356e59efc314707e6247a84
F test/types3.test b730a7db03ef69f0fdb85b2addc20d1a0a04039b
F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b
F www/whentouse.tcl fc46eae081251c3c181bd79c5faef8195d7991a5
-P 63915b54cfb41c2361c387636aa904145b166411
-R 4cb42b2925ae8f45e9151c56cf51f1f9
+P 607247c27b80520b8c25c489757288b8ea186f9e
+R 6e7459c99c887be282a1c6ce2d461da7
U drh
-Z 3b52650fd58593f0d3eb3e17a3cd9c37
+Z 584629669827c756e74626290b2a5564
-607247c27b80520b8c25c489757288b8ea186f9e
\ No newline at end of file
+5ab71c3a79cac04cb2c576f83a62218d05571006
\ No newline at end of file
** This file contains C code routines that are called by the parser
** in order to generate code for DELETE FROM statements.
**
-** $Id: delete.c,v 1.160 2008/01/25 15:04:50 drh Exp $
+** $Id: delete.c,v 1.161 2008/02/12 16:52:14 drh Exp $
*/
#include "sqliteInt.h"
}
+#if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)
+/*
+** Evaluate a view and store its result in an ephemeral table. The
+** pWhere argument is an optional WHERE clause that restricts the
+** set of rows in the view that are to be added to the ephemeral table.
+*/
+void sqlite3MaterializeView(
+ Parse *pParse, /* Parsing context */
+ Select *pView, /* View definition */
+ Expr *pWhere, /* Optional WHERE clause to be added */
+ u32 col_mask, /* Render only the columns in this mask. */
+ int iCur /* Cursor number for ephemerial table */
+){
+ SelectDest dest;
+ Select *pDup;
+ sqlite3 *db = pParse->db;
+
+ pDup = sqlite3SelectDup(db, pView);
+ if( pWhere ){
+ SrcList *pFrom;
+
+ pWhere = sqlite3ExprDup(db, pWhere);
+ pFrom = sqlite3SrcListAppendFromTerm(pParse, 0, 0, 0, 0, pDup, 0, 0);
+ pDup = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, 0, 0, 0, 0);
+ }
+ sqlite3SelectMask(pParse, pDup, col_mask);
+ sqlite3SelectDestInit(&dest, SRT_EphemTab, iCur);
+ sqlite3Select(pParse, pDup, &dest, 0, 0, 0, 0);
+ sqlite3SelectDelete(pDup);
+}
+#endif /* !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER) */
+
+
/*
** Generate code for a DELETE FROM statement.
**
oldIdx = pParse->nTab++;
}
- /* Resolve the column names in the WHERE clause.
+ /* Assign cursor number to the table and all its indices.
*/
assert( pTabList->nSrc==1 );
iCur = pTabList->a[0].iCursor = pParse->nTab++;
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
pParse->nTab++;
}
- memset(&sNC, 0, sizeof(sNC));
- sNC.pParse = pParse;
- sNC.pSrcList = pTabList;
- if( sqlite3ExprResolveNames(&sNC, pWhere) ){
- goto delete_from_cleanup;
- }
/* Start the view context
*/
** a ephemeral table.
*/
if( isView ){
- SelectDest dest;
- Select *pView;
-
- pView = sqlite3SelectDup(db, pTab->pSelect);
- sqlite3SelectMask(pParse, pView, old_col_mask);
- sqlite3SelectDestInit(&dest, SRT_EphemTab, iCur);
- sqlite3Select(pParse, pView, &dest, 0, 0, 0, 0);
- sqlite3SelectDelete(pView);
+ sqlite3MaterializeView(pParse, pTab->pSelect, pWhere, old_col_mask, iCur);
+ }
+
+ /* Resolve the column names in the WHERE clause.
+ */
+ memset(&sNC, 0, sizeof(sNC));
+ sNC.pParse = pParse;
+ sNC.pSrcList = pTabList;
+ if( sqlite3ExprResolveNames(&sNC, pWhere) ){
+ goto delete_from_cleanup;
}
/* Initialize the counter of the number of rows deleted, if
*************************************************************************
** Internal interface definitions for SQLite.
**
-** @(#) $Id: sqliteInt.h,v 1.659 2008/02/02 04:47:09 danielk1977 Exp $
+** @(#) $Id: sqliteInt.h,v 1.660 2008/02/12 16:52:14 drh Exp $
*/
#ifndef _SQLITEINT_H_
#define _SQLITEINT_H_
int sqlite3SafetyCheckOk(sqlite3*);
int sqlite3SafetyCheckSickOrOk(sqlite3*);
void sqlite3ChangeCookie(Parse*, int);
+void sqlite3MaterializeView(Parse*, Select*, Expr*, u32, int);
#ifndef SQLITE_OMIT_TRIGGER
void sqlite3BeginTrigger(Parse*, Token*,Token*,int,int,IdList*,SrcList*,
** This file contains C code routines that are called by the parser
** to handle UPDATE statements.
**
-** $Id: update.c,v 1.170 2008/01/19 03:35:59 drh Exp $
+** $Id: update.c,v 1.171 2008/02/12 16:52:14 drh Exp $
*/
#include "sqliteInt.h"
}
#endif
- /* Resolve the column names in all the expressions in the
- ** WHERE clause.
- */
- if( sqlite3ExprResolveNames(&sNC, pWhere) ){
- goto update_cleanup;
- }
-
/* Start the view context
*/
if( isView ){
** a ephemeral table.
*/
if( isView ){
- Select *pView;
- SelectDest dest;
-
- pView = sqlite3SelectDup(db, pTab->pSelect);
- sqlite3SelectMask(pParse, pView, old_col_mask|new_col_mask);
- sqlite3SelectDestInit(&dest, SRT_EphemTab, iCur);
- sqlite3Select(pParse, pView, &dest, 0, 0, 0, 0);
- sqlite3SelectDelete(pView);
+ sqlite3MaterializeView(pParse, pTab->pSelect, pWhere,
+ old_col_mask|new_col_mask, iCur);
+ }
+
+ /* Resolve the column names in all the expressions in the
+ ** WHERE clause.
+ */
+ if( sqlite3ExprResolveNames(&sNC, pWhere) ){
+ goto update_cleanup;
}
/* Begin the database scan
# focus of this script is testing the sqlite3_set_authorizer() API
# and related functionality.
#
-# $Id: auth.test,v 1.40 2008/01/01 19:02:09 danielk1977 Exp $
+# $Id: auth.test,v 1.41 2008/02/12 16:52:14 drh Exp $
#
set testdir [file dirname $argv0]
set authargs
} [list \
SQLITE_UPDATE v1 x main {} \
- SQLITE_READ v1 x main {} \
SQLITE_INSERT v1chng {} main r2 \
SQLITE_READ v1 x main r2 \
SQLITE_READ v1 x main r2 \
SQLITE_READ t2 a main v1 \
SQLITE_READ t2 b main v1 \
- SQLITE_SELECT {} {} {} v1]
+ SQLITE_SELECT {} {} {} v1 \
+ SQLITE_SELECT {} {} {} v1 \
+ SQLITE_READ v1 x main v1 \
+]
do_test auth-4.4 {
execsql {
CREATE TRIGGER r3 INSTEAD OF DELETE ON v1 BEGIN
set authargs
} [list \
SQLITE_DELETE v1 {} main {} \
- SQLITE_READ v1 x main {} \
SQLITE_INSERT v1chng {} main r3 \
SQLITE_READ v1 x main r3 \
SQLITE_READ t2 a main v1 \
SQLITE_READ t2 b main v1 \
- SQLITE_SELECT {} {} {} v1]
+ SQLITE_SELECT {} {} {} v1 \
+ SQLITE_SELECT {} {} {} v1 \
+ SQLITE_READ v1 x main v1 \
+]
} ;# ifcapable view && trigger
--- /dev/null
+# 2008 February 12
+#
+# 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. Specifically,
+# it tests issues relating to firing an INSTEAD OF trigger on a VIEW
+# when one tries to UPDATE or DELETE from the view. Does the WHERE
+# clause of the UPDATE or DELETE statement get passed down correctly
+# into the query that manifests the view?
+#
+# Ticket #2938
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+ifcapable {!trigger} {
+ finish_test
+ return
+}
+
+# Create two table containing some sample data
+#
+do_test triggerA-1.1 {
+ db eval {
+ CREATE TABLE t1(x INTEGER PRIMARY KEY, y TEXT UNIQUE);
+ CREATE TABLE t2(a INTEGER PRIMARY KEY, b INTEGER UNIQUE, c TEXT);
+ }
+ set i 1
+ foreach word {one two three four five six seven eight nine ten} {
+ set j [expr {$i*100 + [string length $word]}]
+ db eval {
+ INSERT INTO t1 VALUES($i,$word);
+ INSERT INTO t2 VALUES(20-$i,$j,$word);
+ }
+ incr i
+ }
+ db eval {
+ SELECT count(*) FROM t1 UNION ALL SELECT count(*) FROM t2;
+ }
+} {10 10}
+
+# Create views of various forms against one or both of the two tables.
+#
+do_test triggerA-1.2 {
+ db eval {
+ CREATE VIEW v1 AS SELECT y, x FROM t1;
+ SELECT * FROM v1 ORDER BY 1;
+ }
+} {eight 8 five 5 four 4 nine 9 one 1 seven 7 six 6 ten 10 three 3 two 2}
+do_test triggerA-1.3 {
+ db eval {
+ CREATE VIEW v2 AS SELECT x, y FROM t1 WHERE y GLOB '*e*';
+ SELECT * FROM v2 ORDER BY 1;
+ }
+} {1 one 3 three 5 five 7 seven 8 eight 9 nine 10 ten}
+do_test triggerA-1.4 {
+ db eval {
+ CREATE VIEW v3 AS
+ SELECT CAST(x AS TEXT) AS c1 FROM t1 UNION SELECT y FROM t1;
+ SELECT * FROM v3 ORDER BY c1;
+ }
+} {1 10 2 3 4 5 6 7 8 9 eight five four nine one seven six ten three two}
+do_test triggerA-1.5 {
+ db eval {
+ CREATE VIEW v4 AS
+ SELECT CAST(x AS TEXT) AS c1 FROM t1
+ UNION SELECT y FROM t1 WHERE x BETWEEN 3 and 5;
+ SELECT * FROM v4 ORDER BY 1;
+ }
+} {1 10 2 3 4 5 6 7 8 9 five four three}
+do_test triggerA-1.6 {
+ db eval {
+ CREATE VIEW v5 AS SELECT x, b FROM t1, t2 WHERE y=c;
+ SELECT * FROM v5;
+ }
+} {1 103 2 203 3 305 4 404 5 504 6 603 7 705 8 805 9 904 10 1003}
+
+# Create INSTEAD OF triggers on the views. Run UPDATE and DELETE statements
+# using those triggers. Verify correct operation.
+#
+do_test triggerA-2.1 {
+ db eval {
+ CREATE TABLE result2(a,b);
+ CREATE TRIGGER r1d INSTEAD OF DELETE ON v1 BEGIN
+ INSERT INTO result2(a,b) VALUES(old.y, old.x);
+ END;
+ DELETE FROM v1 WHERE x=5;
+ SELECT * FROM result2;
+ }
+} {five 5}
+do_test triggerA-2.2 {
+ db eval {
+ CREATE TABLE result4(a,b,c,d);
+ CREATE TRIGGER r1u INSTEAD OF UPDATE ON v1 BEGIN
+ INSERT INTO result4(a,b,c,d) VALUES(old.y, old.x, new.y, new.x);
+ END;
+ UPDATE v1 SET y=y||'-extra' WHERE x BETWEEN 3 AND 5;
+ SELECT * FROM result4 ORDER BY a;
+ }
+} {five 5 five-extra 5 four 4 four-extra 4 three 3 three-extra 3}
+
+
+do_test triggerA-2.3 {
+ db eval {
+ DELETE FROM result2;
+ CREATE TRIGGER r2d INSTEAD OF DELETE ON v2 BEGIN
+ INSERT INTO result2(a,b) VALUES(old.y, old.x);
+ END;
+ DELETE FROM v2 WHERE x=5;
+ SELECT * FROM result2;
+ }
+} {five 5}
+do_test triggerA-2.4 {
+ db eval {
+ DELETE FROM result4;
+ CREATE TRIGGER r2u INSTEAD OF UPDATE ON v2 BEGIN
+ INSERT INTO result4(a,b,c,d) VALUES(old.y, old.x, new.y, new.x);
+ END;
+ UPDATE v2 SET y=y||'-extra' WHERE x BETWEEN 3 AND 5;
+ SELECT * FROM result4 ORDER BY a;
+ }
+} {five 5 five-extra 5 three 3 three-extra 3}
+
+
+do_test triggerA-2.5 {
+ db eval {
+ CREATE TABLE result1(a);
+ CREATE TRIGGER r3d INSTEAD OF DELETE ON v3 BEGIN
+ INSERT INTO result1(a) VALUES(old.c1);
+ END;
+ DELETE FROM v3 WHERE c1 BETWEEN '8' AND 'eight';
+ SELECT * FROM result1 ORDER BY a;
+ }
+} {8 9 eight}
+do_test triggerA-2.6 {
+ db eval {
+ DELETE FROM result2;
+ CREATE TRIGGER r3u INSTEAD OF UPDATE ON v3 BEGIN
+ INSERT INTO result2(a,b) VALUES(old.c1, new.c1);
+ END;
+ UPDATE v3 SET c1 = c1 || '-extra' WHERE c1 BETWEEN '8' and 'eight';
+ SELECT * FROM result2 ORDER BY a;
+ }
+} {8 8-extra 9 9-extra eight eight-extra}
+
+
+do_test triggerA-2.7 {
+ db eval {
+ DELETE FROM result1;
+ CREATE TRIGGER r4d INSTEAD OF DELETE ON v4 BEGIN
+ INSERT INTO result1(a) VALUES(old.c1);
+ END;
+ DELETE FROM v4 WHERE c1 BETWEEN '8' AND 'eight';
+ SELECT * FROM result1 ORDER BY a;
+ }
+} {8 9}
+do_test triggerA-2.8 {
+ db eval {
+ DELETE FROM result2;
+ CREATE TRIGGER r4u INSTEAD OF UPDATE ON v4 BEGIN
+ INSERT INTO result2(a,b) VALUES(old.c1, new.c1);
+ END;
+ UPDATE v4 SET c1 = c1 || '-extra' WHERE c1 BETWEEN '8' and 'eight';
+ SELECT * FROM result2 ORDER BY a;
+ }
+} {8 8-extra 9 9-extra}
+
+
+do_test triggerA-2.9 {
+ db eval {
+ DELETE FROM result2;
+ CREATE TRIGGER r5d INSTEAD OF DELETE ON v5 BEGIN
+ INSERT INTO result2(a,b) VALUES(old.x, old.b);
+ END;
+ DELETE FROM v5 WHERE x=5;
+ SELECT * FROM result2;
+ }
+} {5 504}
+do_test triggerA-2.10 {
+ db eval {
+ DELETE FROM result4;
+ CREATE TRIGGER r5u INSTEAD OF UPDATE ON v5 BEGIN
+ INSERT INTO result4(a,b,c,d) VALUES(old.x, old.b, new.x, new.b);
+ END;
+ UPDATE v5 SET b = b+9900000 WHERE x BETWEEN 3 AND 5;
+ SELECT * FROM result4 ORDER BY a;
+ }
+} {3 305 3 9900305 4 404 4 9900404 5 504 5 9900504}
+
+# Only run the reamining tests if memory debugging is turned on.
+#
+ifcapable !memdebug {
+ puts "Skipping triggerA malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
+ finish_test
+ return
+}
+source $testdir/malloc_common.tcl
+
+# Save a copy of the current database configuration.
+#
+db close
+file delete -force test.db-triggerA
+file copy test.db test.db-triggerA
+sqlite3 db test.db
+
+# Run malloc tests on the INSTEAD OF trigger firing.
+#
+do_malloc_test triggerA-3 -tclprep {
+ db close
+ file delete -force test.db test.db-journal
+ file copy -force test.db-triggerA test.db
+ sqlite3 db test.db
+ sqlite3_extended_result_codes db 1
+ db eval {SELECT * FROM v5; -- warm up the cache}
+} -sqlbody {
+ DELETE FROM v5 WHERE x=5;
+ UPDATE v5 SET b=b+9900000 WHERE x BETWEEN 3 AND 5;
+}
+
+# Clean up the saved database copy.
+#
+file delete -force test.db-triggerA
+
+finish_test