-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
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
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
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
-c5441d2df2526723f72610cc14dd243223663979e67ecdd76fe06fcd366f2b29
\ No newline at end of file
+2556014514f36808e6d18b25722eae0daeeb8fbb5d18af13a9698ea6c6db1679
\ No newline at end of file
/* 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;
}
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;
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
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 */
};
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 */
--- /dev/null
+# 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