From: drh Date: Fri, 14 Dec 2012 15:36:17 +0000 (+0000) Subject: Fix the virtual table IN optimizer so that it work even if the virtual table X-Git-Tag: version-3.7.16~99^2~2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=e40ed78c1c18326aead4ea1d12c2c3c2d8858093;p=thirdparty%2Fsqlite.git Fix the virtual table IN optimizer so that it work even if the virtual table implementation leaves the sqlite3_index_info.aConstraintUsage[].omit flag clear for an equality constraint that it intends to use. FossilOrigin-Name: d6e045f89c5a4b500b1da7ea1224b132909bafc6 --- diff --git a/manifest b/manifest index f9f1a4b2c9..054bc8cc9a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Enable\soptimization\sof\sIN\soperators\son\sconstraints\sto\svirtual\stables. -D 2012-10-16T23:17:14.207 +C Fix\sthe\svirtual\stable\sIN\soptimizer\sso\sthat\sit\swork\seven\sif\sthe\svirtual\stable\nimplementation\sleaves\sthe\ssqlite3_index_info.aConstraintUsage[].omit\sflag\nclear\sfor\san\sequality\sconstraint\sthat\sit\sintends\sto\suse. +D 2012-12-14T15:36:17.947 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 5f4f26109f9d80829122e0e09f9cda008fa065fb F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -249,7 +249,7 @@ F src/vtab.c 9c64ae18af78c740610df841c6f49fc2d240a279 F src/wal.c f5c7b5027d0ed0e9bc9afeb4a3a8dfea762ec7d2 F src/wal.h 29c197540b19044e6cd73487017e5e47a1d3dac6 F src/walker.c 3d75ba73de15e0f8cd0737643badbeb0e002f07b -F src/where.c 62f667db8cdbb81028bf1c55ba4b0e845b79622c +F src/where.c 87c95ca9005909af852a3a1b62451a43cf76fb6a F test/8_3_names.test 631ea964a3edb091cf73c3b540f6bcfdb36ce823 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/aggnested.test 0be144b453e0622a085fae8665c32f5676708e00 @@ -1021,10 +1021,7 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 F tool/win/sqlite.vsix 67d8a99aceb56384a81b3f30d6c71743146d2cc9 -P 2c3af657fee6153842d660a6ce29aa7d791ebd38 -R 5cbef12c646a3899761eba454f8c4105 -T *branch * vtab-IN-opt -T *sym-vtab-IN-opt * -T -sym-trunk * +P aa650746b19e4a6a373f7e47effff3ab2f48e78c +R 53d6793b7f79df12237947dc6769b11d U drh -Z 07568d163c180c755b6278e856d28eda +Z b558501f7c6a3f6651858b319af65966 diff --git a/manifest.uuid b/manifest.uuid index 1c366b32f0..22bae2629b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -aa650746b19e4a6a373f7e47effff3ab2f48e78c \ No newline at end of file +d6e045f89c5a4b500b1da7ea1224b132909bafc6 \ No newline at end of file diff --git a/src/where.c b/src/where.c index 586d069b3f..b07709581d 100644 --- a/src/where.c +++ b/src/where.c @@ -2193,6 +2193,7 @@ static void bestVirtualIndex(WhereBestIdx *p){ WhereTerm *pTerm; int i, j; int nOrderBy; + int bAllowIN; /* Allow IN optimizations */ double rCost; /* Make sure wsFlags is initialized to some sane value. Otherwise, if the @@ -2227,59 +2228,83 @@ static void bestVirtualIndex(WhereBestIdx *p){ assert( pTab->azModuleArg && pTab->azModuleArg[0] ); assert( sqlite3GetVTable(pParse->db, pTab) ); - /* Set the aConstraint[].usable fields and initialize all - ** output variables to zero. - ** - ** aConstraint[].usable is true for constraints where the right-hand - ** side contains only references to tables to the left of the current - ** table. In other words, if the constraint is of the form: - ** - ** column = expr - ** - ** and we are evaluating a join, then the constraint on column is - ** only valid if all tables referenced in expr occur to the left - ** of the table containing column. - ** - ** The aConstraints[] array contains entries for all constraints - ** on the current table. That way we only have to compute it once - ** even though we might try to pick the best index multiple times. - ** For each attempt at picking an index, the order of tables in the - ** join might be different so we have to recompute the usable flag - ** each time. + /* Try once or twice. On the first attempt, allow IN optimizations. + ** If an IN optimization is accepted, but does not set the + ** pInfo->aConstrainUsage.omit flag, then it will not work (because it + ** will allow duplicate rows in the result set) so try again with + ** IN optimizations disabled. */ - pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; - pUsage = pIdxInfo->aConstraintUsage; - for(i=0; inConstraint; i++, pIdxCons++){ - j = pIdxCons->iTermOffset; - pTerm = &pWC->a[j]; - pIdxCons->usable = (pTerm->prereqRight&p->notReady) ? 0 : 1; - } - memset(pUsage, 0, sizeof(pUsage[0])*pIdxInfo->nConstraint); - if( pIdxInfo->needToFreeIdxStr ){ - sqlite3_free(pIdxInfo->idxStr); - } - pIdxInfo->idxStr = 0; - pIdxInfo->idxNum = 0; - pIdxInfo->needToFreeIdxStr = 0; - pIdxInfo->orderByConsumed = 0; - /* ((double)2) In case of SQLITE_OMIT_FLOATING_POINT... */ - pIdxInfo->estimatedCost = SQLITE_BIG_DBL / ((double)2); - nOrderBy = pIdxInfo->nOrderBy; - if( !p->pOrderBy ){ - pIdxInfo->nOrderBy = 0; - } - - if( vtabBestIndex(pParse, pTab, pIdxInfo) ){ - return; - } - - pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; - for(i=0; inConstraint; i++){ - if( pUsage[i].argvIndex>0 ){ - p->cost.used |= pWC->a[pIdxCons[i].iTermOffset].prereqRight; + for(bAllowIN=1; bAllowIN>=0; bAllowIN--){ + /* Set the aConstraint[].usable fields and initialize all + ** output variables to zero. + ** + ** aConstraint[].usable is true for constraints where the right-hand + ** side contains only references to tables to the left of the current + ** table. In other words, if the constraint is of the form: + ** + ** column = expr + ** + ** and we are evaluating a join, then the constraint on column is + ** only valid if all tables referenced in expr occur to the left + ** of the table containing column. + ** + ** The aConstraints[] array contains entries for all constraints + ** on the current table. That way we only have to compute it once + ** even though we might try to pick the best index multiple times. + ** For each attempt at picking an index, the order of tables in the + ** join might be different so we have to recompute the usable flag + ** each time. + */ + pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; + pUsage = pIdxInfo->aConstraintUsage; + for(i=0; inConstraint; i++, pIdxCons++){ + j = pIdxCons->iTermOffset; + pTerm = &pWC->a[j]; + if( (pTerm->prereqRight&p->notReady)==0 + && (bAllowIN || pTerm->eOperator!=WO_IN) + ){ + pIdxCons->usable = 1; + }else{ + pIdxCons->usable = 0; + } + } + memset(pUsage, 0, sizeof(pUsage[0])*pIdxInfo->nConstraint); + if( pIdxInfo->needToFreeIdxStr ){ + sqlite3_free(pIdxInfo->idxStr); + } + pIdxInfo->idxStr = 0; + pIdxInfo->idxNum = 0; + pIdxInfo->needToFreeIdxStr = 0; + pIdxInfo->orderByConsumed = 0; + /* ((double)2) In case of SQLITE_OMIT_FLOATING_POINT... */ + pIdxInfo->estimatedCost = SQLITE_BIG_DBL / ((double)2); + nOrderBy = pIdxInfo->nOrderBy; + if( !p->pOrderBy ){ + pIdxInfo->nOrderBy = 0; + } + + if( vtabBestIndex(pParse, pTab, pIdxInfo) ){ + return; + } + + pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; + for(i=0; inConstraint; i++, pIdxCons++){ + if( pUsage[i].argvIndex>0 ){ + j = pIdxCons->iTermOffset; + pTerm = &pWC->a[j]; + p->cost.used |= pTerm->prereqRight; + if( pTerm->eOperator==WO_IN && pUsage[i].omit==0 ){ + /* Do not attempt to use an IN constraint if the virtual table + ** says that the equivalent EQ constraint cannot be safely omitted. + ** If we do attempt to use such a constraint, some rows might be + ** repeated in the output. */ + break; + } + } } + if( i>=pIdxInfo->nConstraint ) break; } - + /* If there is an ORDER BY clause, and the selected virtual table index ** does not satisfy it, increase the cost of the scan accordingly. This ** matches the processing for non-virtual tables in bestBtreeIndex().