]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Fix the OP_Once opcode so that it works correctly for recursive triggers.
authordrh <drh@noemail.net>
Fri, 24 Mar 2017 17:59:56 +0000 (17:59 +0000)
committerdrh <drh@noemail.net>
Fri, 24 Mar 2017 17:59:56 +0000 (17:59 +0000)
Ticket [06796225f59c057cd120f1].

FossilOrigin-Name: 2556014514f36808e6d18b25722eae0daeeb8fbb5d18af13a9698ea6c6db1679

manifest
manifest.uuid
src/vdbe.c
src/vdbe.h
src/vdbeInt.h
test/triggerG.test [new file with mode: 0644]

index cfdc62404637e2ed1754220c5e287db5f432d05a..f9ac9232e68fe8c82bd730718921cca134c95e00 100644 (file)
--- 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
index 40296ca7c1ed4ae548abedbad4da893d1bb7a02d..1ba0445ee1f602ce40a1e7b6d758874c5076bc44 100644 (file)
@@ -1 +1 @@
-c5441d2df2526723f72610cc14dd243223663979e67ecdd76fe06fcd366f2b29
\ No newline at end of file
+2556014514f36808e6d18b25722eae0daeeb8fbb5d18af13a9698ea6c6db1679
\ No newline at end of file
index 93c20c0aaab38dd5af55ce249eae99bb0c648206..5e99366b2d0329325a624b53559e456cf81c0ab7 100644 (file)
@@ -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
index a35f3be3443be2f2554a2cdb25384cfa56fecc13..be19bc5316f7fcead8f9267c94e678a28e4d2e10 100644 (file)
@@ -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 */
 };
index 989cdfd34617c9a19ec738c7cc1b7f1ad9030edc..c0435a5a7954839a6a96dbda7eeae6442e4f268c 100644 (file)
@@ -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 (file)
index 0000000..9faa898
--- /dev/null
@@ -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