From: dan Date: Thu, 6 Dec 2012 20:19:09 +0000 (+0000) Subject: Cherrypick [557c69055a3] and [0064bab7714] (OP_Once-related fixes for triggers). X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d916481b23189988ac9ccaa54e986b4c8799fb52;p=thirdparty%2Fsqlite.git Cherrypick [557c69055a3] and [0064bab7714] (OP_Once-related fixes for triggers). FossilOrigin-Name: 0d7b5d455cee06aac4a5b331f26aa5da6963d97f --- diff --git a/manifest b/manifest index c1e228b140..da2c6f3c74 100644 --- 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 diff --git a/manifest.uuid b/manifest.uuid index 070abb7d35..cbfe09f111 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -0391951792c986555c996c654b9b5166267d9094 \ No newline at end of file +0d7b5d455cee06aac4a5b331f26aa5da6963d97f \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 535e2c82fb..29fda61d5a 100644 --- a/src/expr.c +++ b/src/expr.c @@ -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 . */ Expr *pExpr; /* Expression */ int iCol; /* Index of 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 diff --git a/src/insert.c b/src/insert.c index c921053a9d..2e13f36add 100644 --- a/src/insert.c +++ b/src/insert.c @@ -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); diff --git a/src/select.c b/src/select.c index f1f05c8e35..5f0a8fe582 100644 --- a/src/select.c +++ b/src/select.c @@ -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 diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 4a28439d0d..ac4173f5e9 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -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); diff --git a/src/trigger.c b/src/trigger.c index 22c4877b6a..3c4bf62a18 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -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; diff --git a/src/update.c b/src/update.c index 1e3052218e..73d22690b5 100644 --- a/src/update.c +++ b/src/update.c @@ -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; inCol; 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 ){ diff --git a/src/vdbe.c b/src/vdbe.c index 22e6d9c5b6..dfc673ca6e 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -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->p1nOnceFlag ); + 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; } diff --git a/src/vdbe.h b/src/vdbe.h index 948c73bcab..90a43ce6ee 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -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 */ }; diff --git a/src/vdbeInt.h b/src/vdbeInt.h index 803ae1630e..5f75caaf09 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -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 */ }; /* diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 75250238ec..abb437e4af 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -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; naMem--; /* 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; inCursor; 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; inCursor; 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; diff --git a/src/where.c b/src/where.c index 05414da58b..cf7c7ef44c 100644 --- a/src/where.c +++ b/src/where.c @@ -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 index 0000000000..e560a0d4d5 --- /dev/null +++ b/test/tkt-7bbfb7d442.test @@ -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 + +