From ab087d4e67077e80b30caa34bae869af55773647 Mon Sep 17 00:00:00 2001 From: drh Date: Fri, 24 Mar 2017 17:59:56 +0000 Subject: [PATCH] Fix the OP_Once opcode so that it works correctly for recursive triggers. Ticket [06796225f59c057cd120f1]. FossilOrigin-Name: 2556014514f36808e6d18b25722eae0daeeb8fbb5d18af13a9698ea6c6db1679 --- manifest | 17 +++++++++-------- manifest.uuid | 2 +- src/vdbe.c | 40 ++++++++++++++++++++++++++++++--------- src/vdbe.h | 1 + src/vdbeInt.h | 1 + test/triggerG.test | 47 ++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 90 insertions(+), 18 deletions(-) create mode 100644 test/triggerG.test diff --git a/manifest b/manifest index cfdc624046..f9ac9232e6 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sthe\sRFC-7396\sAppendix\sA\stest\scases\sfor\sjson_patch(). -D 2017-03-24T13:31:47.314 +C Fix\sthe\sOP_Once\sopcode\sso\sthat\sit\sworks\scorrectly\sfor\srecursive\striggers.\nTicket\s[06796225f59c057cd120f1]. +D 2017-03-24T17:59:56.312 F Makefile.in 1cc758ce3374a32425e4d130c2fe7b026b20de5b8843243de75f087c0a2661fb F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 1faf9f06aadc9284c212dea7bbc7c0dea7e8337f0287c81001eff500912c790a @@ -468,9 +468,9 @@ F src/update.c 456d4a4656f8a03c2abc88a51b19172197400e58 F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c F src/util.c ca8440ede81e155d15cff7c101654f60b55a9ae6 F src/vacuum.c 1fe4555cd8c9b263afb85b5b4ee3a4a4181ad569 -F src/vdbe.c 89a12451405a17c6e8d39b5826acb6999f1283e4e43d2e83a7ac7c9a7094a86a -F src/vdbe.h 59998ffd71d7caa8886bc78dafaf8caeccd4c13c -F src/vdbeInt.h 4e4b15b2e1330e1636e4e01974eab2b0b985092f +F src/vdbe.c e59bd3416627d11f76da18ed5a85e93b3c6172892a9d44371d05a7c7183d7b94 +F src/vdbe.h caa5346d52bae2a3c8c1dcfa60a7a4dc878a9e3865cb8239da55808b316c8158 +F src/vdbeInt.h 5db089ce18c4feff8820ec6e4cac2d2c82e03d4b1d96f10a6e43832147b8dffe F src/vdbeapi.c 5b08d82592bcff4470601fe78aaabebd50837860 F src/vdbeaux.c ecd0468611925d218e1eb4b3f538907904b136f0e15e333291a232b521bfcef1 F src/vdbeblob.c 359891617358deefc85bef7bcf787fa6b77facb9 @@ -1360,6 +1360,7 @@ F test/triggerC.test 302d8995f5ffe63bbc15053abb3ef7a39cf5a092 F test/triggerD.test 8e7f3921a92a5797d472732108109e44575fa650 F test/triggerE.test 15fa63f1097db1f83dd62d121616006978063d1f F test/triggerF.test 55b1eb13433997faac3a4948c1d8252f6c8c636b +F test/triggerG.test 6eb0dbb4ce35df990034e54951e882c69ffbc3462e9c33a021edf66464f8b91b F test/tt3_checkpoint.c 9e75cf7c1c364f52e1c47fd0f14c4340a9db0fe1 F test/tt3_index.c 39eec10a35f57672225be4d182862152896dee4a F test/tt3_lookaside1.c 0377e202c3c2a50d688cb65ba203afeda6fafeb9 @@ -1568,7 +1569,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 9d5350418b2f6113e0b50c57e8a872006f27753067baf08ffdfa7943c0c9a148 -R f5d3be53ea90768240f7c6616a0ecd20 +P c5441d2df2526723f72610cc14dd243223663979e67ecdd76fe06fcd366f2b29 +R 06e33989e2f9f8999f70d02209e735fb U drh -Z c6323f1875b7921e440a4d008ab9c650 +Z d9ba825914c037a52e49ea53bb54865f diff --git a/manifest.uuid b/manifest.uuid index 40296ca7c1..1ba0445ee1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c5441d2df2526723f72610cc14dd243223663979e67ecdd76fe06fcd366f2b29 \ No newline at end of file +2556014514f36808e6d18b25722eae0daeeb8fbb5d18af13a9698ea6c6db1679 \ No newline at end of file diff --git a/src/vdbe.c b/src/vdbe.c index 93c20c0aaa..5e99366b2d 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -2332,19 +2332,39 @@ case OP_BitNot: { /* same as TK_BITNOT, in1, out2 */ /* Opcode: Once P1 P2 * * * ** -** If the P1 value is equal to the P1 value on the OP_Init opcode at -** instruction 0, then jump to P2. If the two P1 values differ, then -** set the P1 value on this opcode to equal the P1 value on the OP_Init -** and fall through. +** Fall through to the next instruction the first time this opcode is +** encountered on each invocation of the byte-code program. Jump to P2 +** on the second and all subsequent encounters during the same invocation. +** +** Top-level programs determine first invocation by comparing the P1 +** operand against the P1 operand on the OP_Init opcode at the beginning +** of the program. If the P1 values differ, then fall through and make +** the P1 of this opcode equal to the P1 of OP_Init. If P1 values are +** the same then take the jump. +** +** For subprograms, there is a bitmask in the VdbeFrame that determines +** whether or not the jump should be taken. The bitmask is necessary +** because the self-altering code trick does not work for recursive +** triggers. */ case OP_Once: { /* jump */ + u32 iAddr; /* Address of this instruction */ assert( p->aOp[0].opcode==OP_Init ); - VdbeBranchTaken(p->aOp[0].p1==pOp->p1, 2); - if( p->aOp[0].p1==pOp->p1 ){ - goto jump_to_p2; + if( p->pFrame ){ + iAddr = (int)(pOp - p->aOp); + if( (p->pFrame->aOnce[iAddr/8] & (1<<(iAddr & 7)))!=0 ){ + VdbeBranchTaken(1, 2); + p->pFrame->aOnce[iAddr/8] |= 1<<(iAddr & 7); + goto jump_to_p2; + } }else{ - pOp->p1 = p->aOp[0].p1; + if( p->aOp[0].p1==pOp->p1 ){ + VdbeBranchTaken(1, 2); + goto jump_to_p2; + } } + VdbeBranchTaken(0, 2); + pOp->p1 = p->aOp[0].p1; break; } @@ -5870,7 +5890,8 @@ case OP_Program: { /* jump */ if( pProgram->nCsr==0 ) nMem++; nByte = ROUND8(sizeof(VdbeFrame)) + nMem * sizeof(Mem) - + pProgram->nCsr * sizeof(VdbeCursor *); + + pProgram->nCsr * sizeof(VdbeCursor*) + + (pProgram->nOp + 7)/8; pFrame = sqlite3DbMallocZero(db, nByte); if( !pFrame ){ goto no_mem; @@ -5921,6 +5942,7 @@ case OP_Program: { /* jump */ p->nMem = pFrame->nChildMem; p->nCursor = (u16)pFrame->nChildCsr; p->apCsr = (VdbeCursor **)&aMem[p->nMem]; + pFrame->aOnce = (u8*)&p->apCsr[pProgram->nCsr]; p->aOp = aOp = pProgram->aOp; p->nOp = pProgram->nOp; #ifdef SQLITE_ENABLE_STMT_SCANSTATUS diff --git a/src/vdbe.h b/src/vdbe.h index a35f3be344..be19bc5316 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -87,6 +87,7 @@ struct SubProgram { int nOp; /* Elements in aOp[] */ int nMem; /* Number of memory cells required */ int nCsr; /* Number of cursors required */ + u8 *aOnce; /* Array of OP_Once flags */ void *token; /* id that may be used to recursive triggers */ SubProgram *pNext; /* Next sub-program already visited */ }; diff --git a/src/vdbeInt.h b/src/vdbeInt.h index 989cdfd346..c0435a5a79 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -164,6 +164,7 @@ struct VdbeFrame { i64 *anExec; /* Event counters from parent frame */ Mem *aMem; /* Array of memory cells for parent frame */ VdbeCursor **apCsr; /* Array of Vdbe cursors for parent frame */ + u8 *aOnce; /* Bitmask used by OP_Once */ void *token; /* Copy of SubProgram.token */ i64 lastRowid; /* Last insert rowid (sqlite3.lastRowid) */ AuxData *pAuxData; /* Linked list of auxdata allocations */ diff --git a/test/triggerG.test b/test/triggerG.test new file mode 100644 index 0000000000..9faa898dc3 --- /dev/null +++ b/test/triggerG.test @@ -0,0 +1,47 @@ +# 2017-03-24 +# +# 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. +# +#*********************************************************************** +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix triggerG +ifcapable {!trigger} { + finish_test + return +} + +# Test cases for ticket +# https://www.sqlite.org/src/tktview/06796225f59c057cd120f +# +# The OP_Once opcode was not working correctly for recursive triggers. +# +do_execsql_test 100 { + PRAGMA recursive_triggers = 1; + + CREATE TABLE t1(a); + CREATE INDEX i1 ON t1(a); + INSERT INTO t1(a) VALUES(0),(2),(3),(8),(9); + CREATE TABLE t2(b); + CREATE TABLE t3(c); + + CREATE TRIGGER tr AFTER INSERT ON t3 BEGIN + INSERT INTO t3 SELECT new.c+1 WHERE new.c<5; + INSERT INTO t2 SELECT new.c*100+a FROM t1 WHERE a IN (1, 2, 3, 4); + END; + + INSERT INTO t3 VALUES(2); + SELECT c FROM t3 ORDER BY c;; +} {2 3 4 5} +do_execsql_test 110 { + SELECT b FROM t2 ORDER BY b; +} {202 203 302 303 402 403 502 503} + +finish_test