diff --git a/manifest b/manifest index d92c7d7b7c..551c5d8bd6 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sthe\ssqlite3_rtree_query_callback()\sAPI\sto\sthe\sRTree\svirtual\stable. -D 2014-04-28T17:51:12.403 +C Merge\sall\srecent\strunk\senhancements\sand\sfixes\sinto\sthe\ssessions\sbranch. +D 2014-04-28T18:02:21.241 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in e4ee6d36cdf6136aee0158675a3b24dd3bf31a5a F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -196,7 +196,7 @@ F src/global.c 1d7bb7ea8254ae6a68ed9bfaf65fcb3d1690b486 F src/hash.c d139319967164f139c8d1bb8a11b14db9c4ba3cd F src/hash.h 8890a25af81fb85a9ad7790d32eedab4b994da22 F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08 -F src/insert.c e1ebd5d798da6b8dc180841c34e3c91b4d1fb798 +F src/insert.c 2396a980907782f959362c2aef8e1596bb57c5ac F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d F src/legacy.c 0df0b1550b9cc1f58229644735e317ac89131f12 F src/lempar.c cdf0a000315332fc9b50b62f3b5e22e080a0952b @@ -232,12 +232,12 @@ F src/printf.c e5a0005f8b3de21f85da6a709d2fbee76775bf4b F src/random.c d10c1f85b6709ca97278428fd5db5bbb9c74eece F src/resolve.c 273d5f47c4e2c05b2d3d2bffeda939551ab59e66 F src/rowset.c a9c9aae3234b44a6d7c6f5a3cadf90dce1e627be -F src/select.c 269c3e31a450fce642a10569221a49180348c88e +F src/select.c bc7feff0fb4c4a1b9d655b717bef166846b48e33 F src/shell.c 2afe7a7154e97be0c74c5feacf09626bda8493be F src/sqlite.h.in 6e6d3e092ca968abb3e65fbe22ec9220a3b616b6 F src/sqlite3.rc 11094cc6a157a028b301a9f06b3d03089ea37c3e F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc -F src/sqliteInt.h 8697c8e82d4d2ccbfe7fd2f582081e600c9dc7fc +F src/sqliteInt.h 76b4d4f0b9200e58b0a2d5339e2d46b4c7c7c691 F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e @@ -293,7 +293,7 @@ F src/update.c 7bb549d61efc6853f5cc708c1de6931179f8a12d F src/utf.c 6dc9ec9f1b3db43ae8ba0365377f11df1ee4c01c F src/util.c c46c90459ef9bdc0c6c73803cf4c55425b4771cf F src/vacuum.c 3728d74919d4fb1356f9e9a13e27773db60b7179 -F src/vdbe.c 9d883d5c91c5e7636cd56e952a0ff567e2d5c119 +F src/vdbe.c d6038559beb49c57f73a79412821d7143fc276ee F src/vdbe.h d03fcf47890ae1c79a335ca994cb878b302697ca F src/vdbeInt.h c05d4572211384c560b7579e7a299248cf31d6bd F src/vdbeapi.c d3c662762b62e330a03f29de8e2ac7098ef78030 @@ -306,8 +306,8 @@ F src/vtab.c 21b932841e51ebd7d075e2d0ad1415dce8d2d5fd F src/wal.c 76e7fc6de229bea8b30bb2539110f03a494dc3a8 F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4 F src/walker.c 11edb74d587bc87b33ca96a5173e3ec1b8389e45 -F src/where.c 7614c4383d8b6143558dc349da286d0325704d35 -F src/whereInt.h 2564055b440e44ebec8b47f237bbccae6719b7af +F src/where.c 6ae02f1e8b1b29744d9e8cd9b95eac4c5232736d +F src/whereInt.h 929c1349b5355fd44f22cee5c14d72b3329c58a6 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/aggnested.test 45c0201e28045ad38a530b5a144b73cd4aa2cfd6 @@ -448,12 +448,12 @@ F test/descidx3.test 09ddbe3f5295f482d2f8b687cf6db8bad7acd9a2 F test/diskfull.test 106391384780753ea6896b7b4f005d10e9866b6e F test/distinct.test 086e70c765f172e8974e9f83b9ac5ca03c154e77 F test/distinctagg.test 1a6ef9c87a58669438fc771450d7a72577417376 -F test/e_createtable.test ee95d48664503d40f6cc9ef4a7d03216188e2ada +F test/e_createtable.test ed82efcedc4b3656b27a5fcd12335cdb7e20eeee F test/e_delete.test d5186e2f5478b659f16a2c8b66c09892823e542a F test/e_droptrigger.test 3cd080807622c13e5bbb61fc9a57bd7754da2412 F test/e_dropview.test 0c9f7f60989164a70a67a9d9c26d1083bc808306 F test/e_expr.test 5c71d183fbf519a4769fd2e2124afdc70b5b1f42 -F test/e_fkey.test 630597377549af579d34faaf64c6959a5a68ef76 +F test/e_fkey.test a1783fe1f759e1990e6a11adfcf0702dac4d0707 F test/e_fts3.test 5c02288842e4f941896fd44afdef564dd5fc1459 F test/e_insert.test 1e44f84d2abe44d66e4fbf198be4b20e3cc724a0 F test/e_reindex.test 396b7b4f0a66863b4e95116a67d93b227193e589 @@ -739,6 +739,7 @@ F test/orderby3.test 8619d06a3debdcd80a27c0fdea5c40b468854b99 F test/orderby4.test 4d39bfbaaa3ae64d026ca2ff166353d2edca4ba4 F test/orderby5.test 2490183fef54417209d1df253633a605d46bd350 F test/orderby6.test 8b38138ab0972588240b3fca0985d2e400432859 +F test/orderby7.test 3d1383d52ade5b9eb3a173b3147fdd296f0202da F test/oserror.test 50417780d0e0d7cd23cf12a8277bb44024765df3 F test/pager1.test 1acbdb14c5952a72dd43129cabdbf69aaa3ed1fa F test/pager2.test 67b8f40ae98112bcdba1f2b2d03ea83266418c71 @@ -913,6 +914,7 @@ F test/tkt-a8a0d2996a.test eb597379dbcefa24765763d7f682c00cb5924fa9 F test/tkt-b1d3a2e531.test 8f7576e41ca179289ee1a8fee28386fd8e4b0550 F test/tkt-b351d95f9.test d14a503c414c5c58fdde3e80f9a3cfef986498c0 F test/tkt-b72787b1.test a95e8cdad0b98af1853ac7f0afd4ab27b77bf5f3 +F test/tkt-b75a9ca6b0.test 97cc2d5eeaf82799eb42138c0a1ff64370238ce4 F test/tkt-bd484a090c.test 60460bf946f79a79712b71f202eda501ca99b898 F test/tkt-bdc6bbbb38.test fc38bb09bdd440e3513a1f5f98fc60a075182d7d F test/tkt-c48d99d690.test ba61977d62ab612fc515b3c488a6fbd6464a2447 @@ -921,6 +923,7 @@ F test/tkt-d11f09d36e.test d999b548fef885d1d1afa49a0e8544ecf436869d F test/tkt-d635236375.test 9d37e988b47d87505bc9445be0ca447002df5d09 F test/tkt-d82e3f3721.test bcc0dfba658d15bab30fd4a9320c9e35d214ce30 F test/tkt-f3e5abed55.test d5a0126118142d13e27f6ce9f4c47096e9321c00 +F test/tkt-f67b41381a.test a23bc124c981662db712167bacd0ed8ad11abac9 F test/tkt-f777251dc7a.test af6531446c64bfd268416f07b4df7be7f9c749d2 F test/tkt-f7b4edec.test d998a08ff2b18b7f62edce8e3044317c45efe6c7 F test/tkt-f973c7ac31.test 28ef85c7f015477916795246d8286aeda39d4ead @@ -1177,8 +1180,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01 F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff -P 95e77efe076ab421bd246119c47dba5dacf9d087 f7dad408dd46a1e3612b6142a3afb1d0d4fcda00 -R dc0ef589b047e789a0dc76337788157a -T +closed f7dad408dd46a1e3612b6142a3afb1d0d4fcda00 +P 3dca2809352c6c6d56db74447a814f77011c6220 af2cbe64adab5f9e3b0f3da00d06428088589d7f +R 9172deb3091895924031286a7ef97633 U drh -Z d1607abc9a994a369c35e1f132aa6989 +Z dedb5680561bda872be1d3df9c89cc1d diff --git a/manifest.uuid b/manifest.uuid index ce5967cbb0..0367d488c9 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3dca2809352c6c6d56db74447a814f77011c6220 \ No newline at end of file +e158812c34b01ce516d26636a489509bf61d1c27 \ No newline at end of file diff --git a/src/insert.c b/src/insert.c index a0cf527c97..258b1637ae 100644 --- a/src/insert.c +++ b/src/insert.c @@ -1877,15 +1877,24 @@ static int xferOptimization( return 0; /* Both tables must have the same INTEGER PRIMARY KEY */ } for(i=0; inCol; i++){ - if( pDest->aCol[i].affinity!=pSrc->aCol[i].affinity ){ + Column *pDestCol = &pDest->aCol[i]; + Column *pSrcCol = &pSrc->aCol[i]; + if( pDestCol->affinity!=pSrcCol->affinity ){ return 0; /* Affinity must be the same on all columns */ } - if( !xferCompatibleCollation(pDest->aCol[i].zColl, pSrc->aCol[i].zColl) ){ + if( !xferCompatibleCollation(pDestCol->zColl, pSrcCol->zColl) ){ return 0; /* Collating sequence must be the same on all columns */ } - if( pDest->aCol[i].notNull && !pSrc->aCol[i].notNull ){ + if( pDestCol->notNull && !pSrcCol->notNull ){ return 0; /* tab2 must be NOT NULL if tab1 is */ } + /* Default values for second and subsequent columns need to match. */ + if( i>0 + && ((pDestCol->zDflt==0)!=(pSrcCol->zDflt==0) + || (pDestCol->zDflt && strcmp(pDestCol->zDflt, pSrcCol->zDflt)!=0)) + ){ + return 0; /* Default values must be the same for all columns */ + } } for(pDestIdx=pDest->pIndex; pDestIdx; pDestIdx=pDestIdx->pNext){ if( pDestIdx->onError!=OE_None ){ diff --git a/src/select.c b/src/select.c index c7f0b24a4a..6efdde4b72 100644 --- a/src/select.c +++ b/src/select.c @@ -4719,18 +4719,6 @@ int sqlite3Select( } #endif - /* If there is both a GROUP BY and an ORDER BY clause and they are - ** identical, then disable the ORDER BY clause since the GROUP BY - ** will cause elements to come out in the correct order. This is - ** an optimization - the correct answer should result regardless. - ** Use the SQLITE_GroupByOrder flag with SQLITE_TESTCTRL_OPTIMIZER - ** to disable this optimization for testing purposes. - */ - if( sqlite3ExprListCompare(p->pGroupBy, sSort.pOrderBy, -1)==0 - && OptimizationEnabled(db, SQLITE_GroupByOrder) ){ - sSort.pOrderBy = 0; - } - /* If the query is DISTINCT with an ORDER BY but is not an aggregate, and ** if the select-list is the same as the ORDER BY list, then this query ** can be rewritten as a GROUP BY. In other words, this: @@ -4859,6 +4847,7 @@ int sqlite3Select( int addrEnd; /* End of processing for this SELECT */ int sortPTab = 0; /* Pseudotable used to decode sorting results */ int sortOut = 0; /* Output register from the sorter */ + int orderByGrp = 0; /* True if the GROUP BY and ORDER BY are the same */ /* Remove any and all aliases between the result set and the ** GROUP BY clause. @@ -4878,6 +4867,18 @@ 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 + ** 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 + ** variable. */ + if( sqlite3ExprListCompare(pGroupBy, sSort.pOrderBy, -1)==0 ){ + orderByGrp = 1; + } /* Create a label to jump to when we want to abort the query */ addrEnd = sqlite3VdbeMakeLabel(v); @@ -4958,7 +4959,8 @@ int sqlite3Select( */ sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset); pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pGroupBy, 0, - WHERE_GROUPBY, 0); + WHERE_GROUPBY | (orderByGrp ? WHERE_SORTBYGROUP : 0), 0 + ); if( pWInfo==0 ) goto select_end; if( sqlite3WhereIsOrdered(pWInfo)==pGroupBy->nExpr ){ /* The optimizer is able to deliver rows in group by order so @@ -5023,6 +5025,21 @@ int sqlite3Select( VdbeComment((v, "GROUP BY sort")); VdbeCoverage(v); sAggInfo.useSortingIdx = 1; sqlite3ExprCacheClear(pParse); + + } + + /* If the index or temporary table used by the GROUP BY sort + ** will naturally deliver rows in the order required by the ORDER BY + ** clause, cancel the ephemeral table open coded earlier. + ** + ** This is an optimization - the correct answer should result regardless. + ** Use the SQLITE_GroupByOrder flag with SQLITE_TESTCTRL_OPTIMIZER to + ** disable this optimization for testing purposes. */ + if( orderByGrp && OptimizationEnabled(db, SQLITE_GroupByOrder) + && (groupBySort || sqlite3WhereIsSorted(pWInfo)) + ){ + sSort.pOrderBy = 0; + sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex); } /* Evaluate the current GROUP BY terms and store in b0, b1, b2... diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 3a2612c88c..3ab4281ebf 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -2133,6 +2133,7 @@ struct SrcList { #define WHERE_GROUPBY 0x0100 /* pOrderBy is really a GROUP BY */ #define WHERE_DISTINCTBY 0x0200 /* pOrderby is really a DISTINCT clause */ #define WHERE_WANT_DISTINCT 0x0400 /* All output needs to be distinct */ +#define WHERE_SORTBYGROUP 0x0800 /* Support sqlite3WhereIsSorted() */ /* Allowed return values from sqlite3WhereIsDistinct() */ @@ -3104,6 +3105,7 @@ void sqlite3WhereEnd(WhereInfo*); u64 sqlite3WhereOutputRowCount(WhereInfo*); int sqlite3WhereIsDistinct(WhereInfo*); int sqlite3WhereIsOrdered(WhereInfo*); +int sqlite3WhereIsSorted(WhereInfo*); int sqlite3WhereContinueLabel(WhereInfo*); int sqlite3WhereBreakLabel(WhereInfo*); int sqlite3WhereOkOnePass(WhereInfo*, int*); diff --git a/src/vdbe.c b/src/vdbe.c index 1822c807e3..7cf341d938 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -6384,8 +6384,8 @@ default: { /* This is really OP_Noop and OP_Explain */ #ifdef VDBE_PROFILE { - u64 elapsed = sqlite3Hwtime() - start; - pOp->cycles += elapsed; + u64 endTime = sqlite3Hwtime(); + if( endTime>start ) pOp->cycles += endTime - start; pOp->cnt++; } #endif diff --git a/src/where.c b/src/where.c index e51eee5350..6342b7b50b 100644 --- a/src/where.c +++ b/src/where.c @@ -3712,18 +3712,36 @@ static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){ } /* -** Return TRUE if the set of WHERE clause terms used by pA is a proper -** subset of the WHERE clause terms used by pB. +** Return TRUE if both of the following are true: +** +** (1) X has the same or lower cost that Y +** (2) X is a proper subset of Y +** +** By "proper subset" we mean that X uses fewer WHERE clause terms +** than Y and that every WHERE clause term used by X is also used +** by Y. +** +** If X is a proper subset of Y then Y is a better choice and ought +** to have a lower cost. This routine returns TRUE when that cost +** relationship is inverted and needs to be adjusted. */ -static int whereLoopProperSubset(const WhereLoop *pA, const WhereLoop *pB){ +static int whereLoopCheaperProperSubset( + const WhereLoop *pX, /* First WhereLoop to compare */ + const WhereLoop *pY /* Compare against this WhereLoop */ +){ int i, j; - assert( pA->nLTermnLTerm ); /* Checked by calling function */ - for(j=0, i=pA->nLTerm-1; i>=0 && j>=0; i--){ - for(j=pB->nLTerm-1; j>=0; j--){ - if( pB->aLTerm[j]==pA->aLTerm[i] ) break; - } + if( pX->nLTerm >= pY->nLTerm ) return 0; /* X is not a subset of Y */ + if( pX->rRun >= pY->rRun ){ + if( pX->rRun > pY->rRun ) return 0; /* X costs more than Y */ + if( pX->nOut > pY->nOut ) return 0; /* X costs more than Y */ } - return j>=0; + for(j=0, i=pX->nLTerm-1; i>=0; i--){ + for(j=pY->nLTerm-1; j>=0; j--){ + if( pY->aLTerm[j]==pX->aLTerm[i] ) break; + } + if( j<0 ) return 0; /* X not a subset of Y since term X[i] not used by Y */ + } + return 1; /* All conditions meet */ } /* @@ -3745,19 +3763,14 @@ static void whereLoopAdjustCost(const WhereLoop *p, WhereLoop *pTemplate){ for(; p; p=p->pNextLoop){ if( p->iTab!=pTemplate->iTab ) continue; if( (p->wsFlags & WHERE_INDEXED)==0 ) continue; - if( p->nLTermnLTerm - && (p->rRunrRun || (p->rRun==pTemplate->rRun && - p->nOut<=pTemplate->nOut)) - && whereLoopProperSubset(p, pTemplate) - ){ + if( whereLoopCheaperProperSubset(p, pTemplate) ){ + /* Adjust pTemplate cost downward so that it is cheaper than its + ** subset p */ pTemplate->rRun = p->rRun; pTemplate->nOut = p->nOut - 1; - }else - if( p->nLTerm>pTemplate->nLTerm - && (p->rRun>pTemplate->rRun || (p->rRun==pTemplate->rRun && - p->nOut>=pTemplate->nOut)) - && whereLoopProperSubset(pTemplate, p) - ){ + }else if( whereLoopCheaperProperSubset(pTemplate, p) ){ + /* Adjust pTemplate cost upward so that it is costlier than p since + ** pTemplate is a proper subset of p */ pTemplate->rRun = p->rRun; pTemplate->nOut = p->nOut + 1; } @@ -4783,7 +4796,7 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){ ** Note that processing for WHERE_GROUPBY and WHERE_DISTINCTBY is not as ** strict. With GROUP BY and DISTINCT the only requirement is that ** equivalent rows appear immediately adjacent to one another. GROUP BY -** and DISTINT do not require rows to appear in any particular order as long +** and DISTINCT do not require rows to appear in any particular order as long ** as equivelent rows are grouped together. Thus for GROUP BY and DISTINCT ** the pOrderBy terms can be matched in any order. With ORDER BY, the ** pOrderBy terms must be matched in strict left-to-right order. @@ -4844,14 +4857,6 @@ static i8 wherePathSatisfiesOrderBy( */ assert( pOrderBy!=0 ); - - /* Sortability of virtual tables is determined by the xBestIndex method - ** of the virtual table itself */ - if( pLast->wsFlags & WHERE_VIRTUALTABLE ){ - testcase( nLoop>0 ); /* True when outer loops are one-row and match - ** no ORDER BY terms */ - return pLast->u.vtab.isOrdered; - } if( nLoop && OptimizationDisabled(db, SQLITE_OrderByIdxJoin) ) return 0; nOrderBy = pOrderBy->nExpr; @@ -4864,7 +4869,10 @@ static i8 wherePathSatisfiesOrderBy( for(iLoop=0; isOrderDistinct && obSat0 ) ready |= pLoop->maskSelf; pLoop = iLoopaLoop[iLoop] : pLast; - assert( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 ); + if( pLoop->wsFlags & WHERE_VIRTUALTABLE ){ + if( pLoop->u.vtab.isOrdered ) obSat = obDone; + break; + } iCur = pWInfo->pTabList->a[pLoop->iTab].iCursor; /* Mark off any ORDER BY term X that is a column in the table of @@ -4952,7 +4960,7 @@ static i8 wherePathSatisfiesOrderBy( } /* Find the ORDER BY term that corresponds to the j-th column - ** of the index and and mark that ORDER BY term off + ** of the index and mark that ORDER BY term off */ bOnce = 1; isMatch = 0; @@ -5032,6 +5040,36 @@ static i8 wherePathSatisfiesOrderBy( return -1; } + +/* +** If the WHERE_GROUPBY flag is set in the mask passed to sqlite3WhereBegin(), +** the planner assumes that the specified pOrderBy list is actually a GROUP +** BY clause - and so any order that groups rows as required satisfies the +** request. +** +** Normally, in this case it is not possible for the caller to determine +** whether or not the rows are really being delivered in sorted order, or +** just in some other order that provides the required grouping. However, +** if the WHERE_SORTBYGROUP flag is also passed to sqlite3WhereBegin(), then +** this function may be called on the returned WhereInfo object. It returns +** true if the rows really will be sorted in the specified order, or false +** otherwise. +** +** For example, assuming: +** +** CREATE INDEX i1 ON t1(x, Y); +** +** then +** +** SELECT * FROM t1 GROUP BY x,y ORDER BY x,y; -- IsSorted()==1 +** SELECT * FROM t1 GROUP BY y,x ORDER BY y,x; -- IsSorted()==0 +*/ +int sqlite3WhereIsSorted(WhereInfo *pWInfo){ + assert( pWInfo->wctrlFlags & WHERE_GROUPBY ); + assert( pWInfo->wctrlFlags & WHERE_SORTBYGROUP ); + return pWInfo->sorted; +} + #ifdef WHERETRACE_ENABLED /* For debugging use only: */ static const char *wherePathName(WherePath *pPath, int nLoop, WhereLoop *pLast){ @@ -5044,7 +5082,6 @@ static const char *wherePathName(WherePath *pPath, int nLoop, WhereLoop *pLast){ } #endif - /* ** Given the list of WhereLoop objects at pWInfo->pLoops, this routine ** attempts to find the lowest cost path that visits each WhereLoop @@ -5325,7 +5362,19 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ if( pWInfo->nOBSat<0 ) pWInfo->nOBSat = 0; pWInfo->revMask = pFrom->revLoop; } + if( (pWInfo->wctrlFlags & WHERE_SORTBYGROUP) + && pWInfo->nOBSat==pWInfo->pOrderBy->nExpr + ){ + Bitmask notUsed = 0; + int nOrder = wherePathSatisfiesOrderBy(pWInfo, pWInfo->pOrderBy, + pFrom, 0, nLoop-1, pFrom->aLoop[nLoop-1], ¬Used + ); + assert( pWInfo->sorted==0 ); + pWInfo->sorted = (nOrder==pWInfo->pOrderBy->nExpr); + } } + + pWInfo->nRowOut = pFrom->nRow; /* Free temporary memory and return success */ diff --git a/src/whereInt.h b/src/whereInt.h index 419bb81c0e..72e7530db9 100644 --- a/src/whereInt.h +++ b/src/whereInt.h @@ -398,6 +398,7 @@ struct WhereInfo { LogEst nRowOut; /* Estimated number of output rows */ u16 wctrlFlags; /* Flags originally passed to sqlite3WhereBegin() */ i8 nOBSat; /* Number of ORDER BY terms satisfied by indices */ + u8 sorted; /* True if really sorted (not just grouped) */ u8 okOnePass; /* Ok to use one-pass algorithm for UPDATE/DELETE */ u8 untestedTerms; /* Not all WHERE terms resolved by outer loop */ u8 eDistinct; /* One of the WHERE_DISTINCT_* values below */ diff --git a/test/e_createtable.test b/test/e_createtable.test index 2cd63280df..42fc017e2a 100644 --- a/test/e_createtable.test +++ b/test/e_createtable.test @@ -884,9 +884,10 @@ do_execsql_test e_createtable-3.3.1 { ); } {} -# EVIDENCE-OF: R-10288-43169 For the purposes of the DEFAULT clause, an +# EVIDENCE-OF: R-36381-62919 For the purposes of the DEFAULT clause, an # expression is considered constant provided that it does not contain -# any sub-queries or string constants enclosed in double quotes. +# any sub-queries, column or table references, or string literals +# enclosed in double-quotes instead of single-quotes. # do_createtable_tests 3.4.1 -error { default value of column [x] is not constant diff --git a/test/e_fkey.test b/test/e_fkey.test index 921e967176..09756505c3 100644 --- a/test/e_fkey.test +++ b/test/e_fkey.test @@ -135,9 +135,9 @@ reset_db # # This also tests that foreign key constraints are disabled by default. # -# EVIDENCE-OF: R-59578-04990 Foreign key constraints are disabled by +# EVIDENCE-OF: R-44261-39702 Foreign key constraints are disabled by # default (for backwards compatibility), so must be enabled separately -# for each database connection separately. +# for each database connection. # drop_all_tables do_test e_fkey-4.1 { @@ -163,9 +163,10 @@ do_test e_fkey-4.2 { } {world} #------------------------------------------------------------------------- -# EVIDENCE-OF: R-15278-54456 The application can can also use a PRAGMA +# EVIDENCE-OF: R-08013-37737 The application can also use a PRAGMA # foreign_keys statement to determine if foreign keys are currently # enabled. + # # This also tests the example code in section 2 of foreignkeys.in. # @@ -2990,8 +2991,8 @@ if {[clang_sanitize_address]==0} { # The setting of the recursive_triggers pragma does not affect foreign # key actions. # -# EVIDENCE-OF: R-51769-32730 The PRAGMA recursive_triggers setting does -# not not affect the operation of foreign key actions. +# EVIDENCE-OF: R-44355-00270 The PRAGMA recursive_triggers setting does +# not affect the operation of foreign key actions. # foreach recursive_triggers_setting [list 0 1 ON OFF] { drop_all_tables diff --git a/test/orderby7.test b/test/orderby7.test new file mode 100644 index 0000000000..8c5fc9a85f --- /dev/null +++ b/test/orderby7.test @@ -0,0 +1,106 @@ +# 2014-04-25 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this file is testing ORDER BY optimizations on joins +# that involve virtual tables. +# + + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set ::testprefix orderby7 + +ifcapable !fts3 { + finish_test + return +} + +do_execsql_test 1.0 { + CREATE VIRTUAL TABLE fts USING fts3(content TEXT); + INSERT INTO fts(rowid,content) + VALUES(1,'this is a test of the fts3 virtual'), + (2,'table used as part of a join together'), + (3,'with the DISTINCT keyword. There was'), + (4,'a bug at one time (2013-06 through 2014-04)'), + (5,'that prevented this from working correctly.'), + (11,'a row that occurs twice'), + (12,'a row that occurs twice'); + + CREATE TABLE t1(x TEXT PRIMARY KEY, y); + INSERT OR IGNORE INTO t1 SELECT content, rowid+100 FROM fts; +} {} +do_execsql_test 1.1 { + SELECT DISTINCT fts.rowid, t1.y + FROM fts, t1 + WHERE fts MATCH 'that twice' + AND content=x + ORDER BY y; +} {11 111 12 111} +do_execsql_test 1.2 { + SELECT DISTINCT fts.rowid, t1.x + FROM fts, t1 + WHERE fts MATCH 'that twice' + AND content=x + ORDER BY 1; +} {11 {a row that occurs twice} 12 {a row that occurs twice}} +do_execsql_test 1.3 { + SELECT DISTINCT t1.x + FROM fts, t1 + WHERE fts MATCH 'that twice' + AND content=x + ORDER BY 1; +} {{a row that occurs twice}} +do_execsql_test 1.4 { + SELECT t1.x + FROM fts, t1 + WHERE fts MATCH 'that twice' + AND content=x + ORDER BY 1; +} {{a row that occurs twice} {a row that occurs twice}} +do_execsql_test 1.5 { + SELECT DISTINCT t1.x + FROM fts, t1 + WHERE fts MATCH 'that twice' + AND content=x; +} {{a row that occurs twice}} +do_execsql_test 1.6 { + SELECT t1.x + FROM fts, t1 + WHERE fts MATCH 'that twice' + AND content=x; +} {{a row that occurs twice} {a row that occurs twice}} + +do_execsql_test 2.1 { + SELECT DISTINCT t1.x + FROM fts, t1 + WHERE fts.rowid=11 + AND content=x + ORDER BY fts.rowid; +} {{a row that occurs twice}} +do_execsql_test 2.2 { + SELECT DISTINCT t1.* + FROM fts, t1 + WHERE fts.rowid=11 + AND content=x + ORDER BY fts.rowid; +} {{a row that occurs twice} 111} +do_execsql_test 2.3 { + SELECT DISTINCT t1.* + FROM fts, t1 + WHERE fts.rowid=11 + AND content=x + ORDER BY t1.y +} {{a row that occurs twice} 111} + + + + +finish_test diff --git a/test/tkt-b75a9ca6b0.test b/test/tkt-b75a9ca6b0.test new file mode 100644 index 0000000000..0c81a534da --- /dev/null +++ b/test/tkt-b75a9ca6b0.test @@ -0,0 +1,77 @@ +# 2014-04-21 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#************************************************************************* +# +# Test that ticket [b75a9ca6b0] has been fixed. +# +# Ticket [b75a9ca6b0] concerns queries that have both a GROUP BY +# and an ORDER BY. This code verifies that SQLite is able to +# optimize out the ORDER BY in some circumstances, but retains the +# ORDER BY when necessary. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix tkt-b75a9ca6b0 + +do_execsql_test 1 { + CREATE TABLE t1 (x, y); + INSERT INTO t1 VALUES (1, 3); + INSERT INTO t1 VALUES (2, 2); + INSERT INTO t1 VALUES (3, 1); +} + +do_execsql_test 1.1 { + CREATE INDEX i1 ON t1(x, y); +} + +set idxscan {0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1}} +set tblscan {0 0 0 {SCAN TABLE t1}} +set grpsort {0 0 0 {USE TEMP B-TREE FOR GROUP BY}} +set sort {0 0 0 {USE TEMP B-TREE FOR ORDER BY}} + +foreach {tn q res eqp} [subst -nocommands { + 1 "SELECT * FROM t1 GROUP BY x, y ORDER BY x,y" + {1 3 2 2 3 1} {$idxscan} + + 2 "SELECT * FROM t1 GROUP BY x, y ORDER BY x" + {1 3 2 2 3 1} {$idxscan $sort} + + 3 "SELECT * FROM t1 GROUP BY y, x ORDER BY y, x" + {3 1 2 2 1 3} {$idxscan $sort} + + 4 "SELECT * FROM t1 GROUP BY x ORDER BY x" + {1 3 2 2 3 1} {$idxscan} + + 5 "SELECT * FROM t1 GROUP BY y ORDER BY y" + {3 1 2 2 1 3} {$tblscan $grpsort} + + 6 "SELECT * FROM t1 GROUP BY y ORDER BY x" + {1 3 2 2 3 1} {$tblscan $grpsort $sort} + + 7 "SELECT * FROM t1 GROUP BY x, y ORDER BY x, y DESC" + {1 3 2 2 3 1} {$idxscan $sort} + + 8 "SELECT * FROM t1 GROUP BY x, y ORDER BY x DESC, y DESC" + {3 1 2 2 1 3} {$idxscan $sort} + + 9 "SELECT * FROM t1 GROUP BY x, y ORDER BY x ASC, y ASC" + {1 3 2 2 3 1} {$idxscan} + + 10 "SELECT * FROM t1 GROUP BY x, y ORDER BY x COLLATE nocase, y" + {1 3 2 2 3 1} {$idxscan $sort} + +}] { + do_execsql_test 1.$tn.1 $q $res + do_eqp_test 1.$tn.2 $q $eqp +} + + +finish_test diff --git a/test/tkt-f67b41381a.test b/test/tkt-f67b41381a.test new file mode 100644 index 0000000000..1ddec988cd --- /dev/null +++ b/test/tkt-f67b41381a.test @@ -0,0 +1,53 @@ +# 2014 April 26 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# Test that ticket f67b41381a has been resolved. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix tkt-f67b41381a + +do_execsql_test 1.0 { + CREATE TABLE t1(a); + INSERT INTO t1 VALUES(1); + ALTER TABLE t1 ADD COLUMN b DEFAULT 2; + CREATE TABLE t2(a, b); + INSERT INTO t2 SELECT * FROM t1; + SELECT * FROM t2; +} {1 2} + +db cache size 0 +foreach {tn tbls xfer} { + 1 { CREATE TABLE t1(a, b); CREATE TABLE t2(a, b) } 1 + 2 { CREATE TABLE t1(a, b DEFAULT 'x'); CREATE TABLE t2(a, b) } 0 + 3 { CREATE TABLE t1(a, b DEFAULT 'x'); CREATE TABLE t2(a, b DEFAULT 'x') } 1 + 4 { CREATE TABLE t1(a, b DEFAULT NULL); CREATE TABLE t2(a, b) } 0 + 5 { CREATE TABLE t1(a DEFAULT 2, b); CREATE TABLE t2(a DEFAULT 1, b) } 1 + 6 { CREATE TABLE t1(a DEFAULT 1, b); CREATE TABLE t2(a DEFAULT 1, b) } 1 + 7 { CREATE TABLE t1(a DEFAULT 1, b DEFAULT 1); + CREATE TABLE t2(a DEFAULT 3, b DEFAULT 1) } 1 + 8 { CREATE TABLE t1(a DEFAULT 1, b DEFAULT 1); + CREATE TABLE t2(a DEFAULT 3, b DEFAULT 3) } 0 + +} { + + execsql { DROP TABLE t1; DROP TABLE t2 } + execsql $tbls + + set res 1 + db eval { EXPLAIN INSERT INTO t1 SELECT * FROM t2 } { + if {$opcode == "Column"} { set res 0 } + } + + do_test 2.$tn [list set res] $xfer +} + +finish_test