mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-21 09:00:59 +03:00
Merge orderby-planning with this branch.
FossilOrigin-Name: d9549de31741239ece060e448b592ce8fc5b8042
This commit is contained in:
@@ -1371,6 +1371,7 @@ static void decodeIntArray(
|
||||
char *zIntArray, /* String containing int array to decode */
|
||||
int nOut, /* Number of slots in aOut[] */
|
||||
tRowcnt *aOut, /* Store integers here */
|
||||
LogEst *aLog, /* Or, if aOut==0, here */
|
||||
Index *pIndex /* Handle extra flags for this index, if not NULL */
|
||||
){
|
||||
char *z = zIntArray;
|
||||
@@ -1389,7 +1390,17 @@ static void decodeIntArray(
|
||||
v = v*10 + c - '0';
|
||||
z++;
|
||||
}
|
||||
aOut[i] = v;
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
if( aOut ){
|
||||
aOut[i] = v;
|
||||
}else
|
||||
#else
|
||||
assert( aOut==0 );
|
||||
UNUSED_PARAMETER(aOut);
|
||||
#endif
|
||||
{
|
||||
aLog[i] = sqlite3LogEst(v);
|
||||
}
|
||||
if( *z==' ' ) z++;
|
||||
}
|
||||
#ifndef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
@@ -1445,12 +1456,12 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
|
||||
z = argv[2];
|
||||
|
||||
if( pIndex ){
|
||||
decodeIntArray((char*)z, pIndex->nKeyCol+1, pIndex->aiRowEst, pIndex);
|
||||
if( pIndex->pPartIdxWhere==0 ) pTable->nRowEst = pIndex->aiRowEst[0];
|
||||
decodeIntArray((char*)z, pIndex->nKeyCol+1, 0, pIndex->aiRowLogEst, pIndex);
|
||||
if( pIndex->pPartIdxWhere==0 ) pTable->nRowLogEst = pIndex->aiRowLogEst[0];
|
||||
}else{
|
||||
Index fakeIdx;
|
||||
fakeIdx.szIdxRow = pTable->szTabRow;
|
||||
decodeIntArray((char*)z, 1, &pTable->nRowEst, &fakeIdx);
|
||||
decodeIntArray((char*)z, 1, 0, &pTable->nRowLogEst, &fakeIdx);
|
||||
pTable->szTabRow = fakeIdx.szIdxRow;
|
||||
}
|
||||
|
||||
@@ -1642,9 +1653,9 @@ static int loadStatTbl(
|
||||
pPrevIdx = pIdx;
|
||||
}
|
||||
pSample = &pIdx->aSample[pIdx->nSample];
|
||||
decodeIntArray((char*)sqlite3_column_text(pStmt,1), nCol, pSample->anEq, 0);
|
||||
decodeIntArray((char*)sqlite3_column_text(pStmt,2), nCol, pSample->anLt, 0);
|
||||
decodeIntArray((char*)sqlite3_column_text(pStmt,3), nCol, pSample->anDLt,0);
|
||||
decodeIntArray((char*)sqlite3_column_text(pStmt,1),nCol,pSample->anEq,0,0);
|
||||
decodeIntArray((char*)sqlite3_column_text(pStmt,2),nCol,pSample->anLt,0,0);
|
||||
decodeIntArray((char*)sqlite3_column_text(pStmt,3),nCol,pSample->anDLt,0,0);
|
||||
|
||||
/* Take a copy of the sample. Add two 0x00 bytes the end of the buffer.
|
||||
** This is in case the sample record is corrupted. In that case, the
|
||||
|
||||
45
src/build.c
45
src/build.c
@@ -905,7 +905,7 @@ void sqlite3StartTable(
|
||||
pTable->iPKey = -1;
|
||||
pTable->pSchema = db->aDb[iDb].pSchema;
|
||||
pTable->nRef = 1;
|
||||
pTable->nRowEst = 1048576;
|
||||
pTable->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
|
||||
assert( pParse->pNewTable==0 );
|
||||
pParse->pNewTable = pTable;
|
||||
|
||||
@@ -2730,15 +2730,15 @@ Index *sqlite3AllocateIndexObject(
|
||||
|
||||
nByte = ROUND8(sizeof(Index)) + /* Index structure */
|
||||
ROUND8(sizeof(char*)*nCol) + /* Index.azColl */
|
||||
ROUND8(sizeof(tRowcnt)*(nCol+1) + /* Index.aiRowEst */
|
||||
ROUND8(sizeof(LogEst)*(nCol+1) + /* Index.aiRowLogEst */
|
||||
sizeof(i16)*nCol + /* Index.aiColumn */
|
||||
sizeof(u8)*nCol); /* Index.aSortOrder */
|
||||
p = sqlite3DbMallocZero(db, nByte + nExtra);
|
||||
if( p ){
|
||||
char *pExtra = ((char*)p)+ROUND8(sizeof(Index));
|
||||
p->azColl = (char**)pExtra; pExtra += ROUND8(sizeof(char*)*nCol);
|
||||
p->aiRowEst = (tRowcnt*)pExtra; pExtra += sizeof(tRowcnt)*(nCol+1);
|
||||
p->aiColumn = (i16*)pExtra; pExtra += sizeof(i16)*nCol;
|
||||
p->azColl = (char**)pExtra; pExtra += ROUND8(sizeof(char*)*nCol);
|
||||
p->aiRowLogEst = (LogEst*)pExtra; pExtra += sizeof(LogEst)*(nCol+1);
|
||||
p->aiColumn = (i16*)pExtra; pExtra += sizeof(i16)*nCol;
|
||||
p->aSortOrder = (u8*)pExtra;
|
||||
p->nColumn = nCol;
|
||||
p->nKeyCol = nCol - 1;
|
||||
@@ -2968,7 +2968,7 @@ Index *sqlite3CreateIndex(
|
||||
if( db->mallocFailed ){
|
||||
goto exit_create_index;
|
||||
}
|
||||
assert( EIGHT_BYTE_ALIGNMENT(pIndex->aiRowEst) );
|
||||
assert( EIGHT_BYTE_ALIGNMENT(pIndex->aiRowLogEst) );
|
||||
assert( EIGHT_BYTE_ALIGNMENT(pIndex->azColl) );
|
||||
pIndex->zName = zExtra;
|
||||
zExtra += nName + 1;
|
||||
@@ -3249,7 +3249,7 @@ exit_create_index:
|
||||
** Since we do not know, guess 1 million. aiRowEst[1] is an estimate of the
|
||||
** number of rows in the table that match any particular value of the
|
||||
** first column of the index. aiRowEst[2] is an estimate of the number
|
||||
** of rows that match any particular combiniation of the first 2 columns
|
||||
** of rows that match any particular combination of the first 2 columns
|
||||
** of the index. And so forth. It must always be the case that
|
||||
*
|
||||
** aiRowEst[N]<=aiRowEst[N-1]
|
||||
@@ -3260,20 +3260,27 @@ exit_create_index:
|
||||
** are based on typical values found in actual indices.
|
||||
*/
|
||||
void sqlite3DefaultRowEst(Index *pIdx){
|
||||
tRowcnt *a = pIdx->aiRowEst;
|
||||
/* 10, 9, 8, 7, 6 */
|
||||
LogEst aVal[] = { 33, 32, 30, 28, 26 };
|
||||
LogEst *a = pIdx->aiRowLogEst;
|
||||
int nCopy = MIN(ArraySize(aVal), pIdx->nKeyCol);
|
||||
int i;
|
||||
tRowcnt n;
|
||||
assert( a!=0 );
|
||||
a[0] = pIdx->pTable->nRowEst;
|
||||
if( a[0]<10 ) a[0] = 10;
|
||||
n = 10;
|
||||
for(i=1; i<=pIdx->nKeyCol; i++){
|
||||
a[i] = n;
|
||||
if( n>5 ) n--;
|
||||
}
|
||||
if( pIdx->onError!=OE_None ){
|
||||
a[pIdx->nKeyCol] = 1;
|
||||
|
||||
/* Set the first entry (number of rows in the index) to the estimated
|
||||
** number of rows in the table. Or 10, if the estimated number of rows
|
||||
** in the table is less than that. */
|
||||
a[0] = pIdx->pTable->nRowLogEst;
|
||||
if( a[0]<33 ) a[0] = 33; assert( 33==sqlite3LogEst(10) );
|
||||
|
||||
/* Estimate that a[1] is 10, a[2] is 9, a[3] is 8, a[4] is 7, a[5] is
|
||||
** 6 and each subsequent value (if any) is 5. */
|
||||
memcpy(&a[1], aVal, nCopy*sizeof(LogEst));
|
||||
for(i=nCopy+1; i<=pIdx->nKeyCol; i++){
|
||||
a[i] = 23; assert( 23==sqlite3LogEst(5) );
|
||||
}
|
||||
|
||||
assert( 0==sqlite3LogEst(1) );
|
||||
if( pIdx->onError!=OE_None ) a[pIdx->nKeyCol] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
15
src/insert.c
15
src/insert.c
@@ -1865,15 +1865,24 @@ static int xferOptimization(
|
||||
return 0; /* Both tables must have the same INTEGER PRIMARY KEY */
|
||||
}
|
||||
for(i=0; i<pDest->nCol; 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 ){
|
||||
|
||||
@@ -1488,13 +1488,15 @@ void sqlite3Pragma(
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, 2);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer,
|
||||
(int)sqlite3LogEstToInt(pTab->szTabRow), 3);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, (int)pTab->nRowEst, 4);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer,
|
||||
(int)sqlite3LogEstToInt(pTab->nRowLogEst), 4);
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 4);
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, pIdx->zName, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer,
|
||||
(int)sqlite3LogEstToInt(pIdx->szIdxRow), 3);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, (int)pIdx->aiRowEst[0], 4);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer,
|
||||
(int)sqlite3LogEstToInt(pIdx->aiRowLogEst[0]), 4);
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 4);
|
||||
}
|
||||
}
|
||||
|
||||
23
src/select.c
23
src/select.c
@@ -471,7 +471,7 @@ static void pushOntoSorter(
|
||||
int nExpr = pSort->pOrderBy->nExpr; /* No. of ORDER BY terms */
|
||||
int nBase = nExpr + bSeq + nData; /* Fields in sorter record */
|
||||
int regBase; /* Regs for sorter record */
|
||||
int regRecord = sqlite3GetTempReg(pParse); /* Assembled sorter record */
|
||||
int regRecord = ++pParse->nMem; /* Assembled sorter record */
|
||||
int nOBSat = pSort->nOBSat; /* ORDER BY terms to skip */
|
||||
int op; /* Opcode to add sorter record to sorter */
|
||||
|
||||
@@ -480,7 +480,8 @@ static void pushOntoSorter(
|
||||
assert( nPrefixReg==nExpr+bSeq );
|
||||
regBase = regData - nExpr - bSeq;
|
||||
}else{
|
||||
regBase = sqlite3GetTempRange(pParse, nBase);
|
||||
regBase = pParse->nMem + 1;
|
||||
pParse->nMem += nBase;
|
||||
}
|
||||
sqlite3ExprCodeExprList(pParse, pSort->pOrderBy, regBase, SQLITE_ECEL_DUP);
|
||||
if( bSeq ){
|
||||
@@ -511,7 +512,7 @@ static void pushOntoSorter(
|
||||
sqlite3VdbeAddOp3(v, OP_Compare, regPrevKey, regBase, pSort->nOBSat);
|
||||
pOp = sqlite3VdbeGetOp(v, pSort->addrSortIndex);
|
||||
if( pParse->db->mallocFailed ) return;
|
||||
pOp->p2 = nKey + 1;
|
||||
pOp->p2 = nKey + nData;
|
||||
pKI = pOp->p4.pKeyInfo;
|
||||
memset(pKI->aSortOrder, 0, pKI->nField); /* Makes OP_Jump below testable */
|
||||
sqlite3VdbeChangeP4(v, -1, (char*)pKI, P4_KEYINFO);
|
||||
@@ -532,12 +533,6 @@ static void pushOntoSorter(
|
||||
op = OP_IdxInsert;
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, op, pSort->iECursor, regRecord);
|
||||
if( nOBSat==0 ){
|
||||
sqlite3ReleaseTempReg(pParse, regRecord);
|
||||
if( nPrefixReg==0 ){
|
||||
sqlite3ReleaseTempRange(pParse, regBase, nBase);
|
||||
}
|
||||
}
|
||||
if( pSelect->iLimit ){
|
||||
int addr1, addr2;
|
||||
int iLimit;
|
||||
@@ -1722,7 +1717,7 @@ Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect){
|
||||
assert( db->lookaside.bEnabled==0 );
|
||||
pTab->nRef = 1;
|
||||
pTab->zName = 0;
|
||||
pTab->nRowEst = 1048576;
|
||||
pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
|
||||
selectColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol);
|
||||
selectAddColumnTypeAndCollation(pParse, pTab, pSelect);
|
||||
pTab->iPKey = -1;
|
||||
@@ -3861,7 +3856,7 @@ static int withExpand(
|
||||
pTab->nRef = 1;
|
||||
pTab->zName = sqlite3DbStrDup(db, pCte->zName);
|
||||
pTab->iPKey = -1;
|
||||
pTab->nRowEst = 1048576;
|
||||
pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
|
||||
pTab->tabFlags |= TF_Ephemeral;
|
||||
pFrom->pSelect = sqlite3SelectDup(db, pCte->pSelect, 0);
|
||||
if( db->mallocFailed ) return SQLITE_NOMEM;
|
||||
@@ -4037,7 +4032,7 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
while( pSel->pPrior ){ pSel = pSel->pPrior; }
|
||||
selectColumnsFromExprList(pParse, pSel->pEList, &pTab->nCol, &pTab->aCol);
|
||||
pTab->iPKey = -1;
|
||||
pTab->nRowEst = 1048576;
|
||||
pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
|
||||
pTab->tabFlags |= TF_Ephemeral;
|
||||
#endif
|
||||
}else{
|
||||
@@ -4687,7 +4682,7 @@ int sqlite3Select(
|
||||
sqlite3SelectDestInit(&dest, SRT_Coroutine, pItem->regReturn);
|
||||
explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
|
||||
sqlite3Select(pParse, pSub, &dest);
|
||||
pItem->pTab->nRowEst = (unsigned)pSub->nSelectRow;
|
||||
pItem->pTab->nRowLogEst = sqlite3LogEst(pSub->nSelectRow);
|
||||
pItem->viaCoroutine = 1;
|
||||
pItem->regResult = dest.iSdst;
|
||||
sqlite3VdbeAddOp1(v, OP_EndCoroutine, pItem->regReturn);
|
||||
@@ -4718,7 +4713,7 @@ int sqlite3Select(
|
||||
sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor);
|
||||
explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
|
||||
sqlite3Select(pParse, pSub, &dest);
|
||||
pItem->pTab->nRowEst = (unsigned)pSub->nSelectRow;
|
||||
pItem->pTab->nRowLogEst = sqlite3LogEst(pSub->nSelectRow);
|
||||
if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr);
|
||||
retAddr = sqlite3VdbeAddOp1(v, OP_Return, pItem->regReturn);
|
||||
VdbeComment((v, "end %s", pItem->pTab->zName));
|
||||
|
||||
@@ -538,10 +538,10 @@ typedef INT8_TYPE i8; /* 1-byte signed integer */
|
||||
** gives a possible range of values of approximately 1.0e986 to 1e-986.
|
||||
** But the allowed values are "grainy". Not every value is representable.
|
||||
** For example, quantities 16 and 17 are both represented by a LogEst
|
||||
** of 40. However, since LogEst quantatites are suppose to be estimates,
|
||||
** of 40. However, since LogEst quantaties are suppose to be estimates,
|
||||
** not exact values, this imprecision is not a problem.
|
||||
**
|
||||
** "LogEst" is short for "Logarithimic Estimate".
|
||||
** "LogEst" is short for "Logarithmic Estimate".
|
||||
**
|
||||
** Examples:
|
||||
** 1 -> 0 20 -> 43 10000 -> 132
|
||||
@@ -1486,7 +1486,7 @@ struct Table {
|
||||
#ifndef SQLITE_OMIT_CHECK
|
||||
ExprList *pCheck; /* All CHECK constraints */
|
||||
#endif
|
||||
tRowcnt nRowEst; /* Estimated rows in table - from sqlite_stat1 table */
|
||||
LogEst nRowLogEst; /* Estimated rows in table - from sqlite_stat1 table */
|
||||
int tnum; /* Root BTree node for this table (see note above) */
|
||||
i16 iPKey; /* If not negative, use aCol[iPKey] as the primary key */
|
||||
i16 nCol; /* Number of columns in this table */
|
||||
@@ -1695,7 +1695,7 @@ struct UnpackedRecord {
|
||||
struct Index {
|
||||
char *zName; /* Name of this index */
|
||||
i16 *aiColumn; /* Which columns are used by this index. 1st is 0 */
|
||||
tRowcnt *aiRowEst; /* From ANALYZE: Est. rows selected by each column */
|
||||
LogEst *aiRowLogEst; /* From ANALYZE: Est. rows selected by each column */
|
||||
Table *pTable; /* The SQL table being indexed */
|
||||
char *zColAff; /* String defining the affinity of each column */
|
||||
Index *pNext; /* The next index associated with the same table */
|
||||
|
||||
200
src/test_rtree.c
200
src/test_rtree.c
@@ -35,6 +35,8 @@ struct Circle {
|
||||
double centerx;
|
||||
double centery;
|
||||
double radius;
|
||||
double mxArea;
|
||||
int eScoreType;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -50,11 +52,7 @@ static void circle_del(void *p){
|
||||
static int circle_geom(
|
||||
sqlite3_rtree_geometry *p,
|
||||
int nCoord,
|
||||
#ifdef SQLITE_RTREE_INT_ONLY
|
||||
sqlite3_int64 *aCoord,
|
||||
#else
|
||||
double *aCoord,
|
||||
#endif
|
||||
sqlite3_rtree_dbl *aCoord,
|
||||
int *pRes
|
||||
){
|
||||
int i; /* Iterator variable */
|
||||
@@ -62,7 +60,12 @@ static int circle_geom(
|
||||
double xmin, xmax; /* X dimensions of box being tested */
|
||||
double ymin, ymax; /* X dimensions of box being tested */
|
||||
|
||||
if( p->pUser==0 ){
|
||||
xmin = aCoord[0];
|
||||
xmax = aCoord[1];
|
||||
ymin = aCoord[2];
|
||||
ymax = aCoord[3];
|
||||
pCircle = (Circle *)p->pUser;
|
||||
if( pCircle==0 ){
|
||||
/* If pUser is still 0, then the parameter values have not been tested
|
||||
** for correctness or stored into a Circle structure yet. Do this now. */
|
||||
|
||||
@@ -108,14 +111,9 @@ static int circle_geom(
|
||||
pCircle->aBox[1].xmax = pCircle->centerx - pCircle->radius;
|
||||
pCircle->aBox[1].ymin = pCircle->centery;
|
||||
pCircle->aBox[1].ymax = pCircle->centery;
|
||||
pCircle->mxArea = (xmax - xmin)*(ymax - ymin) + 1.0;
|
||||
}
|
||||
|
||||
pCircle = (Circle *)p->pUser;
|
||||
xmin = aCoord[0];
|
||||
xmax = aCoord[1];
|
||||
ymin = aCoord[2];
|
||||
ymax = aCoord[3];
|
||||
|
||||
/* Check if any of the 4 corners of the bounding-box being tested lie
|
||||
** inside the circular region. If they do, then the bounding-box does
|
||||
** intersect the region of interest. Set the output variable to true and
|
||||
@@ -154,6 +152,170 @@ static int circle_geom(
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of "circle" r-tree geometry callback using the
|
||||
** 2nd-generation interface that allows scoring.
|
||||
*/
|
||||
static int circle_query_func(sqlite3_rtree_query_info *p){
|
||||
int i; /* Iterator variable */
|
||||
Circle *pCircle; /* Structure defining circular region */
|
||||
double xmin, xmax; /* X dimensions of box being tested */
|
||||
double ymin, ymax; /* X dimensions of box being tested */
|
||||
int nWithin = 0; /* Number of corners inside the circle */
|
||||
|
||||
xmin = p->aCoord[0];
|
||||
xmax = p->aCoord[1];
|
||||
ymin = p->aCoord[2];
|
||||
ymax = p->aCoord[3];
|
||||
pCircle = (Circle *)p->pUser;
|
||||
if( pCircle==0 ){
|
||||
/* If pUser is still 0, then the parameter values have not been tested
|
||||
** for correctness or stored into a Circle structure yet. Do this now. */
|
||||
|
||||
/* This geometry callback is for use with a 2-dimensional r-tree table.
|
||||
** Return an error if the table does not have exactly 2 dimensions. */
|
||||
if( p->nCoord!=4 ) return SQLITE_ERROR;
|
||||
|
||||
/* Test that the correct number of parameters (4) have been supplied,
|
||||
** and that the parameters are in range (that the radius of the circle
|
||||
** radius is greater than zero). */
|
||||
if( p->nParam!=4 || p->aParam[2]<0.0 ) return SQLITE_ERROR;
|
||||
|
||||
/* Allocate a structure to cache parameter data in. Return SQLITE_NOMEM
|
||||
** if the allocation fails. */
|
||||
pCircle = (Circle *)(p->pUser = sqlite3_malloc(sizeof(Circle)));
|
||||
if( !pCircle ) return SQLITE_NOMEM;
|
||||
p->xDelUser = circle_del;
|
||||
|
||||
/* Record the center and radius of the circular region. One way that
|
||||
** tested bounding boxes that intersect the circular region are detected
|
||||
** is by testing if each corner of the bounding box lies within radius
|
||||
** units of the center of the circle. */
|
||||
pCircle->centerx = p->aParam[0];
|
||||
pCircle->centery = p->aParam[1];
|
||||
pCircle->radius = p->aParam[2];
|
||||
pCircle->eScoreType = (int)p->aParam[3];
|
||||
|
||||
/* Define two bounding box regions. The first, aBox[0], extends to
|
||||
** infinity in the X dimension. It covers the same range of the Y dimension
|
||||
** as the circular region. The second, aBox[1], extends to infinity in
|
||||
** the Y dimension and is constrained to the range of the circle in the
|
||||
** X dimension.
|
||||
**
|
||||
** Then imagine each box is split in half along its short axis by a line
|
||||
** that intersects the center of the circular region. A bounding box
|
||||
** being tested can be said to intersect the circular region if it contains
|
||||
** points from each half of either of the two infinite bounding boxes.
|
||||
*/
|
||||
pCircle->aBox[0].xmin = pCircle->centerx;
|
||||
pCircle->aBox[0].xmax = pCircle->centerx;
|
||||
pCircle->aBox[0].ymin = pCircle->centery + pCircle->radius;
|
||||
pCircle->aBox[0].ymax = pCircle->centery - pCircle->radius;
|
||||
pCircle->aBox[1].xmin = pCircle->centerx + pCircle->radius;
|
||||
pCircle->aBox[1].xmax = pCircle->centerx - pCircle->radius;
|
||||
pCircle->aBox[1].ymin = pCircle->centery;
|
||||
pCircle->aBox[1].ymax = pCircle->centery;
|
||||
pCircle->mxArea = 200.0*200.0;
|
||||
}
|
||||
|
||||
/* Check if any of the 4 corners of the bounding-box being tested lie
|
||||
** inside the circular region. If they do, then the bounding-box does
|
||||
** intersect the region of interest. Set the output variable to true and
|
||||
** return SQLITE_OK in this case. */
|
||||
for(i=0; i<4; i++){
|
||||
double x = (i&0x01) ? xmax : xmin;
|
||||
double y = (i&0x02) ? ymax : ymin;
|
||||
double d2;
|
||||
|
||||
d2 = (x-pCircle->centerx)*(x-pCircle->centerx);
|
||||
d2 += (y-pCircle->centery)*(y-pCircle->centery);
|
||||
if( d2<(pCircle->radius*pCircle->radius) ) nWithin++;
|
||||
}
|
||||
|
||||
/* Check if the bounding box covers any other part of the circular region.
|
||||
** See comments above for a description of how this test works. If it does
|
||||
** cover part of the circular region, set the output variable to true
|
||||
** and return SQLITE_OK. */
|
||||
if( nWithin==0 ){
|
||||
for(i=0; i<2; i++){
|
||||
if( xmin<=pCircle->aBox[i].xmin
|
||||
&& xmax>=pCircle->aBox[i].xmax
|
||||
&& ymin<=pCircle->aBox[i].ymin
|
||||
&& ymax>=pCircle->aBox[i].ymax
|
||||
){
|
||||
nWithin = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( pCircle->eScoreType==1 ){
|
||||
/* Depth first search */
|
||||
p->rScore = p->iLevel;
|
||||
}else if( pCircle->eScoreType==2 ){
|
||||
/* Breadth first search */
|
||||
p->rScore = 100 - p->iLevel;
|
||||
}else if( pCircle->eScoreType==3 ){
|
||||
/* Depth-first search, except sort the leaf nodes by area with
|
||||
** the largest area first */
|
||||
if( p->iLevel==1 ){
|
||||
p->rScore = 1.0 - (xmax-xmin)*(ymax-ymin)/pCircle->mxArea;
|
||||
if( p->rScore<0.01 ) p->rScore = 0.01;
|
||||
}else{
|
||||
p->rScore = 0.0;
|
||||
}
|
||||
}else if( pCircle->eScoreType==4 ){
|
||||
/* Depth-first search, except exclude odd rowids */
|
||||
p->rScore = p->iLevel;
|
||||
if( p->iRowid&1 ) nWithin = 0;
|
||||
}else{
|
||||
/* Breadth-first search, except exclude odd rowids */
|
||||
p->rScore = 100 - p->iLevel;
|
||||
if( p->iRowid&1 ) nWithin = 0;
|
||||
}
|
||||
if( nWithin==0 ){
|
||||
p->eWithin = NOT_WITHIN;
|
||||
}else if( nWithin>=4 ){
|
||||
p->eWithin = FULLY_WITHIN;
|
||||
}else{
|
||||
p->eWithin = PARTLY_WITHIN;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
/*
|
||||
** Implementation of "breadthfirstsearch" r-tree geometry callback using the
|
||||
** 2nd-generation interface that allows scoring.
|
||||
**
|
||||
** ... WHERE id MATCH breadthfirstsearch($x0,$x1,$y0,$y1) ...
|
||||
**
|
||||
** It returns all entries whose bounding boxes overlap with $x0,$x1,$y0,$y1.
|
||||
*/
|
||||
static int bfs_query_func(sqlite3_rtree_query_info *p){
|
||||
double x0,x1,y0,y1; /* Dimensions of box being tested */
|
||||
double bx0,bx1,by0,by1; /* Boundary of the query function */
|
||||
|
||||
if( p->nParam!=4 ) return SQLITE_ERROR;
|
||||
x0 = p->aCoord[0];
|
||||
x1 = p->aCoord[1];
|
||||
y0 = p->aCoord[2];
|
||||
y1 = p->aCoord[3];
|
||||
bx0 = p->aParam[0];
|
||||
bx1 = p->aParam[1];
|
||||
by0 = p->aParam[2];
|
||||
by1 = p->aParam[3];
|
||||
p->rScore = 100 - p->iLevel;
|
||||
if( p->eParentWithin==FULLY_WITHIN ){
|
||||
p->eWithin = FULLY_WITHIN;
|
||||
}else if( x0>=bx0 && x1<=bx1 && y0>=by0 && y1<=by1 ){
|
||||
p->eWithin = FULLY_WITHIN;
|
||||
}else if( x1>=bx0 && x0<=bx1 && y1>=by0 && y0<=by1 ){
|
||||
p->eWithin = PARTLY_WITHIN;
|
||||
}else{
|
||||
p->eWithin = NOT_WITHIN;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* END of implementation of "circle" geometry callback.
|
||||
**************************************************************************
|
||||
*************************************************************************/
|
||||
@@ -194,11 +356,7 @@ static int gHere = 42;
|
||||
static int cube_geom(
|
||||
sqlite3_rtree_geometry *p,
|
||||
int nCoord,
|
||||
#ifdef SQLITE_RTREE_INT_ONLY
|
||||
sqlite3_int64 *aCoord,
|
||||
#else
|
||||
double *aCoord,
|
||||
#endif
|
||||
sqlite3_rtree_dbl *aCoord,
|
||||
int *piRes
|
||||
){
|
||||
Cube *pCube = (Cube *)p->pUser;
|
||||
@@ -293,6 +451,14 @@ static int register_circle_geom(
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
rc = sqlite3_rtree_geometry_callback(db, "circle", circle_geom, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_rtree_query_callback(db, "Qcircle",
|
||||
circle_query_func, 0, 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_rtree_query_callback(db, "breadthfirstsearch",
|
||||
bfs_query_func, 0, 0);
|
||||
}
|
||||
Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC);
|
||||
#endif
|
||||
return TCL_OK;
|
||||
|
||||
@@ -678,7 +678,7 @@ static int vfstraceAccess(
|
||||
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
|
||||
sqlite3_vfs *pRoot = pInfo->pRootVfs;
|
||||
int rc;
|
||||
vfstrace_printf(pInfo, "%s.xDelete(\"%s\",%d)",
|
||||
vfstrace_printf(pInfo, "%s.xAccess(\"%s\",%d)",
|
||||
pInfo->zVfsName, zPath, flags);
|
||||
rc = pRoot->xAccess(pRoot, zPath, flags, pResOut);
|
||||
vfstrace_print_errcode(pInfo, " -> %s", rc);
|
||||
|
||||
@@ -1246,8 +1246,8 @@ LogEst sqlite3LogEstAdd(LogEst a, LogEst b){
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert an integer into a LogEst. In other words, compute a
|
||||
** good approximatation for 10*log2(x).
|
||||
** Convert an integer into a LogEst. In other words, compute an
|
||||
** approximation for 10*log2(x).
|
||||
*/
|
||||
LogEst sqlite3LogEst(u64 x){
|
||||
static LogEst a[] = { 0, 2, 3, 5, 6, 7, 8, 9 };
|
||||
|
||||
@@ -4255,6 +4255,7 @@ case OP_SorterData: {
|
||||
pC = p->apCsr[pOp->p1];
|
||||
assert( isSorter(pC) );
|
||||
rc = sqlite3VdbeSorterRowkey(pC, pOut);
|
||||
assert( rc!=SQLITE_OK || (pOut->flags & MEM_Blob) );
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -6346,8 +6347,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
|
||||
|
||||
@@ -3137,10 +3137,14 @@ void sqlite3VdbeRecordUnpack(
|
||||
** sqlite3VdbeSerialGet() and sqlite3MemCompare() functions. It is used
|
||||
** in assert() statements to ensure that the optimized code in
|
||||
** sqlite3VdbeRecordCompare() returns results with these two primitives.
|
||||
**
|
||||
** Return true if the result of comparison is equivalent to desiredResult.
|
||||
** Return false if there is a disagreement.
|
||||
*/
|
||||
static int vdbeRecordCompareDebug(
|
||||
int nKey1, const void *pKey1, /* Left key */
|
||||
const UnpackedRecord *pPKey2 /* Right key */
|
||||
const UnpackedRecord *pPKey2, /* Right key */
|
||||
int desiredResult /* Correct answer */
|
||||
){
|
||||
u32 d1; /* Offset into aKey[] of next data element */
|
||||
u32 idx1; /* Offset into aKey[] of next header element */
|
||||
@@ -3202,7 +3206,7 @@ static int vdbeRecordCompareDebug(
|
||||
if( pKeyInfo->aSortOrder[i] ){
|
||||
rc = -rc; /* Invert the result for DESC sort order. */
|
||||
}
|
||||
return rc;
|
||||
goto debugCompareEnd;
|
||||
}
|
||||
i++;
|
||||
}while( idx1<szHdr1 && i<pPKey2->nField );
|
||||
@@ -3216,7 +3220,15 @@ static int vdbeRecordCompareDebug(
|
||||
/* rc==0 here means that one of the keys ran out of fields and
|
||||
** all the fields up to that point were equal. Return the the default_rc
|
||||
** value. */
|
||||
return pPKey2->default_rc;
|
||||
rc = pPKey2->default_rc;
|
||||
|
||||
debugCompareEnd:
|
||||
if( desiredResult==0 && rc==0 ) return 1;
|
||||
if( desiredResult<0 && rc<0 ) return 1;
|
||||
if( desiredResult>0 && rc>0 ) return 1;
|
||||
if( CORRUPT_DB ) return 1;
|
||||
if( pKeyInfo->db->mallocFailed ) return 1;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -3570,11 +3582,7 @@ int sqlite3VdbeRecordCompare(
|
||||
if( pKeyInfo->aSortOrder[i] ){
|
||||
rc = -rc;
|
||||
}
|
||||
assert( CORRUPT_DB || pKeyInfo->db==0
|
||||
|| (rc<0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)<0)
|
||||
|| (rc>0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)>0)
|
||||
|| pKeyInfo->db->mallocFailed
|
||||
);
|
||||
assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, rc) );
|
||||
assert( mem1.zMalloc==0 ); /* See comment below */
|
||||
return rc;
|
||||
}
|
||||
@@ -3593,10 +3601,7 @@ int sqlite3VdbeRecordCompare(
|
||||
/* rc==0 here means that one or both of the keys ran out of fields and
|
||||
** all the fields up to that point were equal. Return the the default_rc
|
||||
** value. */
|
||||
assert( CORRUPT_DB || pKeyInfo->db==0
|
||||
|| pPKey2->default_rc==vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)
|
||||
|| pKeyInfo->db->mallocFailed
|
||||
);
|
||||
assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, pPKey2->default_rc) );
|
||||
return pPKey2->default_rc;
|
||||
}
|
||||
|
||||
@@ -3693,12 +3698,7 @@ static int vdbeRecordCompareInt(
|
||||
res = pPKey2->default_rc;
|
||||
}
|
||||
|
||||
assert( pPKey2->pKeyInfo->db==0
|
||||
|| (res==0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)==0)
|
||||
|| (res<0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)<0)
|
||||
|| (res>0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)>0)
|
||||
|| CORRUPT_DB || pPKey2->pKeyInfo->db->mallocFailed
|
||||
);
|
||||
assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, res) );
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -3758,12 +3758,7 @@ static int vdbeRecordCompareString(
|
||||
}
|
||||
}
|
||||
|
||||
assert( pPKey2->pKeyInfo->db==0
|
||||
|| (res==0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)==0)
|
||||
|| (res<0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)<0)
|
||||
|| (res>0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)>0)
|
||||
|| CORRUPT_DB || pPKey2->pKeyInfo->db->mallocFailed
|
||||
);
|
||||
assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, res) );
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
410
src/where.c
410
src/where.c
@@ -227,7 +227,7 @@ static int whereClauseInsert(WhereClause *pWC, Expr *p, u8 wtFlags){
|
||||
if( p && ExprHasProperty(p, EP_Unlikely) ){
|
||||
pTerm->truthProb = sqlite3LogEst(p->iTable) - 99;
|
||||
}else{
|
||||
pTerm->truthProb = -1;
|
||||
pTerm->truthProb = 1;
|
||||
}
|
||||
pTerm->pExpr = sqlite3ExprSkipCollate(p);
|
||||
pTerm->wtFlags = wtFlags;
|
||||
@@ -1956,7 +1956,8 @@ static void whereKeyStats(
|
||||
iLower = 0;
|
||||
iUpper = aSample[0].anLt[iCol];
|
||||
}else{
|
||||
iUpper = i>=pIdx->nSample ? pIdx->aiRowEst[0] : aSample[i].anLt[iCol];
|
||||
i64 nRow0 = sqlite3LogEstToInt(pIdx->aiRowLogEst[0]);
|
||||
iUpper = i>=pIdx->nSample ? nRow0 : aSample[i].anLt[iCol];
|
||||
iLower = aSample[i-1].anEq[iCol] + aSample[i-1].anLt[iCol];
|
||||
}
|
||||
aStat[1] = (pIdx->nKeyCol>iCol ? pIdx->aAvgEq[iCol] : 1);
|
||||
@@ -1975,6 +1976,29 @@ static void whereKeyStats(
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
|
||||
|
||||
/*
|
||||
** If it is not NULL, pTerm is a term that provides an upper or lower
|
||||
** bound on a range scan. Without considering pTerm, it is estimated
|
||||
** that the scan will visit nNew rows. This function returns the number
|
||||
** estimated to be visited after taking pTerm into account.
|
||||
**
|
||||
** If the user explicitly specified a likelihood() value for this term,
|
||||
** then the return value is the likelihood multiplied by the number of
|
||||
** input rows. Otherwise, this function assumes that an "IS NOT NULL" term
|
||||
** has a likelihood of 0.50, and any other term a likelihood of 0.25.
|
||||
*/
|
||||
static LogEst whereRangeAdjust(WhereTerm *pTerm, LogEst nNew){
|
||||
LogEst nRet = nNew;
|
||||
if( pTerm ){
|
||||
if( pTerm->truthProb<=0 ){
|
||||
nRet += pTerm->truthProb;
|
||||
}else if( (pTerm->wtFlags & TERM_VNULL)==0 ){
|
||||
nRet -= 20; assert( 20==sqlite3LogEst(4) );
|
||||
}
|
||||
}
|
||||
return nRet;
|
||||
}
|
||||
|
||||
/*
|
||||
** 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
|
||||
@@ -2067,7 +2091,7 @@ static int whereRangeScanEst(
|
||||
/* Determine iLower and iUpper using ($P) only. */
|
||||
if( nEq==0 ){
|
||||
iLower = 0;
|
||||
iUpper = p->aiRowEst[0];
|
||||
iUpper = sqlite3LogEstToInt(p->aiRowLogEst[0]);
|
||||
}else{
|
||||
/* Note: this call could be optimized away - since the same values must
|
||||
** have been requested when testing key $P in whereEqualScanEst(). */
|
||||
@@ -2127,17 +2151,18 @@ static int whereRangeScanEst(
|
||||
UNUSED_PARAMETER(pBuilder);
|
||||
#endif
|
||||
assert( pLower || pUpper );
|
||||
/* TUNING: Each inequality constraint reduces the search space 4-fold.
|
||||
** A BETWEEN operator, therefore, reduces the search space 16-fold */
|
||||
nNew = nOut;
|
||||
if( pLower && (pLower->wtFlags & TERM_VNULL)==0 ){
|
||||
nNew -= 20; assert( 20==sqlite3LogEst(4) );
|
||||
nOut--;
|
||||
}
|
||||
if( pUpper ){
|
||||
nNew -= 20; assert( 20==sqlite3LogEst(4) );
|
||||
nOut--;
|
||||
}
|
||||
assert( pUpper==0 || (pUpper->wtFlags & TERM_VNULL)==0 );
|
||||
nNew = whereRangeAdjust(pLower, nOut);
|
||||
nNew = whereRangeAdjust(pUpper, nNew);
|
||||
|
||||
/* TUNING: If there is both an upper and lower limit, assume the range is
|
||||
** reduced by an additional 75%. This means that, by default, an open-ended
|
||||
** range query (e.g. col > ?) is assumed to match 1/4 of the rows in the
|
||||
** index. While a closed range (e.g. col BETWEEN ? AND ?) is estimated to
|
||||
** match 1/64 of the index. */
|
||||
if( pLower && pUpper ) nNew -= 20;
|
||||
|
||||
nOut -= (pLower!=0) + (pUpper!=0);
|
||||
if( nNew<10 ) nNew = 10;
|
||||
if( nNew<nOut ) nOut = nNew;
|
||||
pLoop->nOut = (LogEst)nOut;
|
||||
@@ -2234,6 +2259,7 @@ static int whereInScanEst(
|
||||
tRowcnt *pnRow /* Write the revised row estimate here */
|
||||
){
|
||||
Index *p = pBuilder->pNew->u.btree.pIndex;
|
||||
i64 nRow0 = sqlite3LogEstToInt(p->aiRowLogEst[0]);
|
||||
int nRecValid = pBuilder->nRecValid;
|
||||
int rc = SQLITE_OK; /* Subfunction return code */
|
||||
tRowcnt nEst; /* Number of rows for a single term */
|
||||
@@ -2242,14 +2268,14 @@ static int whereInScanEst(
|
||||
|
||||
assert( p->aSample!=0 );
|
||||
for(i=0; rc==SQLITE_OK && i<pList->nExpr; i++){
|
||||
nEst = p->aiRowEst[0];
|
||||
nEst = nRow0;
|
||||
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];
|
||||
if( nRowEst > nRow0 ) nRowEst = nRow0;
|
||||
*pnRow = nRowEst;
|
||||
WHERETRACE(0x10,("IN row estimate: est=%g\n", nRowEst));
|
||||
}
|
||||
@@ -3757,12 +3783,25 @@ static int whereLoopCheaperProperSubset(
|
||||
** To say "WhereLoop X is a proper subset of Y" means that X uses fewer
|
||||
** WHERE clause terms than Y and that every WHERE clause term used by X is
|
||||
** also used by Y.
|
||||
**
|
||||
** This adjustment is omitted for SKIPSCAN loops. In a SKIPSCAN loop, the
|
||||
** WhereLoop.nLTerm field is not an accurate measure of the number of WHERE
|
||||
** clause terms covered, since some of the first nLTerm entries in aLTerm[]
|
||||
** will be NULL (because they are skipped). That makes it more difficult
|
||||
** to compare the loops. We could add extra code to do the comparison, and
|
||||
** perhaps we will someday. But SKIPSCAN is sufficiently uncommon, and this
|
||||
** adjustment is sufficient minor, that it is very difficult to construct
|
||||
** a test case where the extra code would improve the query plan. Better
|
||||
** to avoid the added complexity and just omit cost adjustments to SKIPSCAN
|
||||
** loops.
|
||||
*/
|
||||
static void whereLoopAdjustCost(const WhereLoop *p, WhereLoop *pTemplate){
|
||||
if( (pTemplate->wsFlags & WHERE_INDEXED)==0 ) return;
|
||||
if( (pTemplate->wsFlags & WHERE_SKIPSCAN)!=0 ) return;
|
||||
for(; p; p=p->pNextLoop){
|
||||
if( p->iTab!=pTemplate->iTab ) continue;
|
||||
if( (p->wsFlags & WHERE_INDEXED)==0 ) continue;
|
||||
if( (p->wsFlags & WHERE_SKIPSCAN)!=0 ) continue;
|
||||
if( whereLoopCheaperProperSubset(p, pTemplate) ){
|
||||
/* Adjust pTemplate cost downward so that it is cheaper than its
|
||||
** subset p */
|
||||
@@ -3987,13 +4026,20 @@ static void whereLoopOutputAdjust(WhereClause *pWC, WhereLoop *pLoop){
|
||||
if( pX==pTerm ) break;
|
||||
if( pX->iParent>=0 && (&pWC->a[pX->iParent])==pTerm ) break;
|
||||
}
|
||||
if( j<0 ) pLoop->nOut += pTerm->truthProb;
|
||||
if( j<0 ){
|
||||
pLoop->nOut += (pTerm->truthProb<=0 ? pTerm->truthProb : -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** We have so far matched pBuilder->pNew->u.btree.nEq terms of the index pIndex.
|
||||
** Try to match one more.
|
||||
** We have so far matched pBuilder->pNew->u.btree.nEq terms of the
|
||||
** index pIndex. Try to match one more.
|
||||
**
|
||||
** When this function is called, pBuilder->pNew->nOut contains the
|
||||
** number of rows expected to be visited by filtering using the nEq
|
||||
** terms only. If it is modified, this value is restored before this
|
||||
** function returns.
|
||||
**
|
||||
** If pProbe->tnum==0, that means pIndex is a fake index used for the
|
||||
** INTEGER PRIMARY KEY.
|
||||
@@ -4019,7 +4065,6 @@ static int whereLoopAddBtreeIndex(
|
||||
LogEst saved_nOut; /* Original value of pNew->nOut */
|
||||
int iCol; /* Index of the column in the table */
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
LogEst nRowEst; /* Estimated index selectivity */
|
||||
LogEst rLogSize; /* Logarithm of table size */
|
||||
WhereTerm *pTop = 0, *pBtm = 0; /* Top and bottom range constraints */
|
||||
|
||||
@@ -4040,11 +4085,8 @@ static int whereLoopAddBtreeIndex(
|
||||
assert( pNew->u.btree.nEq<=pProbe->nKeyCol );
|
||||
if( pNew->u.btree.nEq < pProbe->nKeyCol ){
|
||||
iCol = pProbe->aiColumn[pNew->u.btree.nEq];
|
||||
nRowEst = sqlite3LogEst(pProbe->aiRowEst[pNew->u.btree.nEq+1]);
|
||||
if( nRowEst==0 && pProbe->onError==OE_None ) nRowEst = 1;
|
||||
}else{
|
||||
iCol = -1;
|
||||
nRowEst = 0;
|
||||
}
|
||||
pTerm = whereScanInit(&scan, pBuilder->pWC, pSrc->iCursor, iCol,
|
||||
opMask, pProbe);
|
||||
@@ -4055,18 +4097,23 @@ static int whereLoopAddBtreeIndex(
|
||||
saved_prereq = pNew->prereq;
|
||||
saved_nOut = pNew->nOut;
|
||||
pNew->rSetup = 0;
|
||||
rLogSize = estLog(sqlite3LogEst(pProbe->aiRowEst[0]));
|
||||
rLogSize = estLog(pProbe->aiRowLogEst[0]);
|
||||
|
||||
/* Consider using a skip-scan if there are no WHERE clause constraints
|
||||
** available for the left-most terms of the index, and if the average
|
||||
** number of repeats in the left-most terms is at least 18. The magic
|
||||
** number 18 was found by experimentation to be the payoff point where
|
||||
** skip-scan become faster than a full-scan.
|
||||
*/
|
||||
** number of repeats in the left-most terms is at least 18.
|
||||
**
|
||||
** The magic number 18 is selected on the basis that scanning 17 rows
|
||||
** is almost always quicker than an index seek (even though if the index
|
||||
** contains fewer than 2^17 rows we assume otherwise in other parts of
|
||||
** the code). And, even if it is not, it should not be too much slower.
|
||||
** On the other hand, the extra seeks could end up being significantly
|
||||
** more expensive. */
|
||||
assert( 42==sqlite3LogEst(18) );
|
||||
if( pTerm==0
|
||||
&& saved_nEq==saved_nSkip
|
||||
&& saved_nEq+1<pProbe->nKeyCol
|
||||
&& pProbe->aiRowEst[saved_nEq+1]>=18 /* TUNING: Minimum for skip-scan */
|
||||
&& pProbe->aiRowLogEst[saved_nEq+1]>=42 /* TUNING: Minimum for skip-scan */
|
||||
&& (rc = whereLoopResize(db, pNew, pNew->nLTerm+1))==SQLITE_OK
|
||||
){
|
||||
LogEst nIter;
|
||||
@@ -4074,34 +4121,40 @@ static int whereLoopAddBtreeIndex(
|
||||
pNew->u.btree.nSkip++;
|
||||
pNew->aLTerm[pNew->nLTerm++] = 0;
|
||||
pNew->wsFlags |= WHERE_SKIPSCAN;
|
||||
nIter = sqlite3LogEst(pProbe->aiRowEst[0]/pProbe->aiRowEst[saved_nEq+1]);
|
||||
pNew->rRun = rLogSize + nIter;
|
||||
pNew->nOut += nIter;
|
||||
whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, nIter);
|
||||
nIter = pProbe->aiRowLogEst[saved_nEq] - pProbe->aiRowLogEst[saved_nEq+1];
|
||||
pNew->nOut -= nIter;
|
||||
whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, nIter + nInMul);
|
||||
pNew->nOut = saved_nOut;
|
||||
}
|
||||
for(; rc==SQLITE_OK && pTerm!=0; pTerm = whereScanNext(&scan)){
|
||||
u16 eOp = pTerm->eOperator; /* Shorthand for pTerm->eOperator */
|
||||
LogEst rCostIdx;
|
||||
LogEst nOutUnadjusted; /* nOut before IN() and WHERE adjustments */
|
||||
int nIn = 0;
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
int nRecValid = pBuilder->nRecValid;
|
||||
#endif
|
||||
if( (pTerm->eOperator==WO_ISNULL || (pTerm->wtFlags&TERM_VNULL)!=0)
|
||||
if( (eOp==WO_ISNULL || (pTerm->wtFlags&TERM_VNULL)!=0)
|
||||
&& (iCol<0 || pSrc->pTab->aCol[iCol].notNull)
|
||||
){
|
||||
continue; /* ignore IS [NOT] NULL constraints on NOT NULL columns */
|
||||
}
|
||||
if( pTerm->prereqRight & pNew->maskSelf ) continue;
|
||||
|
||||
assert( pNew->nOut==saved_nOut );
|
||||
|
||||
pNew->wsFlags = saved_wsFlags;
|
||||
pNew->u.btree.nEq = saved_nEq;
|
||||
pNew->nLTerm = saved_nLTerm;
|
||||
if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */
|
||||
pNew->aLTerm[pNew->nLTerm++] = pTerm;
|
||||
pNew->prereq = (saved_prereq | pTerm->prereqRight) & ~pNew->maskSelf;
|
||||
pNew->rRun = rLogSize; /* Baseline cost is log2(N). Adjustments below */
|
||||
if( pTerm->eOperator & WO_IN ){
|
||||
|
||||
assert( nInMul==0
|
||||
|| (pNew->wsFlags & WHERE_COLUMN_NULL)!=0
|
||||
|| (pNew->wsFlags & WHERE_COLUMN_IN)!=0
|
||||
|| (pNew->wsFlags & WHERE_SKIPSCAN)!=0
|
||||
);
|
||||
|
||||
if( eOp & WO_IN ){
|
||||
Expr *pExpr = pTerm->pExpr;
|
||||
pNew->wsFlags |= WHERE_COLUMN_IN;
|
||||
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
@@ -4113,83 +4166,118 @@ static int whereLoopAddBtreeIndex(
|
||||
}
|
||||
assert( nIn>0 ); /* RHS always has 2 or more terms... The parser
|
||||
** changes "x IN (?)" into "x=?". */
|
||||
pNew->rRun += nIn;
|
||||
pNew->u.btree.nEq++;
|
||||
pNew->nOut = nRowEst + nInMul + nIn;
|
||||
}else if( pTerm->eOperator & (WO_EQ) ){
|
||||
assert(
|
||||
(pNew->wsFlags & (WHERE_COLUMN_NULL|WHERE_COLUMN_IN|WHERE_SKIPSCAN))!=0
|
||||
|| nInMul==0
|
||||
);
|
||||
|
||||
}else if( eOp & (WO_EQ) ){
|
||||
pNew->wsFlags |= WHERE_COLUMN_EQ;
|
||||
if( iCol<0 || (nInMul==0 && pNew->u.btree.nEq==pProbe->nKeyCol-1)){
|
||||
assert( (pNew->wsFlags & WHERE_COLUMN_IN)==0 || iCol<0 );
|
||||
if( iCol<0 || (nInMul==0 && pNew->u.btree.nEq==pProbe->nKeyCol-1) ){
|
||||
if( iCol>=0 && pProbe->onError==OE_None ){
|
||||
pNew->wsFlags |= WHERE_UNQ_WANTED;
|
||||
}else{
|
||||
pNew->wsFlags |= WHERE_ONEROW;
|
||||
}
|
||||
}
|
||||
pNew->u.btree.nEq++;
|
||||
pNew->nOut = nRowEst + nInMul;
|
||||
}else if( pTerm->eOperator & (WO_ISNULL) ){
|
||||
}else if( eOp & WO_ISNULL ){
|
||||
pNew->wsFlags |= WHERE_COLUMN_NULL;
|
||||
pNew->u.btree.nEq++;
|
||||
/* TUNING: IS NULL selects 2 rows */
|
||||
nIn = 10; assert( 10==sqlite3LogEst(2) );
|
||||
pNew->nOut = nRowEst + nInMul + nIn;
|
||||
}else if( pTerm->eOperator & (WO_GT|WO_GE) ){
|
||||
testcase( pTerm->eOperator & WO_GT );
|
||||
testcase( pTerm->eOperator & WO_GE );
|
||||
}else if( eOp & (WO_GT|WO_GE) ){
|
||||
testcase( eOp & WO_GT );
|
||||
testcase( eOp & WO_GE );
|
||||
pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_BTM_LIMIT;
|
||||
pBtm = pTerm;
|
||||
pTop = 0;
|
||||
}else{
|
||||
assert( pTerm->eOperator & (WO_LT|WO_LE) );
|
||||
testcase( pTerm->eOperator & WO_LT );
|
||||
testcase( pTerm->eOperator & WO_LE );
|
||||
assert( eOp & (WO_LT|WO_LE) );
|
||||
testcase( eOp & WO_LT );
|
||||
testcase( eOp & WO_LE );
|
||||
pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_TOP_LIMIT;
|
||||
pTop = pTerm;
|
||||
pBtm = (pNew->wsFlags & WHERE_BTM_LIMIT)!=0 ?
|
||||
pNew->aLTerm[pNew->nLTerm-2] : 0;
|
||||
}
|
||||
|
||||
/* At this point pNew->nOut is set to the number of rows expected to
|
||||
** be visited by the index scan before considering term pTerm, or the
|
||||
** values of nIn and nInMul. In other words, assuming that all
|
||||
** "x IN(...)" terms are replaced with "x = ?". This block updates
|
||||
** the value of pNew->nOut to account for pTerm (but not nIn/nInMul). */
|
||||
assert( pNew->nOut==saved_nOut );
|
||||
if( pNew->wsFlags & WHERE_COLUMN_RANGE ){
|
||||
/* Adjust nOut and rRun for STAT3 range values */
|
||||
assert( pNew->nOut==saved_nOut );
|
||||
/* Adjust nOut using stat3/stat4 data. Or, if there is no stat3/stat4
|
||||
** data, using some other estimate. */
|
||||
whereRangeScanEst(pParse, pBuilder, pBtm, pTop, pNew);
|
||||
}
|
||||
}else{
|
||||
int nEq = ++pNew->u.btree.nEq;
|
||||
assert( eOp & (WO_ISNULL|WO_EQ|WO_IN) );
|
||||
|
||||
assert( pNew->nOut==saved_nOut );
|
||||
if( pTerm->truthProb<=0 && iCol>=0 ){
|
||||
assert( (eOp & WO_IN) || nIn==0 );
|
||||
testcase( eOp & WO_IN );
|
||||
pNew->nOut += pTerm->truthProb;
|
||||
pNew->nOut -= nIn;
|
||||
pNew->wsFlags |= WHERE_LIKELIHOOD;
|
||||
}else{
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
if( nInMul==0
|
||||
&& pProbe->nSample
|
||||
&& pNew->u.btree.nEq<=pProbe->nSampleCol
|
||||
&& 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, pBuilder, pExpr->pRight, &nOut);
|
||||
}else if( (pTerm->eOperator & WO_IN)
|
||||
&& !ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
rc = whereInScanEst(pParse, pBuilder, pExpr->x.pList, &nOut);
|
||||
}
|
||||
assert( nOut==0 || rc==SQLITE_OK );
|
||||
if( nOut ){
|
||||
pNew->nOut = sqlite3LogEst(nOut);
|
||||
if( pNew->nOut>saved_nOut ) pNew->nOut = saved_nOut;
|
||||
}
|
||||
}
|
||||
tRowcnt nOut = 0;
|
||||
if( nInMul==0
|
||||
&& pProbe->nSample
|
||||
&& pNew->u.btree.nEq<=pProbe->nSampleCol
|
||||
&& OptimizationEnabled(db, SQLITE_Stat3)
|
||||
&& ((eOp & WO_IN)==0 || !ExprHasProperty(pTerm->pExpr, EP_xIsSelect))
|
||||
&& (pNew->wsFlags & WHERE_LIKELIHOOD)==0
|
||||
){
|
||||
Expr *pExpr = pTerm->pExpr;
|
||||
if( (eOp & (WO_EQ|WO_ISNULL))!=0 ){
|
||||
testcase( eOp & WO_EQ );
|
||||
testcase( eOp & WO_ISNULL );
|
||||
rc = whereEqualScanEst(pParse, pBuilder, pExpr->pRight, &nOut);
|
||||
}else{
|
||||
rc = whereInScanEst(pParse, pBuilder, pExpr->x.pList, &nOut);
|
||||
}
|
||||
assert( rc!=SQLITE_OK || nOut>0 );
|
||||
if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK;
|
||||
if( rc!=SQLITE_OK ) break; /* Jump out of the pTerm loop */
|
||||
if( nOut ){
|
||||
pNew->nOut = sqlite3LogEst(nOut);
|
||||
if( pNew->nOut>saved_nOut ) pNew->nOut = saved_nOut;
|
||||
pNew->nOut -= nIn;
|
||||
}
|
||||
}
|
||||
if( nOut==0 )
|
||||
#endif
|
||||
if( (pNew->wsFlags & (WHERE_IDX_ONLY|WHERE_IPK))==0 ){
|
||||
/* Each row involves a step of the index, then a binary search of
|
||||
** the main table */
|
||||
pNew->rRun = sqlite3LogEstAdd(pNew->rRun,rLogSize>27 ? rLogSize-17 : 10);
|
||||
{
|
||||
pNew->nOut += (pProbe->aiRowLogEst[nEq] - pProbe->aiRowLogEst[nEq-1]);
|
||||
if( eOp & WO_ISNULL ){
|
||||
/* TUNING: If there is no likelihood() value, assume that a
|
||||
** "col IS NULL" expression matches twice as many rows
|
||||
** as (col=?). */
|
||||
pNew->nOut += 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Step cost for each output row */
|
||||
pNew->rRun = sqlite3LogEstAdd(pNew->rRun, pNew->nOut);
|
||||
|
||||
/* Set rCostIdx to the cost of visiting selected rows in index. Add
|
||||
** it to pNew->rRun, which is currently set to the cost of the index
|
||||
** seek only. Then, if this is a non-covering index, add the cost of
|
||||
** visiting the rows in the main table. */
|
||||
rCostIdx = pNew->nOut + 1 + (15*pProbe->szIdxRow)/pSrc->pTab->szTabRow;
|
||||
pNew->rRun = sqlite3LogEstAdd(rLogSize, rCostIdx);
|
||||
if( (pNew->wsFlags & (WHERE_IDX_ONLY|WHERE_IPK))==0 ){
|
||||
pNew->rRun = sqlite3LogEstAdd(pNew->rRun, pNew->nOut + 16);
|
||||
}
|
||||
|
||||
nOutUnadjusted = pNew->nOut;
|
||||
pNew->rRun += nInMul + nIn;
|
||||
pNew->nOut += nInMul + nIn;
|
||||
whereLoopOutputAdjust(pBuilder->pWC, pNew);
|
||||
rc = whereLoopInsert(pBuilder, pNew);
|
||||
|
||||
if( pNew->wsFlags & WHERE_COLUMN_RANGE ){
|
||||
pNew->nOut = saved_nOut;
|
||||
}else{
|
||||
pNew->nOut = nOutUnadjusted;
|
||||
}
|
||||
|
||||
if( (pNew->wsFlags & WHERE_TOP_LIMIT)==0
|
||||
&& pNew->u.btree.nEq<(pProbe->nKeyCol + (pProbe->zName!=0))
|
||||
){
|
||||
@@ -4273,6 +4361,29 @@ static int whereUsablePartialIndex(int iTab, WhereClause *pWC, Expr *pWhere){
|
||||
** Add all WhereLoop objects for a single table of the join where the table
|
||||
** is idenfied by pBuilder->pNew->iTab. That table is guaranteed to be
|
||||
** a b-tree table, not a virtual table.
|
||||
**
|
||||
** The costs (WhereLoop.rRun) of the b-tree loops added by this function
|
||||
** are calculated as follows:
|
||||
**
|
||||
** For a full scan, assuming the table (or index) contains nRow rows:
|
||||
**
|
||||
** cost = nRow * 3.0 // full-table scan
|
||||
** cost = nRow * K // scan of covering index
|
||||
** cost = nRow * (K+3.0) // scan of non-covering index
|
||||
**
|
||||
** where K is a value between 1.1 and 3.0 set based on the relative
|
||||
** estimated average size of the index and table records.
|
||||
**
|
||||
** For an index scan, where nVisit is the number of index rows visited
|
||||
** by the scan, and nSeek is the number of seek operations required on
|
||||
** the index b-tree:
|
||||
**
|
||||
** cost = nSeek * (log(nRow) + K * nVisit) // covering index
|
||||
** cost = nSeek * (log(nRow) + (K+3.0) * nVisit) // non-covering index
|
||||
**
|
||||
** Normally, nSeek is 1. nSeek values greater than 1 come about if the
|
||||
** WHERE clause includes "x IN (....)" terms used in place of "x=?". Or when
|
||||
** implicit "x IN (SELECT x FROM tbl)" terms are added for skip-scans.
|
||||
*/
|
||||
static int whereLoopAddBtree(
|
||||
WhereLoopBuilder *pBuilder, /* WHERE clause information */
|
||||
@@ -4281,7 +4392,7 @@ static int whereLoopAddBtree(
|
||||
WhereInfo *pWInfo; /* WHERE analysis context */
|
||||
Index *pProbe; /* An index we are evaluating */
|
||||
Index sPk; /* A fake index object for the primary key */
|
||||
tRowcnt aiRowEstPk[2]; /* The aiRowEst[] value for the sPk index */
|
||||
LogEst aiRowEstPk[2]; /* The aiRowLogEst[] value for the sPk index */
|
||||
i16 aiColumnPk = -1; /* The aColumn[] value for the sPk index */
|
||||
SrcList *pTabList; /* The FROM clause */
|
||||
struct SrcList_item *pSrc; /* The FROM clause btree term to add */
|
||||
@@ -4316,11 +4427,12 @@ static int whereLoopAddBtree(
|
||||
memset(&sPk, 0, sizeof(Index));
|
||||
sPk.nKeyCol = 1;
|
||||
sPk.aiColumn = &aiColumnPk;
|
||||
sPk.aiRowEst = aiRowEstPk;
|
||||
sPk.aiRowLogEst = aiRowEstPk;
|
||||
sPk.onError = OE_Replace;
|
||||
sPk.pTable = pTab;
|
||||
aiRowEstPk[0] = pTab->nRowEst;
|
||||
aiRowEstPk[1] = 1;
|
||||
sPk.szIdxRow = pTab->szTabRow;
|
||||
aiRowEstPk[0] = pTab->nRowLogEst;
|
||||
aiRowEstPk[1] = 0;
|
||||
pFirst = pSrc->pTab->pIndex;
|
||||
if( pSrc->notIndexed==0 ){
|
||||
/* The real indices of the table are only considered if the
|
||||
@@ -4329,7 +4441,7 @@ static int whereLoopAddBtree(
|
||||
}
|
||||
pProbe = &sPk;
|
||||
}
|
||||
rSize = sqlite3LogEst(pTab->nRowEst);
|
||||
rSize = pTab->nRowLogEst;
|
||||
rLogSize = estLog(rSize);
|
||||
|
||||
#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
|
||||
@@ -4379,6 +4491,7 @@ static int whereLoopAddBtree(
|
||||
&& !whereUsablePartialIndex(pNew->iTab, pWC, pProbe->pPartIdxWhere) ){
|
||||
continue; /* Partial index inappropriate for this query */
|
||||
}
|
||||
rSize = pProbe->aiRowLogEst[0];
|
||||
pNew->u.btree.nEq = 0;
|
||||
pNew->u.btree.nSkip = 0;
|
||||
pNew->nLTerm = 0;
|
||||
@@ -4396,10 +4509,8 @@ static int whereLoopAddBtree(
|
||||
|
||||
/* Full table scan */
|
||||
pNew->iSortIdx = b ? iSortIdx : 0;
|
||||
/* TUNING: Cost of full table scan is 3*(N + log2(N)).
|
||||
** + The extra 3 factor is to encourage the use of indexed lookups
|
||||
** over full scans. FIXME */
|
||||
pNew->rRun = sqlite3LogEstAdd(rSize,rLogSize) + 16;
|
||||
/* TUNING: Cost of full table scan is (N*3.0). */
|
||||
pNew->rRun = rSize + 16;
|
||||
whereLoopOutputAdjust(pWC, pNew);
|
||||
rc = whereLoopInsert(pBuilder, pNew);
|
||||
pNew->nOut = rSize;
|
||||
@@ -4426,35 +4537,16 @@ static int whereLoopAddBtree(
|
||||
)
|
||||
){
|
||||
pNew->iSortIdx = b ? iSortIdx : 0;
|
||||
/* TUNING: The base cost of an index scan is N + log2(N).
|
||||
** The log2(N) is for the initial seek to the beginning and the N
|
||||
** is for the scan itself. */
|
||||
pNew->rRun = sqlite3LogEstAdd(rSize, rLogSize);
|
||||
if( m==0 ){
|
||||
/* TUNING: Cost of a covering index scan is K*(N + log2(N)).
|
||||
** + The extra factor K of between 1.1 and 3.0 that depends
|
||||
** on the relative sizes of the table and the index. K
|
||||
** is smaller for smaller indices, thus favoring them.
|
||||
** The upper bound on K (3.0) matches the penalty factor
|
||||
** on a full table scan that tries to encourage the use of
|
||||
** indexed lookups over full scans.
|
||||
*/
|
||||
pNew->rRun += 1 + (15*pProbe->szIdxRow)/pTab->szTabRow;
|
||||
}else{
|
||||
/* TUNING: The cost of scanning a non-covering index is multiplied
|
||||
** by log2(N) to account for the binary search of the main table
|
||||
** that must happen for each row of the index.
|
||||
** TODO: Should there be a multiplier here, analogous to the 3x
|
||||
** multiplier for a fulltable scan or covering index scan, to
|
||||
** further discourage the use of an index scan? Or is the log2(N)
|
||||
** term sufficient discouragement?
|
||||
** TODO: What if some or all of the WHERE clause terms can be
|
||||
** computed without reference to the original table. Then the
|
||||
** penality should reduce to logK where K is the number of output
|
||||
** rows.
|
||||
*/
|
||||
pNew->rRun += rLogSize;
|
||||
|
||||
/* The cost of visiting the index rows is N*K, where K is
|
||||
** between 1.1 and 3.0, depending on the relative sizes of the
|
||||
** index and table rows. If this is a non-covering index scan,
|
||||
** also add the cost of visiting table rows (N*3.0). */
|
||||
pNew->rRun = rSize + 1 + (15*pProbe->szIdxRow)/pTab->szTabRow;
|
||||
if( m!=0 ){
|
||||
pNew->rRun = sqlite3LogEstAdd(pNew->rRun, rSize+16);
|
||||
}
|
||||
|
||||
whereLoopOutputAdjust(pWC, pNew);
|
||||
rc = whereLoopInsert(pBuilder, pNew);
|
||||
pNew->nOut = rSize;
|
||||
@@ -4658,7 +4750,7 @@ static int whereLoopAddOr(WhereLoopBuilder *pBuilder, Bitmask mExtra){
|
||||
int iCur;
|
||||
WhereClause tempWC;
|
||||
WhereLoopBuilder sSubBuild;
|
||||
WhereOrSet sSum, sCur, sPrev;
|
||||
WhereOrSet sSum, sCur;
|
||||
struct SrcList_item *pItem;
|
||||
|
||||
pWC = pBuilder->pWC;
|
||||
@@ -4714,6 +4806,7 @@ static int whereLoopAddOr(WhereLoopBuilder *pBuilder, Bitmask mExtra){
|
||||
whereOrMove(&sSum, &sCur);
|
||||
once = 0;
|
||||
}else{
|
||||
WhereOrSet sPrev;
|
||||
whereOrMove(&sPrev, &sSum);
|
||||
sSum.n = 0;
|
||||
for(i=0; i<sPrev.n; i++){
|
||||
@@ -4732,8 +4825,19 @@ static int whereLoopAddOr(WhereLoopBuilder *pBuilder, Bitmask mExtra){
|
||||
pNew->iSortIdx = 0;
|
||||
memset(&pNew->u, 0, sizeof(pNew->u));
|
||||
for(i=0; rc==SQLITE_OK && i<sSum.n; i++){
|
||||
/* TUNING: Multiple by 3.5 for the secondary table lookup */
|
||||
pNew->rRun = sSum.a[i].rRun + 18;
|
||||
/* TUNING: Currently sSum.a[i].rRun is set to the sum of the costs
|
||||
** of all sub-scans required by the OR-scan. However, due to rounding
|
||||
** errors, it may be that the cost of the OR-scan is equal to its
|
||||
** most expensive sub-scan. Add the smallest possible penalty
|
||||
** (equivalent to multiplying the cost by 1.07) to ensure that
|
||||
** this does not happen. Otherwise, for WHERE clauses such as the
|
||||
** following where there is an index on "y":
|
||||
**
|
||||
** WHERE likelihood(x=?, 0.99) OR y=?
|
||||
**
|
||||
** the planner may elect to "OR" together a full-table scan and an
|
||||
** index lookup. And other similarly odd results. */
|
||||
pNew->rRun = sSum.a[i].rRun + 1;
|
||||
pNew->nOut = sSum.a[i].nOut;
|
||||
pNew->prereq = sSum.a[i].prereq;
|
||||
rc = whereLoopInsert(pBuilder, pNew);
|
||||
@@ -4857,14 +4961,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;
|
||||
@@ -4877,7 +4973,10 @@ static i8 wherePathSatisfiesOrderBy(
|
||||
for(iLoop=0; isOrderDistinct && obSat<obDone && iLoop<=nLoop; iLoop++){
|
||||
if( iLoop>0 ) ready |= pLoop->maskSelf;
|
||||
pLoop = iLoop<nLoop ? pPath->aLoop[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
|
||||
@@ -5184,22 +5283,27 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
|
||||
pWInfo->pOrderBy, pFrom, pWInfo->wctrlFlags,
|
||||
iLoop, pWLoop, &revMask);
|
||||
if( isOrdered>=0 && isOrdered<nOrderBy ){
|
||||
/* TUNING: Estimated cost of sorting is N*log(N).
|
||||
** If the order-by clause has X terms but only the last Y terms
|
||||
** are out of order, then block-sorting will reduce the sorting
|
||||
** cost to N*log(N)*log(Y/X). The log(Y/X) term is computed
|
||||
** by rScale.
|
||||
** TODO: Should the sorting cost get a small multiplier to help
|
||||
** discourage the use of sorting and encourage the use of index
|
||||
** scans instead?
|
||||
*/
|
||||
/* TUNING: Estimated cost of a full external sort, where N is
|
||||
** the number of rows to sort is:
|
||||
**
|
||||
** cost = (3.0 * N * log(N)).
|
||||
**
|
||||
** Or, if the order-by clause has X terms but only the last Y
|
||||
** terms are out of order, then block-sorting will reduce the
|
||||
** sorting cost to:
|
||||
**
|
||||
** cost = (3.0 * N * log(N)) * (Y/X)
|
||||
**
|
||||
** The (Y/X) term is implemented using stack variable rScale
|
||||
** below. */
|
||||
LogEst rScale, rSortCost;
|
||||
assert( nOrderBy>0 );
|
||||
assert( nOrderBy>0 && 66==sqlite3LogEst(100) );
|
||||
rScale = sqlite3LogEst((nOrderBy-isOrdered)*100/nOrderBy) - 66;
|
||||
rSortCost = nRowEst + estLog(nRowEst) + rScale;
|
||||
rSortCost = nRowEst + estLog(nRowEst) + rScale + 16;
|
||||
|
||||
/* TUNING: The cost of implementing DISTINCT using a B-TREE is
|
||||
** also N*log(N) but it has a larger constant of proportionality.
|
||||
** Multiply by 3.0. */
|
||||
** similar but with a larger constant of proportionality.
|
||||
** Multiply by an additional factor of 3.0. */
|
||||
if( pWInfo->wctrlFlags & WHERE_WANT_DISTINCT ){
|
||||
rSortCost += 16;
|
||||
}
|
||||
|
||||
@@ -458,3 +458,4 @@ struct WhereInfo {
|
||||
#define WHERE_AUTO_INDEX 0x00004000 /* Uses an ephemeral index */
|
||||
#define WHERE_SKIPSCAN 0x00008000 /* Uses the skip-scan algorithm */
|
||||
#define WHERE_UNQ_WANTED 0x00010000 /* WHERE_ONEROW would have been helpful*/
|
||||
#define WHERE_LIKELIHOOD 0x00020000 /* A likelihood() is affecting nOut */
|
||||
|
||||
Reference in New Issue
Block a user