1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-30 19:03:16 +03:00

Only evaluate expressions once for UPDATE and INSERT statements that

have BEFORE triggers.  Fix for ticket #980. (CVS 2158)

FossilOrigin-Name: 4852186aca3be6ea40069b6831079197e5fa757a
This commit is contained in:
drh
2004-12-07 15:41:48 +00:00
parent dca7684141
commit 2530378003
7 changed files with 127 additions and 18 deletions

View File

@ -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

View File

@ -1 +1 @@
8e164ab27771aced9a592ea4b7c27e9f184181a5
4852186aca3be6ea40069b6831079197e5fa757a

View 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.

View 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);

View 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);

View 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);

82
test/trigger6.test Normal file
View File

@ -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