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
This commit is contained in:
parent
c612970c9d
commit
7a4192358a
20
manifest
20
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
|
||||
|
@ -1 +1 @@
|
||||
4a51cf289fad8aebc637b5f96488de18e861195d
|
||||
2973f5ca736c4a6f13c653d54b6a29d7cae8d0ed
|
@ -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;
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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));
|
||||
|
106
src/vdbemem.c
106
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; i<pRec->pKeyInfo->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; i<pIdx->nColumn; 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
|
||||
*/
|
||||
|
189
src/where.c
189
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; i<pIdx->nSample; 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 && iBase<pBuilder->nMaxRowcnt ){
|
||||
*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->nRecValid<nEq );
|
||||
|
||||
/* If values are not available for all fields of the index to the left
|
||||
** of this one, no estimate can be made. Return SQLITE_NOTFOUND. */
|
||||
if( pBuilder->nRecValid<(nEq-1) ){
|
||||
return SQLITE_NOTFOUND;
|
||||
}
|
||||
if( pRhs==0 ) return SQLITE_NOTFOUND;
|
||||
rc = whereKeyStats(pParse, p, pRhs, 0, a);
|
||||
|
||||
if( nEq>p->nColumn ){
|
||||
*pnRow = 1;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
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 && i<pList->nExpr; 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. */
|
||||
|
Loading…
Reference in New Issue
Block a user