]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Modify the OP_Once opcode so that it works correctly in trigger sub-programs. This...
authordan <dan@noemail.net>
Fri, 9 Dec 2011 13:24:16 +0000 (13:24 +0000)
committerdan <dan@noemail.net>
Fri, 9 Dec 2011 13:24:16 +0000 (13:24 +0000)
FossilOrigin-Name: 557c69055a300b4082830b5f4803091dca1c3140

12 files changed:
manifest
manifest.uuid
src/expr.c
src/select.c
src/sqliteInt.h
src/trigger.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 9f0b01f8f2365ec386de52fc679117874af9491a..9b3f6a87001e3f23195f22dcb721557a94542f99 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C The\sWindows\sOS\sflavor\s#ifdefs\smust\sbe\sperformed\safter\sthe\s'windows.h'\sfile\shas\sbeen\sincluded.
-D 2011-12-09T05:52:16.276
+C Modify\sthe\sOP_Once\sopcode\sso\sthat\sit\sworks\scorrectly\sin\strigger\ssub-programs.\sThis\sis\sa\scandidate\sfix\sfor\s[7bbfb7d442].
+D 2011-12-09T13:24:16.480
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in 5b4a3e12a850b021547e43daf886b25133b44c07
 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 51d32f0a9c880663e54ce309f52e40c325d5e112
-F src/expr.c 9ac5831769dddee6a55b07cdd439b21929bbe4e7
+F src/expr.c 672c6e5fa3ad2eae9ff2588eac2317d76fcdfaef
 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
 F src/fkey.c 657212460bf5cfd3ae607d12ea62092844c227b5
 F src/func.c 6261ce00aad9c63cd5b4219249b05683979060e9
@@ -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 538b5f096f6aacedf717bf982f9ad9a901008514
+F src/select.c e16b188449a7841e9e801a3c45c973c24e345199
 F src/shell.c 29812a900a780eb0f835c4bc65e216272689def8
 F src/sqlite.h.in 57081d8e6b53ce29541d7437c93bce6087ac53b5
 F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477
-F src/sqliteInt.h 142ef5ed3fe0e6c662e99dbf92f38715b29a53b6
+F src/sqliteInt.h de3db02a1e4762a6ec9e1ab604ebc02d77948030
 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/trigger.c ee7e178fb9188f44b532cebd449a7c1df90fb684
 F src/update.c 25e046a8f69d5e557aabde2000487b8545509d8d
 F src/utf.c 890c67dcfcc7a74623c95baac7535aadfe265e84
 F src/util.c 01238e2b0f24a14779181dbf991fe02620a80e31
 F src/vacuum.c 0c0ba2242355c6048d65e2b333abe0f7c06348fa
-F src/vdbe.c a7ab9993ec5a4d9479dc99671faec061fbf9b889
-F src/vdbe.h f0725ee997db869ecae5bb70a71612aabeca7755
-F src/vdbeInt.h 9498fc98a2c9e349a4ef13455ff5a3e898f40176
+F src/vdbe.c f49816f99b853c6cbdec950eedd4434cb2452376
+F src/vdbe.h 18f581cac1f4339ec3299f3e0cc6e11aec654cdb
+F src/vdbeInt.h 23a9506c9ab31e7823d7257d1828d2d7843443a0
 F src/vdbeapi.c 86189ebba2c49791d75eaa12929f3ce6527596bd
-F src/vdbeaux.c 45713a5f8f4f36195f503b30153ddef292323f88
+F src/vdbeaux.c cdd74a86c6281b4393c5f94d8f5e40e160e44e19
 F src/vdbeblob.c 32f2a4899d67f69634ea4dd93e3f651936d732cb
 F src/vdbemem.c 2fc78b3e0fabcc1eaa23cd79dd2e30e6dcfe1e56
 F src/vdbesort.c 468d43c057063e54da4f1988b38b4f46d60e7790
@@ -252,7 +252,7 @@ F src/vtab.c e9318d88feac85be8e27ee783ac8f5397933fc8a
 F src/wal.c 7e6e7fe68ee649505dca38c8ab83eda0d0d96ae5
 F src/wal.h 66b40bd91bc29a5be1c88ddd1f5ade8f3f48728a
 F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f
-F src/where.c f73752ca85c0ed221753fda98aeaf6b9d4616e0e
+F src/where.c af623942514571895818b9b7ae11db95ae3b3d88
 F test/8_3_names.test 631ea964a3edb091cf73c3b540f6bcfdb36ce823
 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
 F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
@@ -737,6 +737,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
@@ -977,7 +978,7 @@ F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06
 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
 F tool/warnings-clang.sh 9f406d66e750e8ac031c63a9ef3248aaa347ef2a
 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
-P 0d955c20c02da29582b5cd8df2b7124fb9d12ebb
-R cba6a22ba8dad5c2320a7a8f8ac118be
-U mistachkin
-Z 5127f14a32c508056103def7f6cd4a97
+P 3702a31e56fe02d14ce246109b318a124cad9f1a
+R 212172c6a4e482305cc0a6c8ccb724d1
+U dan
+Z fd67074fb89c049640d6ea2f96341088
index 65b2a7467dc93c9567bb7789a77f6c584070d15c..ae75c089600cbb0f7fddc44ad76b726cda0eef8a 100644 (file)
@@ -1 +1 @@
-3702a31e56fe02d14ce246109b318a124cad9f1a
\ No newline at end of file
+557c69055a300b4082830b5f4803091dca1c3140
\ No newline at end of file
index 136f302f902fb745f29cb7964bfca520c3cba930..195e4627d0db01118ae235beb323e26181022bc2 100644 (file)
@@ -1375,6 +1375,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
@@ -1470,10 +1479,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;
@@ -1499,12 +1507,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);
@@ -1601,9 +1608,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 da75a51cd9add885ab747f5e10f9a673e5f87120..a89ffe25c82559eec92ac1b57a25698885a55f9a 100644 (file)
@@ -3845,12 +3845,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);
index 890f44f5e10118c2189baf3fe932d4a09c7a3f08..bd4198c2f7ce83f436bfa29f75e14e49cd3272d4 100644 (file)
@@ -2204,6 +2204,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 */
@@ -2692,6 +2693,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 e7fa05037d1fa9d522cae4e919a3397fc9c035e5..53cd4a2effdc3b9158b78879fd7f146505259dc5 100644 (file)
@@ -2023,14 +2023,19 @@ 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.
-**
-** 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".
+** 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.
 */
+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
@@ -2043,7 +2048,6 @@ case OP_BitNot: {             /* same as TK_BITNOT, in1, out2 */
 ** 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.
 */
-case OP_Once:               /* jump, in1 */
 case OP_If:                 /* jump, in1 */
 case OP_IfNot: {            /* jump, in1 */
   int c;
@@ -2060,12 +2064,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;
 }
@@ -5110,7 +5108,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,6 +5129,8 @@ 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++){
@@ -5155,7 +5156,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 7c69d28cf681e0dfc7d00d7dc0b67586472c05fb..b6e13f1335ccd4bcb8c643c34ee9b5265d7c26e7 100644 (file)
@@ -1469,6 +1469,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 +1485,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 +1532,8 @@ 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*sizeof(u8), 
+                          &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++){
@@ -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;
@@ -1642,6 +1649,7 @@ static void closeAllCursors(Vdbe *p){
     p->pDelFrame = pDel->pParent;
     sqlite3VdbeFrameDelete(pDel);
   }
+  memset(p->aOnceFlag, 0, p->nOnceFlag);
 }
 
 /*
index a604d788c43ae125e8e0b806c09ba642fee256f6..78efbf9790248f17da7b23da1b80be6f051b9c53 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
+
+