1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-11-22 20:22:44 +03:00

Begin a branch that experimentally replaces sqlite_stat2 with a new table

called sqlite_stat3 that will hopefully facilitate better query
planning decisions.

FossilOrigin-Name: 52e1d7e8ddd4bb5ef3a9d00fd2d719a8a784f807
This commit is contained in:
drh
2011-08-12 01:51:45 +00:00
parent 90315a2417
commit faacf17cc1
13 changed files with 714 additions and 455 deletions

View File

@@ -118,7 +118,7 @@ struct WhereTerm {
#define TERM_ORINFO 0x10 /* Need to free the WhereTerm.u.pOrInfo object */
#define TERM_ANDINFO 0x20 /* Need to free the WhereTerm.u.pAndInfo obj */
#define TERM_OR_OK 0x40 /* Used during OR-clause processing */
#ifdef SQLITE_ENABLE_STAT2
#ifdef SQLITE_ENABLE_STAT3
# define TERM_VNULL 0x80 /* Manufactured x>NULL or x<=NULL term */
#else
# define TERM_VNULL 0x00 /* Disabled if not using stat2 */
@@ -1332,7 +1332,7 @@ static void exprAnalyze(
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
#ifdef SQLITE_ENABLE_STAT2
#ifdef SQLITE_ENABLE_STAT3
/* When sqlite_stat2 histogram data is available an operator of the
** form "x IS NOT NULL" can sometimes be evaluated more efficiently
** as "x>NULL" if x is not an INTEGER PRIMARY KEY. So construct a
@@ -1371,7 +1371,7 @@ static void exprAnalyze(
pNewTerm->prereqAll = pTerm->prereqAll;
}
}
#endif /* SQLITE_ENABLE_STAT2 */
#endif /* SQLITE_ENABLE_STAT */
/* Prevent ON clause terms of a LEFT JOIN from being used to drive
** an index for tables to the left of the join.
@@ -2420,67 +2420,70 @@ static void bestVirtualIndex(
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
#ifdef SQLITE_ENABLE_STAT3
/*
** Argument pIdx is a pointer to an index structure that has an array of
** SQLITE_INDEX_SAMPLES evenly spaced samples of the first indexed column
** stored in Index.aSample. These samples divide the domain of values stored
** the index into (SQLITE_INDEX_SAMPLES+1) regions.
** Region 0 contains all values less than the first sample value. Region
** 1 contains values between the first and second samples. Region 2 contains
** values between samples 2 and 3. And so on. Region SQLITE_INDEX_SAMPLES
** contains values larger than the last sample.
** Estimate the location of a particular key among all keys in an
** index. Store the results in aStat as follows:
**
** If the index contains many duplicates of a single value, then it is
** possible that two or more adjacent samples can hold the same value.
** When that is the case, the smallest possible region code is returned
** when roundUp is false and the largest possible region code is returned
** when roundUp is true.
** aStat[0] Est. number of rows less than pVal
** aStat[1] Est. number of rows equal to pVal
**
** If successful, this function determines which of the regions value
** pVal lies in, sets *piRegion to the region index (a value between 0
** and SQLITE_INDEX_SAMPLES+1, inclusive) and returns SQLITE_OK.
** Or, if an OOM occurs while converting text values between encodings,
** SQLITE_NOMEM is returned and *piRegion is undefined.
** Return SQLITE_OK on success.
*/
#ifdef SQLITE_ENABLE_STAT2
static int whereRangeRegion(
static int whereKeyStats(
Parse *pParse, /* Database connection */
Index *pIdx, /* Index to consider domain of */
sqlite3_value *pVal, /* Value to consider */
int roundUp, /* Return largest valid region if true */
int *piRegion /* OUT: Region of domain in which value lies */
int roundUp, /* Round up if true. Round down if false */
tRowcnt *aStat /* OUT: stats written here */
){
assert( roundUp==0 || roundUp==1 );
if( ALWAYS(pVal) ){
IndexSample *aSample = pIdx->aSample;
int i = 0;
int eType = sqlite3_value_type(pVal);
tRowcnt n;
IndexSample *aSample;
int i, eType;
int isEq = 0;
if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
double r = sqlite3_value_double(pVal);
for(i=0; i<SQLITE_INDEX_SAMPLES; i++){
if( aSample[i].eType==SQLITE_NULL ) continue;
if( aSample[i].eType>=SQLITE_TEXT ) break;
if( roundUp ){
if( aSample[i].u.r>r ) break;
}else{
if( aSample[i].u.r>=r ) break;
}
assert( roundUp==0 || roundUp==1 );
if( pVal==0 ) return SQLITE_ERROR;
n = pIdx->aiRowEst[0];
aSample = pIdx->aSample;
i = 0;
eType = sqlite3_value_type(pVal);
if( eType==SQLITE_INTEGER ){
i64 v = sqlite3_value_int64(pVal);
for(i=0; i<pIdx->nSample; i++){
if( aSample[i].eType==SQLITE_NULL ) continue;
if( aSample[i].eType>=SQLITE_TEXT ) break;
if( aSample[i].u.i>=v ){
isEq = aSample[i].u.i==v;
break;
}
}else if( eType==SQLITE_NULL ){
i = 0;
if( roundUp ){
while( i<SQLITE_INDEX_SAMPLES && aSample[i].eType==SQLITE_NULL ) i++;
}
}else if( eType==SQLITE_FLOAT ){
double r = sqlite3_value_double(pVal);
for(i=0; i<pIdx->nSample; i++){
if( aSample[i].eType==SQLITE_NULL ) continue;
if( aSample[i].eType>=SQLITE_TEXT ) break;
if( aSample[i].u.r>=r ){
isEq = aSample[i].u.r==r;
break;
}
}else{
}
}else if( eType==SQLITE_NULL ){
i = 0;
if( pIdx->nSample>=1 && aSample[0].eType==SQLITE_NULL ) isEq = 1;
}else{
assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB );
for(i=0; i<pIdx->nSample; i++){
if( aSample[i].eType==SQLITE_TEXT || aSample[i].eType==SQLITE_BLOB ){
break;
}
}
if( i<pIdx->nSample ){
sqlite3 *db = pParse->db;
CollSeq *pColl;
const u8 *z;
int n;
/* pVal comes from sqlite3ValueFromExpr() so the type cannot be NULL */
assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB );
if( eType==SQLITE_BLOB ){
z = (const u8 *)sqlite3_value_blob(pVal);
pColl = db->pDfltColl;
@@ -2499,12 +2502,12 @@ static int whereRangeRegion(
assert( z && pColl && pColl->xCmp );
}
n = sqlite3ValueBytes(pVal, pColl->enc);
for(i=0; i<SQLITE_INDEX_SAMPLES; i++){
for(; i<pIdx->nSample; i++){
int c;
int eSampletype = aSample[i].eType;
if( eSampletype==SQLITE_NULL || eSampletype<eType ) continue;
if( (eSampletype!=eType) ) break;
if( eSampletype<eType ) continue;
if( eSampletype!=eType ) break;
#ifndef SQLITE_OMIT_UTF16
if( pColl->enc!=SQLITE_UTF8 ){
int nSample;
@@ -2522,16 +2525,51 @@ static int whereRangeRegion(
{
c = pColl->xCmp(pColl->pUser, aSample[i].nByte, aSample[i].u.z, n, z);
}
if( c-roundUp>=0 ) break;
if( c>=0 ){
if( c==0 ) isEq = 1;
break;
}
}
}
}
assert( i>=0 && i<=SQLITE_INDEX_SAMPLES );
*piRegion = i;
/* 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
** than pVal. If aSample[i]==pVal, then isEq==1.
*/
if( isEq ){
assert( i<pIdx->nSample );
aStat[0] = aSample[i].nLt;
aStat[1] = aSample[i].nEq;
}else{
tRowcnt iLower, iUpper, iGap;
if( i==0 ){
iLower = 0;
iUpper = aSample[0].nLt;
}else if( i>=pIdx->nSample ){
iUpper = n;
iLower = aSample[i].nEq + aSample[i].nLt;
}else{
iLower = aSample[i-1].nEq + aSample[i-1].nLt;
iUpper = aSample[i].nLt;
}
aStat[1] = pIdx->aiRowEst[1];
if( iLower>=iUpper ){
iGap = 0;
}else{
iGap = iUpper - iLower;
if( iGap>=aStat[1]/2 ) iGap -= aStat[1]/2;
}
if( roundUp ){
iGap = (iGap*2)/3;
}else{
iGap = iGap/3;
}
aStat[0] = iLower + iGap;
}
return SQLITE_OK;
}
#endif /* #ifdef SQLITE_ENABLE_STAT2 */
#endif /* SQLITE_ENABLE_STAT3 */
/*
** If expression pExpr represents a literal value, set *pp to point to
@@ -2549,7 +2587,7 @@ static int whereRangeRegion(
**
** If an error occurs, return an error code. Otherwise, SQLITE_OK.
*/
#ifdef SQLITE_ENABLE_STAT2
#ifdef SQLITE_ENABLE_STAT3
static int valueFromExpr(
Parse *pParse,
Expr *pExpr,
@@ -2597,17 +2635,15 @@ static int valueFromExpr(
**
** then nEq should be passed 0.
**
** The returned value is an integer between 1 and 100, inclusive. A return
** value of 1 indicates that the proposed range scan is expected to visit
** approximately 1/100th (1%) of the rows selected by the nEq equality
** constraints (if any). A return value of 100 indicates that it is expected
** that the range scan will visit every row (100%) selected by the equality
** constraints.
** The returned value is an integer divisor to reduce the estimated
** search space. A return value of 1 means that range constraints are
** no help at all. A return value of 2 means range constraints are
** expected to reduce the search space by half. And so forth...
**
** In the absence of sqlite_stat2 ANALYZE data, each range inequality
** reduces the search space by 3/4ths. Hence a single constraint (x>?)
** results in a return of 25 and a range constraint (x>? AND x<?) results
** in a return of 6.
** In the absence of sqlite_stat3 ANALYZE data, each range inequality
** reduces the search space by a factor of 4. Hence a single constraint (x>?)
** results in a return of 4 and a range constraint (x>? AND x<?) results
** in a return of 16.
*/
static int whereRangeScanEst(
Parse *pParse, /* Parsing & code generating context */
@@ -2615,84 +2651,72 @@ static int whereRangeScanEst(
int nEq, /* index into p->aCol[] of the range-compared column */
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 */
int *piEst /* OUT: Return value */
tRowcnt *pRangeDiv /* OUT: Reduce search space by this divisor */
){
int rc = SQLITE_OK;
#ifdef SQLITE_ENABLE_STAT2
#ifdef SQLITE_ENABLE_STAT3
if( nEq==0 && p->aSample ){
sqlite3_value *pLowerVal = 0;
sqlite3_value *pUpperVal = 0;
int iEst;
int iLower = 0;
int iUpper = SQLITE_INDEX_SAMPLES;
int roundUpUpper = 0;
int roundUpLower = 0;
if( nEq==0 && p->nSample ){
sqlite3_value *pRangeVal;
tRowcnt iLower = 0;
tRowcnt iUpper = p->aiRowEst[0];
tRowcnt a[2];
u8 aff = p->pTable->aCol[p->aiColumn[0]].affinity;
if( pLower ){
Expr *pExpr = pLower->pExpr->pRight;
rc = valueFromExpr(pParse, pExpr, aff, &pLowerVal);
rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal);
assert( pLower->eOperator==WO_GT || pLower->eOperator==WO_GE );
roundUpLower = (pLower->eOperator==WO_GT) ?1:0;
if( rc==SQLITE_OK
&& whereKeyStats(pParse, p, pRangeVal, 0, a)==SQLITE_OK
){
iLower = a[0];
if( pLower->eOperator==WO_GT ) iLower += a[1];
}
sqlite3ValueFree(pRangeVal);
}
if( rc==SQLITE_OK && pUpper ){
Expr *pExpr = pUpper->pExpr->pRight;
rc = valueFromExpr(pParse, pExpr, aff, &pUpperVal);
rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal);
assert( pUpper->eOperator==WO_LT || pUpper->eOperator==WO_LE );
roundUpUpper = (pUpper->eOperator==WO_LE) ?1:0;
}
if( rc!=SQLITE_OK || (pLowerVal==0 && pUpperVal==0) ){
sqlite3ValueFree(pLowerVal);
sqlite3ValueFree(pUpperVal);
goto range_est_fallback;
}else if( pLowerVal==0 ){
rc = whereRangeRegion(pParse, p, pUpperVal, roundUpUpper, &iUpper);
if( pLower ) iLower = iUpper/2;
}else if( pUpperVal==0 ){
rc = whereRangeRegion(pParse, p, pLowerVal, roundUpLower, &iLower);
if( pUpper ) iUpper = (iLower + SQLITE_INDEX_SAMPLES + 1)/2;
}else{
rc = whereRangeRegion(pParse, p, pUpperVal, roundUpUpper, &iUpper);
if( rc==SQLITE_OK ){
rc = whereRangeRegion(pParse, p, pLowerVal, roundUpLower, &iLower);
if( rc==SQLITE_OK
&& whereKeyStats(pParse, p, pRangeVal, 1, a)==SQLITE_OK
){
iUpper = a[0];
if( pLower->eOperator==WO_LE ) iUpper += a[1];
}
sqlite3ValueFree(pRangeVal);
}
WHERETRACE(("range scan regions: %d..%d\n", iLower, iUpper));
iEst = iUpper - iLower;
testcase( iEst==SQLITE_INDEX_SAMPLES );
assert( iEst<=SQLITE_INDEX_SAMPLES );
if( iEst<1 ){
*piEst = 50/SQLITE_INDEX_SAMPLES;
}else{
*piEst = (iEst*100)/SQLITE_INDEX_SAMPLES;
if( rc==SQLITE_OK ){
if( iUpper<=iLower ){
*pRangeDiv = p->aiRowEst[0];
}else{
*pRangeDiv = p->aiRowEst[0]/(iUpper - iLower);
}
WHERETRACE(("range scan regions: %u..%u div=%u\n",
(u32)iLower, (u32)iUpper, (u32)*pRangeDiv));
return SQLITE_OK;
}
sqlite3ValueFree(pLowerVal);
sqlite3ValueFree(pUpperVal);
return rc;
}
range_est_fallback:
#else
UNUSED_PARAMETER(pParse);
UNUSED_PARAMETER(p);
UNUSED_PARAMETER(nEq);
#endif
assert( pLower || pUpper );
*piEst = 100;
if( pLower && (pLower->wtFlags & TERM_VNULL)==0 ) *piEst /= 4;
if( pUpper ) *piEst /= 4;
*pRangeDiv = 1;
if( pLower && (pLower->wtFlags & TERM_VNULL)==0 ) *pRangeDiv *= 4;
if( pUpper ) *pRangeDiv *= 4;
return rc;
}
#ifdef SQLITE_ENABLE_STAT2
#ifdef SQLITE_ENABLE_STAT3
/*
** Estimate the number of rows that will be returned based on
** an equality constraint x=VALUE and where that VALUE occurs in
** the histogram data. This only works when x is the left-most
** column of an index and sqlite_stat2 histogram data is available
** column of an index and sqlite_stat3 histogram data is available
** for that index. When pExpr==NULL that means the constraint is
** "x IS NULL" instead of "x=VALUE".
**
@@ -2712,10 +2736,9 @@ static int whereEqualScanEst(
double *pnRow /* Write the revised row estimate here */
){
sqlite3_value *pRhs = 0; /* VALUE on right-hand side of pTerm */
int iLower, iUpper; /* Range of histogram regions containing pRhs */
u8 aff; /* Column affinity */
int rc; /* Subfunction return code */
double nRowEst; /* New estimate of the number of rows */
tRowcnt a[2]; /* Statistics */
assert( p->aSample!=0 );
aff = p->pTable->aCol[p->aiColumn[0]].affinity;
@@ -2726,26 +2749,18 @@ static int whereEqualScanEst(
pRhs = sqlite3ValueNew(pParse->db);
}
if( pRhs==0 ) return SQLITE_NOTFOUND;
rc = whereRangeRegion(pParse, p, pRhs, 0, &iLower);
if( rc ) goto whereEqualScanEst_cancel;
rc = whereRangeRegion(pParse, p, pRhs, 1, &iUpper);
if( rc ) goto whereEqualScanEst_cancel;
WHERETRACE(("equality scan regions: %d..%d\n", iLower, iUpper));
if( iLower>=iUpper ){
nRowEst = p->aiRowEst[0]/(SQLITE_INDEX_SAMPLES*2);
if( nRowEst<*pnRow ) *pnRow = nRowEst;
}else{
nRowEst = (iUpper-iLower)*p->aiRowEst[0]/SQLITE_INDEX_SAMPLES;
*pnRow = nRowEst;
rc = whereKeyStats(pParse, p, pRhs, 0, a);
if( rc==SQLITE_OK ){
WHERETRACE(("equality scan regions: %d\n", (int)a[1]));
*pnRow = a[1];
}
whereEqualScanEst_cancel:
sqlite3ValueFree(pRhs);
return rc;
}
#endif /* defined(SQLITE_ENABLE_STAT2) */
#endif /* defined(SQLITE_ENABLE_STAT3) */
#ifdef SQLITE_ENABLE_STAT2
#ifdef SQLITE_ENABLE_STAT3
/*
** Estimate the number of rows that will be returned based on
** an IN constraint where the right-hand side of the IN operator
@@ -2768,60 +2783,25 @@ static int whereInScanEst(
ExprList *pList, /* The value list on the RHS of "x IN (v1,v2,v3,...)" */
double *pnRow /* Write the revised row estimate here */
){
sqlite3_value *pVal = 0; /* One value from list */
int iLower, iUpper; /* Range of histogram regions containing pRhs */
u8 aff; /* Column affinity */
int rc = SQLITE_OK; /* Subfunction return code */
double nEst; /* Number of rows for a single term */
double nRowEst; /* New estimate of the number of rows */
int nSpan = 0; /* Number of histogram regions spanned */
int nSingle = 0; /* Histogram regions hit by a single value */
int nNotFound = 0; /* Count of values that are not constants */
int i; /* Loop counter */
u8 aSpan[SQLITE_INDEX_SAMPLES+1]; /* Histogram regions that are spanned */
u8 aSingle[SQLITE_INDEX_SAMPLES+1]; /* Histogram regions hit once */
int i; /* Loop counter */
assert( p->aSample!=0 );
aff = p->pTable->aCol[p->aiColumn[0]].affinity;
memset(aSpan, 0, sizeof(aSpan));
memset(aSingle, 0, sizeof(aSingle));
for(i=0; i<pList->nExpr; i++){
sqlite3ValueFree(pVal);
rc = valueFromExpr(pParse, pList->a[i].pExpr, aff, &pVal);
if( rc ) break;
if( pVal==0 || sqlite3_value_type(pVal)==SQLITE_NULL ){
nNotFound++;
continue;
}
rc = whereRangeRegion(pParse, p, pVal, 0, &iLower);
if( rc ) break;
rc = whereRangeRegion(pParse, p, pVal, 1, &iUpper);
if( rc ) break;
if( iLower>=iUpper ){
aSingle[iLower] = 1;
}else{
assert( iLower>=0 && iUpper<=SQLITE_INDEX_SAMPLES );
while( iLower<iUpper ) aSpan[iLower++] = 1;
}
for(i=0; rc==SQLITE_OK && i<pList->nExpr; i++){
nEst = p->aiRowEst[0];
rc = whereEqualScanEst(pParse, p, pList->a[i].pExpr, &nEst);
nRowEst += nEst;
}
if( rc==SQLITE_OK ){
for(i=nSpan=0; i<=SQLITE_INDEX_SAMPLES; i++){
if( aSpan[i] ){
nSpan++;
}else if( aSingle[i] ){
nSingle++;
}
}
nRowEst = (nSpan*2+nSingle)*p->aiRowEst[0]/(2*SQLITE_INDEX_SAMPLES)
+ nNotFound*p->aiRowEst[1];
if( nRowEst > p->aiRowEst[0] ) nRowEst = p->aiRowEst[0];
*pnRow = nRowEst;
WHERETRACE(("IN row estimate: nSpan=%d, nSingle=%d, nNotFound=%d, est=%g\n",
nSpan, nSingle, nNotFound, nRowEst));
WHERETRACE(("IN row estimate: est=%g\n", nRowEst));
}
sqlite3ValueFree(pVal);
return rc;
}
#endif /* defined(SQLITE_ENABLE_STAT2) */
#endif /* defined(SQLITE_ENABLE_STAT3) */
/*
@@ -2868,7 +2848,7 @@ static void bestBtreeIndex(
int eqTermMask; /* Current mask of valid equality operators */
int idxEqTermMask; /* Index mask of valid equality operators */
Index sPk; /* A fake index object for the primary key */
unsigned int aiRowEstPk[2]; /* The aiRowEst[] value for the sPk index */
tRowcnt aiRowEstPk[2]; /* The aiRowEst[] value for the sPk index */
int aiColumnPk = -1; /* The aColumn[] value for the sPk index */
int wsFlagMask; /* Allowed flags in pCost->plan.wsFlag */
@@ -2923,7 +2903,7 @@ static void bestBtreeIndex(
/* Loop over all indices looking for the best one to use
*/
for(; pProbe; pIdx=pProbe=pProbe->pNext){
const unsigned int * const aiRowEst = pProbe->aiRowEst;
const tRowcnt * const aiRowEst = pProbe->aiRowEst;
double cost; /* Cost of using pProbe */
double nRow; /* Estimated number of rows in result set */
double log10N; /* base-10 logarithm of nRow (inexact) */
@@ -2966,14 +2946,12 @@ static void bestBtreeIndex(
** IN operator must be a SELECT, not a value list, for this variable
** to be true.
**
** estBound:
** An estimate on the amount of the table that must be searched. A
** value of 100 means the entire table is searched. Range constraints
** might reduce this to a value less than 100 to indicate that only
** a fraction of the table needs searching. In the absence of
** sqlite_stat2 ANALYZE data, a single inequality reduces the search
** space to 1/4rd its original size. So an x>? constraint reduces
** estBound to 25. Two constraints (x>? AND x<?) reduce estBound to 6.
** rangeDiv:
** An estimate of a divisor by which to reduce the search space due
** to inequality constraints. In the absence of sqlite_stat3 ANALYZE
** data, a single inequality reduces the search space to 1/4rd its
** original size (rangeDiv==4). Two inequalities reduce the search
** space to 1/16th of its original size (rangeDiv==16).
**
** bSort:
** Boolean. True if there is an ORDER BY clause that will require an
@@ -2998,13 +2976,13 @@ static void bestBtreeIndex(
int nEq; /* Number of == or IN terms matching index */
int bInEst = 0; /* True if "x IN (SELECT...)" seen */
int nInMul = 1; /* Number of distinct equalities to lookup */
int estBound = 100; /* Estimated reduction in search space */
tRowcnt rangeDiv = 1; /* Estimated reduction in search space */
int nBound = 0; /* Number of range constraints seen */
int bSort = !!pOrderBy; /* True if external sort required */
int bDist = !!pDistinct; /* True if index cannot help with DISTINCT */
int bLookup = 0; /* True if not a covering index */
WhereTerm *pTerm; /* A single term of the WHERE clause */
#ifdef SQLITE_ENABLE_STAT2
#ifdef SQLITE_ENABLE_STAT3
WhereTerm *pFirstTerm = 0; /* First term matching the index */
#endif
@@ -3028,19 +3006,19 @@ static void bestBtreeIndex(
}else if( pTerm->eOperator & WO_ISNULL ){
wsFlags |= WHERE_COLUMN_NULL;
}
#ifdef SQLITE_ENABLE_STAT2
#ifdef SQLITE_ENABLE_STAT3
if( nEq==0 && pProbe->aSample ) pFirstTerm = pTerm;
#endif
used |= pTerm->prereqRight;
}
/* Determine the value of estBound. */
/* Determine the value of rangeDiv */
if( nEq<pProbe->nColumn && pProbe->bUnordered==0 ){
int j = pProbe->aiColumn[nEq];
if( findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE|WO_GT|WO_GE, pIdx) ){
WhereTerm *pTop = findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE, pIdx);
WhereTerm *pBtm = findTerm(pWC, iCur, j, notReady, WO_GT|WO_GE, pIdx);
whereRangeScanEst(pParse, pProbe, nEq, pBtm, pTop, &estBound);
whereRangeScanEst(pParse, pProbe, nEq, pBtm, pTop, &rangeDiv);
if( pTop ){
nBound = 1;
wsFlags |= WHERE_TOP_LIMIT;
@@ -3112,7 +3090,7 @@ static void bestBtreeIndex(
nInMul = (int)(nRow / aiRowEst[nEq]);
}
#ifdef SQLITE_ENABLE_STAT2
#ifdef SQLITE_ENABLE_STAT3
/* If the constraint is of the form x=VALUE or x IN (E1,E2,...)
** and we do not think that values of x are unique and if histogram
** data is available for column x, then it might be possible
@@ -3128,12 +3106,12 @@ static void bestBtreeIndex(
whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList, &nRow);
}
}
#endif /* SQLITE_ENABLE_STAT2 */
#endif /* SQLITE_ENABLE_STAT3 */
/* Adjust the number of output rows and downward to reflect rows
** that are excluded by range constraints.
*/
nRow = (nRow * (double)estBound) / (double)100;
nRow = nRow/(double)rangeDiv;
if( nRow<1 ) nRow = 1;
/* Experiments run on real SQLite databases show that the time needed
@@ -3262,10 +3240,10 @@ static void bestBtreeIndex(
WHERETRACE((
"%s(%s): nEq=%d nInMul=%d estBound=%d bSort=%d bLookup=%d wsFlags=0x%x\n"
"%s(%s): nEq=%d nInMul=%d rangeDiv=%d bSort=%d bLookup=%d wsFlags=0x%x\n"
" notReady=0x%llx log10N=%.1f nRow=%.1f cost=%.1f used=0x%llx\n",
pSrc->pTab->zName, (pIdx ? pIdx->zName : "ipk"),
nEq, nInMul, estBound, bSort, bLookup, wsFlags,
nEq, nInMul, (int)rangeDiv, bSort, bLookup, wsFlags,
notReady, log10N, nRow, cost, used
));