From: dan Date: Tue, 6 Aug 2013 20:01:43 +0000 (+0000) Subject: When possible, use the multi-column samples in sqlite_stat4 to estimate the number... X-Git-Tag: version-3.8.1~132^2~36 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=7a4192358a6aec4cf9828ac7539025fdb659e264;p=thirdparty%2Fsqlite.git When possible, use the multi-column samples in sqlite_stat4 to estimate the number of index rows scanned by a query plan. FossilOrigin-Name: 2973f5ca736c4a6f13c653d54b6a29d7cae8d0ed --- diff --git a/manifest b/manifest index dd8eb47156..0ada153ab5 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Modify\sthe\svdbe\scode\sgenerated\sby\sANALYZE\sto\suse\sfewer\smemory\scells\sand\scursor\sslots. -D 2013-08-05T19:04:07.083 +C When\spossible,\suse\sthe\smulti-column\ssamples\sin\ssqlite_stat4\sto\sestimate\sthe\snumber\sof\sindex\srows\sscanned\sby\sa\squery\splan. +D 2013-08-06T20:01:43.152 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 5e41da95d92656a5004b03d3576e8b226858a28e F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -156,7 +156,7 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc F sqlite3.pc.in ae6f59a76e862f5c561eb32a380228a02afc3cad -F src/alter.c f8db986c03eb0bfb221523fc9bbb9d0b70de3168 +F src/alter.c 2af0330bb1b601af7a7789bf7229675fd772a083 F src/analyze.c 726a63b19f7f1478b321b84fc03ae9d754624841 F src/attach.c 1816f5a9eea8d2010fc2b22b44f0f63eb3a62704 F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34 @@ -221,7 +221,7 @@ F src/shell.c 52f975eae87c8338c4dfbf4c2842d2a0971f01fd F src/sqlite.h.in 442c109e0c3447c34b1794971ecdb673ce08a843 F src/sqlite3.rc fea433eb0a59f4c9393c8e6d76a6e2596b1fe0c0 F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc -F src/sqliteInt.h 37e2952ce88a64d6572b2c40fcb78956e5aa2f7a +F src/sqliteInt.h 31057687e0ebc9e56a025bcebb252bbd84e5db13 F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e @@ -273,7 +273,7 @@ F src/test_vfstrace.c 34b544e80ba7fb77be15395a609c669df2e660a2 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 F src/tokenize.c 70061085a51f2f4fc15ece94f32c03bcb78e63b2 F src/trigger.c 5c0ea9b8755e7c5e1a700f3e27ac4f8d92dd221e -F src/update.c 7f3fe64d8f3b44c44a1eac293f0f85f87c355b7a +F src/update.c 7d9d38e4f341ada7d79035ea969cdefb8b9014d1 F src/utf.c acd0b6f8beb8df4e0ed178c48c81c693bcc31967 F src/util.c f566b5138099a2df8533b190d0dcc74b7dfbe0c9 F src/vacuum.c d9c5759f4c5a438bb43c2086f72c5d2edabc36c8 @@ -283,14 +283,14 @@ F src/vdbeInt.h e9b7c6b165a31a4715c5aa97223d20d265515231 F src/vdbeapi.c 4d13580bd058b39623e8fcfc233b7df4b8191e8b F src/vdbeaux.c 4389b3692969b4415fcfd00de36818a02f84df28 F src/vdbeblob.c 5dc79627775bd9a9b494dd956e26297946417d69 -F src/vdbemem.c 69c6d1c3ef07f4442e074def9a92d15d02f06eba +F src/vdbemem.c 83f9b6e68a421cfcdde125a66b5ebe8220c12de2 F src/vdbesort.c 3937e06b2a0e354500e17dc206ef4c35770a5017 F src/vdbetrace.c e7ec40e1999ff3c6414424365d5941178966dcbc F src/vtab.c 2e8b489db47e20ae36cd247932dc671c9ded0624 F src/wal.c 7dc3966ef98b74422267e7e6e46e07ff6c6eb1b4 F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4 F src/walker.c 4fa43583d0a84b48f93b1e88f11adf2065be4e73 -F src/where.c 072abd8556bf1d05676cc14b561da79acb70abb9 +F src/where.c 37c3dc9e9fddc2362c4926c0c64ca2d270c09d26 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/aggnested.test 45c0201e28045ad38a530b5a144b73cd4aa2cfd6 @@ -1106,7 +1106,7 @@ F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 F tool/wherecosttest.c f407dc4c79786982a475261866a161cd007947ae F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac -P 3a71afe67418ce00097cd9714c395fe9ff16f23b -R a3a130eab7cfa15d823c14448800974a +P 4a51cf289fad8aebc637b5f96488de18e861195d +R 35ea3b91ab20c676c2ada836f868975b U dan -Z c7783dc63850c26ec6b79b95587c6286 +Z 27d68e9c86b12d5cd9aae1be85713ec1 diff --git a/manifest.uuid b/manifest.uuid index 8b49a29bb2..346421d3a1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4a51cf289fad8aebc637b5f96488de18e861195d \ No newline at end of file +2973f5ca736c4a6f13c653d54b6a29d7cae8d0ed \ No newline at end of file diff --git a/src/alter.c b/src/alter.c index a49d3349d7..9d34b07b0c 100644 --- a/src/alter.c +++ b/src/alter.c @@ -687,7 +687,7 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){ ** can handle (i.e. not CURRENT_TIME etc.) */ if( pDflt ){ - sqlite3_value *pVal; + sqlite3_value *pVal = 0; if( sqlite3ValueFromExpr(db, pDflt, SQLITE_UTF8, SQLITE_AFF_NONE, &pVal) ){ db->mallocFailed = 1; return; diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 9d33b4c7a7..63c77085af 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -3101,6 +3101,10 @@ Expr *sqlite3CreateColumnExpr(sqlite3 *, SrcList *, int, int); void sqlite3BackupRestart(sqlite3_backup *); void sqlite3BackupUpdate(sqlite3_backup *, Pgno, const u8 *); +int sqlite3Stat4ProbeSetValue(Parse*, UnpackedRecord*, Expr*, u8, int, int*); +void sqlite3Stat4ProbeFree(UnpackedRecord*); +int sqlite3Stat4ProbeNew(Parse*, Index*, UnpackedRecord**); + /* ** The interface to the LEMON-generated parser */ diff --git a/src/update.c b/src/update.c index 4fbefc3b59..5077f64e35 100644 --- a/src/update.c +++ b/src/update.c @@ -61,7 +61,7 @@ static void updateVirtualTable( void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i, int iReg){ assert( pTab!=0 ); if( !pTab->pSelect ){ - sqlite3_value *pValue; + sqlite3_value *pValue = 0; u8 enc = ENC(sqlite3VdbeDb(v)); Column *pCol = &pTab->aCol[i]; VdbeComment((v, "%s.%s", pTab->zName, pCol->zName)); diff --git a/src/vdbemem.c b/src/vdbemem.c index 67fc695f3f..8627360113 100644 --- a/src/vdbemem.c +++ b/src/vdbemem.c @@ -1005,6 +1005,11 @@ sqlite3_value *sqlite3ValueNew(sqlite3 *db){ return p; } +static sqlite3_value *valueNew(sqlite3 *db, sqlite3_value *pOld){ + if( pOld ) return pOld; + return sqlite3ValueNew(db); +} + /* ** Create a new sqlite3_value object, containing the value of pExpr. ** @@ -1056,7 +1061,7 @@ int sqlite3ValueFromExpr( } if( op==TK_STRING || op==TK_FLOAT || op==TK_INTEGER ){ - pVal = sqlite3ValueNew(db); + pVal = valueNew(db, *ppVal); if( pVal==0 ) goto no_mem; if( ExprHasProperty(pExpr, EP_IntValue) ){ sqlite3VdbeMemSetInt64(pVal, (i64)pExpr->u.iValue*negInt); @@ -1090,7 +1095,7 @@ int sqlite3ValueFromExpr( sqlite3ValueApplyAffinity(pVal, affinity, enc); } }else if( op==TK_NULL ){ - pVal = sqlite3ValueNew(db); + pVal = valueNew(db, *ppVal); if( pVal==0 ) goto no_mem; } #ifndef SQLITE_OMIT_BLOB_LITERAL @@ -1098,7 +1103,7 @@ int sqlite3ValueFromExpr( int nVal; assert( pExpr->u.zToken[0]=='x' || pExpr->u.zToken[0]=='X' ); assert( pExpr->u.zToken[1]=='\'' ); - pVal = sqlite3ValueNew(db); + pVal = valueNew(db, *ppVal); if( !pVal ) goto no_mem; zVal = &pExpr->u.zToken[2]; nVal = sqlite3Strlen30(zVal)-1; @@ -1117,11 +1122,104 @@ int sqlite3ValueFromExpr( no_mem: db->mallocFailed = 1; sqlite3DbFree(db, zVal); - sqlite3ValueFree(pVal); + if( *ppVal==0 ) sqlite3ValueFree(pVal); *ppVal = 0; return SQLITE_NOMEM; } +#ifdef SQLITE_ENABLE_STAT4 +int sqlite3Stat4ProbeSetValue( + Parse *pParse, /* Parse context */ + UnpackedRecord *pRec, /* Set field in this probe */ + Expr *pExpr, /* The expression to extract a value from */ + u8 affinity, /* Affinity to use */ + int iVal, /* Array element to populate */ + int *pbOk /* OUT: True if value was extracted */ +){ + int rc = SQLITE_OK; + sqlite3_value *pVal = &pRec->aMem[iVal]; + +#if 0 + if( iVal>0 ){ *pbOk = 0; return SQLITE_OK; } +#endif + + if( !pExpr ){ + sqlite3VdbeMemSetNull((Mem*)pVal); + *pbOk = 1; + }else if( pExpr->op==TK_VARIABLE + || (pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE) + ){ + Vdbe *v; + int iVar = pExpr->iColumn; + sqlite3VdbeSetVarmask(pParse->pVdbe, iVar); + if( v = pParse->pReprepare ){ + rc = sqlite3VdbeMemCopy((Mem*)pVal, &v->aVar[iVal-1]); + if( rc==SQLITE_OK ){ + sqlite3ValueApplyAffinity(pVal, affinity, SQLITE_UTF8); + } + pVal->db = pParse->db; + *pbOk = 1; + sqlite3VdbeMemStoreType((Mem*)pVal); + }else{ + *pbOk = 0; + } + }else{ + sqlite3 *db = pRec->aMem[0].db; + rc = sqlite3ValueFromExpr(db, pExpr, ENC(db), affinity, &pVal); + *pbOk = (pVal!=0); + } + assert( pVal==0 || pVal->db==pParse->db ); + return rc; +} + +void sqlite3Stat4ProbeFree(UnpackedRecord *pRec){ + if( pRec ){ + int i; + Mem *aMem = pRec->aMem; + sqlite3 *db = aMem[0].db; + for(i=0; ipKeyInfo->nField; i++){ + sqlite3DbFree(db, aMem[i].zMalloc); + } + sqlite3DbFree(db, pRec->pKeyInfo); + sqlite3DbFree(db, pRec); + } +} + +int sqlite3Stat4ProbeNew( + Parse *pParse, /* Parse context */ + Index *pIdx, /* Allocate record for this index */ + UnpackedRecord **ppRec /* OUT: Allocated record */ +){ + sqlite3 *db = pParse->db; /* Database handle */ + UnpackedRecord *pRec; /* Return value */ + int nByte; /* Bytes of space to allocate */ + int i; /* Counter variable */ + + assert( *ppRec==0 ); + if( pIdx->nSample==0 ) return SQLITE_OK; + + nByte = sizeof(Mem) * pIdx->nColumn + sizeof(UnpackedRecord); + *ppRec = pRec = (UnpackedRecord*)sqlite3DbMallocZero(db, nByte); + if( !pRec ) return SQLITE_NOMEM; + pRec->pKeyInfo = sqlite3IndexKeyinfo(pParse, pIdx); + if( !pRec->pKeyInfo ){ + sqlite3DbFree(db, pRec); + *ppRec = 0; + return SQLITE_NOMEM; + } + pRec->pKeyInfo->enc = ENC(pParse->db); + pRec->flags = UNPACKED_PREFIX_MATCH; + pRec->aMem = (Mem *)&pRec[1]; + for(i=0; inColumn; i++){ + pRec->aMem[i].flags = MEM_Null; + pRec->aMem[i].type = SQLITE_NULL; + pRec->aMem[i].db = db; + } + + return SQLITE_OK; +} +#endif /* ifdef SQLITE_ENABLE_STAT4 */ + /* ** Change the string value of an sqlite3_value object */ diff --git a/src/where.c b/src/where.c index 3bc82b0654..ad74928c94 100644 --- a/src/where.c +++ b/src/where.c @@ -390,6 +390,11 @@ struct WhereLoopBuilder { ExprList *pOrderBy; /* ORDER BY clause */ WhereLoop *pNew; /* Template WhereLoop */ WhereOrSet *pOrSet; /* Record best loops here, if not NULL */ +#ifdef SQLITE_ENABLE_STAT4 + UnpackedRecord *pRec; /* Probe for stat4 (if required) */ + int nRecValid; /* Number of valid fields currently in pRec */ + tRowcnt nMaxRowcnt; /* If !=0, the maximum estimated row count */ +#endif }; /* @@ -2405,33 +2410,21 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){ static int whereKeyStats( Parse *pParse, /* Database connection */ Index *pIdx, /* Index to consider domain of */ - sqlite3_value *pVal, /* Value to consider */ + UnpackedRecord *pRec, /* Vector of values to consider */ int roundUp, /* Round up if true. Round down if false */ tRowcnt *aStat /* OUT: stats written here */ ){ IndexSample *aSample = pIdx->aSample; - UnpackedRecord rec; int i; int isEq = 0; - if( pVal==0 ) return SQLITE_ERROR; - - memset(&rec, 0, sizeof(UnpackedRecord)); - rec.pKeyInfo = sqlite3IndexKeyinfo(pParse, pIdx); - if( rec.pKeyInfo==0 ) return SQLITE_NOMEM; - rec.pKeyInfo->enc = ENC(pParse->db); - rec.nField = 1; - rec.flags = UNPACKED_PREFIX_MATCH; - rec.aMem = pVal; - for(i=0; inSample; i++){ - int res = sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, &rec); + int res = sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec); if( res>=0 ){ isEq = (res==0); break; } } - sqlite3DbFree(pParse->db, rec.pKeyInfo); /* At this point, aSample[i] is the first sample that is greater than ** or equal to pVal. Or if i==pIdx->nSample, then all samples are less @@ -2467,41 +2460,6 @@ static int whereKeyStats( } #endif /* SQLITE_ENABLE_STAT4 */ -/* -** If expression pExpr represents a literal value, set *pp to point to -** an sqlite3_value structure containing the same value, with affinity -** aff applied to it, before returning. It is the responsibility of the -** caller to eventually release this structure by passing it to -** sqlite3ValueFree(). -** -** If the current parse is a recompile (sqlite3Reprepare()) and pExpr -** is an SQL variable that currently has a non-NULL value bound to it, -** create an sqlite3_value structure containing this value, again with -** affinity aff applied to it, instead. -** -** If neither of the above apply, set *pp to NULL. -** -** If an error occurs, return an error code. Otherwise, SQLITE_OK. -*/ -#ifdef SQLITE_ENABLE_STAT4 -static int valueFromExpr( - Parse *pParse, - Expr *pExpr, - u8 aff, - sqlite3_value **pp -){ - if( pExpr->op==TK_VARIABLE - || (pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE) - ){ - int iVar = pExpr->iColumn; - sqlite3VdbeSetVarmask(pParse->pVdbe, iVar); - *pp = sqlite3VdbeGetBoundValue(pParse->pReprepare, iVar, aff); - return SQLITE_OK; - } - return sqlite3ValueFromExpr(pParse->db, pExpr, ENC(pParse->db), aff, pp); -} -#endif - /* ** This function is used to estimate the number of rows that will be visited ** by scanning an index for a range of values. The range may have an upper @@ -2543,8 +2501,7 @@ static int valueFromExpr( */ static int whereRangeScanEst( Parse *pParse, /* Parsing & code generating context */ - Index *p, /* The index containing the range-compared column; "x" */ - int nEq, /* index into p->aCol[] of the range-compared column */ + WhereLoopBuilder *pBuilder, WhereTerm *pLower, /* Lower bound on the range. ex: "x>123" Might be NULL */ WhereTerm *pUpper, /* Upper bound on the range. ex: "x<455" Might be NULL */ WhereCost *pRangeDiv /* OUT: Reduce search space by this divisor */ @@ -2552,44 +2509,54 @@ static int whereRangeScanEst( int rc = SQLITE_OK; #ifdef SQLITE_ENABLE_STAT4 + Index *p = pBuilder->pNew->u.btree.pIndex; + int nEq = pBuilder->pNew->u.btree.nEq; - if( nEq==0 && p->nSample && OptimizationEnabled(pParse->db, SQLITE_Stat3) ){ - sqlite3_value *pRangeVal; + if( nEq==pBuilder->nRecValid + && p->nSample + && OptimizationEnabled(pParse->db, SQLITE_Stat3) + ){ + UnpackedRecord *pRec = pBuilder->pRec; tRowcnt iLower = 0; tRowcnt iUpper = p->aiRowEst[0]; tRowcnt a[2]; u8 aff = p->pTable->aCol[p->aiColumn[0]].affinity; - if( pLower ){ + int bOk; /* True if value is extracted from pExpr */ Expr *pExpr = pLower->pExpr->pRight; - rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal); assert( (pLower->eOperator & (WO_GT|WO_GE))!=0 ); - if( rc==SQLITE_OK - && whereKeyStats(pParse, p, pRangeVal, 0, a)==SQLITE_OK + rc = sqlite3Stat4ProbeSetValue(pParse, pRec, pExpr, aff, nEq, &bOk); + pRec->nField = nEq+1; + if( rc==SQLITE_OK && bOk + && whereKeyStats(pParse, p, pRec, 0, a)==SQLITE_OK ){ iLower = a[0]; if( (pLower->eOperator & WO_GT)!=0 ) iLower += a[1]; } - sqlite3ValueFree(pRangeVal); } if( rc==SQLITE_OK && pUpper ){ + int bOk; /* True if value is extracted from pExpr */ Expr *pExpr = pUpper->pExpr->pRight; - rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal); assert( (pUpper->eOperator & (WO_LT|WO_LE))!=0 ); - if( rc==SQLITE_OK - && whereKeyStats(pParse, p, pRangeVal, 1, a)==SQLITE_OK + rc = sqlite3Stat4ProbeSetValue(pParse, pRec, pExpr, aff, nEq, &bOk); + pRec->nField = nEq+1; + if( rc==SQLITE_OK && bOk + && whereKeyStats(pParse, p, pRec, 1, a)==SQLITE_OK ){ iUpper = a[0]; if( (pUpper->eOperator & WO_LE)!=0 ) iUpper += a[1]; } - sqlite3ValueFree(pRangeVal); } if( rc==SQLITE_OK ){ WhereCost iBase = whereCost(p->aiRowEst[0]); if( iUpper>iLower ){ iBase -= whereCost(iUpper - iLower); } - *pRangeDiv = iBase; + if( pBuilder->nMaxRowcnt && iBasenMaxRowcnt ){ + *pRangeDiv = pBuilder->nMaxRowcnt; + }else{ + *pRangeDiv = iBase; + } WHERETRACE(0x100, ("range scan regions: %u..%u div=%d\n", (u32)iLower, (u32)iUpper, *pRangeDiv)); return SQLITE_OK; @@ -2597,7 +2564,7 @@ static int whereRangeScanEst( } #else UNUSED_PARAMETER(pParse); - UNUSED_PARAMETER(p); + UNUSED_PARAMETER(pBuilder); UNUSED_PARAMETER(nEq); #endif assert( pLower || pUpper ); @@ -2633,32 +2600,52 @@ static int whereRangeScanEst( */ static int whereEqualScanEst( Parse *pParse, /* Parsing & code generating context */ - Index *p, /* The index whose left-most column is pTerm */ + WhereLoopBuilder *pBuilder, Expr *pExpr, /* Expression for VALUE in the x=VALUE constraint */ tRowcnt *pnRow /* Write the revised row estimate here */ ){ - sqlite3_value *pRhs = 0; /* VALUE on right-hand side of pTerm */ + Index *p = pBuilder->pNew->u.btree.pIndex; + int nEq = pBuilder->pNew->u.btree.nEq; + UnpackedRecord *pRec = pBuilder->pRec; u8 aff; /* Column affinity */ int rc; /* Subfunction return code */ tRowcnt a[2]; /* Statistics */ + int bOk; + assert( nEq>=1 ); + assert( nEq<=(p->nColumn+1) ); assert( p->aSample!=0 ); assert( p->nSample>0 ); - aff = p->pTable->aCol[p->aiColumn[0]].affinity; - if( pExpr ){ - rc = valueFromExpr(pParse, pExpr, aff, &pRhs); - if( rc ) goto whereEqualScanEst_cancel; - }else{ - pRhs = sqlite3ValueNew(pParse->db); + assert( pBuilder->nRecValidnRecValid<(nEq-1) ){ + return SQLITE_NOTFOUND; + } + + if( nEq>p->nColumn ){ + *pnRow = 1; + return SQLITE_OK; } - if( pRhs==0 ) return SQLITE_NOTFOUND; - rc = whereKeyStats(pParse, p, pRhs, 0, a); + + aff = p->pTable->aCol[p->aiColumn[0]].affinity; + rc = sqlite3Stat4ProbeSetValue(pParse, pRec, pExpr, aff, nEq-1, &bOk); + if( rc!=SQLITE_OK ) return rc; + if( bOk==0 ) return SQLITE_NOTFOUND; + + pBuilder->nRecValid = nEq; + pRec->nField = nEq; + + rc = whereKeyStats(pParse, p, pRec, 0, a); if( rc==SQLITE_OK ){ WHERETRACE(0x100,("equality scan regions: %d\n", (int)a[1])); *pnRow = a[1]; + if( pBuilder->nMaxRowcnt && *pnRow>pBuilder->nMaxRowcnt ){ + *pnRow = pBuilder->nMaxRowcnt; + } } -whereEqualScanEst_cancel: - sqlite3ValueFree(pRhs); + return rc; } #endif /* defined(SQLITE_ENABLE_STAT4) */ @@ -2682,10 +2669,12 @@ whereEqualScanEst_cancel: */ static int whereInScanEst( Parse *pParse, /* Parsing & code generating context */ - Index *p, /* The index whose left-most column is pTerm */ + WhereLoopBuilder *pBuilder, ExprList *pList, /* The value list on the RHS of "x IN (v1,v2,v3,...)" */ tRowcnt *pnRow /* Write the revised row estimate here */ ){ + Index *p = pBuilder->pNew->u.btree.pIndex; + int nRecValid = pBuilder->nRecValid; int rc = SQLITE_OK; /* Subfunction return code */ tRowcnt nEst; /* Number of rows for a single term */ tRowcnt nRowEst = 0; /* New estimate of the number of rows */ @@ -2694,14 +2683,21 @@ static int whereInScanEst( assert( p->aSample!=0 ); for(i=0; rc==SQLITE_OK && inExpr; i++){ nEst = p->aiRowEst[0]; - rc = whereEqualScanEst(pParse, p, pList->a[i].pExpr, &nEst); + rc = whereEqualScanEst(pParse, pBuilder, pList->a[i].pExpr, &nEst); nRowEst += nEst; + pBuilder->nRecValid = nRecValid; } + if( rc==SQLITE_OK ){ if( nRowEst > p->aiRowEst[0] ) nRowEst = p->aiRowEst[0]; - *pnRow = nRowEst; + if( pBuilder->nMaxRowcnt && nRowEst>pBuilder->nMaxRowcnt ){ + *pnRow = pBuilder->nMaxRowcnt; + }else{ + *pnRow = nRowEst; + } WHERETRACE(0x100,("IN row estimate: est=%g\n", nRowEst)); } + assert( pBuilder->nRecValid==nRecValid ); return rc; } #endif /* defined(SQLITE_ENABLE_STAT4) */ @@ -4248,12 +4244,15 @@ static int whereLoopAddBtreeIndex( rLogSize = estLog(whereCost(pProbe->aiRowEst[0])); for(; rc==SQLITE_OK && pTerm!=0; pTerm = whereScanNext(&scan)){ int nIn = 0; - if( pTerm->prereqRight & pNew->maskSelf ) continue; #ifdef SQLITE_ENABLE_STAT4 + int nRecValid = pBuilder->nRecValid; + int nMaxRowcnt = pBuilder->nMaxRowcnt; if( (pTerm->wtFlags & TERM_VNULL)!=0 && pSrc->pTab->aCol[iCol].notNull ){ continue; /* skip IS NOT NULL constraints on a NOT NULL column */ } #endif + if( pTerm->prereqRight & pNew->maskSelf ) continue; + pNew->wsFlags = saved_wsFlags; pNew->u.btree.nEq = saved_nEq; pNew->nLTerm = saved_nLTerm; @@ -4311,21 +4310,22 @@ static int whereLoopAddBtreeIndex( if( pNew->wsFlags & WHERE_COLUMN_RANGE ){ /* Adjust nOut and rRun for STAT3 range values */ WhereCost rDiv; - whereRangeScanEst(pParse, pProbe, pNew->u.btree.nEq, - pBtm, pTop, &rDiv); + whereRangeScanEst(pParse, pBuilder, pBtm, pTop, &rDiv); pNew->nOut = saved_nOut>rDiv+10 ? saved_nOut - rDiv : 10; } #ifdef SQLITE_ENABLE_STAT4 - if( pNew->u.btree.nEq==1 && pProbe->nSample - && OptimizationEnabled(db, SQLITE_Stat3) ){ + if( nInMul==0 && pProbe->nSample && OptimizationEnabled(db, SQLITE_Stat3) ){ + Expr *pExpr = pTerm->pExpr; tRowcnt nOut = 0; if( (pTerm->eOperator & (WO_EQ|WO_ISNULL))!=0 ){ testcase( pTerm->eOperator & WO_EQ ); testcase( pTerm->eOperator & WO_ISNULL ); - rc = whereEqualScanEst(pParse, pProbe, pTerm->pExpr->pRight, &nOut); + rc = whereEqualScanEst(pParse, pBuilder, pExpr->pRight, &nOut); + assert( nOut==0||pBuilder->nMaxRowcnt==0||nOut<=pBuilder->nMaxRowcnt); + if( nOut ) pBuilder->nMaxRowcnt = nOut; }else if( (pTerm->eOperator & WO_IN) - && !ExprHasProperty(pTerm->pExpr, EP_xIsSelect) ){ - rc = whereInScanEst(pParse, pProbe, pTerm->pExpr->x.pList, &nOut); + && !ExprHasProperty(pExpr, EP_xIsSelect) ){ + rc = whereInScanEst(pParse, pBuilder, pExpr->x.pList, &nOut); } assert( nOut==0 || rc==SQLITE_OK ); if( nOut ) pNew->nOut = whereCost(nOut); @@ -4345,6 +4345,10 @@ static int whereLoopAddBtreeIndex( ){ whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, nInMul+nIn); } +#ifdef SQLITE_ENABLE_STAT4 + pBuilder->nRecValid = nRecValid; + pBuilder->nMaxRowcnt = nMaxRowcnt; +#endif } pNew->prereq = saved_prereq; pNew->u.btree.nEq = saved_nEq; @@ -4571,7 +4575,16 @@ static int whereLoopAddBtree( if( rc ) break; } } - rc = whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, 0); + + assert( pBuilder->pRec==0 ); + rc = sqlite3Stat4ProbeNew(pWInfo->pParse, pProbe, &pBuilder->pRec); + if( rc==SQLITE_OK ){ + rc = whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, 0); + sqlite3Stat4ProbeFree(pBuilder->pRec); + pBuilder->nRecValid = 0; + pBuilder->pRec = 0; + } + assert( pBuilder->pRec==0 ); /* If there was an INDEXED BY clause, then only that one index is ** considered. */