]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Only evaluate expressions once for UPDATE and INSERT statements that
authordrh <drh@noemail.net>
Tue, 7 Dec 2004 15:41:48 +0000 (15:41 +0000)
committerdrh <drh@noemail.net>
Tue, 7 Dec 2004 15:41:48 +0000 (15:41 +0000)
have BEFORE triggers.  Fix for ticket #980. (CVS 2158)

FossilOrigin-Name: 4852186aca3be6ea40069b6831079197e5fa757a

manifest
manifest.uuid
src/expr.c
src/insert.c
src/sqliteInt.h
src/update.c
test/trigger6.test [new file with mode: 0644]

index bcd7b91265937ab1e8d0bb36bd2419cbd769cca4..82f4e914aab3aae6f125bbcc150109b0f066cb72 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Simplify\sthe\strigger\slogic\sfor\sDELETE,\sINSERT,\sand\sUPDATE.\s(CVS\s2157)
-D 2004-12-07T14:06:13
+C Only\sevaluate\sexpressions\sonce\sfor\sUPDATE\sand\sINSERT\sstatements\sthat\nhave\sBEFORE\striggers.\s\sFix\sfor\sticket\s#980.\s(CVS\s2158)
+D 2004-12-07T15:41:49
 F Makefile.in da09f379b80c8cd78d78abaa0f32ca90a124e884
 F Makefile.linux-gcc a9e5a0d309fa7c38e7c14d3ecf7690879d3a5457
 F README a01693e454a00cc117967e3f9fdab2d4d52e9bc1
@@ -35,11 +35,11 @@ F src/build.c 306e49e1f6f19741a40c1bbc23140027aa4f8cc9
 F src/cursor.c f883813759742068890b1f699335872bfa8fdf41
 F src/date.c 65536e7ea04fdde6e0551264fca15966966e171f
 F src/delete.c 4a70ac0bd0159fe38efdaf2cb44723256b0f7b37
-F src/expr.c 7987e5f50c189d4c2550f247f8039c06ea272345
+F src/expr.c d61efc526449a7a4c725325a3001a614cbcc3bed
 F src/func.c b668e5ad043176049454c95a6a780367a0e8f6bb
 F src/hash.c a97721a55440b7bea31ffe471bb2f6b4123cddd5
 F src/hash.h 1b0c445e1c89ff2aaad9b4605ba61375af001e84
-F src/insert.c 094972ccacff759042b0692b558a8ac114e5d417
+F src/insert.c 0b9077c6752530e9919a8c84375cfa2c4652260a
 F src/legacy.c d58ea507bce885298a2c8c3cbb0f4bff5d47830b
 F src/main.c fc383dc9cf03847b96e5ed9942696467725cfdfd
 F src/md5.c 7ae1c39044b95de2f62e066f47bb1deb880a1070
@@ -62,7 +62,7 @@ F src/random.c eff68e3f257e05e81eae6c4d50a51eb88beb4ff3
 F src/select.c 36cc9da999596578566e167d310e99f2005a7f03
 F src/shell.c e8f4f486cbf6e60d81173146ac8a6522c930fa51
 F src/sqlite.h.in fa75850f412808afd38fddc1fd6456f4efc6fb97
-F src/sqliteInt.h 3343e12d160d49fcca224c29ee39f5e6d6f980db
+F src/sqliteInt.h e0c5c1af95e975645c7a09b151af258d6fca1c53
 F src/table.c 25b3ff2b39b7d87e8d4a5da0713d68dfc06cbee9
 F src/tclsqlite.c 68b45ae5a96424abdc1732cb03b1efbb0c1821b3
 F src/test1.c b7d94c54e58f95452387a5cabdf98b2be8059f29
@@ -72,7 +72,7 @@ F src/test4.c 7c6b9fc33dd1f3f93c7f1ee6e5e6d016afa6c1df
 F src/test5.c 64f08b2a50ef371a1bd68ff206829e7b1b9997f5
 F src/tokenize.c 2ad3d1ae1a0a70746db0b31a0a74f58050a3c39a
 F src/trigger.c 98f3b07c08ba01b34cff139ef9687883d325ae8e
-F src/update.c 67a95e5c24cbedb7f943cd95c1082c112b401f25
+F src/update.c aa92fa2203b2233008dd75a1e97c4b441be24a7f
 F src/utf.c e45ce11be6922408cd381561721f6cca7d3b992a
 F src/util.c 4a8db4e97a3cfda12ad8dda3e77dd2d00ad1de5e
 F src/vacuum.c 705256e1111521fa04f0029de7f1667bc131d015
@@ -193,6 +193,7 @@ F test/trigger2.test 534390be509127859fee7c23018f03b9bf21a88f
 F test/trigger3.test 9102fd3933db294dc654b5aee9edfe9e94f2b9e2
 F test/trigger4.test e7c0812b14750754602468f15495260e8c6625e0
 F test/trigger5.test 619391a3e9fc194081d22cefd830d811e7badf83
+F test/trigger6.test 88b4c0c9f67e4244af39627538526bbaf97132f5
 F test/types.test f0a98d10c5ecc9865d19dc94486f9873afc6bb6b
 F test/types2.test f23c147a2ab3e51d5dbcfa9987200db5acba7aa7
 F test/unique.test 0e38d4cc7affeef2527720d1dafd1f6870f02f2b
@@ -262,7 +263,7 @@ F www/tclsqlite.tcl 560ecd6a916b320e59f2917317398f3d59b7cc25
 F www/vdbe.tcl 095f106d93875c94b47367384ebc870517431618
 F www/version3.tcl 092a01f5ef430d2c4acc0ae558d74c4bb89638a0
 F www/whentouse.tcl fdacb0ba2d39831e8a6240d05a490026ad4c4e4c
-P 7b20f2b71f679e72b6cb3b78ccb31b4e7c4bd48b
-R d980a20ed4665faa1df32ccf64263d38
+P 8e164ab27771aced9a592ea4b7c27e9f184181a5
+R a25e54595d969baf18fc12c2ed784b4c
 U drh
-Z dcfe3e605fa32f759f8dbe1e7c83903c
+Z c29cc9b7f0c7e31234ed5d8c1f56e385
index bc050fb56f95ec7342d7dcd3c1ebc2f79e0883e0..7ff1e5a58223d996ddda0d807a3117df955a82fb 100644 (file)
@@ -1 +1 @@
-8e164ab27771aced9a592ea4b7c27e9f184181a5
\ No newline at end of file
+4852186aca3be6ea40069b6831079197e5fa757a
\ No newline at end of file
index de96ada74776cefaf860fc411d90d2ab6a02f3ff..9b011c514ecdc12f946c20565bbd8d6d467c492f 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains routines used for analyzing expressions and
 ** for generating VDBE code that evaluates expressions in SQLite.
 **
-** $Id: expr.c,v 1.174 2004/11/23 01:47:30 drh Exp $
+** $Id: expr.c,v 1.175 2004/12/07 15:41:49 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -1547,6 +1547,31 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){
   }
 }
 
+/*
+** Generate code that evalutes the given expression and leaves the result
+** on the stack.  See also sqlite3ExprCode().
+**
+** This routine might also cache the result and modify the pExpr tree
+** so that it will make use of the cached result on subsequent evaluations
+** rather than evaluate the whole expression again.  Trivial expressions are
+** not cached.  If the expression is cached, its result is stored in a 
+** memory location.
+*/
+void sqlite3ExprCodeAndCache(Parse *pParse, Expr *pExpr){
+  Vdbe *v = pParse->pVdbe;
+  int iMem;
+  int addr1, addr2;
+  if( v==0 ) return;
+  addr1 = sqlite3VdbeCurrentAddr(v);
+  sqlite3ExprCode(pParse, pExpr);
+  addr2 = sqlite3VdbeCurrentAddr(v);
+  if( addr2>addr1+1 || sqlite3VdbeGetOp(v, addr1)->opcode==OP_Function ){
+    iMem = pExpr->iTable = pParse->nMem++;
+    sqlite3VdbeAddOp(v, OP_MemStore, iMem, 0);
+    pExpr->op = TK_REGISTER;
+  }
+}
+
 /*
 ** Generate code that pushes the value of every element of the given
 ** expression list onto the stack.
index 025c4205ac5d72df65f48c9e8d4ae9ff1e9cec46..04b930cf0d5d55f49eb440722bc0542a3391c09e 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle INSERT statements in SQLite.
 **
-** $Id: insert.c,v 1.127 2004/12/07 14:06:13 drh Exp $
+** $Id: insert.c,v 1.128 2004/12/07 15:41:49 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -523,7 +523,7 @@ void sqlite3Insert(
       }else if( pSelect ){
         sqlite3VdbeAddOp(v, OP_Dup, nColumn-j-1, 1);
       }else{
-        sqlite3ExprCode(pParse, pList->a[j].pExpr);
+        sqlite3ExprCodeAndCache(pParse, pList->a[j].pExpr);
       }
     }
     sqlite3VdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
index c67d56dff00b424a66d41c29ab8af22a13c43bae..55925ba01db4ef278b796942cda0ef37d6232045 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.345 2004/12/07 14:06:13 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.346 2004/12/07 15:41:49 drh Exp $
 */
 #ifndef _SQLITEINT_H_
 #define _SQLITEINT_H_
@@ -1354,6 +1354,7 @@ void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int);
 WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, int, ExprList**, Fetch*);
 void sqlite3WhereEnd(WhereInfo*);
 void sqlite3ExprCode(Parse*, Expr*);
+void sqlite3ExprCodeAndCache(Parse*, Expr*);
 int sqlite3ExprCodeExprList(Parse*, ExprList*);
 void sqlite3ExprIfTrue(Parse*, Expr*, int, int);
 void sqlite3ExprIfFalse(Parse*, Expr*, int, int);
index 9b7d9fc8c013eb7fddf3227ff639a3c6117e5498..134b4815512ebf96de75f3e34207f2f8d760e4ff 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle UPDATE statements.
 **
-** $Id: update.c,v 1.98 2004/12/07 14:06:13 drh Exp $
+** $Id: update.c,v 1.99 2004/12/07 15:41:49 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -276,11 +276,11 @@ void sqlite3Update(
     /* Generate the NEW table
     */
     if( chngRecno ){
-      sqlite3ExprCode(pParse, pRecnoExpr);
+      sqlite3ExprCodeAndCache(pParse, pRecnoExpr);
     }else{
       sqlite3VdbeAddOp(v, OP_Recno, iCur, 0);
     }
-    for(i=0; i<pTab->nCol; i++){ /* TODO: Factor out this loop as common code */
+    for(i=0; i<pTab->nCol; i++){
       if( i==pTab->iPKey ){
         sqlite3VdbeAddOp(v, OP_String8, 0, 0);
         continue;
@@ -289,7 +289,7 @@ void sqlite3Update(
       if( j<0 ){
         sqlite3VdbeAddOp(v, OP_Column, iCur, i);
       }else{
-        sqlite3ExprCode(pParse, pChanges->a[j].pExpr);
+        sqlite3ExprCodeAndCache(pParse, pChanges->a[j].pExpr);
       }
     }
     sqlite3VdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
diff --git a/test/trigger6.test b/test/trigger6.test
new file mode 100644 (file)
index 0000000..5fb71a5
--- /dev/null
@@ -0,0 +1,82 @@
+# 2004 December 07
+#
+# 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.
+#
+# This file implements tests to make sure expression of an INSERT
+# and UPDATE statement are only evaluated once.  See ticket #980.
+# If an expression uses a function that has side-effects or which
+# is not deterministic (ex: random()) then we want to make sure
+# that the same evaluation occurs for the actual INSERT/UPDATE and
+# for the NEW.* fields of any triggers that fire.
+#
+# $Id: trigger6.test,v 1.1 2004/12/07 15:41:50 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+ifcapable {!trigger} {
+  finish_test
+  return
+}
+
+do_test trigger6-1.1 {
+  execsql {
+    CREATE TABLE t1(x, y);
+    CREATE TABLE log(a, b, c);
+    CREATE TRIGGER r1 BEFORE INSERT ON t1 BEGIN
+      INSERT INTO log VALUES(1, new.x, new.y);
+    END;
+    CREATE TRIGGER r2 BEFORE UPDATE ON t1 BEGIN
+      INSERT INTO log VALUES(2, new.x, new.y);
+    END;
+  }
+  set ::trigger6_cnt 0
+  proc trigger6_counter {args} {
+    incr ::trigger6_cnt
+    return $::trigger6_cnt
+  }
+  db function counter trigger6_counter
+  execsql {
+    INSERT INTO t1 VALUES(1,counter());
+    SELECT * FROM t1;
+  }
+} {1 1}
+do_test trigger6-1.2 {
+  execsql {
+    SELECT * FROM log;
+  }
+} {1 1 1}
+do_test trigger6-1.3 {
+  execsql {
+    DELETE FROM t1;
+    DELETE FROM log;
+    INSERT INTO t1 VALUES(2,counter(2,3)+4);
+    SELECT * FROM t1;
+  }
+} {2 6.0}
+do_test trigger6-1.4 {
+  execsql {
+    SELECT * FROM log;
+  }
+} {1 2 6.0}
+do_test trigger6-1.5 {
+  execsql {
+    DELETE FROM log;
+    UPDATE t1 SET y=counter(5);
+    SELECT * FROM t1;
+  }
+} {2 3}
+do_test trigger6-1.6 {
+  execsql {
+    SELECT * FROM log;
+  }
+} {2 2 3}
+
+finish_test