diff --git a/manifest b/manifest index bcd7b91265..82f4e914aa 100644 --- 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 diff --git a/manifest.uuid b/manifest.uuid index bc050fb56f..7ff1e5a582 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8e164ab27771aced9a592ea4b7c27e9f184181a5 \ No newline at end of file +4852186aca3be6ea40069b6831079197e5fa757a \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index de96ada747..9b011c514e 100644 --- a/src/expr.c +++ b/src/expr.c @@ -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 @@ -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. diff --git a/src/insert.c b/src/insert.c index 025c4205ac..04b930cf0d 100644 --- a/src/insert.c +++ b/src/insert.c @@ -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); diff --git a/src/sqliteInt.h b/src/sqliteInt.h index c67d56dff0..55925ba01d 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -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); diff --git a/src/update.c b/src/update.c index 9b7d9fc8c0..134b481551 100644 --- a/src/update.c +++ b/src/update.c @@ -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; inCol; i++){ /* TODO: Factor out this loop as common code */ + for(i=0; inCol; 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 index 0000000000..5fb71a57ae --- /dev/null +++ b/test/trigger6.test @@ -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