| Index: third_party/sqlite/sqlite-src-3100200/src/select.c
|
| diff --git a/third_party/sqlite/src/src/select.c b/third_party/sqlite/sqlite-src-3100200/src/select.c
|
| similarity index 89%
|
| copy from third_party/sqlite/src/src/select.c
|
| copy to third_party/sqlite/sqlite-src-3100200/src/select.c
|
| index 3b422f1100349609920c592ba6ab4e096d1ef2ff..85f8518cf1c38f0cc49dc159a726dfef1a204540 100644
|
| --- a/third_party/sqlite/src/src/select.c
|
| +++ b/third_party/sqlite/sqlite-src-3100200/src/select.c
|
| @@ -21,7 +21,8 @@
|
| /***/ int sqlite3SelectTrace = 0;
|
| # define SELECTTRACE(K,P,S,X) \
|
| if(sqlite3SelectTrace&(K)) \
|
| - sqlite3DebugPrintf("%*s%s.%p: ",(P)->nSelectIndent*2-2,"",(S)->zSelName,(S)),\
|
| + sqlite3DebugPrintf("%*s%s.%p: ",(P)->nSelectIndent*2-2,"",\
|
| + (S)->zSelName,(S)),\
|
| sqlite3DebugPrintf X
|
| #else
|
| # define SELECTTRACE(K,P,S,X)
|
| @@ -53,25 +54,31 @@ struct SortCtx {
|
| int regReturn; /* Register holding block-output return address */
|
| int labelBkOut; /* Start label for the block-output subroutine */
|
| int addrSortIndex; /* Address of the OP_SorterOpen or OP_OpenEphemeral */
|
| + int labelDone; /* Jump here when done, ex: LIMIT reached */
|
| u8 sortFlags; /* Zero or more SORTFLAG_* bits */
|
| };
|
| #define SORTFLAG_UseSorter 0x01 /* Use SorterOpen instead of OpenEphemeral */
|
|
|
| /*
|
| -** Delete all the content of a Select structure but do not deallocate
|
| -** the select structure itself.
|
| +** Delete all the content of a Select structure. Deallocate the structure
|
| +** itself only if bFree is true.
|
| */
|
| -static void clearSelect(sqlite3 *db, Select *p){
|
| - sqlite3ExprListDelete(db, p->pEList);
|
| - sqlite3SrcListDelete(db, p->pSrc);
|
| - sqlite3ExprDelete(db, p->pWhere);
|
| - sqlite3ExprListDelete(db, p->pGroupBy);
|
| - sqlite3ExprDelete(db, p->pHaving);
|
| - sqlite3ExprListDelete(db, p->pOrderBy);
|
| - sqlite3SelectDelete(db, p->pPrior);
|
| - sqlite3ExprDelete(db, p->pLimit);
|
| - sqlite3ExprDelete(db, p->pOffset);
|
| - sqlite3WithDelete(db, p->pWith);
|
| +static void clearSelect(sqlite3 *db, Select *p, int bFree){
|
| + while( p ){
|
| + Select *pPrior = p->pPrior;
|
| + sqlite3ExprListDelete(db, p->pEList);
|
| + sqlite3SrcListDelete(db, p->pSrc);
|
| + sqlite3ExprDelete(db, p->pWhere);
|
| + sqlite3ExprListDelete(db, p->pGroupBy);
|
| + sqlite3ExprDelete(db, p->pHaving);
|
| + sqlite3ExprListDelete(db, p->pOrderBy);
|
| + sqlite3ExprDelete(db, p->pLimit);
|
| + sqlite3ExprDelete(db, p->pOffset);
|
| + sqlite3WithDelete(db, p->pWith);
|
| + if( bFree ) sqlite3DbFree(db, p);
|
| + p = pPrior;
|
| + bFree = 1;
|
| + }
|
| }
|
|
|
| /*
|
| @@ -106,14 +113,13 @@ Select *sqlite3SelectNew(
|
| Select standin;
|
| sqlite3 *db = pParse->db;
|
| pNew = sqlite3DbMallocZero(db, sizeof(*pNew) );
|
| - assert( db->mallocFailed || !pOffset || pLimit ); /* OFFSET implies LIMIT */
|
| if( pNew==0 ){
|
| assert( db->mallocFailed );
|
| pNew = &standin;
|
| memset(pNew, 0, sizeof(*pNew));
|
| }
|
| if( pEList==0 ){
|
| - pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db,TK_ALL,0));
|
| + pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db,TK_ASTERISK,0));
|
| }
|
| pNew->pEList = pEList;
|
| if( pSrc==0 ) pSrc = sqlite3DbMallocZero(db, sizeof(*pSrc));
|
| @@ -126,12 +132,11 @@ Select *sqlite3SelectNew(
|
| pNew->op = TK_SELECT;
|
| pNew->pLimit = pLimit;
|
| pNew->pOffset = pOffset;
|
| - assert( pOffset==0 || pLimit!=0 );
|
| + assert( pOffset==0 || pLimit!=0 || pParse->nErr>0 || db->mallocFailed!=0 );
|
| pNew->addrOpenEphm[0] = -1;
|
| pNew->addrOpenEphm[1] = -1;
|
| if( db->mallocFailed ) {
|
| - clearSelect(db, pNew);
|
| - if( pNew!=&standin ) sqlite3DbFree(db, pNew);
|
| + clearSelect(db, pNew, pNew!=&standin);
|
| pNew = 0;
|
| }else{
|
| assert( pNew->pSrc!=0 || pParse->nErr>0 );
|
| @@ -156,10 +161,7 @@ void sqlite3SelectSetName(Select *p, const char *zName){
|
| ** Delete the given Select structure and all of its substructures.
|
| */
|
| void sqlite3SelectDelete(sqlite3 *db, Select *p){
|
| - if( p ){
|
| - clearSelect(db, p);
|
| - sqlite3DbFree(db, p);
|
| - }
|
| + clearSelect(db, p, 1);
|
| }
|
|
|
| /*
|
| @@ -365,6 +367,12 @@ static void setJoinExpr(Expr *p, int iTable){
|
| assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) );
|
| ExprSetVVAProperty(p, EP_NoReduce);
|
| p->iRightJoinTable = (i16)iTable;
|
| + if( p->op==TK_FUNCTION && p->x.pList ){
|
| + int i;
|
| + for(i=0; i<p->x.pList->nExpr; i++){
|
| + setJoinExpr(p->x.pList->a[i].pExpr, iTable);
|
| + }
|
| + }
|
| setJoinExpr(p->pLeft, iTable);
|
| p = p->pRight;
|
| }
|
| @@ -399,12 +407,12 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
|
| int isOuter;
|
|
|
| if( NEVER(pLeftTab==0 || pRightTab==0) ) continue;
|
| - isOuter = (pRight->jointype & JT_OUTER)!=0;
|
| + isOuter = (pRight->fg.jointype & JT_OUTER)!=0;
|
|
|
| /* When the NATURAL keyword is present, add WHERE clause terms for
|
| ** every column that the two tables have in common.
|
| */
|
| - if( pRight->jointype & JT_NATURAL ){
|
| + if( pRight->fg.jointype & JT_NATURAL ){
|
| if( pRight->pOn || pRight->pUsing ){
|
| sqlite3ErrorMsg(pParse, "a NATURAL join may not have "
|
| "an ON or USING clause", 0);
|
| @@ -489,6 +497,7 @@ static void pushOntoSorter(
|
| SortCtx *pSort, /* Information about the ORDER BY clause */
|
| Select *pSelect, /* The whole SELECT statement */
|
| int regData, /* First register holding data to be sorted */
|
| + int regOrigData, /* First register holding data before packing */
|
| int nData, /* Number of elements in the data array */
|
| int nPrefixReg /* No. of reg prior to regData available for use */
|
| ){
|
| @@ -500,8 +509,10 @@ static void pushOntoSorter(
|
| int regRecord = ++pParse->nMem; /* Assembled sorter record */
|
| int nOBSat = pSort->nOBSat; /* ORDER BY terms to skip */
|
| int op; /* Opcode to add sorter record to sorter */
|
| + int iLimit; /* LIMIT counter */
|
|
|
| assert( bSeq==0 || bSeq==1 );
|
| + assert( nData==1 || regData==regOrigData );
|
| if( nPrefixReg ){
|
| assert( nPrefixReg==nExpr+bSeq );
|
| regBase = regData - nExpr - bSeq;
|
| @@ -509,14 +520,17 @@ static void pushOntoSorter(
|
| regBase = pParse->nMem + 1;
|
| pParse->nMem += nBase;
|
| }
|
| - sqlite3ExprCodeExprList(pParse, pSort->pOrderBy, regBase, SQLITE_ECEL_DUP);
|
| + assert( pSelect->iOffset==0 || pSelect->iLimit!=0 );
|
| + iLimit = pSelect->iOffset ? pSelect->iOffset+1 : pSelect->iLimit;
|
| + pSort->labelDone = sqlite3VdbeMakeLabel(v);
|
| + sqlite3ExprCodeExprList(pParse, pSort->pOrderBy, regBase, regOrigData,
|
| + SQLITE_ECEL_DUP|SQLITE_ECEL_REF);
|
| if( bSeq ){
|
| sqlite3VdbeAddOp2(v, OP_Sequence, pSort->iECursor, regBase+nExpr);
|
| }
|
| if( nPrefixReg==0 ){
|
| sqlite3ExprCodeMove(pParse, regData, regBase+nExpr+bSeq, nData);
|
| }
|
| -
|
| sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase+nOBSat, nBase-nOBSat, regRecord);
|
| if( nOBSat>0 ){
|
| int regPrevKey; /* The first nOBSat columns of the previous row */
|
| @@ -542,13 +556,19 @@ static void pushOntoSorter(
|
| pKI = pOp->p4.pKeyInfo;
|
| memset(pKI->aSortOrder, 0, pKI->nField); /* Makes OP_Jump below testable */
|
| sqlite3VdbeChangeP4(v, -1, (char*)pKI, P4_KEYINFO);
|
| - pOp->p4.pKeyInfo = keyInfoFromExprList(pParse, pSort->pOrderBy, nOBSat, 1);
|
| + testcase( pKI->nXField>2 );
|
| + pOp->p4.pKeyInfo = keyInfoFromExprList(pParse, pSort->pOrderBy, nOBSat,
|
| + pKI->nXField-1);
|
| addrJmp = sqlite3VdbeCurrentAddr(v);
|
| sqlite3VdbeAddOp3(v, OP_Jump, addrJmp+1, 0, addrJmp+1); VdbeCoverage(v);
|
| pSort->labelBkOut = sqlite3VdbeMakeLabel(v);
|
| pSort->regReturn = ++pParse->nMem;
|
| sqlite3VdbeAddOp2(v, OP_Gosub, pSort->regReturn, pSort->labelBkOut);
|
| sqlite3VdbeAddOp1(v, OP_ResetSorter, pSort->iECursor);
|
| + if( iLimit ){
|
| + sqlite3VdbeAddOp2(v, OP_IfNot, iLimit, pSort->labelDone);
|
| + VdbeCoverage(v);
|
| + }
|
| sqlite3VdbeJumpHere(v, addrFirst);
|
| sqlite3ExprCodeMove(pParse, regBase, regPrevKey, pSort->nOBSat);
|
| sqlite3VdbeJumpHere(v, addrJmp);
|
| @@ -559,21 +579,12 @@ static void pushOntoSorter(
|
| op = OP_IdxInsert;
|
| }
|
| sqlite3VdbeAddOp2(v, op, pSort->iECursor, regRecord);
|
| - if( pSelect->iLimit ){
|
| - int addr1, addr2;
|
| - int iLimit;
|
| - if( pSelect->iOffset ){
|
| - iLimit = pSelect->iOffset+1;
|
| - }else{
|
| - iLimit = pSelect->iLimit;
|
| - }
|
| - addr1 = sqlite3VdbeAddOp1(v, OP_IfZero, iLimit); VdbeCoverage(v);
|
| - sqlite3VdbeAddOp2(v, OP_AddImm, iLimit, -1);
|
| - addr2 = sqlite3VdbeAddOp0(v, OP_Goto);
|
| - sqlite3VdbeJumpHere(v, addr1);
|
| + if( iLimit ){
|
| + int addr;
|
| + addr = sqlite3VdbeAddOp3(v, OP_IfNotZero, iLimit, 0, 1); VdbeCoverage(v);
|
| sqlite3VdbeAddOp1(v, OP_Last, pSort->iECursor);
|
| sqlite3VdbeAddOp1(v, OP_Delete, pSort->iECursor);
|
| - sqlite3VdbeJumpHere(v, addr2);
|
| + sqlite3VdbeJumpHere(v, addr);
|
| }
|
| }
|
|
|
| @@ -586,11 +597,8 @@ static void codeOffset(
|
| int iContinue /* Jump here to skip the current record */
|
| ){
|
| if( iOffset>0 ){
|
| - int addr;
|
| - addr = sqlite3VdbeAddOp3(v, OP_IfNeg, iOffset, 0, -1); VdbeCoverage(v);
|
| - sqlite3VdbeAddOp2(v, OP_Goto, 0, iContinue);
|
| - VdbeComment((v, "skip OFFSET records"));
|
| - sqlite3VdbeJumpHere(v, addr);
|
| + sqlite3VdbeAddOp3(v, OP_IfPos, iOffset, iContinue, 1); VdbeCoverage(v);
|
| + VdbeComment((v, "OFFSET"));
|
| }
|
| }
|
|
|
| @@ -714,8 +722,13 @@ static void selectInnerLoop(
|
| /* If the destination is an EXISTS(...) expression, the actual
|
| ** values returned by the SELECT are not required.
|
| */
|
| - sqlite3ExprCodeExprList(pParse, pEList, regResult,
|
| - (eDest==SRT_Output||eDest==SRT_Coroutine)?SQLITE_ECEL_DUP:0);
|
| + u8 ecelFlags;
|
| + if( eDest==SRT_Mem || eDest==SRT_Output || eDest==SRT_Coroutine ){
|
| + ecelFlags = SQLITE_ECEL_DUP;
|
| + }else{
|
| + ecelFlags = 0;
|
| + }
|
| + sqlite3ExprCodeExprList(pParse, pEList, regResult, 0, ecelFlags);
|
| }
|
|
|
| /* If the DISTINCT keyword was present on the SELECT statement
|
| @@ -770,7 +783,8 @@ static void selectInnerLoop(
|
|
|
| default: {
|
| assert( pDistinct->eTnctType==WHERE_DISTINCT_UNORDERED );
|
| - codeDistinct(pParse, pDistinct->tabTnct, iContinue, nResultCol, regResult);
|
| + codeDistinct(pParse, pDistinct->tabTnct, iContinue, nResultCol,
|
| + regResult);
|
| break;
|
| }
|
| }
|
| @@ -812,6 +826,8 @@ static void selectInnerLoop(
|
| int r1 = sqlite3GetTempRange(pParse, nPrefixReg+1);
|
| testcase( eDest==SRT_Table );
|
| testcase( eDest==SRT_EphemTab );
|
| + testcase( eDest==SRT_Fifo );
|
| + testcase( eDest==SRT_DistFifo );
|
| sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1+nPrefixReg);
|
| #ifndef SQLITE_OMIT_CTE
|
| if( eDest==SRT_DistFifo ){
|
| @@ -821,13 +837,14 @@ static void selectInnerLoop(
|
| ** current row to the index and proceed with writing it to the
|
| ** output table as well. */
|
| int addr = sqlite3VdbeCurrentAddr(v) + 4;
|
| - sqlite3VdbeAddOp4Int(v, OP_Found, iParm+1, addr, r1, 0); VdbeCoverage(v);
|
| + sqlite3VdbeAddOp4Int(v, OP_Found, iParm+1, addr, r1, 0);
|
| + VdbeCoverage(v);
|
| sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm+1, r1);
|
| assert( pSort==0 );
|
| }
|
| #endif
|
| if( pSort ){
|
| - pushOntoSorter(pParse, pSort, p, r1+nPrefixReg, 1, nPrefixReg);
|
| + pushOntoSorter(pParse, pSort, p, r1+nPrefixReg,regResult,1,nPrefixReg);
|
| }else{
|
| int r2 = sqlite3GetTempReg(pParse);
|
| sqlite3VdbeAddOp2(v, OP_NewRowid, iParm, r2);
|
| @@ -853,7 +870,7 @@ static void selectInnerLoop(
|
| ** ORDER BY in this case since the order of entries in the set
|
| ** does not matter. But there might be a LIMIT clause, in which
|
| ** case the order does matter */
|
| - pushOntoSorter(pParse, pSort, p, regResult, 1, nPrefixReg);
|
| + pushOntoSorter(pParse, pSort, p, regResult, regResult, 1, nPrefixReg);
|
| }else{
|
| int r1 = sqlite3GetTempReg(pParse);
|
| sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult,1,r1, &pDest->affSdst, 1);
|
| @@ -879,7 +896,7 @@ static void selectInnerLoop(
|
| case SRT_Mem: {
|
| assert( nResultCol==1 );
|
| if( pSort ){
|
| - pushOntoSorter(pParse, pSort, p, regResult, 1, nPrefixReg);
|
| + pushOntoSorter(pParse, pSort, p, regResult, regResult, 1, nPrefixReg);
|
| }else{
|
| assert( regResult==iParm );
|
| /* The LIMIT clause will jump out of the loop for us */
|
| @@ -893,7 +910,8 @@ static void selectInnerLoop(
|
| testcase( eDest==SRT_Coroutine );
|
| testcase( eDest==SRT_Output );
|
| if( pSort ){
|
| - pushOntoSorter(pParse, pSort, p, regResult, nResultCol, nPrefixReg);
|
| + pushOntoSorter(pParse, pSort, p, regResult, regResult, nResultCol,
|
| + nPrefixReg);
|
| }else if( eDest==SRT_Coroutine ){
|
| sqlite3VdbeAddOp1(v, OP_Yield, pDest->iSDParm);
|
| }else{
|
| @@ -970,7 +988,7 @@ static void selectInnerLoop(
|
| ** the output for us.
|
| */
|
| if( pSort==0 && p->iLimit ){
|
| - sqlite3VdbeAddOp3(v, OP_IfZero, p->iLimit, iBreak, -1); VdbeCoverage(v);
|
| + sqlite3VdbeAddOp2(v, OP_DecrJumpZero, p->iLimit, iBreak); VdbeCoverage(v);
|
| }
|
| }
|
|
|
| @@ -1053,7 +1071,7 @@ static KeyInfo *keyInfoFromExprList(
|
| int i;
|
|
|
| nExpr = pList->nExpr;
|
| - pInfo = sqlite3KeyInfoAlloc(db, nExpr+nExtra-iStart, 1);
|
| + pInfo = sqlite3KeyInfoAlloc(db, nExpr-iStart, nExtra+1);
|
| if( pInfo ){
|
| assert( sqlite3KeyInfoIsWriteable(pInfo) );
|
| for(i=iStart, pItem=pList->a+iStart; i<nExpr; i++, pItem++){
|
| @@ -1067,7 +1085,6 @@ static KeyInfo *keyInfoFromExprList(
|
| return pInfo;
|
| }
|
|
|
| -#ifndef SQLITE_OMIT_COMPOUND_SELECT
|
| /*
|
| ** Name of the connection operator, used for error messages.
|
| */
|
| @@ -1081,7 +1098,6 @@ static const char *selectOpName(int id){
|
| }
|
| return z;
|
| }
|
| -#endif /* SQLITE_OMIT_COMPOUND_SELECT */
|
|
|
| #ifndef SQLITE_OMIT_EXPLAIN
|
| /*
|
| @@ -1168,7 +1184,7 @@ static void generateSortTail(
|
| SelectDest *pDest /* Write the sorted results here */
|
| ){
|
| Vdbe *v = pParse->pVdbe; /* The prepared statement */
|
| - int addrBreak = sqlite3VdbeMakeLabel(v); /* Jump here to exit loop */
|
| + int addrBreak = pSort->labelDone; /* Jump here to exit loop */
|
| int addrContinue = sqlite3VdbeMakeLabel(v); /* Jump here for next cycle */
|
| int addr;
|
| int addrOnce = 0;
|
| @@ -1187,9 +1203,10 @@ static void generateSortTail(
|
| struct ExprList_item *aOutEx = p->pEList->a;
|
| #endif
|
|
|
| + assert( addrBreak<0 );
|
| if( pSort->labelBkOut ){
|
| sqlite3VdbeAddOp2(v, OP_Gosub, pSort->regReturn, pSort->labelBkOut);
|
| - sqlite3VdbeAddOp2(v, OP_Goto, 0, addrBreak);
|
| + sqlite3VdbeGoto(v, addrBreak);
|
| sqlite3VdbeResolveLabel(v, pSort->labelBkOut);
|
| }
|
| iTab = pSort->iECursor;
|
| @@ -1227,10 +1244,7 @@ static void generateSortTail(
|
| VdbeComment((v, "%s", aOutEx[i].zName ? aOutEx[i].zName : aOutEx[i].zSpan));
|
| }
|
| switch( eDest ){
|
| - case SRT_Table:
|
| case SRT_EphemTab: {
|
| - testcase( eDest==SRT_Table );
|
| - testcase( eDest==SRT_EphemTab );
|
| sqlite3VdbeAddOp2(v, OP_NewRowid, iParm, regRowid);
|
| sqlite3VdbeAddOp3(v, OP_Insert, iParm, regRow, regRowid);
|
| sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
|
| @@ -1307,30 +1321,30 @@ static void generateSortTail(
|
| */
|
| #ifdef SQLITE_ENABLE_COLUMN_METADATA
|
| # define columnType(A,B,C,D,E,F) columnTypeImpl(A,B,C,D,E,F)
|
| +#else /* if !defined(SQLITE_ENABLE_COLUMN_METADATA) */
|
| +# define columnType(A,B,C,D,E,F) columnTypeImpl(A,B,F)
|
| +#endif
|
| static const char *columnTypeImpl(
|
| NameContext *pNC,
|
| Expr *pExpr,
|
| +#ifdef SQLITE_ENABLE_COLUMN_METADATA
|
| const char **pzOrigDb,
|
| const char **pzOrigTab,
|
| const char **pzOrigCol,
|
| +#endif
|
| u8 *pEstWidth
|
| ){
|
| - char const *zOrigDb = 0;
|
| - char const *zOrigTab = 0;
|
| - char const *zOrigCol = 0;
|
| -#else /* if !defined(SQLITE_ENABLE_COLUMN_METADATA) */
|
| -# define columnType(A,B,C,D,E,F) columnTypeImpl(A,B,F)
|
| -static const char *columnTypeImpl(
|
| - NameContext *pNC,
|
| - Expr *pExpr,
|
| - u8 *pEstWidth
|
| -){
|
| -#endif /* !defined(SQLITE_ENABLE_COLUMN_METADATA) */
|
| char const *zType = 0;
|
| int j;
|
| u8 estWidth = 1;
|
| +#ifdef SQLITE_ENABLE_COLUMN_METADATA
|
| + char const *zOrigDb = 0;
|
| + char const *zOrigTab = 0;
|
| + char const *zOrigCol = 0;
|
| +#endif
|
|
|
| - if( NEVER(pExpr==0) || pNC->pSrcList==0 ) return 0;
|
| + assert( pExpr!=0 );
|
| + assert( pNC->pSrcList!=0 );
|
| switch( pExpr->op ){
|
| case TK_AGG_COLUMN:
|
| case TK_COLUMN: {
|
| @@ -1385,6 +1399,9 @@ static const char *columnTypeImpl(
|
| /* If iCol is less than zero, then the expression requests the
|
| ** rowid of the sub-select or view. This expression is legal (see
|
| ** test case misc2.2.2) - it always evaluates to NULL.
|
| + **
|
| + ** The ALWAYS() is because iCol>=pS->pEList->nExpr will have been
|
| + ** caught already by name resolution.
|
| */
|
| NameContext sNC;
|
| Expr *p = pS->pEList->a[iCol].pExpr;
|
| @@ -1515,7 +1532,9 @@ static void generateColumnNames(
|
| }
|
| #endif
|
|
|
| - if( pParse->colNamesSet || NEVER(v==0) || db->mallocFailed ) return;
|
| + if( pParse->colNamesSet || db->mallocFailed ) return;
|
| + assert( v!=0 );
|
| + assert( pTabList!=0 );
|
| pParse->colNamesSet = 1;
|
| fullNames = (db->flags & SQLITE_FullColNames)!=0;
|
| shortNames = (db->flags & SQLITE_ShortColNames)!=0;
|
| @@ -1527,7 +1546,7 @@ static void generateColumnNames(
|
| if( pEList->a[i].zName ){
|
| char *zName = pEList->a[i].zName;
|
| sqlite3VdbeSetColName(v, i, COLNAME_NAME, zName, SQLITE_TRANSIENT);
|
| - }else if( (p->op==TK_COLUMN || p->op==TK_AGG_COLUMN) && pTabList ){
|
| + }else if( p->op==TK_COLUMN || p->op==TK_AGG_COLUMN ){
|
| Table *pTab;
|
| char *zCol;
|
| int iCol = p->iColumn;
|
| @@ -1575,7 +1594,7 @@ static void generateColumnNames(
|
| ** Return SQLITE_OK on success. If a memory allocation error occurs,
|
| ** store NULL in *paCol and 0 in *pnCol and return SQLITE_NOMEM.
|
| */
|
| -static int selectColumnsFromExprList(
|
| +int sqlite3ColumnsFromExprList(
|
| Parse *pParse, /* Parsing context */
|
| ExprList *pEList, /* Expr list from which to derive column names */
|
| i16 *pnCol, /* Write the number of columns here */
|
| @@ -1583,13 +1602,15 @@ static int selectColumnsFromExprList(
|
| ){
|
| sqlite3 *db = pParse->db; /* Database connection */
|
| int i, j; /* Loop counters */
|
| - int cnt; /* Index added to make the name unique */
|
| + u32 cnt; /* Index added to make the name unique */
|
| Column *aCol, *pCol; /* For looping over result columns */
|
| int nCol; /* Number of columns in the result set */
|
| Expr *p; /* Expression for a single result column */
|
| char *zName; /* Column name */
|
| int nName; /* Size of name in zName[] */
|
| + Hash ht; /* Hash table of column names */
|
|
|
| + sqlite3HashInit(&ht);
|
| if( pEList ){
|
| nCol = pEList->nExpr;
|
| aCol = sqlite3DbMallocZero(db, sizeof(aCol[0])*nCol);
|
| @@ -1598,16 +1619,16 @@ static int selectColumnsFromExprList(
|
| nCol = 0;
|
| aCol = 0;
|
| }
|
| + assert( nCol==(i16)nCol );
|
| *pnCol = nCol;
|
| *paCol = aCol;
|
|
|
| - for(i=0, pCol=aCol; i<nCol; i++, pCol++){
|
| + for(i=0, pCol=aCol; i<nCol && !db->mallocFailed; i++, pCol++){
|
| /* Get an appropriate name for the column
|
| */
|
| p = sqlite3ExprSkipCollate(pEList->a[i].pExpr);
|
| if( (zName = pEList->a[i].zName)!=0 ){
|
| /* If the column contains an "AS <name>" phrase, use <name> as the name */
|
| - zName = sqlite3DbStrDup(db, zName);
|
| }else{
|
| Expr *pColExpr = p; /* The expression that is the result column name */
|
| Table *pTab; /* Table associated with this expression */
|
| @@ -1620,41 +1641,37 @@ static int selectColumnsFromExprList(
|
| int iCol = pColExpr->iColumn;
|
| pTab = pColExpr->pTab;
|
| if( iCol<0 ) iCol = pTab->iPKey;
|
| - zName = sqlite3MPrintf(db, "%s",
|
| - iCol>=0 ? pTab->aCol[iCol].zName : "rowid");
|
| + zName = iCol>=0 ? pTab->aCol[iCol].zName : "rowid";
|
| }else if( pColExpr->op==TK_ID ){
|
| assert( !ExprHasProperty(pColExpr, EP_IntValue) );
|
| - zName = sqlite3MPrintf(db, "%s", pColExpr->u.zToken);
|
| + zName = pColExpr->u.zToken;
|
| }else{
|
| /* Use the original text of the column expression as its name */
|
| - zName = sqlite3MPrintf(db, "%s", pEList->a[i].zSpan);
|
| + zName = pEList->a[i].zSpan;
|
| }
|
| }
|
| - if( db->mallocFailed ){
|
| - sqlite3DbFree(db, zName);
|
| - break;
|
| - }
|
| + zName = sqlite3MPrintf(db, "%s", zName);
|
|
|
| /* Make sure the column name is unique. If the name is not unique,
|
| ** append an integer to the name so that it becomes unique.
|
| */
|
| - nName = sqlite3Strlen30(zName);
|
| - for(j=cnt=0; j<i; j++){
|
| - if( sqlite3StrICmp(aCol[j].zName, zName)==0 ){
|
| - char *zNewName;
|
| - int k;
|
| - for(k=nName-1; k>1 && sqlite3Isdigit(zName[k]); k--){}
|
| - if( k>=0 && zName[k]==':' ) nName = k;
|
| - zName[nName] = 0;
|
| - zNewName = sqlite3MPrintf(db, "%s:%d", zName, ++cnt);
|
| - sqlite3DbFree(db, zName);
|
| - zName = zNewName;
|
| - j = -1;
|
| - if( zName==0 ) break;
|
| + cnt = 0;
|
| + while( zName && sqlite3HashFind(&ht, zName)!=0 ){
|
| + nName = sqlite3Strlen30(zName);
|
| + if( nName>0 ){
|
| + for(j=nName-1; j>0 && sqlite3Isdigit(zName[j]); j--){}
|
| + if( zName[j]==':' ) nName = j;
|
| }
|
| + zName = sqlite3MPrintf(db, "%.*z:%u", nName, zName, ++cnt);
|
| + if( cnt>3 ) sqlite3_randomness(sizeof(cnt), &cnt);
|
| }
|
| pCol->zName = zName;
|
| + sqlite3ColumnPropertiesFromName(0, pCol);
|
| + if( zName && sqlite3HashInsert(&ht, zName, pCol)==pCol ){
|
| + db->mallocFailed = 1;
|
| + }
|
| }
|
| + sqlite3HashClear(&ht);
|
| if( db->mallocFailed ){
|
| for(j=0; j<i; j++){
|
| sqlite3DbFree(db, aCol[j].zName);
|
| @@ -1701,12 +1718,15 @@ static void selectAddColumnTypeAndCollation(
|
| a = pSelect->pEList->a;
|
| for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){
|
| p = a[i].pExpr;
|
| - pCol->zType = sqlite3DbStrDup(db, columnType(&sNC, p,0,0,0, &pCol->szEst));
|
| + if( pCol->zType==0 ){
|
| + pCol->zType = sqlite3DbStrDup(db,
|
| + columnType(&sNC, p,0,0,0, &pCol->szEst));
|
| + }
|
| szAll += pCol->szEst;
|
| pCol->affinity = sqlite3ExprAffinity(p);
|
| - if( pCol->affinity==0 ) pCol->affinity = SQLITE_AFF_NONE;
|
| + if( pCol->affinity==0 ) pCol->affinity = SQLITE_AFF_BLOB;
|
| pColl = sqlite3ExprCollSeq(pParse, p);
|
| - if( pColl ){
|
| + if( pColl && pCol->zColl==0 ){
|
| pCol->zColl = sqlite3DbStrDup(db, pColl->zName);
|
| }
|
| }
|
| @@ -1739,7 +1759,7 @@ Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect){
|
| pTab->nRef = 1;
|
| pTab->zName = 0;
|
| pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
|
| - selectColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol);
|
| + sqlite3ColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol);
|
| selectAddColumnTypeAndCollation(pParse, pTab, pSelect);
|
| pTab->iPKey = -1;
|
| if( db->mallocFailed ){
|
| @@ -1796,7 +1816,7 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){
|
| Vdbe *v = 0;
|
| int iLimit = 0;
|
| int iOffset;
|
| - int addr1, n;
|
| + int n;
|
| if( p->iLimit ) return;
|
|
|
| /*
|
| @@ -1815,7 +1835,7 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){
|
| sqlite3VdbeAddOp2(v, OP_Integer, n, iLimit);
|
| VdbeComment((v, "LIMIT counter"));
|
| if( n==0 ){
|
| - sqlite3VdbeAddOp2(v, OP_Goto, 0, iBreak);
|
| + sqlite3VdbeGoto(v, iBreak);
|
| }else if( n>=0 && p->nSelectRow>(u64)n ){
|
| p->nSelectRow = n;
|
| }
|
| @@ -1823,7 +1843,7 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){
|
| sqlite3ExprCode(pParse, p->pLimit, iLimit);
|
| sqlite3VdbeAddOp1(v, OP_MustBeInt, iLimit); VdbeCoverage(v);
|
| VdbeComment((v, "LIMIT counter"));
|
| - sqlite3VdbeAddOp2(v, OP_IfZero, iLimit, iBreak); VdbeCoverage(v);
|
| + sqlite3VdbeAddOp2(v, OP_IfNot, iLimit, iBreak); VdbeCoverage(v);
|
| }
|
| if( p->pOffset ){
|
| p->iOffset = iOffset = ++pParse->nMem;
|
| @@ -1831,14 +1851,10 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){
|
| sqlite3ExprCode(pParse, p->pOffset, iOffset);
|
| sqlite3VdbeAddOp1(v, OP_MustBeInt, iOffset); VdbeCoverage(v);
|
| VdbeComment((v, "OFFSET counter"));
|
| - addr1 = sqlite3VdbeAddOp1(v, OP_IfPos, iOffset); VdbeCoverage(v);
|
| - sqlite3VdbeAddOp2(v, OP_Integer, 0, iOffset);
|
| - sqlite3VdbeJumpHere(v, addr1);
|
| + sqlite3VdbeAddOp3(v, OP_SetIfNotPos, iOffset, iOffset, 0);
|
| sqlite3VdbeAddOp3(v, OP_Add, iLimit, iOffset, iOffset+1);
|
| VdbeComment((v, "LIMIT+OFFSET"));
|
| - addr1 = sqlite3VdbeAddOp1(v, OP_IfPos, iLimit); VdbeCoverage(v);
|
| - sqlite3VdbeAddOp2(v, OP_Integer, -1, iOffset+1);
|
| - sqlite3VdbeJumpHere(v, addr1);
|
| + sqlite3VdbeAddOp3(v, OP_SetIfNotPos, iLimit, iOffset+1, -1);
|
| }
|
| }
|
| }
|
| @@ -1860,7 +1876,10 @@ static CollSeq *multiSelectCollSeq(Parse *pParse, Select *p, int iCol){
|
| pRet = 0;
|
| }
|
| assert( iCol>=0 );
|
| - if( pRet==0 && iCol<p->pEList->nExpr ){
|
| + /* iCol must be less than p->pEList->nExpr. Otherwise an error would
|
| + ** have been thrown during name resolution and we would not have gotten
|
| + ** this far */
|
| + if( pRet==0 && ALWAYS(iCol<p->pEList->nExpr) ){
|
| pRet = sqlite3ExprCollSeq(pParse, p->pEList->a[iCol].pExpr);
|
| }
|
| return pRet;
|
| @@ -1915,7 +1934,7 @@ static KeyInfo *multiSelectOrderByKeyInfo(Parse *pParse, Select *p, int nExtra){
|
| **
|
| **
|
| ** There is exactly one reference to the recursive-table in the FROM clause
|
| -** of recursive-query, marked with the SrcList->a[].isRecursive flag.
|
| +** of recursive-query, marked with the SrcList->a[].fg.isRecursive flag.
|
| **
|
| ** The setup-query runs once to generate an initial set of rows that go
|
| ** into a Queue table. Rows are extracted from the Queue table one by
|
| @@ -1980,7 +1999,7 @@ static void generateWithRecursiveQuery(
|
|
|
| /* Locate the cursor number of the Current table */
|
| for(i=0; ALWAYS(i<pSrc->nSrc); i++){
|
| - if( pSrc->a[i].isRecursive ){
|
| + if( pSrc->a[i].fg.isRecursive ){
|
| iCurrent = pSrc->a[i].iCursor;
|
| break;
|
| }
|
| @@ -2042,7 +2061,7 @@ static void generateWithRecursiveQuery(
|
| selectInnerLoop(pParse, p, p->pEList, iCurrent,
|
| 0, 0, pDest, addrCont, addrBreak);
|
| if( regLimit ){
|
| - sqlite3VdbeAddOp3(v, OP_IfZero, regLimit, addrBreak, -1);
|
| + sqlite3VdbeAddOp2(v, OP_DecrJumpZero, regLimit, addrBreak);
|
| VdbeCoverage(v);
|
| }
|
| sqlite3VdbeResolveLabel(v, addrCont);
|
| @@ -2050,13 +2069,17 @@ static void generateWithRecursiveQuery(
|
| /* Execute the recursive SELECT taking the single row in Current as
|
| ** the value for the recursive-table. Store the results in the Queue.
|
| */
|
| - p->pPrior = 0;
|
| - sqlite3Select(pParse, p, &destQueue);
|
| - assert( p->pPrior==0 );
|
| - p->pPrior = pSetup;
|
| + if( p->selFlags & SF_Aggregate ){
|
| + sqlite3ErrorMsg(pParse, "recursive aggregate queries not supported");
|
| + }else{
|
| + p->pPrior = 0;
|
| + sqlite3Select(pParse, p, &destQueue);
|
| + assert( p->pPrior==0 );
|
| + p->pPrior = pSetup;
|
| + }
|
|
|
| /* Keep running the loop until the Queue is empty */
|
| - sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop);
|
| + sqlite3VdbeGoto(v, addrTop);
|
| sqlite3VdbeResolveLabel(v, addrBreak);
|
|
|
| end_of_recursive_query:
|
| @@ -2075,6 +2098,48 @@ static int multiSelectOrderBy(
|
| SelectDest *pDest /* What to do with query results */
|
| );
|
|
|
| +/*
|
| +** Handle the special case of a compound-select that originates from a
|
| +** VALUES clause. By handling this as a special case, we avoid deep
|
| +** recursion, and thus do not need to enforce the SQLITE_LIMIT_COMPOUND_SELECT
|
| +** on a VALUES clause.
|
| +**
|
| +** Because the Select object originates from a VALUES clause:
|
| +** (1) It has no LIMIT or OFFSET
|
| +** (2) All terms are UNION ALL
|
| +** (3) There is no ORDER BY clause
|
| +*/
|
| +static int multiSelectValues(
|
| + Parse *pParse, /* Parsing context */
|
| + Select *p, /* The right-most of SELECTs to be coded */
|
| + SelectDest *pDest /* What to do with query results */
|
| +){
|
| + Select *pPrior;
|
| + int nRow = 1;
|
| + int rc = 0;
|
| + assert( p->selFlags & SF_MultiValue );
|
| + do{
|
| + assert( p->selFlags & SF_Values );
|
| + assert( p->op==TK_ALL || (p->op==TK_SELECT && p->pPrior==0) );
|
| + assert( p->pLimit==0 );
|
| + assert( p->pOffset==0 );
|
| + assert( p->pNext==0 || p->pEList->nExpr==p->pNext->pEList->nExpr );
|
| + if( p->pPrior==0 ) break;
|
| + assert( p->pPrior->pNext==p );
|
| + p = p->pPrior;
|
| + nRow++;
|
| + }while(1);
|
| + while( p ){
|
| + pPrior = p->pPrior;
|
| + p->pPrior = 0;
|
| + rc = sqlite3Select(pParse, p, pDest);
|
| + p->pPrior = pPrior;
|
| + if( rc ) break;
|
| + p->nSelectRow = nRow;
|
| + p = p->pNext;
|
| + }
|
| + return rc;
|
| +}
|
|
|
| /*
|
| ** This routine is called to process a compound query form from
|
| @@ -2156,20 +2221,18 @@ static int multiSelect(
|
| dest.eDest = SRT_Table;
|
| }
|
|
|
| + /* Special handling for a compound-select that originates as a VALUES clause.
|
| + */
|
| + if( p->selFlags & SF_MultiValue ){
|
| + rc = multiSelectValues(pParse, p, &dest);
|
| + goto multi_select_end;
|
| + }
|
| +
|
| /* Make sure all SELECTs in the statement have the same number of elements
|
| ** in their result sets.
|
| */
|
| assert( p->pEList && pPrior->pEList );
|
| - if( p->pEList->nExpr!=pPrior->pEList->nExpr ){
|
| - if( p->selFlags & SF_Values ){
|
| - sqlite3ErrorMsg(pParse, "all VALUES must have the same number of terms");
|
| - }else{
|
| - sqlite3ErrorMsg(pParse, "SELECTs to the left and right of %s"
|
| - " do not have the same number of result columns", selectOpName(p->op));
|
| - }
|
| - rc = 1;
|
| - goto multi_select_end;
|
| - }
|
| + assert( p->pEList->nExpr==pPrior->pEList->nExpr );
|
|
|
| #ifndef SQLITE_OMIT_CTE
|
| if( p->selFlags & SF_Recursive ){
|
| @@ -2205,8 +2268,13 @@ static int multiSelect(
|
| p->iLimit = pPrior->iLimit;
|
| p->iOffset = pPrior->iOffset;
|
| if( p->iLimit ){
|
| - addr = sqlite3VdbeAddOp1(v, OP_IfZero, p->iLimit); VdbeCoverage(v);
|
| + addr = sqlite3VdbeAddOp1(v, OP_IfNot, p->iLimit); VdbeCoverage(v);
|
| VdbeComment((v, "Jump ahead if LIMIT reached"));
|
| + if( p->iOffset ){
|
| + sqlite3VdbeAddOp3(v, OP_SetIfNotPos, p->iOffset, p->iOffset, 0);
|
| + sqlite3VdbeAddOp3(v, OP_Add, p->iLimit, p->iOffset, p->iOffset+1);
|
| + sqlite3VdbeAddOp3(v, OP_SetIfNotPos, p->iLimit, p->iOffset+1, -1);
|
| + }
|
| }
|
| explainSetInteger(iSub2, pParse->iNextSelectId);
|
| rc = sqlite3Select(pParse, p, &dest);
|
| @@ -2307,7 +2375,7 @@ static int multiSelect(
|
| if( dest.eDest==SRT_Output ){
|
| Select *pFirst = p;
|
| while( pFirst->pPrior ) pFirst = pFirst->pPrior;
|
| - generateColumnNames(pParse, 0, pFirst->pEList);
|
| + generateColumnNames(pParse, pFirst->pSrc, pFirst->pEList);
|
| }
|
| iBreak = sqlite3VdbeMakeLabel(v);
|
| iCont = sqlite3VdbeMakeLabel(v);
|
| @@ -2382,7 +2450,7 @@ static int multiSelect(
|
| if( dest.eDest==SRT_Output ){
|
| Select *pFirst = p;
|
| while( pFirst->pPrior ) pFirst = pFirst->pPrior;
|
| - generateColumnNames(pParse, 0, pFirst->pEList);
|
| + generateColumnNames(pParse, pFirst->pSrc, pFirst->pEList);
|
| }
|
| iBreak = sqlite3VdbeMakeLabel(v);
|
| iCont = sqlite3VdbeMakeLabel(v);
|
| @@ -2462,6 +2530,19 @@ multi_select_end:
|
| #endif /* SQLITE_OMIT_COMPOUND_SELECT */
|
|
|
| /*
|
| +** Error message for when two or more terms of a compound select have different
|
| +** size result sets.
|
| +*/
|
| +void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p){
|
| + if( p->selFlags & SF_Values ){
|
| + sqlite3ErrorMsg(pParse, "all VALUES must have the same number of terms");
|
| + }else{
|
| + sqlite3ErrorMsg(pParse, "SELECTs to the left and right of %s"
|
| + " do not have the same number of result columns", selectOpName(p->op));
|
| + }
|
| +}
|
| +
|
| +/*
|
| ** Code an output subroutine for a coroutine implementation of a
|
| ** SELECT statment.
|
| **
|
| @@ -2501,12 +2582,12 @@ static int generateOutputSubroutine(
|
| /* Suppress duplicates for UNION, EXCEPT, and INTERSECT
|
| */
|
| if( regPrev ){
|
| - int j1, j2;
|
| - j1 = sqlite3VdbeAddOp1(v, OP_IfNot, regPrev); VdbeCoverage(v);
|
| - j2 = sqlite3VdbeAddOp4(v, OP_Compare, pIn->iSdst, regPrev+1, pIn->nSdst,
|
| + int addr1, addr2;
|
| + addr1 = sqlite3VdbeAddOp1(v, OP_IfNot, regPrev); VdbeCoverage(v);
|
| + addr2 = sqlite3VdbeAddOp4(v, OP_Compare, pIn->iSdst, regPrev+1, pIn->nSdst,
|
| (char*)sqlite3KeyInfoRef(pKeyInfo), P4_KEYINFO);
|
| - sqlite3VdbeAddOp3(v, OP_Jump, j2+2, iContinue, j2+2); VdbeCoverage(v);
|
| - sqlite3VdbeJumpHere(v, j1);
|
| + sqlite3VdbeAddOp3(v, OP_Jump, addr2+2, iContinue, addr2+2); VdbeCoverage(v);
|
| + sqlite3VdbeJumpHere(v, addr1);
|
| sqlite3VdbeAddOp3(v, OP_Copy, pIn->iSdst, regPrev+1, pIn->nSdst-1);
|
| sqlite3VdbeAddOp2(v, OP_Integer, 1, regPrev);
|
| }
|
| @@ -2516,15 +2597,14 @@ static int generateOutputSubroutine(
|
| */
|
| codeOffset(v, p->iOffset, iContinue);
|
|
|
| + assert( pDest->eDest!=SRT_Exists );
|
| + assert( pDest->eDest!=SRT_Table );
|
| switch( pDest->eDest ){
|
| /* Store the result as data using a unique key.
|
| */
|
| - case SRT_Table:
|
| case SRT_EphemTab: {
|
| int r1 = sqlite3GetTempReg(pParse);
|
| int r2 = sqlite3GetTempReg(pParse);
|
| - testcase( pDest->eDest==SRT_Table );
|
| - testcase( pDest->eDest==SRT_EphemTab );
|
| sqlite3VdbeAddOp3(v, OP_MakeRecord, pIn->iSdst, pIn->nSdst, r1);
|
| sqlite3VdbeAddOp2(v, OP_NewRowid, pDest->iSDParm, r2);
|
| sqlite3VdbeAddOp3(v, OP_Insert, pDest->iSDParm, r1, r2);
|
| @@ -2541,7 +2621,7 @@ static int generateOutputSubroutine(
|
| */
|
| case SRT_Set: {
|
| int r1;
|
| - assert( pIn->nSdst==1 );
|
| + assert( pIn->nSdst==1 || pParse->nErr>0 );
|
| pDest->affSdst =
|
| sqlite3CompareAffinity(p->pEList->a[0].pExpr, pDest->affSdst);
|
| r1 = sqlite3GetTempReg(pParse);
|
| @@ -2552,22 +2632,12 @@ static int generateOutputSubroutine(
|
| break;
|
| }
|
|
|
| -#if 0 /* Never occurs on an ORDER BY query */
|
| - /* If any row exist in the result set, record that fact and abort.
|
| - */
|
| - case SRT_Exists: {
|
| - sqlite3VdbeAddOp2(v, OP_Integer, 1, pDest->iSDParm);
|
| - /* The LIMIT clause will terminate the loop for us */
|
| - break;
|
| - }
|
| -#endif
|
| -
|
| /* If this is a scalar select that is part of an expression, then
|
| ** store the results in the appropriate memory cell and break out
|
| ** of the scan loop.
|
| */
|
| case SRT_Mem: {
|
| - assert( pIn->nSdst==1 );
|
| + assert( pIn->nSdst==1 || pParse->nErr>0 ); testcase( pIn->nSdst!=1 );
|
| sqlite3ExprCodeMove(pParse, pIn->iSdst, pDest->iSDParm, 1);
|
| /* The LIMIT clause will jump out of the loop for us */
|
| break;
|
| @@ -2582,7 +2652,7 @@ static int generateOutputSubroutine(
|
| pDest->iSdst = sqlite3GetTempRange(pParse, pIn->nSdst);
|
| pDest->nSdst = pIn->nSdst;
|
| }
|
| - sqlite3ExprCodeMove(pParse, pIn->iSdst, pDest->iSdst, pDest->nSdst);
|
| + sqlite3ExprCodeMove(pParse, pIn->iSdst, pDest->iSdst, pIn->nSdst);
|
| sqlite3VdbeAddOp1(v, OP_Yield, pDest->iSDParm);
|
| break;
|
| }
|
| @@ -2606,7 +2676,7 @@ static int generateOutputSubroutine(
|
| /* Jump to the end of the loop if the LIMIT is reached.
|
| */
|
| if( p->iLimit ){
|
| - sqlite3VdbeAddOp3(v, OP_IfZero, p->iLimit, iBreak, -1); VdbeCoverage(v);
|
| + sqlite3VdbeAddOp2(v, OP_DecrJumpZero, p->iLimit, iBreak); VdbeCoverage(v);
|
| }
|
|
|
| /* Generate the subroutine return
|
| @@ -2734,7 +2804,7 @@ static int multiSelectOrderBy(
|
| 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; /* Jump instructions that get retargetted */
|
| + int addr1; /* Jump instructions that get retargetted */
|
| int op; /* One of TK_ALL, TK_UNION, TK_EXCEPT, TK_INTERSECT */
|
| KeyInfo *pKeyDup = 0; /* Comparison information for duplicate removal */
|
| KeyInfo *pKeyMerge; /* Comparison information for merging rows */
|
| @@ -2798,8 +2868,8 @@ static int multiSelectOrderBy(
|
| if( aPermute ){
|
| struct ExprList_item *pItem;
|
| for(i=0, pItem=pOrderBy->a; i<nOrderBy; i++, pItem++){
|
| - assert( pItem->u.x.iOrderByCol>0
|
| - && pItem->u.x.iOrderByCol<=p->pEList->nExpr );
|
| + assert( pItem->u.x.iOrderByCol>0 );
|
| + assert( pItem->u.x.iOrderByCol<=p->pEList->nExpr );
|
| aPermute[i] = pItem->u.x.iOrderByCol - 1;
|
| }
|
| pKeyMerge = multiSelectOrderByKeyInfo(pParse, p, 1);
|
| @@ -2870,19 +2940,19 @@ static int multiSelectOrderBy(
|
| ** left of the compound operator - the "A" select.
|
| */
|
| addrSelectA = sqlite3VdbeCurrentAddr(v) + 1;
|
| - j1 = sqlite3VdbeAddOp3(v, OP_InitCoroutine, regAddrA, 0, addrSelectA);
|
| + addr1 = sqlite3VdbeAddOp3(v, OP_InitCoroutine, regAddrA, 0, addrSelectA);
|
| VdbeComment((v, "left SELECT"));
|
| pPrior->iLimit = regLimitA;
|
| explainSetInteger(iSub1, pParse->iNextSelectId);
|
| sqlite3Select(pParse, pPrior, &destA);
|
| sqlite3VdbeAddOp1(v, OP_EndCoroutine, regAddrA);
|
| - sqlite3VdbeJumpHere(v, j1);
|
| + sqlite3VdbeJumpHere(v, addr1);
|
|
|
| /* Generate a coroutine to evaluate the SELECT statement on
|
| ** the right - the "B" select
|
| */
|
| addrSelectB = sqlite3VdbeCurrentAddr(v) + 1;
|
| - j1 = sqlite3VdbeAddOp3(v, OP_InitCoroutine, regAddrB, 0, addrSelectB);
|
| + addr1 = sqlite3VdbeAddOp3(v, OP_InitCoroutine, regAddrB, 0, addrSelectB);
|
| VdbeComment((v, "right SELECT"));
|
| savedLimit = p->iLimit;
|
| savedOffset = p->iOffset;
|
| @@ -2923,7 +2993,7 @@ static int multiSelectOrderBy(
|
| addrEofA = sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB);
|
| addrEofA_noB = sqlite3VdbeAddOp2(v, OP_Yield, regAddrB, labelEnd);
|
| VdbeCoverage(v);
|
| - sqlite3VdbeAddOp2(v, OP_Goto, 0, addrEofA);
|
| + sqlite3VdbeGoto(v, addrEofA);
|
| p->nSelectRow += pPrior->nSelectRow;
|
| }
|
|
|
| @@ -2937,7 +3007,7 @@ static int multiSelectOrderBy(
|
| VdbeNoopComment((v, "eof-B subroutine"));
|
| addrEofB = sqlite3VdbeAddOp2(v, OP_Gosub, regOutA, addrOutA);
|
| sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, labelEnd); VdbeCoverage(v);
|
| - sqlite3VdbeAddOp2(v, OP_Goto, 0, addrEofB);
|
| + sqlite3VdbeGoto(v, addrEofB);
|
| }
|
|
|
| /* Generate code to handle the case of A<B
|
| @@ -2945,7 +3015,7 @@ static int multiSelectOrderBy(
|
| VdbeNoopComment((v, "A-lt-B subroutine"));
|
| addrAltB = sqlite3VdbeAddOp2(v, OP_Gosub, regOutA, addrOutA);
|
| sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, addrEofA); VdbeCoverage(v);
|
| - sqlite3VdbeAddOp2(v, OP_Goto, 0, labelCmpr);
|
| + sqlite3VdbeGoto(v, labelCmpr);
|
|
|
| /* Generate code to handle the case of A==B
|
| */
|
| @@ -2958,7 +3028,7 @@ static int multiSelectOrderBy(
|
| VdbeNoopComment((v, "A-eq-B subroutine"));
|
| addrAeqB =
|
| sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, addrEofA); VdbeCoverage(v);
|
| - sqlite3VdbeAddOp2(v, OP_Goto, 0, labelCmpr);
|
| + sqlite3VdbeGoto(v, labelCmpr);
|
| }
|
|
|
| /* Generate code to handle the case of A>B
|
| @@ -2969,11 +3039,11 @@ static int multiSelectOrderBy(
|
| sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB);
|
| }
|
| sqlite3VdbeAddOp2(v, OP_Yield, regAddrB, addrEofB); VdbeCoverage(v);
|
| - sqlite3VdbeAddOp2(v, OP_Goto, 0, labelCmpr);
|
| + sqlite3VdbeGoto(v, labelCmpr);
|
|
|
| /* This code runs once to initialize everything.
|
| */
|
| - sqlite3VdbeJumpHere(v, j1);
|
| + sqlite3VdbeJumpHere(v, addr1);
|
| sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, addrEofA_noB); VdbeCoverage(v);
|
| sqlite3VdbeAddOp2(v, OP_Yield, regAddrB, addrEofB); VdbeCoverage(v);
|
|
|
| @@ -2995,7 +3065,7 @@ static int multiSelectOrderBy(
|
| if( pDest->eDest==SRT_Output ){
|
| Select *pFirst = pPrior;
|
| while( pFirst->pPrior ) pFirst = pFirst->pPrior;
|
| - generateColumnNames(pParse, 0, pFirst->pEList);
|
| + generateColumnNames(pParse, pFirst->pSrc, pFirst->pEList);
|
| }
|
|
|
| /* Reassembly the compound query so that it will be freed correctly
|
| @@ -3009,14 +3079,14 @@ static int multiSelectOrderBy(
|
| /*** TBD: Insert subroutine calls to close cursors on incomplete
|
| **** subqueries ****/
|
| explainComposite(pParse, p->op, iSub1, iSub2, 0);
|
| - return SQLITE_OK;
|
| + return pParse->nErr!=0;
|
| }
|
| #endif
|
|
|
| #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
|
| /* Forward Declarations */
|
| static void substExprList(sqlite3*, ExprList*, int, ExprList*);
|
| -static void substSelect(sqlite3*, Select *, int, ExprList *);
|
| +static void substSelect(sqlite3*, Select *, int, ExprList*, int);
|
|
|
| /*
|
| ** Scan through the expression pExpr. Replace every reference to
|
| @@ -3053,7 +3123,7 @@ static Expr *substExpr(
|
| pExpr->pLeft = substExpr(db, pExpr->pLeft, iTable, pEList);
|
| pExpr->pRight = substExpr(db, pExpr->pRight, iTable, pEList);
|
| if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
| - substSelect(db, pExpr->x.pSelect, iTable, pEList);
|
| + substSelect(db, pExpr->x.pSelect, iTable, pEList, 1);
|
| }else{
|
| substExprList(db, pExpr->x.pList, iTable, pEList);
|
| }
|
| @@ -3076,25 +3146,28 @@ static void substSelect(
|
| sqlite3 *db, /* Report malloc errors here */
|
| Select *p, /* SELECT statement in which to make substitutions */
|
| int iTable, /* Table to be replaced */
|
| - ExprList *pEList /* Substitute values */
|
| + ExprList *pEList, /* Substitute values */
|
| + int doPrior /* Do substitutes on p->pPrior too */
|
| ){
|
| SrcList *pSrc;
|
| struct SrcList_item *pItem;
|
| int i;
|
| if( !p ) return;
|
| - substExprList(db, p->pEList, iTable, pEList);
|
| - substExprList(db, p->pGroupBy, iTable, pEList);
|
| - substExprList(db, p->pOrderBy, iTable, pEList);
|
| - p->pHaving = substExpr(db, p->pHaving, iTable, pEList);
|
| - p->pWhere = substExpr(db, p->pWhere, iTable, pEList);
|
| - substSelect(db, p->pPrior, iTable, pEList);
|
| - pSrc = p->pSrc;
|
| - assert( pSrc ); /* Even for (SELECT 1) we have: pSrc!=0 but pSrc->nSrc==0 */
|
| - if( ALWAYS(pSrc) ){
|
| + do{
|
| + substExprList(db, p->pEList, iTable, pEList);
|
| + substExprList(db, p->pGroupBy, iTable, pEList);
|
| + substExprList(db, p->pOrderBy, iTable, pEList);
|
| + p->pHaving = substExpr(db, p->pHaving, iTable, pEList);
|
| + p->pWhere = substExpr(db, p->pWhere, iTable, pEList);
|
| + pSrc = p->pSrc;
|
| + assert( pSrc!=0 );
|
| for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){
|
| - substSelect(db, pItem->pSelect, iTable, pEList);
|
| + substSelect(db, pItem->pSelect, iTable, pEList, 1);
|
| + if( pItem->fg.isTabFunc ){
|
| + substExprList(db, pItem->u1.pFuncArg, iTable, pEList);
|
| + }
|
| }
|
| - }
|
| + }while( doPrior && (p = p->pPrior)!=0 );
|
| }
|
| #endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
|
|
|
| @@ -3129,7 +3202,10 @@ static void substSelect(
|
| **
|
| ** (1) The subquery and the outer query do not both use aggregates.
|
| **
|
| -** (2) The subquery is not an aggregate or the outer query is not a join.
|
| +** (2) The subquery is not an aggregate or (2a) the outer query is not a join
|
| +** and (2b) the outer query does not use subqueries other than the one
|
| +** FROM-clause subquery that is a candidate for flattening. (2b is
|
| +** due to ticket [2f7170d73bf9abf80] from 2015-02-09.)
|
| **
|
| ** (3) The subquery is not the right operand of a left outer join
|
| ** (Originally ticket #306. Strengthened by ticket #3300)
|
| @@ -3155,8 +3231,8 @@ static void substSelect(
|
| **
|
| ** (**) Restriction (10) was removed from the code on 2005-02-05 but we
|
| ** accidently carried the comment forward until 2014-09-15. Original
|
| -** text: "The subquery does not use aggregates or the outer query does not
|
| -** use LIMIT."
|
| +** text: "The subquery does not use aggregates or the outer query
|
| +** does not use LIMIT."
|
| **
|
| ** (11) The subquery and the outer query do not both have ORDER BY clauses.
|
| **
|
| @@ -3243,7 +3319,7 @@ static int flattenSubquery(
|
| int subqueryIsAgg /* True if the subquery uses aggregate functions */
|
| ){
|
| const char *zSavedAuthContext = pParse->zAuthContext;
|
| - Select *pParent;
|
| + Select *pParent; /* Current UNION ALL term of the other query */
|
| Select *pSub; /* The inner query or "subquery" */
|
| Select *pSub1; /* Pointer to the rightmost select in sub-query */
|
| SrcList *pSrc; /* The FROM clause of the outer query */
|
| @@ -3266,8 +3342,17 @@ static int flattenSubquery(
|
| iParent = pSubitem->iCursor;
|
| pSub = pSubitem->pSelect;
|
| assert( pSub!=0 );
|
| - if( isAgg && subqueryIsAgg ) return 0; /* Restriction (1) */
|
| - if( subqueryIsAgg && pSrc->nSrc>1 ) return 0; /* Restriction (2) */
|
| + if( subqueryIsAgg ){
|
| + if( isAgg ) return 0; /* Restriction (1) */
|
| + if( pSrc->nSrc>1 ) return 0; /* Restriction (2a) */
|
| + if( (p->pWhere && ExprHasProperty(p->pWhere,EP_Subquery))
|
| + || (sqlite3ExprListFlags(p->pEList) & EP_Subquery)!=0
|
| + || (sqlite3ExprListFlags(p->pOrderBy) & EP_Subquery)!=0
|
| + ){
|
| + return 0; /* Restriction (2b) */
|
| + }
|
| + }
|
| +
|
| pSubSrc = pSub->pSrc;
|
| assert( pSubSrc );
|
| /* Prior to version 3.1.2, when LIMIT and OFFSET had to be simple constants,
|
| @@ -3337,7 +3422,7 @@ static int flattenSubquery(
|
| ** is fraught with danger. Best to avoid the whole thing. If the
|
| ** subquery is the right term of a LEFT JOIN, then do not flatten.
|
| */
|
| - if( (pSubitem->jointype & JT_OUTER)!=0 ){
|
| + if( (pSubitem->fg.jointype & JT_OUTER)!=0 ){
|
| return 0;
|
| }
|
|
|
| @@ -3357,10 +3442,10 @@ static int flattenSubquery(
|
| testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct );
|
| testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Aggregate );
|
| assert( pSub->pSrc!=0 );
|
| + assert( pSub->pEList->nExpr==pSub1->pEList->nExpr );
|
| if( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))!=0
|
| || (pSub1->pPrior && pSub1->op!=TK_ALL)
|
| || pSub1->pSrc->nSrc<1
|
| - || pSub->pEList->nExpr!=pSub1->pEList->nExpr
|
| ){
|
| return 0;
|
| }
|
| @@ -3508,7 +3593,7 @@ static int flattenSubquery(
|
|
|
| if( pSrc ){
|
| assert( pParent==p ); /* First time through the loop */
|
| - jointype = pSubitem->jointype;
|
| + jointype = pSubitem->fg.jointype;
|
| }else{
|
| assert( pParent!=p ); /* 2nd and subsequent times through the loop */
|
| pSrc = pParent->pSrc = sqlite3SrcListAppend(db, 0, 0, 0);
|
| @@ -3529,9 +3614,9 @@ static int flattenSubquery(
|
| **
|
| ** The outer query has 3 slots in its FROM clause. One slot of the
|
| ** outer query (the middle slot) is used by the subquery. The next
|
| - ** block of code will expand the out query to 4 slots. The middle
|
| - ** slot is expanded to two slots in order to make space for the
|
| - ** two elements in the FROM clause of the subquery.
|
| + ** block of code will expand the outer query FROM clause to 4 slots.
|
| + ** The middle slot is expanded to two slots in order to make space
|
| + ** for the two elements in the FROM clause of the subquery.
|
| */
|
| if( nSubSrc>1 ){
|
| pParent->pSrc = pSrc = sqlite3SrcListEnlarge(db, pSrc, nSubSrc-1,iFrom+1);
|
| @@ -3545,10 +3630,11 @@ static int flattenSubquery(
|
| */
|
| for(i=0; i<nSubSrc; i++){
|
| sqlite3IdListDelete(db, pSrc->a[i+iFrom].pUsing);
|
| + assert( pSrc->a[i+iFrom].fg.isTabFunc==0 );
|
| pSrc->a[i+iFrom] = pSubSrc->a[i];
|
| memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i]));
|
| }
|
| - pSrc->a[iFrom].jointype = jointype;
|
| + pSrc->a[iFrom].fg.jointype = jointype;
|
|
|
| /* Now begin substituting subquery result set expressions for
|
| ** references to the iParent in the outer query.
|
| @@ -3570,11 +3656,6 @@ static int flattenSubquery(
|
| pList->a[i].zName = zName;
|
| }
|
| }
|
| - substExprList(db, pParent->pEList, iParent, pSub->pEList);
|
| - if( isAgg ){
|
| - substExprList(db, pParent->pGroupBy, iParent, pSub->pEList);
|
| - pParent->pHaving = substExpr(db, pParent->pHaving, iParent, pSub->pEList);
|
| - }
|
| if( pSub->pOrderBy ){
|
| /* At this point, any non-zero iOrderByCol values indicate that the
|
| ** ORDER BY column expression is identical to the iOrderByCol'th
|
| @@ -3594,27 +3675,20 @@ static int flattenSubquery(
|
| assert( pSub->pPrior==0 );
|
| pParent->pOrderBy = pOrderBy;
|
| pSub->pOrderBy = 0;
|
| - }else if( pParent->pOrderBy ){
|
| - substExprList(db, pParent->pOrderBy, iParent, pSub->pEList);
|
| - }
|
| - if( pSub->pWhere ){
|
| - pWhere = sqlite3ExprDup(db, pSub->pWhere, 0);
|
| - }else{
|
| - pWhere = 0;
|
| }
|
| + pWhere = sqlite3ExprDup(db, pSub->pWhere, 0);
|
| if( subqueryIsAgg ){
|
| assert( pParent->pHaving==0 );
|
| pParent->pHaving = pParent->pWhere;
|
| pParent->pWhere = pWhere;
|
| - pParent->pHaving = substExpr(db, pParent->pHaving, iParent, pSub->pEList);
|
| pParent->pHaving = sqlite3ExprAnd(db, pParent->pHaving,
|
| sqlite3ExprDup(db, pSub->pHaving, 0));
|
| assert( pParent->pGroupBy==0 );
|
| pParent->pGroupBy = sqlite3ExprListDup(db, pSub->pGroupBy, 0);
|
| }else{
|
| - pParent->pWhere = substExpr(db, pParent->pWhere, iParent, pSub->pEList);
|
| pParent->pWhere = sqlite3ExprAnd(db, pParent->pWhere, pWhere);
|
| }
|
| + substSelect(db, pParent, iParent, pSub->pEList, 0);
|
|
|
| /* The flattened query is distinct if either the inner or the
|
| ** outer query is distinct.
|
| @@ -3640,7 +3714,7 @@ static int flattenSubquery(
|
|
|
| #if SELECTTRACE_ENABLED
|
| if( sqlite3SelectTrace & 0x100 ){
|
| - sqlite3DebugPrintf("After flattening:\n");
|
| + SELECTTRACE(0x100,pParse,p,("After flattening:\n"));
|
| sqlite3TreeViewSelect(0, p, 0);
|
| }
|
| #endif
|
| @@ -3649,6 +3723,77 @@ static int flattenSubquery(
|
| }
|
| #endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
|
|
|
| +
|
| +
|
| +#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
|
| +/*
|
| +** Make copies of relevant WHERE clause terms of the outer query into
|
| +** the WHERE clause of subquery. Example:
|
| +**
|
| +** SELECT * FROM (SELECT a AS x, c-d AS y FROM t1) WHERE x=5 AND y=10;
|
| +**
|
| +** Transformed into:
|
| +**
|
| +** SELECT * FROM (SELECT a AS x, c-d AS y FROM t1 WHERE a=5 AND c-d=10)
|
| +** WHERE x=5 AND y=10;
|
| +**
|
| +** The hope is that the terms added to the inner query will make it more
|
| +** efficient.
|
| +**
|
| +** Do not attempt this optimization if:
|
| +**
|
| +** (1) The inner query is an aggregate. (In that case, we'd really want
|
| +** to copy the outer WHERE-clause terms onto the HAVING clause of the
|
| +** inner query. But they probably won't help there so do not bother.)
|
| +**
|
| +** (2) The inner query is the recursive part of a common table expression.
|
| +**
|
| +** (3) The inner query has a LIMIT clause (since the changes to the WHERE
|
| +** close would change the meaning of the LIMIT).
|
| +**
|
| +** (4) The inner query is the right operand of a LEFT JOIN. (The caller
|
| +** enforces this restriction since this routine does not have enough
|
| +** information to know.)
|
| +**
|
| +** (5) The WHERE clause expression originates in the ON or USING clause
|
| +** of a LEFT JOIN.
|
| +**
|
| +** Return 0 if no changes are made and non-zero if one or more WHERE clause
|
| +** terms are duplicated into the subquery.
|
| +*/
|
| +static int pushDownWhereTerms(
|
| + sqlite3 *db, /* The database connection (for malloc()) */
|
| + Select *pSubq, /* The subquery whose WHERE clause is to be augmented */
|
| + Expr *pWhere, /* The WHERE clause of the outer query */
|
| + int iCursor /* Cursor number of the subquery */
|
| +){
|
| + Expr *pNew;
|
| + int nChng = 0;
|
| + if( pWhere==0 ) return 0;
|
| + if( (pSubq->selFlags & (SF_Aggregate|SF_Recursive))!=0 ){
|
| + return 0; /* restrictions (1) and (2) */
|
| + }
|
| + if( pSubq->pLimit!=0 ){
|
| + return 0; /* restriction (3) */
|
| + }
|
| + while( pWhere->op==TK_AND ){
|
| + nChng += pushDownWhereTerms(db, pSubq, pWhere->pRight, iCursor);
|
| + pWhere = pWhere->pLeft;
|
| + }
|
| + if( ExprHasProperty(pWhere,EP_FromJoin) ) return 0; /* restriction 5 */
|
| + if( sqlite3ExprIsTableConstant(pWhere, iCursor) ){
|
| + nChng++;
|
| + while( pSubq ){
|
| + pNew = sqlite3ExprDup(db, pWhere, 0);
|
| + pNew = substExpr(db, pNew, iCursor, pSubq->pEList);
|
| + pSubq->pWhere = sqlite3ExprAnd(db, pSubq->pWhere, pNew);
|
| + pSubq = pSubq->pPrior;
|
| + }
|
| + }
|
| + return nChng;
|
| +}
|
| +#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
|
| +
|
| /*
|
| ** Based on the contents of the AggInfo structure indicated by the first
|
| ** argument, this function checks if the following are true:
|
| @@ -3732,20 +3877,20 @@ static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){
|
| ** pFrom->pIndex and return SQLITE_OK.
|
| */
|
| int sqlite3IndexedByLookup(Parse *pParse, struct SrcList_item *pFrom){
|
| - if( pFrom->pTab && pFrom->zIndex ){
|
| + if( pFrom->pTab && pFrom->fg.isIndexedBy ){
|
| Table *pTab = pFrom->pTab;
|
| - char *zIndex = pFrom->zIndex;
|
| + char *zIndexedBy = pFrom->u1.zIndexedBy;
|
| Index *pIdx;
|
| for(pIdx=pTab->pIndex;
|
| - pIdx && sqlite3StrICmp(pIdx->zName, zIndex);
|
| + pIdx && sqlite3StrICmp(pIdx->zName, zIndexedBy);
|
| pIdx=pIdx->pNext
|
| );
|
| if( !pIdx ){
|
| - sqlite3ErrorMsg(pParse, "no such index: %s", zIndex, 0);
|
| + sqlite3ErrorMsg(pParse, "no such index: %s", zIndexedBy, 0);
|
| pParse->checkSchema = 1;
|
| return SQLITE_ERROR;
|
| }
|
| - pFrom->pIndex = pIdx;
|
| + pFrom->pIBIndex = pIdx;
|
| }
|
| return SQLITE_OK;
|
| }
|
| @@ -3801,7 +3946,7 @@ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){
|
| if( pNewSrc==0 ) return WRC_Abort;
|
| *pNew = *p;
|
| p->pSrc = pNewSrc;
|
| - p->pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db, TK_ALL, 0));
|
| + p->pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db, TK_ASTERISK, 0));
|
| p->op = TK_SELECT;
|
| p->pWhere = 0;
|
| pNew->pGroupBy = 0;
|
| @@ -3809,7 +3954,10 @@ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){
|
| pNew->pOrderBy = 0;
|
| p->pPrior = 0;
|
| p->pNext = 0;
|
| + p->pWith = 0;
|
| p->selFlags &= ~SF_Compound;
|
| + assert( (p->selFlags & SF_Converted)==0 );
|
| + p->selFlags |= SF_Converted;
|
| assert( pNew->pPrior!=0 );
|
| pNew->pPrior->pNext = pNew;
|
| pNew->pLimit = 0;
|
| @@ -3817,6 +3965,19 @@ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){
|
| return WRC_Continue;
|
| }
|
|
|
| +/*
|
| +** Check to see if the FROM clause term pFrom has table-valued function
|
| +** arguments. If it does, leave an error message in pParse and return
|
| +** non-zero, since pFrom is not allowed to be a table-valued function.
|
| +*/
|
| +static int cannotBeFunction(Parse *pParse, struct SrcList_item *pFrom){
|
| + if( pFrom->fg.isTabFunc ){
|
| + sqlite3ErrorMsg(pParse, "'%s' is not a function", pFrom->zName);
|
| + return 1;
|
| + }
|
| + return 0;
|
| +}
|
| +
|
| #ifndef SQLITE_OMIT_CTE
|
| /*
|
| ** Argument pWith (which may be NULL) points to a linked list of nested
|
| @@ -3829,7 +3990,7 @@ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){
|
| ** object that the returned CTE belongs to.
|
| */
|
| static struct Cte *searchWith(
|
| - With *pWith, /* Current outermost WITH clause */
|
| + With *pWith, /* Current innermost WITH clause */
|
| struct SrcList_item *pItem, /* FROM clause element to resolve */
|
| With **ppContext /* OUT: WITH clause return value belongs to */
|
| ){
|
| @@ -3860,11 +4021,12 @@ static struct Cte *searchWith(
|
| ** statement with which it is associated.
|
| */
|
| void sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){
|
| - assert( bFree==0 || pParse->pWith==0 );
|
| + assert( bFree==0 || (pParse->pWith==0 && pParse->pWithToFree==0) );
|
| if( pWith ){
|
| + assert( pParse->pWith!=pWith );
|
| pWith->pOuter = pParse->pWith;
|
| pParse->pWith = pWith;
|
| - pParse->bFreeWith = bFree;
|
| + if( bFree ) pParse->pWithToFree = pWith;
|
| }
|
| }
|
|
|
| @@ -3903,14 +4065,15 @@ static int withExpand(
|
| int bMayRecursive; /* True if compound joined by UNION [ALL] */
|
| With *pSavedWith; /* Initial value of pParse->pWith */
|
|
|
| - /* If pCte->zErr is non-NULL at this point, then this is an illegal
|
| + /* If pCte->zCteErr is non-NULL at this point, then this is an illegal
|
| ** recursive reference to CTE pCte. Leave an error in pParse and return
|
| - ** early. If pCte->zErr is NULL, then this is not a recursive reference.
|
| + ** early. If pCte->zCteErr is NULL, then this is not a recursive reference.
|
| ** In this case, proceed. */
|
| - if( pCte->zErr ){
|
| - sqlite3ErrorMsg(pParse, pCte->zErr, pCte->zName);
|
| + if( pCte->zCteErr ){
|
| + sqlite3ErrorMsg(pParse, pCte->zCteErr, pCte->zName);
|
| return SQLITE_ERROR;
|
| }
|
| + if( cannotBeFunction(pParse, pFrom) ) return SQLITE_ERROR;
|
|
|
| assert( pFrom->pTab==0 );
|
| pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table));
|
| @@ -3919,7 +4082,7 @@ static int withExpand(
|
| pTab->zName = sqlite3DbStrDup(db, pCte->zName);
|
| pTab->iPKey = -1;
|
| pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
|
| - pTab->tabFlags |= TF_Ephemeral;
|
| + pTab->tabFlags |= TF_Ephemeral | TF_NoVisibleRowid;
|
| pFrom->pSelect = sqlite3SelectDup(db, pCte->pSelect, 0);
|
| if( db->mallocFailed ) return SQLITE_NOMEM;
|
| assert( pFrom->pSelect );
|
| @@ -3937,7 +4100,7 @@ static int withExpand(
|
| && 0==sqlite3StrICmp(pItem->zName, pCte->zName)
|
| ){
|
| pItem->pTab = pTab;
|
| - pItem->isRecursive = 1;
|
| + pItem->fg.isRecursive = 1;
|
| pTab->nRef++;
|
| pSel->selFlags |= SF_Recursive;
|
| }
|
| @@ -3953,15 +4116,16 @@ static int withExpand(
|
| }
|
| assert( pTab->nRef==1 || ((pSel->selFlags&SF_Recursive) && pTab->nRef==2 ));
|
|
|
| - pCte->zErr = "circular reference: %s";
|
| + pCte->zCteErr = "circular reference: %s";
|
| pSavedWith = pParse->pWith;
|
| pParse->pWith = pWith;
|
| sqlite3WalkSelect(pWalker, bMayRecursive ? pSel->pPrior : pSel);
|
| + pParse->pWith = pWith;
|
|
|
| for(pLeft=pSel; pLeft->pPrior; pLeft=pLeft->pPrior);
|
| pEList = pLeft->pEList;
|
| if( pCte->pCols ){
|
| - if( pEList->nExpr!=pCte->pCols->nExpr ){
|
| + if( pEList && pEList->nExpr!=pCte->pCols->nExpr ){
|
| sqlite3ErrorMsg(pParse, "table %s has %d values for %d columns",
|
| pCte->zName, pEList->nExpr, pCte->pCols->nExpr
|
| );
|
| @@ -3971,16 +4135,16 @@ static int withExpand(
|
| pEList = pCte->pCols;
|
| }
|
|
|
| - selectColumnsFromExprList(pParse, pEList, &pTab->nCol, &pTab->aCol);
|
| + sqlite3ColumnsFromExprList(pParse, pEList, &pTab->nCol, &pTab->aCol);
|
| if( bMayRecursive ){
|
| if( pSel->selFlags & SF_Recursive ){
|
| - pCte->zErr = "multiple recursive references: %s";
|
| + pCte->zCteErr = "multiple recursive references: %s";
|
| }else{
|
| - pCte->zErr = "recursive reference in a subquery: %s";
|
| + pCte->zCteErr = "recursive reference in a subquery: %s";
|
| }
|
| sqlite3WalkSelect(pWalker, pSel);
|
| }
|
| - pCte->zErr = 0;
|
| + pCte->zCteErr = 0;
|
| pParse->pWith = pSavedWith;
|
| }
|
|
|
| @@ -4052,7 +4216,9 @@ static int selectExpander(Walker *pWalker, Select *p){
|
| }
|
| pTabList = p->pSrc;
|
| pEList = p->pEList;
|
| - sqlite3WithPush(pParse, findRightmost(p)->pWith, 0);
|
| + if( pWalker->xSelectCallback2==selectPopWith ){
|
| + sqlite3WithPush(pParse, findRightmost(p)->pWith, 0);
|
| + }
|
|
|
| /* Make sure cursor numbers have been assigned to all entries in
|
| ** the FROM clause of the SELECT statement.
|
| @@ -4065,17 +4231,9 @@ static int selectExpander(Walker *pWalker, Select *p){
|
| */
|
| for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
|
| Table *pTab;
|
| - assert( pFrom->isRecursive==0 || pFrom->pTab );
|
| - if( pFrom->isRecursive ) continue;
|
| - if( pFrom->pTab!=0 ){
|
| - /* This statement has already been prepared. There is no need
|
| - ** to go further. */
|
| - assert( i==0 );
|
| -#ifndef SQLITE_OMIT_CTE
|
| - selectPopWith(pWalker, p);
|
| -#endif
|
| - return WRC_Prune;
|
| - }
|
| + assert( pFrom->fg.isRecursive==0 || pFrom->pTab!=0 );
|
| + if( pFrom->fg.isRecursive ) continue;
|
| + assert( pFrom->pTab==0 );
|
| #ifndef SQLITE_OMIT_CTE
|
| if( withExpand(pWalker, pFrom) ) return WRC_Abort;
|
| if( pFrom->pTab ) {} else
|
| @@ -4086,13 +4244,13 @@ static int selectExpander(Walker *pWalker, Select *p){
|
| /* A sub-query in the FROM clause of a SELECT */
|
| assert( pSel!=0 );
|
| assert( pFrom->pTab==0 );
|
| - sqlite3WalkSelect(pWalker, pSel);
|
| + if( sqlite3WalkSelect(pWalker, pSel) ) return WRC_Abort;
|
| pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table));
|
| if( pTab==0 ) return WRC_Abort;
|
| pTab->nRef = 1;
|
| pTab->zName = sqlite3MPrintf(db, "sqlite_sq_%p", (void*)pTab);
|
| while( pSel->pPrior ){ pSel = pSel->pPrior; }
|
| - selectColumnsFromExprList(pParse, pSel->pEList, &pTab->nCol, &pTab->aCol);
|
| + sqlite3ColumnsFromExprList(pParse, pSel->pEList,&pTab->nCol,&pTab->aCol);
|
| pTab->iPKey = -1;
|
| pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
|
| pTab->tabFlags |= TF_Ephemeral;
|
| @@ -4109,14 +4267,20 @@ static int selectExpander(Walker *pWalker, Select *p){
|
| return WRC_Abort;
|
| }
|
| pTab->nRef++;
|
| + if( !IsVirtual(pTab) && cannotBeFunction(pParse, pFrom) ){
|
| + return WRC_Abort;
|
| + }
|
| #if !defined(SQLITE_OMIT_VIEW) || !defined (SQLITE_OMIT_VIRTUALTABLE)
|
| - if( pTab->pSelect || IsVirtual(pTab) ){
|
| - /* We reach here if the named table is a really a view */
|
| + if( IsVirtual(pTab) || pTab->pSelect ){
|
| + i16 nCol;
|
| if( sqlite3ViewGetColumnNames(pParse, pTab) ) return WRC_Abort;
|
| assert( pFrom->pSelect==0 );
|
| pFrom->pSelect = sqlite3SelectDup(db, pTab->pSelect, 0);
|
| sqlite3SelectSetName(pFrom->pSelect, pTab->zName);
|
| + nCol = pTab->nCol;
|
| + pTab->nCol = -1;
|
| sqlite3WalkSelect(pWalker, pFrom->pSelect);
|
| + pTab->nCol = nCol;
|
| }
|
| #endif
|
| }
|
| @@ -4136,19 +4300,20 @@ static int selectExpander(Walker *pWalker, Select *p){
|
| /* For every "*" that occurs in the column list, insert the names of
|
| ** all columns in all tables. And for every TABLE.* insert the names
|
| ** of all columns in TABLE. The parser inserted a special expression
|
| - ** with the TK_ALL operator for each "*" that it found in the column list.
|
| - ** The following code just has to locate the TK_ALL expressions and expand
|
| - ** each one to the list of all columns in all tables.
|
| + ** with the TK_ASTERISK operator for each "*" that it found in the column
|
| + ** list. The following code just has to locate the TK_ASTERISK
|
| + ** expressions and expand each one to the list of all columns in
|
| + ** all tables.
|
| **
|
| ** The first loop just checks to see if there are any "*" operators
|
| ** that need expanding.
|
| */
|
| for(k=0; k<pEList->nExpr; k++){
|
| pE = pEList->a[k].pExpr;
|
| - if( pE->op==TK_ALL ) break;
|
| + if( pE->op==TK_ASTERISK ) break;
|
| assert( pE->op!=TK_DOT || pE->pRight!=0 );
|
| assert( pE->op!=TK_DOT || (pE->pLeft!=0 && pE->pLeft->op==TK_ID) );
|
| - if( pE->op==TK_DOT && pE->pRight->op==TK_ALL ) break;
|
| + if( pE->op==TK_DOT && pE->pRight->op==TK_ASTERISK ) break;
|
| }
|
| if( k<pEList->nExpr ){
|
| /*
|
| @@ -4162,18 +4327,13 @@ static int selectExpander(Walker *pWalker, Select *p){
|
| int longNames = (flags & SQLITE_FullColNames)!=0
|
| && (flags & SQLITE_ShortColNames)==0;
|
|
|
| - /* When processing FROM-clause subqueries, it is always the case
|
| - ** that full_column_names=OFF and short_column_names=ON. The
|
| - ** sqlite3ResultSetOfSelect() routine makes it so. */
|
| - assert( (p->selFlags & SF_NestedFrom)==0
|
| - || ((flags & SQLITE_FullColNames)==0 &&
|
| - (flags & SQLITE_ShortColNames)!=0) );
|
| -
|
| for(k=0; k<pEList->nExpr; k++){
|
| pE = a[k].pExpr;
|
| pRight = pE->pRight;
|
| assert( pE->op!=TK_DOT || pRight!=0 );
|
| - if( pE->op!=TK_ALL && (pE->op!=TK_DOT || pRight->op!=TK_ALL) ){
|
| + if( pE->op!=TK_ASTERISK
|
| + && (pE->op!=TK_DOT || pRight->op!=TK_ASTERISK)
|
| + ){
|
| /* This particular expression does not need to be expanded.
|
| */
|
| pNew = sqlite3ExprListAppend(pParse, pNew, a[k].pExpr);
|
| @@ -4225,18 +4385,19 @@ static int selectExpander(Walker *pWalker, Select *p){
|
| continue;
|
| }
|
|
|
| - /* If a column is marked as 'hidden' (currently only possible
|
| - ** for virtual tables), do not include it in the expanded
|
| - ** result-set list.
|
| + /* If a column is marked as 'hidden', omit it from the expanded
|
| + ** result-set list unless the SELECT has the SF_IncludeHidden
|
| + ** bit set.
|
| */
|
| - if( IsHiddenColumn(&pTab->aCol[j]) ){
|
| - assert(IsVirtual(pTab));
|
| + if( (p->selFlags & SF_IncludeHidden)==0
|
| + && IsHiddenColumn(&pTab->aCol[j])
|
| + ){
|
| continue;
|
| }
|
| tableSeen = 1;
|
|
|
| if( i>0 && zTName==0 ){
|
| - if( (pFrom->jointype & JT_NATURAL)!=0
|
| + if( (pFrom->fg.jointype & JT_NATURAL)!=0
|
| && tableAndColumnIndex(pTabList, i, zName, 0, 0)
|
| ){
|
| /* In a NATURAL join, omit the join columns from the
|
| @@ -4301,6 +4462,7 @@ static int selectExpander(Walker *pWalker, Select *p){
|
| #if SQLITE_MAX_COLUMN
|
| if( p->pEList && p->pEList->nExpr>db->aLimit[SQLITE_LIMIT_COLUMN] ){
|
| sqlite3ErrorMsg(pParse, "too many columns in result set");
|
| + return WRC_Abort;
|
| }
|
| #endif
|
| return WRC_Continue;
|
| @@ -4315,7 +4477,7 @@ static int selectExpander(Walker *pWalker, Select *p){
|
| ** Walker.xSelectCallback is set to do something useful for every
|
| ** subquery in the parser tree.
|
| */
|
| -static int exprWalkNoop(Walker *NotUsed, Expr *NotUsed2){
|
| +int sqlite3ExprWalkNoop(Walker *NotUsed, Expr *NotUsed2){
|
| UNUSED_PARAMETER2(NotUsed, NotUsed2);
|
| return WRC_Continue;
|
| }
|
| @@ -4336,14 +4498,16 @@ static int exprWalkNoop(Walker *NotUsed, Expr *NotUsed2){
|
| static void sqlite3SelectExpand(Parse *pParse, Select *pSelect){
|
| Walker w;
|
| memset(&w, 0, sizeof(w));
|
| - w.xExprCallback = exprWalkNoop;
|
| + w.xExprCallback = sqlite3ExprWalkNoop;
|
| w.pParse = pParse;
|
| if( pParse->hasCompound ){
|
| w.xSelectCallback = convertCompoundSelectToSubquery;
|
| sqlite3WalkSelect(&w, pSelect);
|
| }
|
| w.xSelectCallback = selectExpander;
|
| - w.xSelectCallback2 = selectPopWith;
|
| + if( (pSelect->selFlags & SF_MultiValue)==0 ){
|
| + w.xSelectCallback2 = selectPopWith;
|
| + }
|
| sqlite3WalkSelect(&w, pSelect);
|
| }
|
|
|
| @@ -4369,19 +4533,19 @@ static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){
|
| struct SrcList_item *pFrom;
|
|
|
| assert( p->selFlags & SF_Resolved );
|
| - if( (p->selFlags & SF_HasTypeInfo)==0 ){
|
| - p->selFlags |= SF_HasTypeInfo;
|
| - pParse = pWalker->pParse;
|
| - pTabList = p->pSrc;
|
| - for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
|
| - Table *pTab = pFrom->pTab;
|
| - if( ALWAYS(pTab!=0) && (pTab->tabFlags & TF_Ephemeral)!=0 ){
|
| - /* A sub-query in the FROM clause of a SELECT */
|
| - Select *pSel = pFrom->pSelect;
|
| - if( pSel ){
|
| - while( pSel->pPrior ) pSel = pSel->pPrior;
|
| - selectAddColumnTypeAndCollation(pParse, pTab, pSel);
|
| - }
|
| + assert( (p->selFlags & SF_HasTypeInfo)==0 );
|
| + p->selFlags |= SF_HasTypeInfo;
|
| + pParse = pWalker->pParse;
|
| + pTabList = p->pSrc;
|
| + for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
|
| + Table *pTab = pFrom->pTab;
|
| + assert( pTab!=0 );
|
| + if( (pTab->tabFlags & TF_Ephemeral)!=0 ){
|
| + /* A sub-query in the FROM clause of a SELECT */
|
| + Select *pSel = pFrom->pSelect;
|
| + if( pSel ){
|
| + while( pSel->pPrior ) pSel = pSel->pPrior;
|
| + selectAddColumnTypeAndCollation(pParse, pTab, pSel);
|
| }
|
| }
|
| }
|
| @@ -4401,7 +4565,7 @@ static void sqlite3SelectAddTypeInfo(Parse *pParse, Select *pSelect){
|
| Walker w;
|
| memset(&w, 0, sizeof(w));
|
| w.xSelectCallback2 = selectAddSubqueryTypeInfo;
|
| - w.xExprCallback = exprWalkNoop;
|
| + w.xExprCallback = sqlite3ExprWalkNoop;
|
| w.pParse = pParse;
|
| sqlite3WalkSelect(&w, pSelect);
|
| #endif
|
| @@ -4520,14 +4684,15 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){
|
| if( pList ){
|
| nArg = pList->nExpr;
|
| regAgg = sqlite3GetTempRange(pParse, nArg);
|
| - sqlite3ExprCodeExprList(pParse, pList, regAgg, SQLITE_ECEL_DUP);
|
| + sqlite3ExprCodeExprList(pParse, pList, regAgg, 0, SQLITE_ECEL_DUP);
|
| }else{
|
| nArg = 0;
|
| regAgg = 0;
|
| }
|
| if( pF->iDistinct>=0 ){
|
| addrNext = sqlite3VdbeMakeLabel(v);
|
| - assert( nArg==1 );
|
| + testcase( nArg==0 ); /* Error condition */
|
| + testcase( nArg>1 ); /* Also an error */
|
| codeDistinct(pParse, pF->iDistinct, addrNext, 1, regAgg);
|
| }
|
| if( pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){
|
| @@ -4544,7 +4709,7 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){
|
| if( regHit==0 && pAggInfo->nAccumulator ) regHit = ++pParse->nMem;
|
| sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0, (char *)pColl, P4_COLLSEQ);
|
| }
|
| - sqlite3VdbeAddOp4(v, OP_AggStep, 0, regAgg, pF->iMem,
|
| + sqlite3VdbeAddOp4(v, OP_AggStep0, 0, regAgg, pF->iMem,
|
| (void*)pF->pFunc, P4_FUNCDEF);
|
| sqlite3VdbeChangeP5(v, (u8)nArg);
|
| sqlite3ExprCacheAffinityChange(pParse, regAgg, nArg);
|
| @@ -4627,7 +4792,7 @@ int sqlite3Select(
|
| WhereInfo *pWInfo; /* Return from sqlite3WhereBegin() */
|
| Vdbe *v; /* The virtual machine under construction */
|
| int isAgg; /* True for select lists like "count(*)" */
|
| - ExprList *pEList; /* List of columns to extract. */
|
| + ExprList *pEList = 0; /* List of columns to extract. */
|
| SrcList *pTabList; /* List of tables to select from */
|
| Expr *pWhere; /* The WHERE clause. May be NULL */
|
| ExprList *pGroupBy; /* The GROUP BY clause. May be NULL */
|
| @@ -4677,36 +4842,90 @@ int sqlite3Select(
|
| memset(&sSort, 0, sizeof(sSort));
|
| sSort.pOrderBy = p->pOrderBy;
|
| pTabList = p->pSrc;
|
| - pEList = p->pEList;
|
| if( pParse->nErr || db->mallocFailed ){
|
| goto select_end;
|
| }
|
| + assert( p->pEList!=0 );
|
| isAgg = (p->selFlags & SF_Aggregate)!=0;
|
| - assert( pEList!=0 );
|
| +#if SELECTTRACE_ENABLED
|
| + if( sqlite3SelectTrace & 0x100 ){
|
| + SELECTTRACE(0x100,pParse,p, ("after name resolution:\n"));
|
| + sqlite3TreeViewSelect(0, p, 0);
|
| + }
|
| +#endif
|
|
|
| - /* Begin generating code.
|
| - */
|
| - v = sqlite3GetVdbe(pParse);
|
| - if( v==0 ) goto select_end;
|
|
|
| /* If writing to memory or generating a set
|
| ** only a single column may be output.
|
| */
|
| #ifndef SQLITE_OMIT_SUBQUERY
|
| - if( checkForMultiColumnSelectError(pParse, pDest, pEList->nExpr) ){
|
| + if( checkForMultiColumnSelectError(pParse, pDest, p->pEList->nExpr) ){
|
| goto select_end;
|
| }
|
| #endif
|
|
|
| - /* Generate code for all sub-queries in the FROM clause
|
| + /* Try to flatten subqueries in the FROM clause up into the main query
|
| */
|
| #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
|
| for(i=0; !p->pPrior && i<pTabList->nSrc; i++){
|
| struct SrcList_item *pItem = &pTabList->a[i];
|
| - SelectDest dest;
|
| Select *pSub = pItem->pSelect;
|
| int isAggSub;
|
| + Table *pTab = pItem->pTab;
|
| + if( pSub==0 ) continue;
|
| +
|
| + /* Catch mismatch in the declared columns of a view and the number of
|
| + ** columns in the SELECT on the RHS */
|
| + if( pTab->nCol!=pSub->pEList->nExpr ){
|
| + sqlite3ErrorMsg(pParse, "expected %d columns for '%s' but got %d",
|
| + pTab->nCol, pTab->zName, pSub->pEList->nExpr);
|
| + goto select_end;
|
| + }
|
| +
|
| + isAggSub = (pSub->selFlags & SF_Aggregate)!=0;
|
| + if( flattenSubquery(pParse, p, i, isAgg, isAggSub) ){
|
| + /* This subquery can be absorbed into its parent. */
|
| + if( isAggSub ){
|
| + isAgg = 1;
|
| + p->selFlags |= SF_Aggregate;
|
| + }
|
| + i = -1;
|
| + }
|
| + pTabList = p->pSrc;
|
| + if( db->mallocFailed ) goto select_end;
|
| + if( !IgnorableOrderby(pDest) ){
|
| + sSort.pOrderBy = p->pOrderBy;
|
| + }
|
| + }
|
| +#endif
|
|
|
| + /* Get a pointer the VDBE under construction, allocating a new VDBE if one
|
| + ** does not already exist */
|
| + v = sqlite3GetVdbe(pParse);
|
| + if( v==0 ) goto select_end;
|
| +
|
| +#ifndef SQLITE_OMIT_COMPOUND_SELECT
|
| + /* Handle compound SELECT statements using the separate multiSelect()
|
| + ** procedure.
|
| + */
|
| + if( p->pPrior ){
|
| + rc = multiSelect(pParse, p, pDest);
|
| + explainSetInteger(pParse->iSelectId, iRestoreSelectId);
|
| +#if SELECTTRACE_ENABLED
|
| + SELECTTRACE(1,pParse,p,("end compound-select processing\n"));
|
| + pParse->nSelectIndent--;
|
| +#endif
|
| + return rc;
|
| + }
|
| +#endif
|
| +
|
| + /* Generate code for all sub-queries in the FROM clause
|
| + */
|
| +#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
|
| + for(i=0; i<pTabList->nSrc; i++){
|
| + struct SrcList_item *pItem = &pTabList->a[i];
|
| + SelectDest dest;
|
| + Select *pSub = pItem->pSelect;
|
| if( pSub==0 ) continue;
|
|
|
| /* Sometimes the code for a subquery will be generated more than
|
| @@ -4716,7 +4935,7 @@ int sqlite3Select(
|
| ** is sufficient, though the subroutine to manifest the view does need
|
| ** to be invoked again. */
|
| if( pItem->addrFillSub ){
|
| - if( pItem->viaCoroutine==0 ){
|
| + if( pItem->fg.viaCoroutine==0 ){
|
| sqlite3VdbeAddOp2(v, OP_Gosub, pItem->regReturn, pItem->addrFillSub);
|
| }
|
| continue;
|
| @@ -4731,16 +4950,25 @@ int sqlite3Select(
|
| */
|
| pParse->nHeight += sqlite3SelectExprHeight(p);
|
|
|
| - isAggSub = (pSub->selFlags & SF_Aggregate)!=0;
|
| - if( flattenSubquery(pParse, p, i, isAgg, isAggSub) ){
|
| - /* This subquery can be absorbed into its parent. */
|
| - if( isAggSub ){
|
| - isAgg = 1;
|
| - p->selFlags |= SF_Aggregate;
|
| + /* Make copies of constant WHERE-clause terms in the outer query down
|
| + ** inside the subquery. This can help the subquery to run more efficiently.
|
| + */
|
| + if( (pItem->fg.jointype & JT_OUTER)==0
|
| + && pushDownWhereTerms(db, pSub, p->pWhere, pItem->iCursor)
|
| + ){
|
| +#if SELECTTRACE_ENABLED
|
| + if( sqlite3SelectTrace & 0x100 ){
|
| + SELECTTRACE(0x100,pParse,p,("After WHERE-clause push-down:\n"));
|
| + sqlite3TreeViewSelect(0, p, 0);
|
| }
|
| - i = -1;
|
| - }else if( pTabList->nSrc==1
|
| - && OptimizationEnabled(db, SQLITE_SubqCoroutine)
|
| +#endif
|
| + }
|
| +
|
| + /* Generate code to implement the subquery
|
| + */
|
| + if( pTabList->nSrc==1
|
| + && (p->selFlags & SF_All)==0
|
| + && OptimizationEnabled(db, SQLITE_SubqCoroutine)
|
| ){
|
| /* Implement a co-routine that will return a single row of the result
|
| ** set on each invocation.
|
| @@ -4754,7 +4982,7 @@ int sqlite3Select(
|
| explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
|
| sqlite3Select(pParse, pSub, &dest);
|
| pItem->pTab->nRowLogEst = sqlite3LogEst(pSub->nSelectRow);
|
| - pItem->viaCoroutine = 1;
|
| + pItem->fg.viaCoroutine = 1;
|
| pItem->regResult = dest.iSdst;
|
| sqlite3VdbeAddOp1(v, OP_EndCoroutine, pItem->regReturn);
|
| sqlite3VdbeJumpHere(v, addrTop-1);
|
| @@ -4772,7 +5000,7 @@ int sqlite3Select(
|
| pItem->regReturn = ++pParse->nMem;
|
| topAddr = sqlite3VdbeAddOp2(v, OP_Integer, 0, pItem->regReturn);
|
| pItem->addrFillSub = topAddr+1;
|
| - if( pItem->isCorrelated==0 ){
|
| + if( pItem->fg.isCorrelated==0 ){
|
| /* If the subquery is not correlated and if we are not inside of
|
| ** a trigger, then we only need to compute the value of the subquery
|
| ** once. */
|
| @@ -4791,33 +5019,23 @@ int sqlite3Select(
|
| sqlite3VdbeChangeP1(v, topAddr, retAddr);
|
| sqlite3ClearTempRegCache(pParse);
|
| }
|
| - if( /*pParse->nErr ||*/ db->mallocFailed ){
|
| - goto select_end;
|
| - }
|
| + if( db->mallocFailed ) goto select_end;
|
| pParse->nHeight -= sqlite3SelectExprHeight(p);
|
| - pTabList = p->pSrc;
|
| - if( !IgnorableOrderby(pDest) ){
|
| - sSort.pOrderBy = p->pOrderBy;
|
| - }
|
| }
|
| - pEList = p->pEList;
|
| #endif
|
| +
|
| + /* Various elements of the SELECT copied into local variables for
|
| + ** convenience */
|
| + pEList = p->pEList;
|
| pWhere = p->pWhere;
|
| pGroupBy = p->pGroupBy;
|
| pHaving = p->pHaving;
|
| sDistinct.isTnct = (p->selFlags & SF_Distinct)!=0;
|
|
|
| -#ifndef SQLITE_OMIT_COMPOUND_SELECT
|
| - /* If there is are a sequence of queries, do the earlier ones first.
|
| - */
|
| - if( p->pPrior ){
|
| - rc = multiSelect(pParse, p, pDest);
|
| - explainSetInteger(pParse->iSelectId, iRestoreSelectId);
|
| #if SELECTTRACE_ENABLED
|
| - SELECTTRACE(1,pParse,p,("end compound-select processing\n"));
|
| - pParse->nSelectIndent--;
|
| -#endif
|
| - return rc;
|
| + if( sqlite3SelectTrace & 0x400 ){
|
| + SELECTTRACE(0x400,pParse,p,("After all FROM-clause analysis:\n"));
|
| + sqlite3TreeViewSelect(0, p, 0);
|
| }
|
| #endif
|
|
|
| @@ -4829,7 +5047,7 @@ int sqlite3Select(
|
| **
|
| ** is transformed to:
|
| **
|
| - ** SELECT xyz FROM ... GROUP BY xyz
|
| + ** SELECT xyz FROM ... GROUP BY xyz ORDER BY xyz
|
| **
|
| ** The second form is preferred as a single index (or temp-table) may be
|
| ** used for both the ORDER BY and DISTINCT processing. As originally
|
| @@ -4837,28 +5055,27 @@ int sqlite3Select(
|
| ** BY and DISTINCT, and an index or separate temp-table for the other.
|
| */
|
| if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct
|
| - && sqlite3ExprListCompare(sSort.pOrderBy, p->pEList, -1)==0
|
| + && sqlite3ExprListCompare(sSort.pOrderBy, pEList, -1)==0
|
| ){
|
| p->selFlags &= ~SF_Distinct;
|
| - p->pGroupBy = sqlite3ExprListDup(db, p->pEList, 0);
|
| - pGroupBy = p->pGroupBy;
|
| - sSort.pOrderBy = 0;
|
| + pGroupBy = p->pGroupBy = sqlite3ExprListDup(db, pEList, 0);
|
| /* Notice that even thought SF_Distinct has been cleared from p->selFlags,
|
| ** the sDistinct.isTnct is still set. Hence, isTnct represents the
|
| ** original setting of the SF_Distinct flag, not the current setting */
|
| assert( sDistinct.isTnct );
|
| }
|
|
|
| - /* If there is an ORDER BY clause, then this sorting
|
| - ** index might end up being unused if the data can be
|
| - ** extracted in pre-sorted order. If that is the case, then the
|
| - ** OP_OpenEphemeral instruction will be changed to an OP_Noop once
|
| - ** we figure out that the sorting index is not needed. The addrSortIndex
|
| - ** variable is used to facilitate that change.
|
| + /* If there is an ORDER BY clause, then create an ephemeral index to
|
| + ** do the sorting. But this sorting ephemeral index might end up
|
| + ** being unused if the data can be extracted in pre-sorted order.
|
| + ** If that is the case, then the OP_OpenEphemeral instruction will be
|
| + ** changed to an OP_Noop once we figure out that the sorting index is
|
| + ** not needed. The sSort.addrSortIndex variable is used to facilitate
|
| + ** that change.
|
| */
|
| if( sSort.pOrderBy ){
|
| KeyInfo *pKeyInfo;
|
| - pKeyInfo = keyInfoFromExprList(pParse, sSort.pOrderBy, 0, 0);
|
| + pKeyInfo = keyInfoFromExprList(pParse, sSort.pOrderBy, 0, pEList->nExpr);
|
| sSort.iECursor = pParse->nTab++;
|
| sSort.addrSortIndex =
|
| sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
|
| @@ -4881,18 +5098,18 @@ int sqlite3Select(
|
| p->nSelectRow = LARGEST_INT64;
|
| computeLimitRegisters(pParse, p, iEnd);
|
| if( p->iLimit==0 && sSort.addrSortIndex>=0 ){
|
| - sqlite3VdbeGetOp(v, sSort.addrSortIndex)->opcode = OP_SorterOpen;
|
| + sqlite3VdbeChangeOpcode(v, sSort.addrSortIndex, OP_SorterOpen);
|
| sSort.sortFlags |= SORTFLAG_UseSorter;
|
| }
|
|
|
| - /* Open a virtual index to use for the distinct set.
|
| + /* Open an ephemeral index to use for the distinct set.
|
| */
|
| if( p->selFlags & SF_Distinct ){
|
| sDistinct.tabTnct = pParse->nTab++;
|
| sDistinct.addrTnct = sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
|
| - sDistinct.tabTnct, 0, 0,
|
| - (char*)keyInfoFromExprList(pParse, p->pEList,0,0),
|
| - P4_KEYINFO);
|
| + sDistinct.tabTnct, 0, 0,
|
| + (char*)keyInfoFromExprList(pParse, p->pEList,0,0),
|
| + P4_KEYINFO);
|
| sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
|
| sDistinct.eTnctType = WHERE_DISTINCT_UNORDERED;
|
| }else{
|
| @@ -4970,11 +5187,10 @@ int sqlite3Select(
|
| p->nSelectRow = 1;
|
| }
|
|
|
| -
|
| /* If there is both a GROUP BY and an ORDER BY clause and they are
|
| ** identical, then it may be possible to disable the ORDER BY clause
|
| ** on the grounds that the GROUP BY will cause elements to come out
|
| - ** in the correct order. It also may not - the GROUP BY may use a
|
| + ** in the correct order. It also may not - the GROUP BY might use a
|
| ** database index that causes rows to be grouped together as required
|
| ** but not actually sorted. Either way, record the fact that the
|
| ** ORDER BY and GROUP BY clauses are the same by setting the orderByGrp
|
| @@ -5017,7 +5233,7 @@ int sqlite3Select(
|
| */
|
| if( pGroupBy ){
|
| KeyInfo *pKeyInfo; /* Keying information for the group by clause */
|
| - int j1; /* A-vs-B comparision jump */
|
| + int addr1; /* A-vs-B comparision jump */
|
| int addrOutputRow; /* Start of subroutine that outputs a result row */
|
| int regOutputRow; /* Return address register for output subroutine */
|
| int addrSetAbort; /* Set the abort flag and return */
|
| @@ -5032,7 +5248,7 @@ int sqlite3Select(
|
| ** will be converted into a Noop.
|
| */
|
| sAggInfo.sortingIdx = pParse->nTab++;
|
| - pKeyInfo = keyInfoFromExprList(pParse, pGroupBy, 0, 0);
|
| + pKeyInfo = keyInfoFromExprList(pParse, pGroupBy, 0, sAggInfo.nColumn);
|
| addrSortingIdx = sqlite3VdbeAddOp4(v, OP_SorterOpen,
|
| sAggInfo.sortingIdx, sAggInfo.nSortingColumn,
|
| 0, (char*)pKeyInfo, P4_KEYINFO);
|
| @@ -5098,19 +5314,14 @@ int sqlite3Select(
|
| }
|
| regBase = sqlite3GetTempRange(pParse, nCol);
|
| sqlite3ExprCacheClear(pParse);
|
| - sqlite3ExprCodeExprList(pParse, pGroupBy, regBase, 0);
|
| + sqlite3ExprCodeExprList(pParse, pGroupBy, regBase, 0, 0);
|
| j = nGroupBy;
|
| for(i=0; i<sAggInfo.nColumn; i++){
|
| struct AggInfo_col *pCol = &sAggInfo.aCol[i];
|
| if( pCol->iSorterColumn>=j ){
|
| int r1 = j + regBase;
|
| - int r2;
|
| -
|
| - r2 = sqlite3ExprCodeGetColumn(pParse,
|
| - pCol->pTab, pCol->iColumn, pCol->iTable, r1, 0);
|
| - if( r1!=r2 ){
|
| - sqlite3VdbeAddOp2(v, OP_SCopy, r2, r1);
|
| - }
|
| + sqlite3ExprCodeGetColumnToReg(pParse,
|
| + pCol->pTab, pCol->iColumn, pCol->iTable, r1);
|
| j++;
|
| }
|
| }
|
| @@ -5152,7 +5363,8 @@ int sqlite3Select(
|
| addrTopOfLoop = sqlite3VdbeCurrentAddr(v);
|
| sqlite3ExprCacheClear(pParse);
|
| if( groupBySort ){
|
| - sqlite3VdbeAddOp3(v, OP_SorterData, sAggInfo.sortingIdx, sortOut,sortPTab);
|
| + sqlite3VdbeAddOp3(v, OP_SorterData, sAggInfo.sortingIdx,
|
| + sortOut, sortPTab);
|
| }
|
| for(j=0; j<pGroupBy->nExpr; j++){
|
| if( groupBySort ){
|
| @@ -5164,8 +5376,8 @@ int sqlite3Select(
|
| }
|
| sqlite3VdbeAddOp4(v, OP_Compare, iAMem, iBMem, pGroupBy->nExpr,
|
| (char*)sqlite3KeyInfoRef(pKeyInfo), P4_KEYINFO);
|
| - j1 = sqlite3VdbeCurrentAddr(v);
|
| - sqlite3VdbeAddOp3(v, OP_Jump, j1+1, 0, j1+1); VdbeCoverage(v);
|
| + addr1 = sqlite3VdbeCurrentAddr(v);
|
| + sqlite3VdbeAddOp3(v, OP_Jump, addr1+1, 0, addr1+1); VdbeCoverage(v);
|
|
|
| /* Generate code that runs whenever the GROUP BY changes.
|
| ** Changes in the GROUP BY are detected by the previous code
|
| @@ -5187,7 +5399,7 @@ int sqlite3Select(
|
| /* Update the aggregate accumulators based on the content of
|
| ** the current row
|
| */
|
| - sqlite3VdbeJumpHere(v, j1);
|
| + sqlite3VdbeJumpHere(v, addr1);
|
| updateAccumulator(pParse, &sAggInfo);
|
| sqlite3VdbeAddOp2(v, OP_Integer, 1, iUseFlag);
|
| VdbeComment((v, "indicate data in accumulator"));
|
| @@ -5209,7 +5421,7 @@ int sqlite3Select(
|
|
|
| /* Jump over the subroutines
|
| */
|
| - sqlite3VdbeAddOp2(v, OP_Goto, 0, addrEnd);
|
| + sqlite3VdbeGoto(v, addrEnd);
|
|
|
| /* Generate a subroutine that outputs a single row of the result
|
| ** set. This subroutine first looks at the iUseFlag. If iUseFlag
|
| @@ -5224,7 +5436,8 @@ int sqlite3Select(
|
| sqlite3VdbeAddOp1(v, OP_Return, regOutputRow);
|
| sqlite3VdbeResolveLabel(v, addrOutputRow);
|
| addrOutputRow = sqlite3VdbeCurrentAddr(v);
|
| - sqlite3VdbeAddOp2(v, OP_IfPos, iUseFlag, addrOutputRow+2); VdbeCoverage(v);
|
| + sqlite3VdbeAddOp2(v, OP_IfPos, iUseFlag, addrOutputRow+2);
|
| + VdbeCoverage(v);
|
| VdbeComment((v, "Groupby result generator entry point"));
|
| sqlite3VdbeAddOp1(v, OP_Return, regOutputRow);
|
| finalizeAggFunctions(pParse, &sAggInfo);
|
| @@ -5362,7 +5575,7 @@ int sqlite3Select(
|
| updateAccumulator(pParse, &sAggInfo);
|
| assert( pMinMax==0 || pMinMax->nExpr==1 );
|
| if( sqlite3WhereIsOrdered(pWInfo)>0 ){
|
| - sqlite3VdbeAddOp2(v, OP_Goto, 0, sqlite3WhereBreakLabel(pWInfo));
|
| + sqlite3VdbeGoto(v, sqlite3WhereBreakLabel(pWInfo));
|
| VdbeComment((v, "%s() by index",
|
| (flag==WHERE_ORDERBY_MIN?"min":"max")));
|
| }
|
| @@ -5388,7 +5601,8 @@ int sqlite3Select(
|
| ** and send them to the callback one by one.
|
| */
|
| if( sSort.pOrderBy ){
|
| - explainTempTable(pParse, sSort.nOBSat>0 ? "RIGHT PART OF ORDER BY":"ORDER BY");
|
| + explainTempTable(pParse,
|
| + sSort.nOBSat>0 ? "RIGHT PART OF ORDER BY":"ORDER BY");
|
| generateSortTail(pParse, p, &sSort, pEList->nExpr, pDest);
|
| }
|
|
|
| @@ -5396,10 +5610,9 @@ int sqlite3Select(
|
| */
|
| sqlite3VdbeResolveLabel(v, iEnd);
|
|
|
| - /* The SELECT was successfully coded. Set the return code to 0
|
| - ** to indicate no errors.
|
| - */
|
| - rc = 0;
|
| + /* The SELECT has been coded. If there is an error in the Parse structure,
|
| + ** set the return code to 1. Otherwise 0. */
|
| + rc = (pParse->nErr>0);
|
|
|
| /* Control jumps to here if an error is encountered above, or upon
|
| ** successful coding of the SELECT.
|
| @@ -5421,97 +5634,3 @@ select_end:
|
| #endif
|
| return rc;
|
| }
|
| -
|
| -#ifdef SQLITE_DEBUG
|
| -/*
|
| -** Generate a human-readable description of a the Select object.
|
| -*/
|
| -void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 moreToFollow){
|
| - int n = 0;
|
| - pView = sqlite3TreeViewPush(pView, moreToFollow);
|
| - sqlite3TreeViewLine(pView, "SELECT%s%s",
|
| - ((p->selFlags & SF_Distinct) ? " DISTINCT" : ""),
|
| - ((p->selFlags & SF_Aggregate) ? " agg_flag" : "")
|
| - );
|
| - if( p->pSrc && p->pSrc->nSrc ) n++;
|
| - if( p->pWhere ) n++;
|
| - if( p->pGroupBy ) n++;
|
| - if( p->pHaving ) n++;
|
| - if( p->pOrderBy ) n++;
|
| - if( p->pLimit ) n++;
|
| - if( p->pOffset ) n++;
|
| - if( p->pPrior ) n++;
|
| - sqlite3TreeViewExprList(pView, p->pEList, (n--)>0, "result-set");
|
| - if( p->pSrc && p->pSrc->nSrc ){
|
| - int i;
|
| - pView = sqlite3TreeViewPush(pView, (n--)>0);
|
| - sqlite3TreeViewLine(pView, "FROM");
|
| - for(i=0; i<p->pSrc->nSrc; i++){
|
| - struct SrcList_item *pItem = &p->pSrc->a[i];
|
| - StrAccum x;
|
| - char zLine[100];
|
| - sqlite3StrAccumInit(&x, zLine, sizeof(zLine), 0);
|
| - sqlite3XPrintf(&x, 0, "{%d,*}", pItem->iCursor);
|
| - if( pItem->zDatabase ){
|
| - sqlite3XPrintf(&x, 0, " %s.%s", pItem->zDatabase, pItem->zName);
|
| - }else if( pItem->zName ){
|
| - sqlite3XPrintf(&x, 0, " %s", pItem->zName);
|
| - }
|
| - if( pItem->pTab ){
|
| - sqlite3XPrintf(&x, 0, " tabname=%Q", pItem->pTab->zName);
|
| - }
|
| - if( pItem->zAlias ){
|
| - sqlite3XPrintf(&x, 0, " (AS %s)", pItem->zAlias);
|
| - }
|
| - if( pItem->jointype & JT_LEFT ){
|
| - sqlite3XPrintf(&x, 0, " LEFT-JOIN");
|
| - }
|
| - sqlite3StrAccumFinish(&x);
|
| - sqlite3TreeViewItem(pView, zLine, i<p->pSrc->nSrc-1);
|
| - if( pItem->pSelect ){
|
| - sqlite3TreeViewSelect(pView, pItem->pSelect, 0);
|
| - }
|
| - sqlite3TreeViewPop(pView);
|
| - }
|
| - sqlite3TreeViewPop(pView);
|
| - }
|
| - if( p->pWhere ){
|
| - sqlite3TreeViewItem(pView, "WHERE", (n--)>0);
|
| - sqlite3TreeViewExpr(pView, p->pWhere, 0);
|
| - sqlite3TreeViewPop(pView);
|
| - }
|
| - if( p->pGroupBy ){
|
| - sqlite3TreeViewExprList(pView, p->pGroupBy, (n--)>0, "GROUPBY");
|
| - }
|
| - if( p->pHaving ){
|
| - sqlite3TreeViewItem(pView, "HAVING", (n--)>0);
|
| - sqlite3TreeViewExpr(pView, p->pHaving, 0);
|
| - sqlite3TreeViewPop(pView);
|
| - }
|
| - if( p->pOrderBy ){
|
| - sqlite3TreeViewExprList(pView, p->pOrderBy, (n--)>0, "ORDERBY");
|
| - }
|
| - if( p->pLimit ){
|
| - sqlite3TreeViewItem(pView, "LIMIT", (n--)>0);
|
| - sqlite3TreeViewExpr(pView, p->pLimit, 0);
|
| - sqlite3TreeViewPop(pView);
|
| - }
|
| - if( p->pOffset ){
|
| - sqlite3TreeViewItem(pView, "OFFSET", (n--)>0);
|
| - sqlite3TreeViewExpr(pView, p->pOffset, 0);
|
| - sqlite3TreeViewPop(pView);
|
| - }
|
| - if( p->pPrior ){
|
| - const char *zOp = "UNION";
|
| - switch( p->op ){
|
| - case TK_ALL: zOp = "UNION ALL"; break;
|
| - case TK_INTERSECT: zOp = "INTERSECT"; break;
|
| - case TK_EXCEPT: zOp = "EXCEPT"; break;
|
| - }
|
| - sqlite3TreeViewItem(pView, zOp, (n--)>0);
|
| - sqlite3TreeViewSelect(pView, p->pPrior, 0);
|
| - sqlite3TreeViewPop(pView);
|
| - }
|
| - sqlite3TreeViewPop(pView);
|
| -}
|
| -#endif /* SQLITE_DEBUG */
|
|
|