]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Cherrypick [557c69055a3] and [0064bab7714] (OP_Once-related fixes for triggers).
authordan <dan@noemail.net>
Thu, 6 Dec 2012 20:19:09 +0000 (20:19 +0000)
committerdan <dan@noemail.net>
Thu, 6 Dec 2012 20:19:09 +0000 (20:19 +0000)
FossilOrigin-Name: 0d7b5d455cee06aac4a5b331f26aa5da6963d97f

14 files changed:
manifest
manifest.uuid
src/expr.c
src/insert.c
src/select.c
src/sqliteInt.h
src/trigger.c
src/update.c
src/vdbe.c
src/vdbe.h
src/vdbeInt.h
src/vdbeaux.c
src/where.c
test/tkt-7bbfb7d442.test [new file with mode: 0644]

index c1e228b14011bd4369f2c30d217e543e7435a864..da2c6f3c741ab91d5c5d3ec033cd81fcfe4cf8f9 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Merge\sthe\somit-wal-fix\sbranch\sinto\sbranch-3.7.9\swhere\sit\sbelongs,\sand\sin\sso\ndoing\sget\sbranch-3.7.9\sworking\swith\sSQLITE_OMIT_WAL.
-D 2012-11-05T16:36:10.077
+C Cherrypick\s[557c69055a3]\sand\s[0064bab7714]\s(OP_Once-related\sfixes\sfor\striggers).
+D 2012-12-06T20:19:09.714
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in a162fe39e249b8ed4a65ee947c30152786cfe897
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -134,7 +134,7 @@ F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
 F src/ctime.c a9c26822515f81ec21588cbb482ca6724be02e33
 F src/date.c 067a81c9942c497aafd2c260e13add8a7d0c7dd4
 F src/delete.c e3a945da4aecffdb0fc9226da004f7273acecdcb
-F src/expr.c 92a2d01903f4a22aa78cc24bf31ef591853293b1
+F src/expr.c 96d029a9b7b5c012cc00ce11c38215fb7d13488b
 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
 F src/fkey.c 657212460bf5cfd3ae607d12ea62092844c227b5
 F src/func.c 6261ce00aad9c63cd5b4219249b05683979060e9
@@ -142,7 +142,7 @@ F src/global.c e230227de13601714b29f9363028514aada5ae2f
 F src/hash.c 458488dcc159c301b8e7686280ab209f1fb915af
 F src/hash.h 2894c932d84d9f892d4b4023a75e501f83050970
 F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
-F src/insert.c ef88d0e4388494f59513d5ba18ae49106478275c
+F src/insert.c abe16e63ef0865467e958fc28994eadc2849e169
 F src/journal.c 552839e54d1bf76fb8f7abe51868b66acacf6a0e
 F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f
 F src/lempar.c 0ee69fca0be54cd93939df98d2aca4ca46f44416
@@ -180,11 +180,11 @@ F src/printf.c 03104cbff6959ff45df69dc9060ba6212f60a869
 F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50
 F src/resolve.c 365ab1c870e38596d6869e76fb544fe6e4ffc809
 F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697
-F src/select.c aa35f4ed3eadc2e4845ba094035e7d4309fd8466
+F src/select.c 3d4543b9611030df49eca1618bde8bd8468bf3b3
 F src/shell.c f0ab793261ab045a0b8c47fa2707e8a894d2898f
 F src/sqlite.h.in ff950aef7b378963c67add42dda5d446a0b7330e
 F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477
-F src/sqliteInt.h f68df9333dd8cb5cb933b7cef286f219301e4fd8
+F src/sqliteInt.h d660a5ad543d775f28a46cf9510b5cb86e8097d2
 F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
 F src/status.c 4568e72dfd36b6a5911f93457364deb072e0b03a
 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
@@ -234,16 +234,16 @@ F src/test_vfstrace.c 0b884e06094a746da729119a2cabdc7aa790063d
 F src/test_wholenumber.c 6129adfbe7c7444f2e60cc785927f3aa74e12290
 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
 F src/tokenize.c c819d9f72168a035d545a5bdafe9b085b20df705
-F src/trigger.c 1cfb80e2290ef66ea89cb4e821caae65a02c0d56
-F src/update.c 25e046a8f69d5e557aabde2000487b8545509d8d
+F src/trigger.c ee7e178fb9188f44b532cebd449a7c1df90fb684
+F src/update.c d3076782c887c10e882996550345da9c4c9f9dea
 F src/utf.c 890c67dcfcc7a74623c95baac7535aadfe265e84
 F src/util.c df83983bd57057df4951516880066b42b7055269
 F src/vacuum.c 0c0ba2242355c6048d65e2b333abe0f7c06348fa
-F src/vdbe.c 326994a64a9a08853122200dc9f62cb96b8f0831
-F src/vdbe.h f0725ee997db869ecae5bb70a71612aabeca7755
-F src/vdbeInt.h 9498fc98a2c9e349a4ef13455ff5a3e898f40176
+F src/vdbe.c fed44fdc7c34838d8c6a6a89a7e124944846f66c
+F src/vdbe.h 18f581cac1f4339ec3299f3e0cc6e11aec654cdb
+F src/vdbeInt.h 23a9506c9ab31e7823d7257d1828d2d7843443a0
 F src/vdbeapi.c 4dbba7f94f127f6ea8d2d0505ee1f98e5ffbf546
-F src/vdbeaux.c a950e34449a508d48d90475acc287943a4094f3a
+F src/vdbeaux.c 15b1ef21b59c308812e49b33b1b42516fdc48817
 F src/vdbeblob.c 32f2a4899d67f69634ea4dd93e3f651936d732cb
 F src/vdbemem.c 2fc78b3e0fabcc1eaa23cd79dd2e30e6dcfe1e56
 F src/vdbesort.c 468d43c057063e54da4f1988b38b4f46d60e7790
@@ -252,7 +252,7 @@ F src/vtab.c f36d8f8386e59c225f9c458ec45a3c20ff8efe78
 F src/wal.c 9658df8d404b82e6b2d40fd05944463214e2d935
 F src/wal.h 66b40bd91bc29a5be1c88ddd1f5ade8f3f48728a
 F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f
-F src/where.c 7c85f4c93058e27100d404f0777aaeb0d1b296ae
+F src/where.c 13e6e3e15bd2b2991e73027cd23e584bf7a629ad
 F test/8_3_names.test 631ea964a3edb091cf73c3b540f6bcfdb36ce823
 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
 F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
@@ -735,6 +735,7 @@ F test/tkt-5e10420e8d.test 904d1687b3c06d43e5b3555bbcf6802e7c0ffd84
 F test/tkt-5ee23731f.test 9db6e1d7209dc0794948b260d6f82b2b1de83a9f
 F test/tkt-752e1646fc.test ea78d88d14fe9866bdd991c634483334639e13bf
 F test/tkt-78e04e52ea.test ab52f0c1e2de6e46c910f4cc16b086bba05952b7
+F test/tkt-7bbfb7d442.test 8e7658f77d1ccea9d88dc9e255d3ed7fb68f8bdf
 F test/tkt-80ba201079.test a09684db1a0bd55b8838f606adccee456a51ddbf
 F test/tkt-80e031a00f.test 9a154173461a4dbe2de49cda73963e04842d52f7
 F test/tkt-8454a207b9.test c583a9f814a82a2b5ba95207f55001c9f0cd816c
@@ -974,7 +975,7 @@ F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06
 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
 F tool/warnings-clang.sh 9f406d66e750e8ac031c63a9ef3248aaa347ef2a
 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
-P 9078ea75f52fc76039100b3cc0b9a2a3b9a5bff0 a499ae3835ed7f1591c40bea20e0c97f58ceedbb
-R df5458efb6b35dd58a12691fa478e848
-U drh
-Z 1b1a0deed720e56c598218ee2e4bad91
+P 0391951792c986555c996c654b9b5166267d9094
+R f3af1c83cb060930ae53f2e980547792
+U dan
+Z 3bdb6f7a206b8062e49e353b5fa0d9da
index 070abb7d3598b3ab4ee067b0b73f42266eb2c2be..cbfe09f1118dec129b631f53502a2f8c2351157c 100644 (file)
@@ -1 +1 @@
-0391951792c986555c996c654b9b5166267d9094
\ No newline at end of file
+0d7b5d455cee06aac4a5b331f26aa5da6963d97f
\ No newline at end of file
index 535e2c82fb8f1b68d60d5e9a39c5ed7fda26ce4c..29fda61d5ab3229704617d5c0b09b98f6744377a 100644 (file)
@@ -1374,6 +1374,15 @@ static int isCandidateForInOpt(Select *p){
 }
 #endif /* SQLITE_OMIT_SUBQUERY */
 
+/*
+** Code an OP_Once instruction and allocate space for its flag. Return the 
+** address of the new instruction.
+*/
+int sqlite3CodeOnce(Parse *pParse){
+  Vdbe *v = sqlite3GetVdbe(pParse);      /* Virtual machine being coded */
+  return sqlite3VdbeAddOp1(v, OP_Once, pParse->nOnce++);
+}
+
 /*
 ** This function is used by the implementation of the IN (...) operator.
 ** It's job is to find or create a b-tree structure that may be used
@@ -1434,6 +1443,7 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
   int eType = 0;                        /* Type of RHS table. IN_INDEX_* */
   int iTab = pParse->nTab++;            /* Cursor of the RHS table */
   int mustBeUnique = (prNotFound==0);   /* True if RHS must be unique */
+  Vdbe *v = sqlite3GetVdbe(pParse);     /* Virtual machine being coded */
 
   assert( pX->op==TK_IN );
 
@@ -1444,7 +1454,6 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
   p = (ExprHasProperty(pX, EP_xIsSelect) ? pX->x.pSelect : 0);
   if( ALWAYS(pParse->nErr==0) && isCandidateForInOpt(p) ){
     sqlite3 *db = pParse->db;              /* Database connection */
-    Vdbe *v = sqlite3GetVdbe(pParse);      /* Virtual machine being coded */
     Table *pTab;                           /* Table <table>. */
     Expr *pExpr;                           /* Expression <column> */
     int iCol;                              /* Index of column <column> */
@@ -1469,10 +1478,9 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
     */
     assert(v);
     if( iCol<0 ){
-      int iMem = ++pParse->nMem;
       int iAddr;
 
-      iAddr = sqlite3VdbeAddOp1(v, OP_Once, iMem);
+      iAddr = sqlite3CodeOnce(pParse);
 
       sqlite3OpenTable(pParse, iTab, iDb, pTab, OP_OpenRead);
       eType = IN_INDEX_ROWID;
@@ -1498,12 +1506,11 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
          && sqlite3FindCollSeq(db, ENC(db), pIdx->azColl[0], 0)==pReq
          && (!mustBeUnique || (pIdx->nColumn==1 && pIdx->onError!=OE_None))
         ){
-          int iMem = ++pParse->nMem;
           int iAddr;
           char *pKey;
   
           pKey = (char *)sqlite3IndexKeyinfo(pParse, pIdx);
-          iAddr = sqlite3VdbeAddOp1(v, OP_Once, iMem);
+          iAddr = sqlite3CodeOnce(pParse);
   
           sqlite3VdbeAddOp4(v, OP_OpenRead, iTab, pIdx->tnum, iDb,
                                pKey,P4_KEYINFO_HANDOFF);
@@ -1513,6 +1520,7 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
           sqlite3VdbeJumpHere(v, iAddr);
           if( prNotFound && !pTab->aCol[iCol].notNull ){
             *prNotFound = ++pParse->nMem;
+            sqlite3VdbeAddOp2(v, OP_Null, 0, *prNotFound);
           }
         }
       }
@@ -1528,6 +1536,7 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
     eType = IN_INDEX_EPH;
     if( prNotFound ){
       *prNotFound = rMayHaveNull = ++pParse->nMem;
+      sqlite3VdbeAddOp2(v, OP_Null, 0, *prNotFound);
     }else{
       testcase( pParse->nQueryLoop>(double)1 );
       pParse->nQueryLoop = (double)1;
@@ -1600,9 +1609,8 @@ int sqlite3CodeSubselect(
   ** If all of the above are false, then we can run this code just once
   ** save the results, and reuse the same result on subsequent invocations.
   */
-  if( !ExprHasAnyProperty(pExpr, EP_VarSelect) && !pParse->pTriggerTab ){
-    int mem = ++pParse->nMem;
-    testAddr = sqlite3VdbeAddOp1(v, OP_Once, mem);
+  if( !ExprHasAnyProperty(pExpr, EP_VarSelect) ){
+    testAddr = sqlite3CodeOnce(pParse);
   }
 
 #ifndef SQLITE_OMIT_EXPLAIN
index c921053a9dea4e50a9dd45c1957ea8830a2d3999..2e13f36add4b6417d9328abd94e4bfec6dac1d6f 100644 (file)
@@ -239,6 +239,7 @@ void sqlite3AutoincrementBegin(Parse *pParse){
     memId = p->regCtr;
     assert( sqlite3SchemaMutexHeld(db, 0, pDb->pSchema) );
     sqlite3OpenTable(pParse, 0, p->iDb, pDb->pSchema->pSeqTab, OP_OpenRead);
+    sqlite3VdbeAddOp3(v, OP_Null, 0, memId, memId+1);
     addr = sqlite3VdbeCurrentAddr(v);
     sqlite3VdbeAddOp4(v, OP_String8, 0, memId-1, 0, p->pTab->zName, 0);
     sqlite3VdbeAddOp2(v, OP_Rewind, 0, addr+9);
index f1f05c8e351877b45a4e29a4e532f8b58349e582..5f0a8fe582e777d559fe29c7b6b633d29d5cc24b 100644 (file)
@@ -3844,12 +3844,11 @@ int sqlite3Select(
       topAddr = sqlite3VdbeAddOp2(v, OP_Integer, 0, pItem->regReturn);
       pItem->addrFillSub = topAddr+1;
       VdbeNoopComment((v, "materialize %s", pItem->pTab->zName));
-      if( pItem->isCorrelated==0 && pParse->pTriggerTab==0 ){
+      if( pItem->isCorrelated==0 ){
         /* If the subquery is no correlated and if we are not inside of
         ** a trigger, then we only need to compute the value of the subquery
         ** once. */
-        int regOnce = ++pParse->nMem;
-        onceAddr = sqlite3VdbeAddOp1(v, OP_Once, regOnce);
+        onceAddr = sqlite3CodeOnce(pParse);
       }
       sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor);
       explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
@@ -4154,6 +4153,7 @@ int sqlite3Select(
       VdbeComment((v, "clear abort flag"));
       sqlite3VdbeAddOp2(v, OP_Integer, 0, iUseFlag);
       VdbeComment((v, "indicate accumulator empty"));
+      sqlite3VdbeAddOp3(v, OP_Null, 0, iAMem, iAMem+pGroupBy->nExpr-1);
 
       /* Begin a loop that will extract all source rows in GROUP BY order.
       ** This might involve two separate loops with an OP_Sort in between, or
index 4a28439d0dfbe04c353fb800052a8f7f2bf8e567..ac4173f5e9aa34948f7d2bf22556503ae18d1386 100644 (file)
@@ -2219,6 +2219,7 @@ struct Parse {
   int nTab;            /* Number of previously allocated VDBE cursors */
   int nMem;            /* Number of memory cells used so far */
   int nSet;            /* Number of sets used so far */
+  int nOnce;           /* Number of OP_Once instructions so far */
   int ckBase;          /* Base register of data during check constraints */
   int iCacheLevel;     /* ColCache valid when aColCache[].iLevel<=iCacheLevel */
   int iCacheCnt;       /* Counter used to generate aColCache[].lru values */
@@ -2708,6 +2709,7 @@ void sqlite3AddCollateType(Parse*, Token*);
 void sqlite3EndTable(Parse*,Token*,Token*,Select*);
 int sqlite3ParseUri(const char*,const char*,unsigned int*,
                     sqlite3_vfs**,char**,char **);
+int sqlite3CodeOnce(Parse *);
 
 Bitvec *sqlite3BitvecCreate(u32);
 int sqlite3BitvecTest(Bitvec*, u32);
index 22c4877b6a3ea3fd340aee9819b16106949849ca..3c4bf62a18c949e7535f2ada4faddc75c6dd9a3b 100644 (file)
@@ -904,6 +904,7 @@ static TriggerPrg *codeRowTrigger(
     }
     pProgram->nMem = pSubParse->nMem;
     pProgram->nCsr = pSubParse->nTab;
+    pProgram->nOnce = pSubParse->nOnce;
     pProgram->token = (void *)pTrigger;
     pPrg->aColmask[0] = pSubParse->oldmask;
     pPrg->aColmask[1] = pSubParse->newmask;
index 1e3052218e062e2d3b182b244ae248a800687b07..73d22690b5ea7dab40e4fdd3377c474fab51f3e5 100644 (file)
@@ -126,8 +126,8 @@ void sqlite3Update(
   int regRowCount = 0;   /* A count of rows changed */
   int regOldRowid;       /* The old rowid */
   int regNewRowid;       /* The new rowid */
-  int regNew;
-  int regOld = 0;
+  int regNew;            /* Content of the NEW.* table in triggers */
+  int regOld = 0;        /* Content of OLD.* table in triggers */
   int regRowSet = 0;     /* Rowset of rows to be updated */
 
   memset(&sContext, 0, sizeof(sContext));
@@ -276,6 +276,7 @@ void sqlite3Update(
 #endif
 
   /* Allocate required registers. */
+  regRowSet = ++pParse->nMem;
   regOldRowid = regNewRowid = ++pParse->nMem;
   if( pTrigger || hasFK ){
     regOld = pParse->nMem + 1;
@@ -310,7 +311,7 @@ void sqlite3Update(
 
   /* Begin the database scan
   */
-  sqlite3VdbeAddOp2(v, OP_Null, 0, regOldRowid);
+  sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid);
   pWInfo = sqlite3WhereBegin(
       pParse, pTabList, pWhere, 0, 0, WHERE_ONEPASS_DESIRED
   );
@@ -321,7 +322,6 @@ void sqlite3Update(
   */
   sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regOldRowid);
   if( !okOnePass ){
-    regRowSet = ++pParse->nMem;
     sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid);
   }
 
@@ -425,9 +425,10 @@ void sqlite3Update(
   newmask = sqlite3TriggerColmask(
       pParse, pTrigger, pChanges, 1, TRIGGER_BEFORE, pTab, onError
   );
+  sqlite3VdbeAddOp3(v, OP_Null, 0, regNew, regNew+pTab->nCol-1);
   for(i=0; i<pTab->nCol; i++){
     if( i==pTab->iPKey ){
-      sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);
+      /*sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);*/
     }else{
       j = aXRef[i];
       if( j>=0 ){
index 22e6d9c5b676149dd761188e9695de9015c2f2cb..dfc673ca6e6222490bb4fa7704e3be68e5b6b38b 100644 (file)
@@ -764,7 +764,8 @@ case OP_Goto: {             /* jump */
 ** Write the current address onto register P1
 ** and then jump to address P2.
 */
-case OP_Gosub: {            /* jump, in1 */
+case OP_Gosub: {            /* jump */
+  assert( pOp->p1>0 && pOp->p1<=p->nMem );
   pIn1 = &aMem[pOp->p1];
   assert( (pIn1->flags & MEM_Dyn)==0 );
   memAboutToChange(p, pIn1);
@@ -961,12 +962,25 @@ case OP_String: {          /* out2-prerelease */
   break;
 }
 
-/* Opcode: Null * P2 * * *
+/* Opcode: Null * P2 P3 * *
 **
-** Write a NULL into register P2.
+** Write a NULL into registers P2.  If P3 greater than P2, then also write
+** NULL into register P3 and ever register in between P2 and P3.  If P3
+** is less than P2 (typically P3 is zero) then only register P2 is
+** set to NULL
 */
 case OP_Null: {           /* out2-prerelease */
+  int cnt;
+  cnt = pOp->p3-pOp->p2;
+  assert( pOp->p3<=p->nMem );
   pOut->flags = MEM_Null;
+  while( cnt>0 ){
+    pOut++;
+    memAboutToChange(p, pOut);
+    MemReleaseExt(pOut);
+    pOut->flags = MEM_Null;
+    cnt--;
+  }
   break;
 }
 
@@ -2023,27 +2037,33 @@ case OP_BitNot: {             /* same as TK_BITNOT, in1, out2 */
 
 /* Opcode: Once P1 P2 * * *
 **
-** Jump to P2 if the value in register P1 is a not null or zero.  If
-** the value is NULL or zero, fall through and change the P1 register
-** to an integer 1.
+** Check if OP_Once flag P1 is set. If so, jump to instruction P2. Otherwise,
+** set the flag and fall through to the next instruction.
 **
-** When P1 is not used otherwise in a program, this opcode falls through
-** once and jumps on all subsequent invocations.  It is the equivalent
-** of "OP_If P1 P2", followed by "OP_Integer 1 P1".
+** See also: JumpOnce
 */
+case OP_Once: {             /* jump */
+  assert( pOp->p1<p->nOnceFlag );
+  if( p->aOnceFlag[pOp->p1] ){
+    pc = pOp->p2-1;
+  }else{
+    p->aOnceFlag[pOp->p1] = 1;
+  }
+  break;
+}
+
 /* Opcode: If P1 P2 P3 * *
 **
 ** Jump to P2 if the value in register P1 is true.  The value
 ** is considered true if it is numeric and non-zero.  If the value
-** in P1 is NULL then take the jump if P3 is true.
+** in P1 is NULL then take the jump if P3 is non-zero.
 */
 /* Opcode: IfNot P1 P2 P3 * *
 **
 ** Jump to P2 if the value in register P1 is False.  The value
-** is considered true if it has a numeric value of zero.  If the value
-** in P1 is NULL then take the jump if P3 is true.
+** is considered false if it has a numeric value of zero.  If the value
+** in P1 is NULL then take the jump if P3 is zero.
 */
-case OP_Once:               /* jump, in1 */
 case OP_If:                 /* jump, in1 */
 case OP_IfNot: {            /* jump, in1 */
   int c;
@@ -2060,12 +2080,6 @@ case OP_IfNot: {            /* jump, in1 */
   }
   if( c ){
     pc = pOp->p2-1;
-  }else if( pOp->opcode==OP_Once ){
-    assert( (pIn1->flags & (MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame))==0 );
-    memAboutToChange(p, pIn1);
-    pIn1->flags = MEM_Int;
-    pIn1->u.i = 1;
-    REGISTER_TRACE(pOp->p1, pIn1);
   }
   break;
 }
@@ -5071,7 +5085,6 @@ case OP_Program: {        /* jump */
 
   pProgram = pOp->p4.pProgram;
   pRt = &aMem[pOp->p3];
-  assert( memIsValid(pRt) );
   assert( pProgram->nOp>0 );
   
   /* If the p5 flag is clear, then recursive invocation of triggers is 
@@ -5110,7 +5123,8 @@ case OP_Program: {        /* jump */
     nMem = pProgram->nMem + pProgram->nCsr;
     nByte = ROUND8(sizeof(VdbeFrame))
               + nMem * sizeof(Mem)
-              + pProgram->nCsr * sizeof(VdbeCursor *);
+              + pProgram->nCsr * sizeof(VdbeCursor *)
+              + pProgram->nOnce * sizeof(u8);
     pFrame = sqlite3DbMallocZero(db, nByte);
     if( !pFrame ){
       goto no_mem;
@@ -5130,10 +5144,12 @@ case OP_Program: {        /* jump */
     pFrame->aOp = p->aOp;
     pFrame->nOp = p->nOp;
     pFrame->token = pProgram->token;
+    pFrame->aOnceFlag = p->aOnceFlag;
+    pFrame->nOnceFlag = p->nOnceFlag;
 
     pEnd = &VdbeFrameMem(pFrame)[pFrame->nChildMem];
     for(pMem=VdbeFrameMem(pFrame); pMem!=pEnd; pMem++){
-      pMem->flags = MEM_Null;
+      pMem->flags = MEM_Invalid;
       pMem->db = db;
     }
   }else{
@@ -5155,7 +5171,11 @@ case OP_Program: {        /* jump */
   p->apCsr = (VdbeCursor **)&aMem[p->nMem+1];
   p->aOp = aOp = pProgram->aOp;
   p->nOp = pProgram->nOp;
+  p->aOnceFlag = (u8 *)&p->apCsr[p->nCursor];
+  p->nOnceFlag = pProgram->nOnce;
+  p->nOp = pProgram->nOp;
   pc = -1;
+  memset(p->aOnceFlag, 0, p->nOnceFlag);
 
   break;
 }
index 948c73bcabf28556ddc86dd23e8b2e8dc262bd3a..90a43ce6ee73929189705809acfefebe71bf843a 100644 (file)
@@ -82,6 +82,7 @@ struct SubProgram {
   int nOp;                      /* Elements in aOp[] */
   int nMem;                     /* Number of memory cells required */
   int nCsr;                     /* Number of cursors required */
+  int nOnce;                    /* Number of OP_Once instructions */
   void *token;                  /* id that may be used to recursive triggers */
   SubProgram *pNext;            /* Next sub-program already visited */
 };
index 803ae1630e4d4fa9f2ac9ba87d0256c72637fac7..5f75caaf09c09d2c50dbe523479a389017f8f4ff 100644 (file)
@@ -117,6 +117,8 @@ struct VdbeFrame {
   int nOp;                /* Size of aOp array */
   Mem *aMem;              /* Array of memory cells for parent frame */
   int nMem;               /* Number of entries in aMem */
+  u8 *aOnceFlag;          /* Array of OP_Once flags for parent frame */
+  int nOnceFlag;          /* Number of entries in aOnceFlag */
   VdbeCursor **apCsr;     /* Array of Vdbe cursors for parent frame */
   u16 nCursor;            /* Number of entries in apCsr */
   void *token;            /* Copy of SubProgram.token */
@@ -326,6 +328,8 @@ struct Vdbe {
   int nFrame;             /* Number of frames in pFrame list */
   u32 expmask;            /* Binding to these vars invalidates VM */
   SubProgram *pProgram;   /* Linked list of all sub-programs used by VM */
+  int nOnceFlag;          /* Size of array aOnceFlag[] */
+  u8 *aOnceFlag;          /* Flags for OP_Once */
 };
 
 /*
index 75250238ecb83c9a73a66119233b96d1f532155d..abb437e4af68392f9364f79420a1faccd5de2b4b 100644 (file)
@@ -913,13 +913,14 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
     }
     case P4_MEM: {
       Mem *pMem = pOp->p4.pMem;
-      assert( (pMem->flags & MEM_Null)==0 );
       if( pMem->flags & MEM_Str ){
         zP4 = pMem->z;
       }else if( pMem->flags & MEM_Int ){
         sqlite3_snprintf(nTemp, zTemp, "%lld", pMem->u.i);
       }else if( pMem->flags & MEM_Real ){
         sqlite3_snprintf(nTemp, zTemp, "%.16g", pMem->r);
+      }else if( pMem->flags & MEM_Null ){
+        sqlite3_snprintf(nTemp, zTemp, "NULL");
       }else{
         assert( pMem->flags & MEM_Blob );
         zP4 = "(blob)";
@@ -1094,7 +1095,7 @@ static void releaseMemArray(Mem *p, int N){
         p->zMalloc = 0;
       }
 
-      p->flags = MEM_Null;
+      p->flags = MEM_Invalid;
     }
     db->mallocFailed = malloc_failed;
   }
@@ -1469,6 +1470,7 @@ void sqlite3VdbeMakeReady(
   int nMem;                      /* Number of VM memory registers */
   int nCursor;                   /* Number of cursors required */
   int nArg;                      /* Number of arguments in subprograms */
+  int nOnce;                     /* Number of OP_Once instructions */
   int n;                         /* Loop counter */
   u8 *zCsr;                      /* Memory available for allocation */
   u8 *zEnd;                      /* First byte past allocated memory */
@@ -1484,6 +1486,7 @@ void sqlite3VdbeMakeReady(
   nMem = pParse->nMem;
   nCursor = pParse->nTab;
   nArg = pParse->nMaxArg;
+  nOnce = pParse->nOnce;
   
   /* For each cursor required, also allocate a memory cell. Memory
   ** cells (nMem+1-nCursor)..nMem, inclusive, will never be used by
@@ -1530,6 +1533,7 @@ void sqlite3VdbeMakeReady(
     p->azVar = allocSpace(p->azVar, nVar*sizeof(char*), &zCsr, zEnd, &nByte);
     p->apCsr = allocSpace(p->apCsr, nCursor*sizeof(VdbeCursor*),
                           &zCsr, zEnd, &nByte);
+    p->aOnceFlag = allocSpace(p->aOnceFlag, nOnce, &zCsr, zEnd, &nByte);
     if( nByte ){
       p->pFree = sqlite3DbMallocZero(db, nByte);
     }
@@ -1538,6 +1542,7 @@ void sqlite3VdbeMakeReady(
   }while( nByte && !db->mallocFailed );
 
   p->nCursor = (u16)nCursor;
+  p->nOnceFlag = nOnce;
   if( p->aVar ){
     p->nVar = (ynVar)nVar;
     for(n=0; n<nVar; n++){
@@ -1554,7 +1559,7 @@ void sqlite3VdbeMakeReady(
     p->aMem--;                      /* aMem[] goes from 1..nMem */
     p->nMem = nMem;                 /*       not from 0..nMem-1 */
     for(n=1; n<=nMem; n++){
-      p->aMem[n].flags = MEM_Null;
+      p->aMem[n].flags = MEM_Invalid;
       p->aMem[n].db = db;
     }
   }
@@ -1596,6 +1601,8 @@ void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){
 */
 int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){
   Vdbe *v = pFrame->v;
+  v->aOnceFlag = pFrame->aOnceFlag;
+  v->nOnceFlag = pFrame->nOnceFlag;
   v->aOp = pFrame->aOp;
   v->nOp = pFrame->nOp;
   v->aMem = pFrame->aMem;
@@ -1658,8 +1665,10 @@ static void Cleanup(Vdbe *p){
   /* Execute assert() statements to ensure that the Vdbe.apCsr[] and 
   ** Vdbe.aMem[] arrays have already been cleaned up.  */
   int i;
-  for(i=0; i<p->nCursor; i++) assert( p->apCsr==0 || p->apCsr[i]==0 );
-  for(i=1; i<=p->nMem; i++) assert( p->aMem==0 || p->aMem[i].flags==MEM_Null );
+  if( p->apCsr ) for(i=0; i<p->nCursor; i++) assert( p->apCsr[i]==0 );
+  if( p->aMem ){
+    for(i=1; i<=p->nMem; i++) assert( p->aMem[i].flags==MEM_Invalid );
+  }
 #endif
 
   sqlite3DbFree(db, p->zErrMsg);
@@ -2127,6 +2136,7 @@ int sqlite3VdbeHalt(Vdbe *p){
   if( p->db->mallocFailed ){
     p->rc = SQLITE_NOMEM;
   }
+  if( p->aOnceFlag ) memset(p->aOnceFlag, 0, p->nOnceFlag);
   closeAllCursors(p);
   if( p->magic!=VDBE_MAGIC_RUN ){
     return SQLITE_OK;
index 05414da58b4cf02dcca176d42a25ab192eca4426..cf7c7ef44c8e4594ffe0a381cb81ea61ae359cb7 100644 (file)
@@ -2005,7 +2005,6 @@ static void constructAutomaticIndex(
   int nByte;                  /* Byte of memory needed for pIdx */
   Index *pIdx;                /* Object describing the transient index */
   Vdbe *v;                    /* Prepared statement under construction */
-  int regIsInit;              /* Register set by initialization */
   int addrInit;               /* Address of the initialization bypass jump */
   Table *pTable;              /* The table being indexed */
   KeyInfo *pKeyinfo;          /* Key information for the index */   
@@ -2022,8 +2021,7 @@ static void constructAutomaticIndex(
   ** transient index on 2nd and subsequent iterations of the loop. */
   v = pParse->pVdbe;
   assert( v!=0 );
-  regIsInit = ++pParse->nMem;
-  addrInit = sqlite3VdbeAddOp1(v, OP_Once, regIsInit);
+  addrInit = sqlite3CodeOnce(pParse);
 
   /* Count the number of columns that will be added to the index
   ** and used to match WHERE clause constraints */
diff --git a/test/tkt-7bbfb7d442.test b/test/tkt-7bbfb7d442.test
new file mode 100644 (file)
index 0000000..e560a0d
--- /dev/null
@@ -0,0 +1,154 @@
+# 2011 December 9
+#
+# 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 verify that ticket [7bbfb7d442] has been
+# fixed.  
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix tkt-7bbfb7d442
+
+do_execsql_test 1.1 {
+  CREATE TABLE t1(a, b);
+  INSERT INTO t1 VALUES(1, 'one');
+  INSERT INTO t1 VALUES(2, 'two');
+  INSERT INTO t1 VALUES(3, 'three');
+
+  CREATE TABLE t2(c, d);
+  INSERT INTO t2 VALUES('one', 'I');
+  INSERT INTO t2 VALUES('two', 'II');
+  INSERT INTO t2 VALUES('three', 'III');
+
+  CREATE TABLE t3(t3_a PRIMARY KEY, t3_d);
+  CREATE TRIGGER t3t AFTER INSERT ON t3 WHEN new.t3_d IS NULL BEGIN
+    UPDATE t3 SET t3_d = (
+      SELECT d FROM 
+        (SELECT * FROM t2 WHERE (new.t3_a%2)=(rowid%2) LIMIT 10),
+        (SELECT * FROM t1 WHERE (new.t3_a%2)=(rowid%2) LIMIT 10)
+      WHERE a = new.t3_a AND b = c
+    ) WHERE t3_a = new.t3_a;
+  END;
+}
+
+do_execsql_test 1.2 {
+  INSERT INTO t3(t3_a) VALUES(1);
+  INSERT INTO t3(t3_a) VALUES(2);
+  INSERT INTO t3(t3_a) VALUES(3);
+  SELECT * FROM t3;
+} {1 I 2 II 3 III}
+
+do_execsql_test 1.3 { DELETE FROM t3 }
+
+do_execsql_test 1.4 {
+  INSERT INTO t3(t3_a) SELECT 1 UNION SELECT 2 UNION SELECT 3;
+  SELECT * FROM t3;
+} {1 I 2 II 3 III}
+
+
+
+#-------------------------------------------------------------------------
+# The following test case - 2.* - is from the original bug report as 
+# posted to the mailing list.
+#
+do_execsql_test 2.1 {
+  CREATE TABLE InventoryControl (
+    InventoryControlId INTEGER PRIMARY KEY AUTOINCREMENT,
+    SKU INTEGER NOT NULL,
+    Variant INTEGER NOT NULL DEFAULT 0,
+    ControlDate DATE NOT NULL,
+    ControlState INTEGER NOT NULL DEFAULT -1,
+    DeliveredQty VARCHAR(30)
+  );
+  
+  CREATE TRIGGER TGR_InventoryControl_AfterInsert
+  AFTER INSERT ON InventoryControl 
+  FOR EACH ROW WHEN NEW.ControlState=-1 BEGIN 
+
+  INSERT OR REPLACE INTO InventoryControl(
+        InventoryControlId,SKU,Variant,ControlDate,ControlState,DeliveredQty
+  ) SELECT
+          T1.InventoryControlId AS InventoryControlId,
+          T1.SKU AS SKU,
+          T1.Variant AS Variant,
+          T1.ControlDate AS ControlDate,
+          1 AS ControlState,
+          COALESCE(T2.DeliveredQty,0) AS DeliveredQty
+      FROM (
+          SELECT
+              NEW.InventoryControlId AS InventoryControlId,
+              II.SKU AS SKU,
+              II.Variant AS Variant,
+              COALESCE(LastClosedIC.ControlDate,NEW.ControlDate) AS ControlDate
+          FROM
+              InventoryItem II
+          LEFT JOIN
+              InventoryControl LastClosedIC
+              ON  LastClosedIC.InventoryControlId IN ( SELECT 99999 )
+          WHERE
+              II.SKU=NEW.SKU AND
+              II.Variant=NEW.Variant
+      )   T1
+      LEFT JOIN (
+          SELECT
+              TD.SKU AS SKU,
+              TD.Variant AS Variant,
+              10 AS DeliveredQty
+          FROM
+              TransactionDetail TD
+          WHERE
+              TD.SKU=NEW.SKU AND
+              TD.Variant=NEW.Variant
+      )   T2
+      ON  T2.SKU=T1.SKU AND
+          T2.Variant=T1.Variant;
+  END;
+  
+  CREATE TABLE InventoryItem (
+    SKU INTEGER NOT NULL,
+    Variant INTEGER NOT NULL DEFAULT 0,
+    DeptCode INTEGER NOT NULL,
+    GroupCode INTEGER NOT NULL,
+    ItemDescription VARCHAR(120) NOT NULL,
+    PRIMARY KEY(SKU, Variant)
+  );
+  
+  INSERT INTO InventoryItem VALUES(220,0,1,170,'Scoth Tampon Recurer');
+  INSERT INTO InventoryItem VALUES(31,0,1,110,'Fromage');
+  
+  CREATE TABLE TransactionDetail (
+    TransactionId INTEGER NOT NULL,
+    SKU INTEGER NOT NULL,
+    Variant INTEGER NOT NULL DEFAULT 0,
+    PRIMARY KEY(TransactionId, SKU, Variant)
+  );
+  INSERT INTO TransactionDetail(TransactionId, SKU, Variant) VALUES(44, 31, 0);
+  
+  
+  INSERT INTO InventoryControl(SKU, Variant, ControlDate) SELECT 
+      II.SKU AS SKU, II.Variant AS Variant, '2011-08-30' AS ControlDate 
+      FROM InventoryItem II;
+}
+
+do_execsql_test 2.2 {
+  SELECT SKU, DeliveredQty FROM InventoryControl WHERE SKU=31
+} {31 10}
+
+do_execsql_test 2.3 {
+  SELECT CASE WHEN DeliveredQty=10 THEN "TEST PASSED!" ELSE "TEST FAILED!" END 
+  FROM InventoryControl WHERE SKU=31; 
+} {{TEST PASSED!}}
+
+
+finish_test
+
+