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:
19
manifest
19
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
|
||||
|
@ -1 +1 @@
|
||||
8e164ab27771aced9a592ea4b7c27e9f184181a5
|
||||
4852186aca3be6ea40069b6831079197e5fa757a
|
27
src/expr.c
27
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 <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.
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
82
test/trigger6.test
Normal 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
|
Reference in New Issue
Block a user