From: drh Date: Tue, 24 Jun 2008 00:32:35 +0000 (+0000) Subject: The compound-select merge optimization is mostly working with this check-in. X-Git-Tag: version-3.6.10~886 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=92b01d53c2da8fa9d2b336132b1b6ff353c6d214;p=thirdparty%2Fsqlite.git The compound-select merge optimization is mostly working with this check-in. But there are still a few problems and so the optimization is disabled by and "#if 0". This check-in is to synchronize with the other changes happening in parallel. (CVS 5291) FossilOrigin-Name: e2ba324cbcac0ba35bbde50048677e085abb092b --- diff --git a/manifest b/manifest index 9b35a095f8..02b29aa0a4 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Update\sOS/2\smutex\simplementation:\smake\smethods\sstatic\sand\sdon't\suse\sthem\sby\sthe\sold\snames\sany\smore.\sHeld/Notheld\sshould\sbe\sdebug\sonly.\s(CVS\s5290) -D 2008-06-23T22:13:28 +C The\scompound-select\smerge\soptimization\sis\smostly\sworking\swith\sthis\scheck-in.\nBut\sthere\sare\sstill\sa\sfew\sproblems\sand\sso\sthe\soptimization\sis\sdisabled\sby\nand\s"#if\s0".\s\sThis\scheck-in\sis\sto\ssynchronize\swith\sthe\sother\schanges\shappening\nin\sparallel.\s(CVS\s5291) +D 2008-06-24T00:32:35 F Makefile.arm-wince-mingw32ce-gcc ac5f7b2cef0cd850d6f755ba6ee4ab961b1fadf7 F Makefile.in ff6f90048555a0088f6a4b7406bed5e55a7c4eff F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 @@ -103,14 +103,14 @@ F src/callback.c 3ba98ae46f60aa7c2c40eac7d18fe5ba9b706b83 F src/complete.c cb14e06dbe79dee031031f0d9e686ff306afe07c F src/date.c e841168e5520bbbb2a1cbcdce7531d8b23017b4d F src/delete.c d3fc5987f2eb88f7b9549d58a5dfea079a83fe8b -F src/expr.c bb0b5ff5fa81e2fb7563d17fb16d457bc60bd44f +F src/expr.c 18af707c4346de39c25d0c30a7db4c3ea449d3df F src/fault.c 3638519d1e0b82bccfafcb9f5ff491918b28f8e1 F src/func.c 1e7d9569570134ac0771a00382d9d4b41c4aa052 F src/global.c 2304cfa3288763bd2fed10caf8c6fbaa2b383f4e F src/hash.c eb64e48f3781100e5934f759fbe72a63a8fe78cb F src/hash.h 031cd9f915aff27e12262cb9eb570ac1b8326b53 F src/hwtime.h 745961687a65ef8918cd551c02e5ccb4b8e772de -F src/insert.c 93231fd0199f044bcefda3d857420f4d377e4056 +F src/insert.c 4656c5231059d8f40a5bbe21a28a9344c07a49bd F src/journal.c cffd2cd214e58c0e99c3ff632b3bee6c7cbb260e F src/legacy.c 3626c71fb70912abec9a4312beba753a9ce800df F src/loadext.c 40024a0f476c1279494876b9a002001b29e5d3e3 @@ -140,11 +140,11 @@ F src/pragma.c e6c55362d164e4bc8ebc83a9a01635552d854800 F src/prepare.c aba51dad52308e3d9d2074d8ff4e612e7f1cab51 F src/printf.c 8b063da9dcde26b7c500a01444b718d86f21bc6e F src/random.c 5c754319d38abdd6acd74601ee0105504adc508a -F src/select.c ea3e5e233cf16f4cb43f6ec35972683ae7bc03f3 +F src/select.c 1ebcd83ab51be47f31b2ec93daad844baf3b0788 F src/shell.c 61fa61932ed52825720ebfd3f8381b8d550ef766 F src/sqlite.h.in 6a80d00621a43271f01c77eb42bbf57e0f52051b F src/sqlite3ext.h f162a72daef5ebf8b211fe8c0ec96e85d22fbf9b -F src/sqliteInt.h 0129e546b43a06844d1d66fd9d9b3090676d0873 +F src/sqliteInt.h ec8e71249994b8a5b23b1a098a576c02e8548244 F src/sqliteLimit.h f435e728c6b620ef7312814d660a81f9356eb5c8 F src/status.c 6cb10377992505bd69f1ca1d75c1240a65f25a58 F src/table.c 1fa8f8113ac9cbc09ae4801c6d2a7f0af82c5822 @@ -438,7 +438,7 @@ F test/rtree.test a8404a59bbc3a7827db9bfb334790c852f0391b3 F test/safety.test b69e2b2dd5d52a3f78e216967086884bbc1a09c6 F test/schema.test a8b000723375fd42c68d310091bdbd744fde647c F test/schema2.test 35e1c9696443d6694c8980c411497c2b5190d32e -F test/select1.test 2f011e7aa0accada53795bc95f00d268a3811bd4 +F test/select1.test 64f6231c75b01ec30c88dd9d45d0979169ee4d95 F test/select2.test 06a2660de57673e2d076c29c0fd73f961a930f87 F test/select3.test 2ce595f8fb8e2ac10071d3b4e424cadd4634a054 F test/select4.test 7cc135c8343e6e433bdad185de6a720b112c40e7 @@ -592,7 +592,7 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81 F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 1dbced29de5f59ba2ebf877edcadf171540374d1 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e -P bf2e283d6fd40cabe55864b06b502524eb8a3b07 -R bfadbc404a2d8b31ffee6a81899b30a6 -U pweilbacher -Z 4ec4b9feb6cdd59c7ef6420f4a9a5810 +P d92418ca502f5f58dc968668e11c42955a7b1e52 +R f88ace7929fab768ff8007a473e6b665 +U drh +Z 9ed7972d77404d7a52eef908bd3410d8 diff --git a/manifest.uuid b/manifest.uuid index 54a9197172..83bcd7c61a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d92418ca502f5f58dc968668e11c42955a7b1e52 \ No newline at end of file +e2ba324cbcac0ba35bbde50048677e085abb092b \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index ef88cfecc0..da051c75eb 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.374 2008/06/22 12:37:58 drh Exp $ +** $Id: expr.c,v 1.375 2008/06/24 00:32:35 drh Exp $ */ #include "sqliteInt.h" #include @@ -756,8 +756,8 @@ Select *sqlite3SelectDup(sqlite3 *db, Select *p){ pNew->pPrior = sqlite3SelectDup(db, p->pPrior); pNew->pLimit = sqlite3ExprDup(db, p->pLimit); pNew->pOffset = sqlite3ExprDup(db, p->pOffset); - pNew->iLimit = -1; - pNew->iOffset = -1; + pNew->iLimit = 0; + pNew->iOffset = 0; pNew->isResolved = p->isResolved; pNew->isAgg = p->isAgg; pNew->usesEphm = 0; @@ -1018,27 +1018,35 @@ int sqlite3ExprIsConstantOrFunction(Expr *p){ ** to fit in a signed 32-bit integer, return 0 and leave *pValue unchanged. */ int sqlite3ExprIsInteger(Expr *p, int *pValue){ + int rc = 0; + if( p->flags & EP_IntValue ){ + *pValue = p->iTable; + return 1; + } switch( p->op ){ case TK_INTEGER: { - if( sqlite3GetInt32((char*)p->token.z, pValue) ){ - return 1; - } + rc = sqlite3GetInt32((char*)p->token.z, pValue); break; } case TK_UPLUS: { - return sqlite3ExprIsInteger(p->pLeft, pValue); + rc = sqlite3ExprIsInteger(p->pLeft, pValue); } case TK_UMINUS: { int v; if( sqlite3ExprIsInteger(p->pLeft, &v) ){ *pValue = -v; - return 1; + rc = 1; } break; } default: break; } - return 0; + if( rc ){ + p->op = TK_INTEGER; + p->flags |= EP_IntValue; + p->iTable = *pValue; + } + return rc; } /* @@ -1986,10 +1994,15 @@ static void codeReal(Vdbe *v, const char *z, int n, int negateFlag, int iMem){ ** z[n] character is guaranteed to be something that does not look ** like the continuation of the number. */ -static void codeInteger(Vdbe *v, const char *z, int n, int negFlag, int iMem){ - assert( z || v==0 || sqlite3VdbeDb(v)->mallocFailed ); - if( z ){ +static void codeInteger(Vdbe *v, Expr *pExpr, int negFlag, int iMem){ + const char *z; + if( pExpr->flags & EP_IntValue ){ + int i = pExpr->iTable; + if( negFlag ) i = -i; + sqlite3VdbeAddOp2(v, OP_Integer, i, iMem); + }else if( (z = (char*)pExpr->token.z)!=0 ){ int i; + int n = pExpr->token.n; assert( !isdigit(z[n]) ); if( sqlite3GetInt32(z, &i) ){ if( negFlag ) i = -i; @@ -2127,6 +2140,18 @@ void sqlite3ExprCodeMove(Parse *pParse, int iFrom, int iTo, int nReg){ } } +/* +** Generate code to copy content from registers iFrom...iFrom+nReg-1 +** over to iTo..iTo+nReg-1. +*/ +void sqlite3ExprCodeCopy(Parse *pParse, int iFrom, int iTo, int nReg){ + int i; + if( iFrom==iTo ) return; + for(i=0; ipVdbe, OP_Copy, iFrom+i, iTo+i); + } +} + /* ** Return true if any register in the range iFrom..iTo (inclusive) ** is used as part of the column cache. @@ -2246,7 +2271,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ break; } case TK_INTEGER: { - codeInteger(v, (char*)pExpr->token.z, pExpr->token.n, 0, target); + codeInteger(v, pExpr, 0, target); break; } case TK_FLOAT: { @@ -2384,11 +2409,10 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ Expr *pLeft = pExpr->pLeft; assert( pLeft ); if( pLeft->op==TK_FLOAT || pLeft->op==TK_INTEGER ){ - Token *p = &pLeft->token; if( pLeft->op==TK_FLOAT ){ - codeReal(v, (char*)p->z, p->n, 1, target); + codeReal(v, (char*)pLeft->token.z, pLeft->token.n, 1, target); }else{ - codeInteger(v, (char*)p->z, p->n, 1, target); + codeInteger(v, pLeft, 1, target); } }else{ regFree1 = r1 = sqlite3GetTempReg(pParse); diff --git a/src/insert.c b/src/insert.c index 856228352f..14f86e695e 100644 --- a/src/insert.c +++ b/src/insert.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle INSERT statements in SQLite. ** -** $Id: insert.c,v 1.241 2008/06/20 15:24:02 drh Exp $ +** $Id: insert.c,v 1.242 2008/06/24 00:32:35 drh Exp $ */ #include "sqliteInt.h" @@ -523,10 +523,9 @@ void sqlite3Insert( regEof = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Integer, 0, regEof); /* EOF <- 0 */ VdbeComment((v, "SELECT eof flag")); - sqlite3SelectDestInit(&dest, SRT_Coroutine, 0); - dest.regCoroutine = ++pParse->nMem; + sqlite3SelectDestInit(&dest, SRT_Coroutine, ++pParse->nMem); addrSelect = sqlite3VdbeCurrentAddr(v)+2; - sqlite3VdbeAddOp2(v, OP_Integer, addrSelect-1, dest.regCoroutine); + sqlite3VdbeAddOp2(v, OP_Integer, addrSelect-1, dest.iParm); j1 = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0); VdbeComment((v, "Jump over SELECT coroutine")); @@ -536,7 +535,7 @@ void sqlite3Insert( goto insert_cleanup; } sqlite3VdbeAddOp2(v, OP_Integer, 1, regEof); /* EOF <- 1 */ - sqlite3VdbeAddOp1(v, OP_Yield, dest.regCoroutine); /* yield X */ + sqlite3VdbeAddOp1(v, OP_Yield, dest.iParm); /* yield X */ sqlite3VdbeAddOp2(v, OP_Halt, SQLITE_INTERNAL, OE_Abort); VdbeComment((v, "End of SELECT coroutine")); sqlite3VdbeJumpHere(v, j1); /* label B: */ @@ -580,7 +579,7 @@ void sqlite3Insert( regRec = sqlite3GetTempReg(pParse); regRowid = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp2(v, OP_OpenEphemeral, srcTab, nColumn); - addrTop = sqlite3VdbeAddOp1(v, OP_Yield, dest.regCoroutine); + addrTop = sqlite3VdbeAddOp1(v, OP_Yield, dest.iParm); addrIf = sqlite3VdbeAddOp1(v, OP_If, regEof); sqlite3VdbeAddOp3(v, OP_MakeRecord, regFromSelect, nColumn, regRec); sqlite3VdbeAddOp2(v, OP_NewRowid, srcTab, regRowid); @@ -725,7 +724,7 @@ void sqlite3Insert( ** goto C ** D: ... */ - addrCont = sqlite3VdbeAddOp1(v, OP_Yield, dest.regCoroutine); + addrCont = sqlite3VdbeAddOp1(v, OP_Yield, dest.iParm); addrInsTop = sqlite3VdbeAddOp1(v, OP_If, regEof); } diff --git a/src/select.c b/src/select.c index d072b2042a..02bbff7a6d 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.433 2008/06/22 12:37:58 drh Exp $ +** $Id: select.c,v 1.434 2008/06/24 00:32:36 drh Exp $ */ #include "sqliteInt.h" @@ -39,7 +39,6 @@ static void clearSelect(Select *p){ void sqlite3SelectDestInit(SelectDest *pDest, int eDest, int iParm){ pDest->eDest = eDest; pDest->iParm = iParm; - pDest->regCoroutine = 0; pDest->affinity = 0; pDest->iMem = 0; pDest->nMem = 0; @@ -85,8 +84,6 @@ Select *sqlite3SelectNew( assert( pOffset==0 || pLimit!=0 ); pNew->pLimit = pLimit; pNew->pOffset = pOffset; - pNew->iLimit = -1; - pNew->iOffset = -1; pNew->addrOpenEphm[0] = -1; pNew->addrOpenEphm[1] = -1; pNew->addrOpenEphm[2] = -1; @@ -430,7 +427,7 @@ static void pushOntoSorter( sqlite3VdbeAddOp2(v, OP_IdxInsert, pOrderBy->iECursor, regRecord); sqlite3ReleaseTempReg(pParse, regRecord); sqlite3ReleaseTempRange(pParse, regBase, nExpr+2); - if( pSelect->iLimit>=0 ){ + if( pSelect->iLimit ){ int addr1, addr2; int iLimit; if( pSelect->pOffset ){ @@ -445,7 +442,7 @@ static void pushOntoSorter( sqlite3VdbeAddOp1(v, OP_Last, pOrderBy->iECursor); sqlite3VdbeAddOp1(v, OP_Delete, pOrderBy->iECursor); sqlite3VdbeJumpHere(v, addr2); - pSelect->iLimit = -1; + pSelect->iLimit = 0; } } @@ -457,7 +454,7 @@ static void codeOffset( Select *p, /* The SELECT statement being coded */ int iContinue /* Jump here to skip the current record */ ){ - if( p->iOffset>=0 && iContinue!=0 ){ + if( p->iOffset && iContinue!=0 ){ int addr; sqlite3VdbeAddOp2(v, OP_AddImm, p->iOffset, -1); addr = sqlite3VdbeAddOp1(v, OP_IfNeg, p->iOffset); @@ -712,7 +709,7 @@ static void selectInnerLoop( pushOntoSorter(pParse, pOrderBy, p, r1); sqlite3ReleaseTempReg(pParse, r1); }else if( eDest==SRT_Coroutine ){ - sqlite3VdbeAddOp1(v, OP_Yield, pDest->regCoroutine); + sqlite3VdbeAddOp1(v, OP_Yield, pDest->iParm); }else{ sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, nColumn); sqlite3ExprCacheAffinityChange(pParse, regResult, nColumn); @@ -735,7 +732,7 @@ static void selectInnerLoop( /* Jump to the end of the loop if the LIMIT is reached. */ - if( p->iLimit>=0 && pOrderBy==0 ){ + if( p->iLimit && pOrderBy==0 ){ sqlite3VdbeAddOp2(v, OP_AddImm, p->iLimit, -1); sqlite3VdbeAddOp2(v, OP_IfZero, p->iLimit, iBreak); } @@ -859,7 +856,7 @@ static void generateSortTail( sqlite3VdbeAddOp2(v, OP_ResultRow, pDest->iMem, nColumn); sqlite3ExprCacheAffinityChange(pParse, pDest->iMem, nColumn); }else if( eDest==SRT_Coroutine ){ - sqlite3VdbeAddOp1(v, OP_Yield, pDest->regCoroutine); + sqlite3VdbeAddOp1(v, OP_Yield, pDest->iParm); } break; } @@ -873,7 +870,7 @@ static void generateSortTail( /* Jump to the end of the loop when the LIMIT is reached */ - if( p->iLimit>=0 ){ + if( p->iLimit ){ sqlite3VdbeAddOp2(v, OP_AddImm, p->iLimit, -1); sqlite3VdbeAddOp2(v, OP_IfZero, p->iLimit, brk); } @@ -1508,9 +1505,9 @@ static int prepSelectStmt(Parse *pParse, Select *p){ ** pE is a pointer to an expression which is a single term in ** ORDER BY or GROUP BY clause. ** -** If pE evaluates to an integer constant i, then return i. -** This is an indication to the caller that it should sort -** by the i-th column of the result set. +** At the point this routine is called, we already know that the +** ORDER BY term is not an integer index into the result set. That +** casee is handled by the calling routine. ** ** If pE is a well-formed expression and the SELECT statement ** is not compound, then return 0. This indicates to the @@ -1535,20 +1532,8 @@ static int matchOrderByTermToExprList( ExprList *pEList; /* The columns of the result set */ NameContext nc; /* Name context for resolving pE */ - - /* If the term is an integer constant, return the value of that - ** constant */ + assert( sqlite3ExprIsInteger(pE, &i)==0 ); pEList = pSelect->pEList; - if( sqlite3ExprIsInteger(pE, &i) ){ - if( i<=0 ){ - /* If i is too small, make it too big. That way the calling - ** function still sees a value that is out of range, but does - ** not confuse the column number with 0 or -1 result code. - */ - i = pEList->nExpr+1; - } - return i; - } /* If the term is a simple identifier that try to match that identifier ** against a column name in the result set. @@ -1638,16 +1623,19 @@ static int processOrderGroupBy( for(i=0; inExpr; i++){ int iCol; Expr *pE = pOrderBy->a[i].pExpr; - iCol = matchOrderByTermToExprList(pParse, pSelect, pE, i+1, 0, pHasAgg); - if( iCol<0 ){ - return 1; - } - if( iCol>pEList->nExpr ){ - const char *zType = isOrder ? "ORDER" : "GROUP"; - sqlite3ErrorMsg(pParse, - "%r %s BY term out of range - should be " - "between 1 and %d", i+1, zType, pEList->nExpr); - return 1; + if( sqlite3ExprIsInteger(pE, &iCol) ){ + if( iCol<=0 || iCol>pEList->nExpr ){ + const char *zType = isOrder ? "ORDER" : "GROUP"; + sqlite3ErrorMsg(pParse, + "%r %s BY term out of range - should be " + "between 1 and %d", i+1, zType, pEList->nExpr); + return 1; + } + }else{ + iCol = matchOrderByTermToExprList(pParse, pSelect, pE, i+1, 0, pHasAgg); + if( iCol<0 ){ + return 1; + } } if( iCol>0 ){ CollSeq *pColl = pE->pColl; @@ -1668,11 +1656,11 @@ static int processOrderGroupBy( ** Analyze and ORDER BY or GROUP BY clause in a SELECT statement. Return ** the number of errors seen. ** -** For compound SELECT statements, every expression needs to be of -** type TK_COLUMN with a iTable value as given in the 4th parameter. -** If any expression is an integer, that becomes the column number. -** Otherwise, match the expression against result set columns from -** the left-most SELECT. +** If iTable>0 then make the N-th term of the ORDER BY clause refer to +** the N-th column of table iTable. +** +** If iTable==0 then transform each term of the ORDER BY clause to refer +** to a column of the result set by number. */ static int processCompoundOrderBy( Parse *pParse, /* Parsing context. Leave error messages here */ @@ -1702,36 +1690,45 @@ static int processCompoundOrderBy( } while( pSelect && moreToDo ){ moreToDo = 0; + pEList = pSelect->pEList; + if( pEList==0 ){ + return 1; + } for(i=0; inExpr; i++){ int iCol = -1; Expr *pE, *pDup; if( pOrderBy->a[i].done ) continue; pE = pOrderBy->a[i].pExpr; - pDup = sqlite3ExprDup(db, pE); - if( !db->mallocFailed ){ - assert(pDup); - iCol = matchOrderByTermToExprList(pParse, pSelect, pDup, i+1, 1, 0); - } - sqlite3ExprDelete(pDup); - if( iCol<0 ){ - return 1; - } - pEList = pSelect->pEList; - if( pEList==0 ){ - return 1; - } - if( iCol>pEList->nExpr ){ - sqlite3ErrorMsg(pParse, - "%r ORDER BY term out of range - should be " - "between 1 and %d", i+1, pEList->nExpr); - return 1; + if( sqlite3ExprIsInteger(pE, &iCol) ){ + if( iCol<0 || iCol>pEList->nExpr ){ + sqlite3ErrorMsg(pParse, + "%r ORDER BY term out of range - should be " + "between 1 and %d", i+1, pEList->nExpr); + return 1; + } + }else{ + pDup = sqlite3ExprDup(db, pE); + if( !db->mallocFailed ){ + assert(pDup); + iCol = matchOrderByTermToExprList(pParse, pSelect, pDup, i+1, 1, 0); + } + sqlite3ExprDelete(pDup); + if( iCol<0 ){ + return 1; + } } if( iCol>0 ){ - pE->op = TK_COLUMN; - pE->iTable = iTable; - pE->iAgg = -1; - pE->iColumn = iCol-1; - pE->pTab = 0; + if( iTable ){ + pE->op = TK_COLUMN; + pE->iTable = iTable; + pE->iAgg = -1; + pE->iColumn = iCol-1; + pE->pTab = 0; + }else{ + pE->op = TK_INTEGER; + pE->flags |= EP_IntValue; + pE->iTable = iCol; + } pOrderBy->a[i].done = 1; }else{ moreToDo = 1; @@ -1989,7 +1986,7 @@ static int multiSelect( p->pPrior = 0; p->iLimit = pPrior->iLimit; p->iOffset = pPrior->iOffset; - if( p->iLimit>=0 ){ + if( p->iLimit ){ addr = sqlite3VdbeAddOp1(v, OP_IfZero, p->iLimit); VdbeComment((v, "Jump ahead if LIMIT reached")); } @@ -2075,8 +2072,8 @@ static int multiSelect( sqlite3ExprDelete(p->pLimit); p->pLimit = pLimit; p->pOffset = pOffset; - p->iLimit = -1; - p->iOffset = -1; + p->iLimit = 0; + p->iOffset = 0; if( rc ){ goto multi_select_end; } @@ -2313,23 +2310,26 @@ multi_select_end: } #endif /* SQLITE_OMIT_COMPOUND_SELECT */ -#if 0 /****** ################ ******/ /* ** Code an output subroutine for a coroutine implementation of a ** SELECT statment. */ static int outputSubroutine( - Parse *pParse, - SelectDest *pIn - SelectDest *pDest + Parse *pParse, /* Parsing context */ + Select *p, /* The SELECT statement */ + SelectDest *pIn, /* Coroutine supplying data */ + SelectDest *pDest, /* Where to send the data */ + int regReturn, /* The return address register */ + int iBreak /* Jump here if we hit the LIMIT */ ){ Vdbe *v = pParse->pVdbe; - if( v==0 ) return; + int iContinue; + int addr; + if( v==0 ) return 0; - if( pDest->iMem==0 ){ - pDest->iMem = sqlite3GetTempRange(pParse, pIn->nMem); - pDest->nMem = nResultCol; - } + addr = sqlite3VdbeCurrentAddr(v); + iContinue = sqlite3VdbeMakeLabel(v); + codeOffset(v, p, iContinue); switch( pDest->eDest ){ /* Store the result as data using a unique key. @@ -2338,9 +2338,9 @@ static int outputSubroutine( case SRT_EphemTab: { int r1 = sqlite3GetTempReg(pParse); int r2 = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nColumn, r1); - sqlite3VdbeAddOp2(v, OP_NewRowid, iParm, r2); - sqlite3VdbeAddOp3(v, OP_Insert, iParm, r1, r2); + sqlite3VdbeAddOp3(v, OP_MakeRecord, pIn->iMem, pIn->nMem, r1); + sqlite3VdbeAddOp2(v, OP_NewRowid, pDest->iParm, r2); + sqlite3VdbeAddOp3(v, OP_Insert, pDest->iParm, r1, r2); sqlite3VdbeChangeP5(v, OPFLAG_APPEND); sqlite3ReleaseTempReg(pParse, r2); sqlite3ReleaseTempReg(pParse, r1); @@ -2354,13 +2354,14 @@ static int outputSubroutine( */ case SRT_Set: { int addr2, r1; - assert( nColumn==1 ); - addr2 = sqlite3VdbeAddOp1(v, OP_IsNull, regResult); - p->affinity = sqlite3CompareAffinity(pEList->a[0].pExpr, pDest->affinity); + assert( pIn->nMem==1 ); + addr2 = sqlite3VdbeAddOp1(v, OP_IsNull, pIn->iMem); + p->affinity = + sqlite3CompareAffinity(p->pEList->a[0].pExpr, pDest->affinity); r1 = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult, 1, r1, &p->affinity, 1); - sqlite3ExprCacheAffinityChange(pParse, regResult, 1); - sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1); + sqlite3VdbeAddOp4(v, OP_MakeRecord, pIn->iMem, 1, r1, &p->affinity, 1); + sqlite3ExprCacheAffinityChange(pParse, pIn->iMem, 1); + sqlite3VdbeAddOp2(v, OP_IdxInsert, pDest->iParm, r1); sqlite3ReleaseTempReg(pParse, r1); sqlite3VdbeJumpHere(v, addr2); break; @@ -2369,7 +2370,7 @@ static int outputSubroutine( /* If any row exist in the result set, record that fact and abort. */ case SRT_Exists: { - sqlite3VdbeAddOp2(v, OP_Integer, 1, iParm); + sqlite3VdbeAddOp2(v, OP_Integer, 1, pDest->iParm); /* The LIMIT clause will terminate the loop for us */ break; } @@ -2379,8 +2380,8 @@ static int outputSubroutine( ** of the scan loop. */ case SRT_Mem: { - assert( nColumn==1 ); - sqlite3ExprCodeMove(pParse, regResult, iParm, 1); + assert( pIn->nMem==1 ); + sqlite3ExprCodeMove(pParse, pIn->iMem, pDest->iParm, 1); /* The LIMIT clause will jump out of the loop for us */ break; } @@ -2390,14 +2391,19 @@ static int outputSubroutine( ** case of a subroutine, the subroutine itself is responsible for ** popping the data from the stack. */ - case SRT_Coroutine: - case SRT_Callback: { - if( eDest==SRT_Coroutine ){ - sqlite3VdbeAddOp1(v, OP_Yield, pDest->regCoroutine); - }else{ - sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, nColumn); - sqlite3ExprCacheAffinityChange(pParse, regResult, nColumn); + case SRT_Coroutine: { + if( pDest->iMem==0 ){ + pDest->iMem = sqlite3GetTempRange(pParse, pIn->nMem); + pDest->nMem = pIn->nMem; } + sqlite3ExprCodeMove(pParse, pIn->iMem, pDest->iMem, pDest->nMem); + sqlite3VdbeAddOp1(v, OP_Yield, pDest->iParm); + break; + } + + case SRT_Callback: { + sqlite3VdbeAddOp2(v, OP_ResultRow, pIn->iMem, pIn->nMem); + sqlite3ExprCacheAffinityChange(pParse, pIn->iMem, pIn->nMem); break; } @@ -2412,6 +2418,24 @@ static int outputSubroutine( } #endif } + + /* Jump to the end of the loop if the LIMIT is reached. + */ + if( p->iLimit ){ + sqlite3VdbeAddOp2(v, OP_AddImm, p->iLimit, -1); + sqlite3VdbeAddOp2(v, OP_IfZero, p->iLimit, iBreak); + } + + /* Advance the coroutine to its next value. + */ + sqlite3VdbeResolveLabel(v, iContinue); + sqlite3VdbeAddOp1(v, OP_Yield, pIn->iParm); + + /* Generate the subroutine return + */ + sqlite3VdbeAddOp1(v, OP_Return, regReturn); + + return addr; } /* @@ -2498,37 +2522,74 @@ static int multiSelectOrderBy( SelectDest *pDest, /* What to do with query results */ char *aff /* If eDest is SRT_Union, the affinity string */ ){ - int rc = SQLITE_OK; /* Success code from a subroutine */ Select *pPrior; /* Another SELECT immediately to our left */ Vdbe *v; /* Generate code to this VDBE */ - int nCol; /* Number of columns in the result set */ - ExprList *pOrderBy; /* The ORDER BY clause on p */ - int aSetP2[2]; /* Set P2 value of these op to number of columns */ - int nSetP2 = 0; /* Number of slots in aSetP2[] used */ SelectDest destA; /* Destination for coroutine A */ SelectDest destB; /* Destination for coroutine B */ - int regAddrA; - int regEofA; - int regAddrB; - int regEofB; - int addrSelectA; - int addrSelectB; - int regOutA; - int regOutB; - int addrOutA; - int addrOutB; - int addrEofA; - int addrEofB; - int addrAltB; - int addrAeqB; - int addrAgtB; - int labelCmpr; - int labelEnd; - int j1, j2, j3; - - /* Patch up the ORDER BY clause */ + int regAddrA; /* Address register for select-A coroutine */ + int regEofA; /* Flag to indicate when select-A is complete */ + int regAddrB; /* Address register for select-B coroutine */ + int regEofB; /* Flag to indicate when select-B is complete */ + int addrSelectA; /* Address of the select-A coroutine */ + int addrSelectB; /* Address of the select-B coroutine */ + int regOutA; /* Address register for the output-A subroutine */ + int regOutB; /* Address register for the output-B subroutine */ + int addrOutA; /* Address of the output-A subroutine */ + int addrOutB; /* Address of the output-B subroutine */ + int addrEofA; /* Address of the select-A-exhausted subroutine */ + int addrEofB; /* Address of the select-B-exhausted subroutine */ + int addrAltB; /* Address of the AB subroutine */ + int regLimitA; /* Limit register for select-A */ + int regLimitB; /* Limit register for select-A */ + int savedLimit; /* Saved value of p->iLimit */ + int savedOffset; /* Saved value of p->iOffset */ + int labelCmpr; /* Label for the start of the merge algorithm */ + int labelEnd; /* Label for the end of the overall SELECT stmt */ + int j1, j2, j3; /* Jump instructions that get retargetted */ + int op; /* One of TK_ALL, TK_UNION, TK_EXCEPT, TK_INTERSECT */ + KeyInfo *pKeyInfo; /* Type data for comparisons */ + int p4type; /* P4 type used for pKeyInfo */ + u8 NotUsed; /* Dummy variable */ + + assert( p->pOrderBy!=0 ); + v = pParse->pVdbe; + if( v==0 ) return SQLITE_NOMEM; + labelEnd = sqlite3VdbeMakeLabel(v); + labelCmpr = sqlite3VdbeMakeLabel(v); + pKeyInfo = keyInfoFromExprList(pParse, p->pEList); + p4type = P4_KEYINFO_HANDOFF; + /* Patch up the ORDER BY clause + */ + op = p->op; pPrior = p->pPrior; + assert( pPrior->pOrderBy==0 ); + if( processCompoundOrderBy(pParse, p, 0) ){ + return SQLITE_ERROR; + } + pPrior->pOrderBy = sqlite3ExprListDup(pParse->db, p->pOrderBy); + + /* Separate the left and the right query from one another + */ + p->pPrior = 0; + pPrior->pRightmost = 0; + processOrderGroupBy(pParse, p, p->pOrderBy, 1, &NotUsed); + processOrderGroupBy(pParse, pPrior, pPrior->pOrderBy, 1, &NotUsed); + + /* Compute the limit registers */ + computeLimitRegisters(pParse, p, labelEnd); + if( p->iLimit ){ + regLimitA = ++pParse->nMem; + regLimitB = ++pParse->nMem; + sqlite3VdbeAddOp2(v, OP_Copy, p->iOffset ? p->iOffset+1 : p->iLimit, + regLimitA); + sqlite3VdbeAddOp2(v, OP_Copy, regLimitA, regLimitB); + }else{ + regLimitA = regLimitB = 0; + } + regAddrA = ++pParse->nMem; regEofA = ++pParse->nMem; regAddrB = ++pParse->nMem; @@ -2538,123 +2599,194 @@ static int multiSelectOrderBy( sqlite3SelectDestInit(&destA, SRT_Coroutine, regAddrA); sqlite3SelectDestInit(&destB, SRT_Coroutine, regAddrB); + /* Jump past the various subroutines and coroutines to the main + ** merge loop + */ j1 = sqlite3VdbeAddOp0(v, OP_Goto); addrSelectA = sqlite3VdbeCurrentAddr(v); + + /* Generate a coroutine to evaluate the SELECT statement to the + ** left of the compound operator - the "A" select. */ VdbeNoopComment((v, "Begin coroutine for left SELECT")); - sqlite3SelectDestInit(&destA, SRT_Coroutine, 0); - sqlite3Select(); + pPrior->iLimit = regLimitA; + sqlite3Select(pParse, pPrior, &destA, 0, 0, 0, 0); sqlite3VdbeAddOp2(v, OP_Integer, 1, regEofA); - sqlite3VdbeAddOp2(v, OP_Yield, regAddrA); + sqlite3VdbeAddOp1(v, OP_Yield, regAddrA); VdbeNoopComment((v, "End coroutine for left SELECT")); + /* Generate a coroutine to evaluate the SELECT statement on + ** the right - the "B" select + */ addrSelectB = sqlite3VdbeCurrentAddr(v); VdbeNoopComment((v, "Begin coroutine for right SELECT")); - sqlite3SelectDestInit(&destB, SRT_Coroutine, 0); - sqlite3Select(); + savedLimit = p->iLimit; + savedOffset = p->iOffset; + p->iLimit = regLimitB; + p->iOffset = 0; + sqlite3Select(pParse, p, &destB, 0, 0, 0, 0); + p->iLimit = savedLimit; + p->iOffset = savedOffset; sqlite3VdbeAddOp2(v, OP_Integer, 1, regEofB); - sqlite3VdbeAddOp2(v, OP_Yield, regAddrB); + sqlite3VdbeAddOp1(v, OP_Yield, regAddrB); VdbeNoopComment((v, "End coroutine for right SELECT")); + /* Generate a subroutine that outputs the current row of the A + ** select as the next output row of the compound select and then + ** advances the A select to its next row + */ VdbeNoopComment((v, "Output routine for A")); - addrOutA = outputSubroutine(pParse, &destA, pDest); + addrOutA = outputSubroutine(pParse, p, &destA, pDest, regOutA, labelEnd); + /* Generate a subroutine that outputs the current row of the B + ** select as the next output row of the compound select and then + ** advances the B select to its next row + */ VdbeNoopComment((v, "Output routine for B")); - addrOutB = outputSubroutine(pParse, &destB, pDest); + addrOutB = outputSubroutine(pParse, p, &destB, pDest, regOutB, labelEnd); + /* Generate a subroutine to run when the results from select A + ** are exhausted and only data in select B remains. + */ + VdbeNoopComment((v, "eof-A subroutine")); + addrEofA = sqlite3VdbeCurrentAddr(v); if( op==TK_EXCEPT || op==TK_INTERSECT ){ - addrEofA = iEnd; + sqlite3VdbeAddOp2(v, OP_Goto, 0, labelEnd); }else{ - VdbeNoopCommment((v, "eof-A subroutine")); - addrEofA = sqlite3VdbeCurrentAddr(v); if( op==TK_ALL ){ j2 = sqlite3VdbeAddOp2(v, OP_If, regEofB, labelEnd); sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB); - sqlite3VdbeAddOp1(v, OP_Yield, regAddrB); sqlite3VdbeAddOp2(v, OP_Goto, 0, j2); }else{ assert( op==TK_UNION ); - sqlite3VdbeAddOp2(v, OP_If, regEofB, labelEnd); - sqlite3ExprCodeMove(pParse, destB.iMem, destA.iMem, destB.nMem); + sqlite3ExprCodeCopy(pParse, destB.iMem, destA.iMem, destB.nMem); j2 = sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB); - sqlite3VdbeAddOp1(v, OP_Yield, regAddrB); sqlite3VdbeAddOp2(v, OP_If, regEofB, labelEnd); - sqlite3VdbeAddOp3(v, OP_Compare, destA.iMem, destB.iMem, destB.nMem); - sqlite3VdbeAddOp3(v, OP_Jump, j2, j2+1, j2); + sqlite3VdbeAddOp4(v, OP_Compare, destA.iMem, destB.iMem, destB.nMem, + (char*)pKeyInfo, p4type); + p4type = P4_KEYINFO_STATIC; + sqlite3VdbeAddOp3(v, OP_Jump, j2, j2+4, j2); + sqlite3VdbeAddOp1(v, OP_Yield, regAddrB); + sqlite3VdbeAddOp2(v, OP_Goto, 0, j2+1); } } - + /* Generate a subroutine to run when the results from select B + ** are exhausted and only data in select A remains. + */ if( op==TK_INTERSECT ){ - addrEofA = iEnd; + addrEofB = addrEofA; }else{ - VdbeNoopCommment((v, "eof-B subroutine")); - addrEofA = sqlite3VdbeCurrentAddr(v); + VdbeNoopComment((v, "eof-B subroutine")); + addrEofB = sqlite3VdbeCurrentAddr(v); if( op==TK_ALL || op==TK_EXCEPT ){ j2 = sqlite3VdbeAddOp2(v, OP_If, regEofA, labelEnd); sqlite3VdbeAddOp2(v, OP_Gosub, regOutA, addrOutA); - sqlite3VdbeAddOp1(v, OP_Yield, regAddrA); sqlite3VdbeAddOp2(v, OP_Goto, 0, j2); }else{ assert( op==TK_UNION ); sqlite3VdbeAddOp2(v, OP_If, regEofA, labelEnd); - sqlite3ExprCodeMove(pParse, destA.iMem, destB.iMem, destA.nMem); + sqlite3ExprCodeCopy(pParse, destA.iMem, destB.iMem, destA.nMem); j2 = sqlite3VdbeAddOp2(v, OP_Gosub, regOutA, addrOutA); - sqlite3VdbeAddOp1(v, OP_Yield, regAddrA); sqlite3VdbeAddOp2(v, OP_If, regEofA, labelEnd); - sqlite3VdbeAddOp3(v, OP_Compare, destA.iMem, destB.iMem, destB.nMem); - sqlite3VdbeAddOp3(v, OP_Jump, j2, j2+1, j2); + sqlite3VdbeAddOp4(v, OP_Compare, destA.iMem, destB.iMem, destB.nMem, + (char*)pKeyInfo, p4type); + p4type = P4_KEYINFO_STATIC; + sqlite3VdbeAddOp3(v, OP_Jump, j2, j2+4, j2); + sqlite3VdbeAddOp1(v, OP_Yield, regAddrA); + sqlite3VdbeAddOp2(v, OP_Goto, 0, j2+1); } } + /* Generate code to handle the case of AB + */ VdbeNoopComment((v, "A-gt-B subroutine")); addrAgtB = sqlite3VdbeCurrentAddr(v); if( op==TK_ALL || op==TK_UNION ){ sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB); + }else{ + sqlite3VdbeAddOp1(v, OP_Yield, regAddrB); } - sqlite3VdbeAddOp1(v, OP_Yield, regAddrB); sqlite3VdbeAddOp2(v, OP_If, regEofB, addrEofB); - sqlite3VdbeAddOp2(v, OP_Goto, 0, labelCompare); + sqlite3VdbeAddOp2(v, OP_Goto, 0, labelCmpr); + /* This code runs once to initialize everything. + */ sqlite3VdbeJumpHere(v, j1); sqlite3VdbeAddOp2(v, OP_Integer, 0, regEofA); sqlite3VdbeAddOp2(v, OP_Integer, 0, regEofB); - sqlite3VdbeAddOp2(v, OP_Integer, addrSelectA, regAddrA); sqlite3VdbeAddOp2(v, OP_Integer, addrSelectB, regAddrB); - sqlite3VdbeAddOp1(v, OP_Yield, regAddrA); + sqlite3VdbeAddOp2(v, OP_Gosub, regAddrA, addrSelectA); sqlite3VdbeAddOp2(v, OP_If, regEofA, addrEofA); sqlite3VdbeAddOp1(v, OP_Yield, regAddrB); sqlite3VdbeAddOp2(v, OP_If, regEofB, addrEofB); - sqlite3VdbeResolve(v, labelCompare); - sqlite3VdbeAddOp4(v, OP_Compare, destA.iMem, destB.iMem, destA.iMem, - pKeyInfo, P4_KEYINFO_HANDOFF); + + /* Implement the main merge loop + */ + sqlite3VdbeResolveLabel(v, labelCmpr); + sqlite3VdbeAddOp4(v, OP_Compare, destA.iMem, destB.iMem, destA.nMem, + (char*)pKeyInfo, p4type); + p4type = P4_KEYINFO_STATIC; sqlite3VdbeAddOp3(v, OP_Jump, addrAltB, addrAeqB, addrAgtB); + + /* Jump to the this point in order to terminate the query. + */ sqlite3VdbeResolveLabel(v, labelEnd); - + + /* Set the number of output columns + */ + if( pDest->eDest==SRT_Callback ){ + Select *pFirst = p; + while( pFirst->pPrior ) pFirst = pFirst->pPrior; + generateColumnNames(pParse, 0, pFirst->pEList); + } + + /* Free the KeyInfo if unused. + */ + if( p4type==P4_KEYINFO_HANDOFF ){ + sqlite3_free(pKeyInfo); + } + + + /*** TBD: Insert subroutine calls to close cursors on incomplete + **** subqueries ****/ + return SQLITE_OK; } -#endif /***** ########### *****/ #ifndef SQLITE_OMIT_VIEW /* Forward Declarations */ diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 999d855def..a23c6b11db 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.724 2008/06/23 14:03:45 danielk1977 Exp $ +** @(#) $Id: sqliteInt.h,v 1.725 2008/06/24 00:32:36 drh Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ @@ -1161,7 +1161,7 @@ struct Expr { #define EP_ExpCollate 0x0100 /* Collating sequence specified explicitly */ #define EP_AnyAff 0x0200 /* Can take a cached column of any affinity */ #define EP_FixedDest 0x0400 /* Result needed in a specific register */ - +#define EP_IntValue 0x0800 /* Integer value contained in iTable */ /* ** These macros can be used to test, set, or clear bits in the ** Expr.flags field. @@ -1449,10 +1449,8 @@ struct SelectDest { u8 eDest; /* How to dispose of the results */ u8 affinity; /* Affinity used when eDest==SRT_Set */ int iParm; /* A parameter used by the eDest disposal method */ - int regCoroutine; /* Program counter register for SRT_Coroutine */ int iMem; /* Base register where results are written */ int nMem; /* Number of registers allocated */ - int eofMem; /* Register holding EOF flag */ }; /* @@ -1913,6 +1911,7 @@ WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, ExprList**, u8); 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 sqlite3ExprCacheAffinityChange(Parse*, int, int); int sqlite3ExprWritableRegister(Parse*,int,int); diff --git a/test/select1.test b/test/select1.test index caadda86c6..dea5643260 100644 --- a/test/select1.test +++ b/test/select1.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing the SELECT statement. # -# $Id: select1.test,v 1.58 2008/06/23 18:49:45 danielk1977 Exp $ +# $Id: select1.test,v 1.59 2008/06/24 00:32:36 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -544,6 +544,7 @@ do_test select1-6.21 { } } {d} do_test select1-6.22 { +breakpoint execsql { SELECT a FROM t6 WHERE b IN (SELECT b FROM t6 WHERE a<='b' UNION SELECT '3' AS x @@ -932,4 +933,3 @@ do_test select1-14.2 { } {0} finish_test -