From ceea33217b5c825244e7cd1c3a4be47af0d56919 Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 23 Apr 2009 13:22:42 +0000 Subject: [PATCH] Rework the column-cache mechanism to be more robust (and more correct). The column-alias cache is currently disabled, (CVS 6538) FossilOrigin-Name: dd4d67a67454a3ff13c286a2a8360c5f0432c91d --- manifest | 26 ++--- manifest.uuid | 2 +- src/build.c | 4 +- src/expr.c | 262 +++++++++++++++++++++++++++++++--------------- src/select.c | 11 +- src/sqliteInt.h | 32 ++++-- src/trigger.c | 4 +- src/where.c | 22 ++-- test/alias.test | 6 +- test/tkt3461.test | 3 +- 10 files changed, 242 insertions(+), 130 deletions(-) diff --git a/manifest b/manifest index 4c8d91ae76..e50b4cd82f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Eliminate\sthe\sOP_VRowid\sopcode.\s\sThe\sregular\sOP_Rowid\snow\swork\sfor\sboth\nregular\sand\svirtual\stables.\s(CVS\s6537) -D 2009-04-22T17:15:03 +C Rework\sthe\scolumn-cache\smechanism\sto\sbe\smore\srobust\s(and\smore\scorrect).\nThe\scolumn-alias\scache\sis\scurrently\sdisabled,\s(CVS\s6538) +D 2009-04-23T13:22:43 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.in 583e87706abc3026960ed759aff6371faf84c211 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 @@ -106,12 +106,12 @@ F src/btmutex.c 9b899c0d8df3bd68f527b0afe03088321b696d3c F src/btree.c b4ec46b3adc3e2d82704c949d4b654b031a64ad6 F src/btree.h 99fcc7e8c4a1e35afe271bcb38de1a698dfc904e F src/btreeInt.h df64030d632f8c8ac217ed52e8b6b3eacacb33a5 -F src/build.c 18c5e51c2cbaab95b1d90d38f5a15005a430d047 +F src/build.c d4c6d22636607aa3c937e619de6781e83db48b6f F src/callback.c 73016376d6848ba987709e8c9048d4f0e0776036 F src/complete.c cb14e06dbe79dee031031f0d9e686ff306afe07c F src/date.c d327ec7bb2f64b08d32b1035de82b9ba8675de91 F src/delete.c eb1066b2f35489fee46ad765d2b66386fc7d8adf -F src/expr.c 51ec16f86855a0a6ba44ba9d219abb72f06fd1c8 +F src/expr.c 015bdfc73af9d867558761cd8dc8652aa6c8cc04 F src/fault.c dc88c821842157460750d2d61a8a8b4197d047ff F src/func.c f667fe886309707c7178542073bb0ced00a9fae7 F src/global.c 448419c44ce0701104c2121b0e06919b44514c0c @@ -155,11 +155,11 @@ F src/printf.c ea2d76000cc5f4579d7e9cb2f5460433eec0d384 F src/random.c 676b9d7ac820fe81e6fb2394ac8c10cff7f38628 F src/resolve.c 094e44450371fb27869eb8bf679aacbe51fdc56d F src/rowset.c 14d12b5e81b5907b87d511f6f4219805f96a4b55 -F src/select.c 35225756c247484f473678e5bd191d70a6e4dba0 +F src/select.c b3d9b7a56e08ec007286aad55cc46f36b13b63dd F src/shell.c 0a11f831603f17fea20ca97133c0f64e716af4a7 F src/sqlite.h.in 8e0e256079bac2319380bdfebf403fcbe630510f F src/sqlite3ext.h 1db7d63ab5de4b3e6b83dd03d1a4e64fef6d2a17 -F src/sqliteInt.h b663bb04bc2d71d8f156f559ff267045de64787a +F src/sqliteInt.h 2ce7f412fbc5a56e35befea0625f05c786ddad83 F src/sqliteLimit.h ffe93f5a0c4e7bd13e70cd7bf84cfb5c3465f45d F src/status.c 237b193efae0cf6ac3f0817a208de6c6c6ef6d76 F src/table.c cc86ad3d6ad54df7c63a3e807b5783c90411a08d @@ -195,7 +195,7 @@ F src/test_tclvar.c 9e42fa59d3d2f064b7ab8628e7ab2dc8a9fe93d4 F src/test_thread.c b8a1ab7ca1a632f18e8a361880d5d65eeea08eac F src/test_wsd.c 3ae5101de6cbfda2720152ab659ea84079719241 F src/tokenize.c 7bd3b6dd56566604ad24ed4aa017e6618166b500 -F src/trigger.c 21f39db410cdc32166a94900ac1b3df98ea560e6 +F src/trigger.c c029d5262768faa43962080a170bb707afa0b9d7 F src/update.c 9617202877aa55c585b55937e0097bf050970c67 F src/utf.c 9541d28f40441812c0b40f00334372a0542c00ff F src/util.c 828c552a22a1d5b650b8a5ea0009546715c45d93 @@ -209,9 +209,9 @@ F src/vdbeblob.c e67757450ae8581a8b354d9d7e467e41502dfe38 F src/vdbemem.c 111d8193859d16aefd5d3cb57472808584ea5503 F src/vtab.c 6118d71c5137e20a7ac51fb5d9beb0361fbedb89 F src/walker.c 7cdf63223c953d4343c6833e940f110281a378ee -F src/where.c 423ed1dab78a8a62bfc4f349e7d53756b31deeaa +F src/where.c 6f7199cff0f05934bdadd6c67a0c14c0cbf5bcb4 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 -F test/alias.test 597662c5d777a122f9a3df0047ea5c5bd383a911 +F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87 F test/all.test 14165b3e32715b700b5f0cbf8f6e3833dda0be45 F test/alter.test 645b2e8d23c9936f9494af9d2fa7f8351a248c6e F test/alter2.test d0133bfa7a0a24aa84c034051410b95217d24a35 @@ -621,7 +621,7 @@ F test/tkt3419.test 1bbf36d7ea03b638c15804251287c2391f5c1f6b F test/tkt3424.test 3171193ce340cff6b7ea81c03b8fa1cbc34ec36e F test/tkt3442.test 33722a3fa4bdc0614448044eb5e28765aea28eb7 F test/tkt3457.test e9ca2b90f0eb1fb8be73a30d29aacb2e3abedeb9 -F test/tkt3461.test 5a63e8d8ee5ce00f076b1e2f82aba5480a0f14ed +F test/tkt3461.test f79d027198b7e2bcf3d2d1a5501b6efef52096ee F test/tkt3472.test 98c7e54b8fef2b1266a552a66c8e5d88a6908d1d F test/tkt3493.test 8472b3464e49a27ff7271308eec46154209e667b F test/tkt3508.test d9e285ff91731247d4673f9252fe5934639d7f0d @@ -720,7 +720,7 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81 F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e -P 1c508a99822caa383e7e24b5d09a9bddd2ee3a00 -R 999e0378f327461c6dde395dd11ca7e1 +P ecbef45011f1f98d940b2d3492941213d9f04172 +R 809b7ccefbb8e27249a22441796f79d9 U drh -Z 6d0b2cfca513b61db3f6e2221f153ea3 +Z 84c7a808c7a31870885d027c8c29d439 diff --git a/manifest.uuid b/manifest.uuid index f1918a913c..ffe6186f30 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ecbef45011f1f98d940b2d3492941213d9f04172 \ No newline at end of file +dd4d67a67454a3ff13c286a2a8360c5f0432c91d \ No newline at end of file diff --git a/src/build.c b/src/build.c index 987a90c998..73d2f5ca7b 100644 --- a/src/build.c +++ b/src/build.c @@ -22,7 +22,7 @@ ** COMMIT ** ROLLBACK ** -** $Id: build.c,v 1.529 2009/04/20 17:43:03 drh Exp $ +** $Id: build.c,v 1.530 2009/04/23 13:22:43 drh Exp $ */ #include "sqliteInt.h" @@ -190,7 +190,7 @@ void sqlite3FinishCoding(Parse *pParse){ FILE *trace = (db->flags & SQLITE_VdbeTrace)!=0 ? stdout : 0; sqlite3VdbeTrace(v, trace); #endif - assert( pParse->disableColCache==0 ); /* Disables and re-enables match */ + assert( pParse->iCacheLevel==0 ); /* Disables and re-enables match */ sqlite3VdbeMakeReady(v, pParse->nVar, pParse->nMem, pParse->nTab, pParse->explain); pParse->rc = SQLITE_DONE; diff --git a/src/expr.c b/src/expr.c index b7d30412b5..003480c7d0 100644 --- a/src/expr.c +++ b/src/expr.c @@ -12,7 +12,7 @@ ** This file contains routines used for analyzing expressions and ** for generating VDBE code that evaluates expressions in SQLite. ** -** $Id: expr.c,v 1.427 2009/04/22 17:15:03 drh Exp $ +** $Id: expr.c,v 1.428 2009/04/23 13:22:43 drh Exp $ */ #include "sqliteInt.h" @@ -1463,7 +1463,7 @@ void sqlite3CodeSubselect( int testAddr = 0; /* One-time test address */ Vdbe *v = sqlite3GetVdbe(pParse); if( v==0 ) return; - + sqlite3ExprCachePush(pParse); /* This code must be run in its entirety every time it is encountered ** if any of the following is true: @@ -1570,11 +1570,7 @@ void sqlite3CodeSubselect( } /* Evaluate the expression and insert it into the temp table */ - pParse->disableColCache++; r3 = sqlite3ExprCodeTarget(pParse, pE2, r1); - assert( pParse->disableColCache>0 ); - pParse->disableColCache--; - if( isRowid ){ sqlite3VdbeAddOp2(v, OP_MustBeInt, r3, sqlite3VdbeCurrentAddr(v)+2); sqlite3VdbeAddOp3(v, OP_Insert, pExpr->iTable, r2, r3); @@ -1628,6 +1624,7 @@ void sqlite3CodeSubselect( if( testAddr ){ sqlite3VdbeJumpHere(v, testAddr-1); } + sqlite3ExprCachePop(pParse, 1); return; } @@ -1705,6 +1702,120 @@ static void codeInteger(Vdbe *v, Expr *pExpr, int negFlag, int iMem){ } } +/* +** Clear a cache entry. +*/ +static void cacheEntryClear(Parse *pParse, struct yColCache *p){ + if( p->tempReg ){ + if( pParse->nTempRegaTempReg) ){ + pParse->aTempReg[pParse->nTempReg++] = p->iReg; + } + p->tempReg = 0; + } +} + + +/* +** Record in the column cache that a particular column from a +** particular table is stored in a particular register. +*/ +void sqlite3ExprCacheStore(Parse *pParse, int iTab, int iCol, int iReg){ + int i; + int minLru; + int idxLru; + struct yColCache *p; + + /* First replace any existing entry */ + for(i=0, p=pParse->aColCache; iiReg && p->iTable==iTab && p->iColumn==iCol ){ + cacheEntryClear(pParse, p); + p->iLevel = pParse->iCacheLevel; + p->iReg = iReg; + p->affChange = 0; + p->lru = pParse->iCacheCnt++; + return; + } + } + if( iReg<=0 ) return; + + /* Find an empty slot and replace it */ + for(i=0, p=pParse->aColCache; iiReg==0 ){ + p->iLevel = pParse->iCacheLevel; + p->iTable = iTab; + p->iColumn = iCol; + p->iReg = iReg; + p->affChange = 0; + p->tempReg = 0; + p->lru = pParse->iCacheCnt++; + return; + } + } + + /* Replace the last recently used */ + minLru = 0x7fffffff; + idxLru = -1; + for(i=0, p=pParse->aColCache; ilrulru; + } + } + if( idxLru>=0 ){ + p = &pParse->aColCache[idxLru]; + p->iLevel = pParse->iCacheLevel; + p->iTable = iTab; + p->iColumn = iCol; + p->iReg = iReg; + p->affChange = 0; + p->tempReg = 0; + p->lru = pParse->iCacheCnt++; + return; + } +} + +/* +** Indicate that a register is being overwritten. Purge the register +** from the column cache. +*/ +void sqlite3ExprCacheRemove(Parse *pParse, int iReg){ + int i; + struct yColCache *p; + for(i=0, p=pParse->aColCache; iiReg==iReg ){ + cacheEntryClear(pParse, p); + p->iReg = 0; + } + } +} + +/* +** Remember the current column cache context. Any new entries added +** added to the column cache after this call are removed when the +** corresponding pop occurs. +*/ +void sqlite3ExprCachePush(Parse *pParse){ + pParse->iCacheLevel++; +} + +/* +** Remove from the column cache any entries that were added since the +** the previous N Push operations. In other words, restore the cache +** to the state it was in N Pushes ago. +*/ +void sqlite3ExprCachePop(Parse *pParse, int N){ + int i; + struct yColCache *p; + assert( N>0 ); + assert( pParse->iCacheLevel>=N ); + pParse->iCacheLevel -= N; + for(i=0, p=pParse->aColCache; iiReg && p->iLevel>pParse->iCacheLevel ){ + cacheEntryClear(pParse, p); + p->iReg = 0; + } + } +} /* ** Generate code that will extract the iColumn-th column from @@ -1733,13 +1844,15 @@ int sqlite3ExprCodeGetColumn( int i; struct yColCache *p; - for(i=0, p=pParse->aColCache; inColCache; i++, p++){ - if( p->iTable==iTable && p->iColumn==iColumn + for(i=0, p=pParse->aColCache; iiReg>0 && p->iTable==iTable && p->iColumn==iColumn && (!p->affChange || allowAffChng) ){ #if 0 sqlite3VdbeAddOp0(v, OP_Noop); VdbeComment((v, "OPT: tab%d.col%d -> r%d", iTable, iColumn, p->iReg)); #endif + p->lru = pParse->iCacheCnt++; + p->tempReg = 0; /* This pins the register, but also leaks it */ return p->iReg; } } @@ -1758,37 +1871,21 @@ int sqlite3ExprCodeGetColumn( } #endif } - if( pParse->disableColCache==0 ){ - i = pParse->iColCache; - p = &pParse->aColCache[i]; - p->iTable = iTable; - p->iColumn = iColumn; - p->iReg = iReg; - p->affChange = 0; - i++; - if( i>=ArraySize(pParse->aColCache) ) i = 0; - if( i>pParse->nColCache ) pParse->nColCache = i; - pParse->iColCache = i; - } + sqlite3ExprCacheStore(pParse, iTable, iColumn, iReg); return iReg; } /* -** Clear all column cache entries associated with the vdbe -** cursor with cursor number iTable. +** Clear all column cache entries. */ -void sqlite3ExprClearColumnCache(Parse *pParse, int iTable){ - if( iTable<0 ){ - pParse->nColCache = 0; - pParse->iColCache = 0; - }else{ - int i; - for(i=0; inColCache; i++){ - if( pParse->aColCache[i].iTable==iTable ){ - testcase( i==pParse->nColCache-1 ); - pParse->aColCache[i] = pParse->aColCache[--pParse->nColCache]; - pParse->iColCache = pParse->nColCache; - } +void sqlite3ExprCacheClear(Parse *pParse){ + int i; + struct yColCache *p; + + for(i=0, p=pParse->aColCache; iiReg ){ + cacheEntryClear(pParse, p); + p->iReg = 0; } } } @@ -1800,10 +1897,11 @@ void sqlite3ExprClearColumnCache(Parse *pParse, int iTable){ void sqlite3ExprCacheAffinityChange(Parse *pParse, int iStart, int iCount){ int iEnd = iStart + iCount - 1; int i; - for(i=0; inColCache; i++){ - int r = pParse->aColCache[i].iReg; + struct yColCache *p; + for(i=0, p=pParse->aColCache; iiReg; if( r>=iStart && r<=iEnd ){ - pParse->aColCache[i].affChange = 1; + p->affChange = 1; } } } @@ -1814,12 +1912,13 @@ void sqlite3ExprCacheAffinityChange(Parse *pParse, int iStart, int iCount){ */ void sqlite3ExprCodeMove(Parse *pParse, int iFrom, int iTo, int nReg){ int i; + struct yColCache *p; if( iFrom==iTo ) return; sqlite3VdbeAddOp3(pParse->pVdbe, OP_Move, iFrom, iTo, nReg); - for(i=0; inColCache; i++){ - int x = pParse->aColCache[i].iReg; + for(i=0, p=pParse->aColCache; iiReg; if( x>=iFrom && xaColCache[i].iReg += iTo-iFrom; + p->iReg += iTo-iFrom; } } } @@ -1842,32 +1941,14 @@ void sqlite3ExprCodeCopy(Parse *pParse, int iFrom, int iTo, int nReg){ */ static int usedAsColumnCache(Parse *pParse, int iFrom, int iTo){ int i; - for(i=0; inColCache; i++){ - int r = pParse->aColCache[i].iReg; + struct yColCache *p; + for(i=0, p=pParse->aColCache; iiReg; if( r>=iFrom && r<=iTo ) return 1; } return 0; } -/* -** There is a value in register iReg. -** -** We are going to modify the value, so we need to make sure it -** is not a cached register. If iReg is a cached register, -** then clear the corresponding cache line. -*/ -void sqlite3ExprWritableRegister(Parse *pParse, int iReg){ - int i; - if( usedAsColumnCache(pParse, iReg, iReg) ){ - for(i=0; inColCache; i++){ - if( pParse->aColCache[i].iReg==iReg ){ - pParse->aColCache[i] = pParse->aColCache[--pParse->nColCache]; - pParse->iColCache = pParse->nColCache; - } - } - } -} - /* ** If the last instruction coded is an ephemeral copy of any of ** the registers in the nReg registers beginning with iReg, then @@ -1905,6 +1986,7 @@ void sqlite3ExprHardCopy(Parse *pParse, int iReg, int nReg){ ** alias has not yet been computed. */ static int codeAlias(Parse *pParse, int iAlias, Expr *pExpr, int target){ +#if 0 sqlite3 *db = pParse->db; int iReg; if( pParse->nAliasAllocnAlias ){ @@ -1919,7 +2001,7 @@ static int codeAlias(Parse *pParse, int iAlias, Expr *pExpr, int target){ assert( iAlias>0 && iAlias<=pParse->nAlias ); iReg = pParse->aAlias[iAlias-1]; if( iReg==0 ){ - if( pParse->disableColCache ){ + if( pParse->iCacheLevel>0 ){ iReg = sqlite3ExprCodeTarget(pParse, pExpr, target); }else{ iReg = ++pParse->nMem; @@ -1928,6 +2010,9 @@ static int codeAlias(Parse *pParse, int iAlias, Expr *pExpr, int target){ } } return iReg; +#else + return sqlite3ExprCodeTarget(pParse, pExpr, target); +#endif } /* @@ -2307,9 +2392,8 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ /* Code the from " IN (...)". The temporary table ** pExpr->iTable contains the values that make up the (...) set. */ - pParse->disableColCache++; + sqlite3ExprCachePush(pParse); sqlite3ExprCode(pParse, pExpr->pLeft, target); - pParse->disableColCache--; j2 = sqlite3VdbeAddOp1(v, OP_IsNull, target); if( eType==IN_INDEX_ROWID ){ j3 = sqlite3VdbeAddOp1(v, OP_MustBeInt, target); @@ -2370,6 +2454,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ } sqlite3VdbeJumpHere(v, j2); sqlite3VdbeJumpHere(v, j5); + sqlite3ExprCachePop(pParse, 1); VdbeComment((v, "end IN expr r%d", target)); break; } @@ -2446,6 +2531,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ Expr cacheX; /* Cached expression X */ Expr *pX; /* The X expression */ Expr *pTest = 0; /* X==Ei (form A) or just Ei (form B) */ + VVA_ONLY( int iCacheLevel = pParse->iCacheLevel; ) assert( !ExprHasProperty(pExpr, EP_xIsSelect) && pExpr->x.pList ); assert((pExpr->x.pList->nExpr % 2) == 0); @@ -2464,8 +2550,8 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ opCompare.pLeft = &cacheX; pTest = &opCompare; } - pParse->disableColCache++; for(i=0; iop==TK_REGISTER ); sqlite3ExprCode(pParse, aListelem[i+1].pExpr, target); sqlite3VdbeAddOp2(v, OP_Goto, 0, endLabel); + sqlite3ExprCachePop(pParse, 1); sqlite3VdbeResolveLabel(v, nextCase); } if( pExpr->pRight ){ + sqlite3ExprCachePush(pParse); sqlite3ExprCode(pParse, pExpr->pRight, target); + sqlite3ExprCachePop(pParse, 1); }else{ sqlite3VdbeAddOp2(v, OP_Null, 0, target); } + assert( pParse->iCacheLevel==iCacheLevel ); sqlite3VdbeResolveLabel(v, endLabel); - assert( pParse->disableColCache>0 ); - pParse->disableColCache--; break; } #ifndef SQLITE_OMIT_TRIGGER @@ -2763,23 +2851,17 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ case TK_AND: { int d2 = sqlite3VdbeMakeLabel(v); testcase( jumpIfNull==0 ); - testcase( pParse->disableColCache==0 ); + sqlite3ExprCachePush(pParse); sqlite3ExprIfFalse(pParse, pExpr->pLeft, d2,jumpIfNull^SQLITE_JUMPIFNULL); - pParse->disableColCache++; sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull); - assert( pParse->disableColCache>0 ); - pParse->disableColCache--; sqlite3VdbeResolveLabel(v, d2); + sqlite3ExprCachePop(pParse, 1); break; } case TK_OR: { testcase( jumpIfNull==0 ); - testcase( pParse->disableColCache==0 ); sqlite3ExprIfTrue(pParse, pExpr->pLeft, dest, jumpIfNull); - pParse->disableColCache++; sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull); - assert( pParse->disableColCache>0 ); - pParse->disableColCache--; break; } case TK_NOT: { @@ -2923,24 +3005,18 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ switch( pExpr->op ){ case TK_AND: { testcase( jumpIfNull==0 ); - testcase( pParse->disableColCache==0 ); sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull); - pParse->disableColCache++; sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull); - assert( pParse->disableColCache>0 ); - pParse->disableColCache--; break; } case TK_OR: { int d2 = sqlite3VdbeMakeLabel(v); testcase( jumpIfNull==0 ); - testcase( pParse->disableColCache==0 ); + sqlite3ExprCachePush(pParse); sqlite3ExprIfTrue(pParse, pExpr->pLeft, d2, jumpIfNull^SQLITE_JUMPIFNULL); - pParse->disableColCache++; sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull); - assert( pParse->disableColCache>0 ); - pParse->disableColCache--; sqlite3VdbeResolveLabel(v, d2); + sqlite3ExprCachePop(pParse, 1); break; } case TK_NOT: { @@ -3276,7 +3352,7 @@ void sqlite3ExprAnalyzeAggList(NameContext *pNC, ExprList *pList){ } /* -** Allocate or deallocate temporary use registers during code generation. +** Allocate a single new register for use to hold some intermediate result. */ int sqlite3GetTempReg(Parse *pParse){ if( pParse->nTempReg==0 ){ @@ -3284,9 +3360,25 @@ int sqlite3GetTempReg(Parse *pParse){ } return pParse->aTempReg[--pParse->nTempReg]; } + +/* +** Deallocate a register, making available for reuse for some other +** purpose. +** +** If a register is currently being used by the column cache, then +** the dallocation is deferred until the column cache line that uses +** the register becomes stale. +*/ void sqlite3ReleaseTempReg(Parse *pParse, int iReg){ if( iReg && pParse->nTempRegaTempReg) ){ - sqlite3ExprWritableRegister(pParse, iReg); + int i; + struct yColCache *p; + for(i=0, p=pParse->aColCache; iiReg==iReg ){ + p->tempReg = 1; + return; + } + } pParse->aTempReg[pParse->nTempReg++] = iReg; } } diff --git a/src/select.c b/src/select.c index 98afa508e1..f00f708f04 100644 --- a/src/select.c +++ b/src/select.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle SELECT statements in SQLite. ** -** $Id: select.c,v 1.508 2009/04/16 00:24:24 drh Exp $ +** $Id: select.c,v 1.509 2009/04/23 13:22:43 drh Exp $ */ #include "sqliteInt.h" @@ -421,6 +421,7 @@ static void pushOntoSorter( int nExpr = pOrderBy->nExpr; int regBase = sqlite3GetTempRange(pParse, nExpr+2); int regRecord = sqlite3GetTempReg(pParse); + sqlite3ExprCacheClear(pParse); sqlite3ExprCodeExprList(pParse, pOrderBy, regBase, 0); sqlite3VdbeAddOp2(v, OP_Sequence, pOrderBy->iECursor, regBase+nExpr); sqlite3ExprCodeMove(pParse, regData, regBase+nExpr+1, 1); @@ -573,6 +574,7 @@ static void selectInnerLoop( /* If the destination is an EXISTS(...) expression, the actual ** values returned by the SELECT are not required. */ + sqlite3ExprCacheClear(pParse); sqlite3ExprCodeExprList(pParse, pEList, regResult, eDest==SRT_Output); } nColumn = nResultCol; @@ -1346,6 +1348,7 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){ ** The current implementation interprets "LIMIT 0" to mean ** no rows. */ + sqlite3ExprCacheClear(pParse); if( p->pLimit ){ p->iLimit = iLimit = ++pParse->nMem; v = sqlite3GetVdbe(pParse); @@ -3456,6 +3459,7 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){ struct AggInfo_col *pC; pAggInfo->directMode = 1; + sqlite3ExprCacheClear(pParse); for(i=0, pF=pAggInfo->aFunc; inFunc; i++, pF++){ int nArg; int addrNext = 0; @@ -3495,12 +3499,14 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){ sqlite3ExprCacheAffinityChange(pParse, regAgg, nArg); if( addrNext ){ sqlite3VdbeResolveLabel(v, addrNext); + sqlite3ExprCacheClear(pParse); } } for(i=0, pC=pAggInfo->aCol; inAccumulator; i++, pC++){ sqlite3ExprCode(pParse, pC->pExpr, pC->iMem); } pAggInfo->directMode = 0; + sqlite3ExprCacheClear(pParse); } /* @@ -3918,6 +3924,7 @@ int sqlite3Select( } } regBase = sqlite3GetTempRange(pParse, nCol); + sqlite3ExprCacheClear(pParse); sqlite3ExprCodeExprList(pParse, pGroupBy, regBase, 0); sqlite3VdbeAddOp2(v, OP_Sequence, sAggInfo.sortingIdx,regBase+nGroupBy); j = nGroupBy+1; @@ -3944,6 +3951,7 @@ int sqlite3Select( sqlite3VdbeAddOp2(v, OP_Sort, sAggInfo.sortingIdx, addrEnd); VdbeComment((v, "GROUP BY sort")); sAggInfo.useSortingIdx = 1; + sqlite3ExprCacheClear(pParse); } /* Evaluate the current GROUP BY terms and store in b0, b1, b2... @@ -3952,6 +3960,7 @@ int sqlite3Select( ** from the previous row currently stored in a0, a1, a2... */ addrTopOfLoop = sqlite3VdbeCurrentAddr(v); + sqlite3ExprCacheClear(pParse); for(j=0; jnExpr; j++){ if( groupBySort ){ sqlite3VdbeAddOp3(v, OP_Column, sAggInfo.sortingIdx, j, iBMem+j); diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 66a22ded16..51729d16c9 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.859 2009/04/22 15:32:59 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.860 2009/04/23 13:22:44 drh Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ @@ -1893,6 +1893,13 @@ struct SelectDest { int nMem; /* Number of registers allocated */ }; +/* +** Size of the column cache +*/ +#ifndef SQLITE_N_COLCACHE +# define SQLITE_N_COLCACHE 10 +#endif + /* ** An SQL parser context. A copy of this structure is passed through ** the parser and down into all the parser action routine in order to @@ -1929,15 +1936,19 @@ struct Parse { int nMem; /* Number of memory cells used so far */ int nSet; /* Number of sets used so far */ int ckBase; /* Base register of data during check constraints */ - int disableColCache; /* True to disable adding to column cache */ - int nColCache; /* Number of entries in the column cache */ - int iColCache; /* Next entry of the cache to replace */ + int iCacheLevel; /* ColCache valid when aColCache[].iLevel<=iCacheLevel */ + int iCacheCnt; /* Counter used to generate aColCache[].lru values */ + u8 nColCache; /* Number of entries in the column cache */ + u8 iColCache; /* Next entry of the cache to replace */ struct yColCache { int iTable; /* Table cursor number */ int iColumn; /* Table column number */ - char affChange; /* True if this register has had an affinity change */ - int iReg; /* Register holding value of this column */ - } aColCache[10]; /* One for each valid column cache entry */ + u8 affChange; /* True if this register has had an affinity change */ + u8 tempReg; /* iReg is a temp register that needs to be freed */ + int iLevel; /* Nesting level */ + int iReg; /* Reg with value of this column. 0 means none. */ + int lru; /* Least recently used entry has the smallest value */ + } aColCache[SQLITE_N_COLCACHE]; /* One for each column cache entry */ u32 writeMask; /* Start a write transaction on these databases */ u32 cookieMask; /* Bitmask of schema verified databases */ int cookieGoto; /* Address of OP_Goto to cookie verifier subroutine */ @@ -2446,9 +2457,12 @@ void sqlite3WhereEnd(WhereInfo*); int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int, int); void sqlite3ExprCodeMove(Parse*, int, int, int); void sqlite3ExprCodeCopy(Parse*, int, int, int); -void sqlite3ExprClearColumnCache(Parse*, int); +void sqlite3ExprCacheStore(Parse*, int, int, int); +void sqlite3ExprCachePush(Parse*); +void sqlite3ExprCachePop(Parse*, int); +void sqlite3ExprCacheRemove(Parse*, int); +void sqlite3ExprCacheClear(Parse*); void sqlite3ExprCacheAffinityChange(Parse*, int, int); -void sqlite3ExprWritableRegister(Parse*,int); void sqlite3ExprHardCopy(Parse*,int,int); int sqlite3ExprCode(Parse*, Expr*, int); int sqlite3ExprCodeTemp(Parse*, Expr*, int*); diff --git a/src/trigger.c b/src/trigger.c index 34b3f099dd..98a49eddcd 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -10,7 +10,7 @@ ************************************************************************* ** ** -** $Id: trigger.c,v 1.135 2009/02/28 10:47:42 danielk1977 Exp $ +** $Id: trigger.c,v 1.136 2009/04/23 13:22:44 drh Exp $ */ #include "sqliteInt.h" @@ -689,7 +689,7 @@ static int codeTriggerProgram( sqlite3VdbeAddOp2(v, OP_ContextPush, 0, 0); VdbeComment((v, "begin trigger %s", pStepList->pTrig->name)); while( pTriggerStep ){ - sqlite3ExprClearColumnCache(pParse, -1); + sqlite3ExprCacheClear(pParse); orconf = (orconfin == OE_Default)?pTriggerStep->orconf:orconfin; pParse->trigStack->orconf = orconf; switch( pTriggerStep->op ){ diff --git a/src/where.c b/src/where.c index 3b81602fb4..3fa674d11a 100644 --- a/src/where.c +++ b/src/where.c @@ -16,7 +16,7 @@ ** so is applicable. Because this module is responsible for selecting ** indices, you might also think of this module as the "query optimizer". ** -** $Id: where.c,v 1.387 2009/04/22 17:15:03 drh Exp $ +** $Id: where.c,v 1.388 2009/04/23 13:22:44 drh Exp $ */ #include "sqliteInt.h" @@ -2469,20 +2469,16 @@ static Bitmask codeOneLoopStart( pVtabIdx->aConstraint; iReg = sqlite3GetTempRange(pParse, nConstraint+2); - pParse->disableColCache++; for(j=1; j<=nConstraint; j++){ for(k=0; kdisableColCache ); sqlite3ExprCode(pParse, pWC->a[iTerm].pExpr->pRight, iReg+j+1); break; } } if( k==nConstraint ) break; } - assert( pParse->disableColCache ); - pParse->disableColCache--; sqlite3VdbeAddOp2(v, OP_Integer, pVtabIdx->idxNum, iReg); sqlite3VdbeAddOp2(v, OP_Integer, j-1, iReg+1); sqlite3VdbeAddOp4(v, OP_VFilter, iCur, addrBrk, iReg, pVtabIdx->idxStr, @@ -2517,6 +2513,7 @@ static Bitmask codeOneLoopStart( addrNxt = pLevel->addrNxt; sqlite3VdbeAddOp2(v, OP_MustBeInt, iRowidReg, addrNxt); sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addrNxt, iRowidReg); + sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg); VdbeComment((v, "pk")); pLevel->op = OP_Noop; }else if( pLevel->plan.wsFlags & WHERE_ROWID_RANGE ){ @@ -2586,6 +2583,7 @@ static Bitmask codeOneLoopStart( if( testOp!=OP_Noop ){ iRowidReg = iReleaseReg = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp2(v, OP_Rowid, iCur, iRowidReg); + sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg); sqlite3VdbeAddOp3(v, testOp, memEndValue, addrBrk, iRowidReg); sqlite3VdbeChangeP5(v, SQLITE_AFF_NUMERIC | SQLITE_JUMPIFNULL); } @@ -2712,12 +2710,7 @@ static Bitmask codeOneLoopStart( /* Seek the index cursor to the start of the range. */ nConstraint = nEq; if( pRangeStart ){ - int dcc = pParse->disableColCache; - if( pRangeEnd ){ - pParse->disableColCache++; - } sqlite3ExprCode(pParse, pRangeStart->pExpr->pRight, regBase+nEq); - pParse->disableColCache = dcc; sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, addrNxt); nConstraint++; }else if( isMinQuery ){ @@ -2743,6 +2736,7 @@ static Bitmask codeOneLoopStart( */ nConstraint = nEq; if( pRangeEnd ){ + sqlite3ExprCacheRemove(pParse, regBase+nEq); sqlite3ExprCode(pParse, pRangeEnd->pExpr->pRight, regBase+nEq); sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, addrNxt); codeApplyAffinity(pParse, regBase, nEq+1, pIdx); @@ -2782,6 +2776,7 @@ static Bitmask codeOneLoopStart( if( !omitTable ){ iRowidReg = iReleaseReg = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, iRowidReg); + sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg); sqlite3VdbeAddOp2(v, OP_Seek, iCur, iRowidReg); /* Deferred seek */ } @@ -2939,9 +2934,7 @@ static Bitmask codeOneLoopStart( if( pLevel->iLeftJoin && !ExprHasProperty(pE, EP_FromJoin) ){ continue; } - pParse->disableColCache += k; sqlite3ExprIfFalse(pParse, pE, addrCont, SQLITE_JUMPIFNULL); - pParse->disableColCache -= k; k = 1; pTerm->wtFlags |= TERM_CODED; } @@ -2953,8 +2946,7 @@ static Bitmask codeOneLoopStart( pLevel->addrFirst = sqlite3VdbeCurrentAddr(v); sqlite3VdbeAddOp2(v, OP_Integer, 1, pLevel->iLeftJoin); VdbeComment((v, "record LEFT JOIN hit")); - sqlite3ExprClearColumnCache(pParse, pLevel->iTabCur); - sqlite3ExprClearColumnCache(pParse, pLevel->iIdxCur); + sqlite3ExprCacheClear(pParse); for(pTerm=pWC->a, j=0; jnTerm; j++, pTerm++){ testcase( pTerm->wtFlags & TERM_VIRTUAL ); testcase( pTerm->wtFlags & TERM_CODED ); @@ -3507,7 +3499,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){ /* Generate loop termination code. */ - sqlite3ExprClearColumnCache(pParse, -1); + sqlite3ExprCacheClear(pParse); for(i=pTabList->nSrc-1; i>=0; i--){ pLevel = &pWInfo->a[i]; sqlite3VdbeResolveLabel(v, pLevel->addrCont); diff --git a/test/alias.test b/test/alias.test index 69a2c241dc..290798974b 100644 --- a/test/alias.test +++ b/test/alias.test @@ -13,11 +13,15 @@ # focus of this script is correct code generation of aliased result-set # values. See ticket #3343. # -# $Id: alias.test,v 1.2 2008/10/25 15:03:21 drh Exp $ +# $Id: alias.test,v 1.3 2009/04/23 13:22:44 drh Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl +# Aliases are currently evaluated twice. We might try to change this +# in the future. But not now. +return + # A procedure to return a sequence of increasing integers. # namespace eval ::seq { diff --git a/test/tkt3461.test b/test/tkt3461.test index 7aef30a496..8eec5c2f08 100644 --- a/test/tkt3461.test +++ b/test/tkt3461.test @@ -13,7 +13,7 @@ # This file implements tests to verify that ticket #3461 has been # fixed. # -# $Id: tkt3461.test,v 1.2 2008/10/25 15:03:21 drh Exp $ +# $Id: tkt3461.test,v 1.3 2009/04/23 13:22:44 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -57,6 +57,7 @@ do_test tkt3461-3.1 { INSERT INTO t2 VALUES(3, 4); } # execsql { PRAGMA vdbe_trace = 1; PRAGMA vdbe_listing=1 } +breakpoint execsql { SELECT a, b+1 AS b_plus_one, c, d FROM t1 LEFT JOIN t2 -- 2.47.2