mirror of
https://github.com/sqlite/sqlite.git
synced 2026-01-06 08:01:16 +03:00
Merge ORDER BY optimization refactoring and repair into trunk.
FossilOrigin-Name: c027a9af9137f3346dbb5c5e100a087c2e89797b
This commit is contained in:
292
src/where.c
292
src/where.c
@@ -1442,22 +1442,6 @@ static void exprAnalyze(
|
||||
pTerm->prereqRight |= extraRight;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return TRUE if the given index is UNIQUE and all columns past the
|
||||
** first nSkip columns are NOT NULL.
|
||||
*/
|
||||
static int indexIsUniqueNotNull(Index *pIdx, int nSkip){
|
||||
Table *pTab = pIdx->pTable;
|
||||
int i;
|
||||
if( pIdx->onError==OE_None ) return 0;
|
||||
for(i=nSkip; i<pIdx->nColumn; i++){
|
||||
int j = pIdx->aiColumn[i];
|
||||
assert( j>=0 && j<pTab->nCol );
|
||||
if( pTab->aCol[j].notNull==0 ) return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function searches the expression list passed as the second argument
|
||||
** for an expression of type TK_COLUMN that refers to the same column and
|
||||
@@ -2726,14 +2710,20 @@ static int whereInScanEst(
|
||||
|
||||
/*
|
||||
** Check to see if column iCol of the table with cursor iTab will appear
|
||||
** in sorted order according to the current query plan. Return true if
|
||||
** it will and false if not.
|
||||
** in sorted order according to the current query plan.
|
||||
**
|
||||
** If *pbRev is initially 2 (meaning "unknown") then set *pbRev to the
|
||||
** sort order of iTab.iCol. If *pbRev is 0 or 1 but does not match
|
||||
** the sort order of iTab.iCol, then consider the column to be unordered.
|
||||
** Return values:
|
||||
**
|
||||
** 0 iCol is not ordered
|
||||
** 1 iCol has only a single value
|
||||
** 2 iCol is in ASC order
|
||||
** 3 iCol is in DESC order
|
||||
*/
|
||||
static int isOrderedColumn(WhereBestIdx *p, int iTab, int iCol, int *pbRev){
|
||||
static int isOrderedColumn(
|
||||
WhereBestIdx *p,
|
||||
int iTab,
|
||||
int iCol
|
||||
){
|
||||
int i, j;
|
||||
WhereLevel *pLevel = &p->aLevel[p->i-1];
|
||||
Index *pIdx;
|
||||
@@ -2769,38 +2759,11 @@ static int isOrderedColumn(WhereBestIdx *p, int iTab, int iCol, int *pbRev){
|
||||
testcase( sortOrder==1 );
|
||||
sortOrder = 1 - sortOrder;
|
||||
}
|
||||
if( *pbRev==2 ){
|
||||
*pbRev = sortOrder;
|
||||
return 1;
|
||||
}
|
||||
return (*pbRev==sortOrder);
|
||||
return sortOrder+2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** pTerm is an == constraint. Check to see if the other side of
|
||||
** the == is a constant or a value that is guaranteed to be ordered
|
||||
** by outer loops. Return 1 if pTerm is ordered, and 0 if not.
|
||||
*/
|
||||
static int isOrderedTerm(WhereBestIdx *p, WhereTerm *pTerm, int *pbRev){
|
||||
Expr *pExpr = pTerm->pExpr;
|
||||
assert( pExpr->op==TK_EQ );
|
||||
assert( pExpr->pLeft!=0 && pExpr->pLeft->op==TK_COLUMN );
|
||||
assert( pExpr->pRight!=0 );
|
||||
if( pTerm->prereqRight==0 ){
|
||||
return 1; /* RHS of the == is a constant */
|
||||
}
|
||||
if( pExpr->pRight->op==TK_COLUMN
|
||||
&& isOrderedColumn(p, pExpr->pRight->iTable, pExpr->pRight->iColumn, pbRev)
|
||||
){
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* If we cannot prove that the constraint is ordered, assume it is not */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine decides if pIdx can be used to satisfy the ORDER BY
|
||||
** clause, either in whole or in part. The return value is the
|
||||
@@ -2825,45 +2788,45 @@ static int isSortingIndex(
|
||||
WhereBestIdx *p, /* Best index search context */
|
||||
Index *pIdx, /* The index we are testing */
|
||||
int base, /* Cursor number for the table to be sorted */
|
||||
int nEqCol, /* Number of index columns with ordered == constraints */
|
||||
int wsFlags, /* Index usages flags */
|
||||
int bOuterRev, /* True if outer loops scan in reverse order */
|
||||
int *pbRev /* Set to 1 for reverse-order scan of pIdx */
|
||||
){
|
||||
int i; /* Number of pIdx terms used */
|
||||
int j; /* Number of ORDER BY terms satisfied */
|
||||
int sortOrder = 0; /* XOR of index and ORDER BY sort direction */
|
||||
int sortOrder = 2; /* 0: forward. 1: backward. 2: unknown */
|
||||
int nTerm; /* Number of ORDER BY terms */
|
||||
struct ExprList_item *pTerm; /* A term of the ORDER BY clause */
|
||||
struct ExprList_item *pOBItem;/* A term of the ORDER BY clause */
|
||||
Table *pTab = pIdx->pTable; /* Table that owns index pIdx */
|
||||
ExprList *pOrderBy; /* The ORDER BY clause */
|
||||
Parse *pParse = p->pParse; /* Parser context */
|
||||
sqlite3 *db = pParse->db; /* Database connection */
|
||||
int nPriorSat; /* ORDER BY terms satisfied by outer loops */
|
||||
int seenRowid = 0; /* True if an ORDER BY rowid term is seen */
|
||||
int nEqOneRow; /* Idx columns that ref unique values */
|
||||
int uniqueNotNull; /* pIdx is UNIQUE with all terms are NOT NULL */
|
||||
|
||||
if( p->i==0 ){
|
||||
nPriorSat = 0;
|
||||
}else{
|
||||
nPriorSat = p->aLevel[p->i-1].plan.nOBSat;
|
||||
if( OptimizationDisabled(db, SQLITE_OrderByIdxJoin) ) return nPriorSat;
|
||||
}
|
||||
if( nEqCol==0 ){
|
||||
if( p->i && (p->aLevel[p->i-1].plan.wsFlags & WHERE_ORDERED)==0 ){
|
||||
if( (p->aLevel[p->i-1].plan.wsFlags & WHERE_ORDERED)==0 ){
|
||||
/* This loop cannot be ordered unless the next outer loop is
|
||||
** also ordered */
|
||||
return nPriorSat;
|
||||
}
|
||||
if( OptimizationDisabled(db, SQLITE_OrderByIdxJoin) ){
|
||||
/* Only look at the outer-most loop if the OrderByIdxJoin
|
||||
** optimization is disabled */
|
||||
return nPriorSat;
|
||||
}
|
||||
nEqOneRow = 0;
|
||||
}else if( p->i==0 || (p->aLevel[p->i-1].plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){
|
||||
nEqOneRow = nEqCol;
|
||||
}else{
|
||||
sortOrder = bOuterRev;
|
||||
nEqOneRow = -1;
|
||||
}
|
||||
pOrderBy = p->pOrderBy;
|
||||
assert( pOrderBy!=0 );
|
||||
if( wsFlags & WHERE_COLUMN_IN ) return nPriorSat;
|
||||
if( pIdx->bUnordered ) return nPriorSat;
|
||||
if( pIdx->bUnordered ){
|
||||
/* Hash indices (indicated by the "unordered" tag on sqlite_stat1) cannot
|
||||
** be used for sorting */
|
||||
return nPriorSat;
|
||||
}
|
||||
nTerm = pOrderBy->nExpr;
|
||||
uniqueNotNull = pIdx->onError!=OE_None;
|
||||
assert( nTerm>0 );
|
||||
|
||||
/* Argument pIdx must either point to a 'real' named index structure,
|
||||
@@ -2879,25 +2842,28 @@ static int isSortingIndex(
|
||||
** of the index is also allowed to match against the ORDER BY
|
||||
** clause.
|
||||
*/
|
||||
for(i=0,j=nPriorSat,pTerm=&pOrderBy->a[j]; j<nTerm; i++){
|
||||
Expr *pExpr; /* The expression of the ORDER BY pTerm */
|
||||
CollSeq *pColl; /* The collating sequence of pExpr */
|
||||
int termSortOrder; /* Sort order for this term */
|
||||
int iColumn; /* The i-th column of the index. -1 for rowid */
|
||||
int iSortOrder; /* 1 for DESC, 0 for ASC on the i-th index term */
|
||||
const char *zColl; /* Name of the collating sequence for i-th index term */
|
||||
j = nPriorSat;
|
||||
for(i=0,pOBItem=&pOrderBy->a[j]; j<nTerm && i<=pIdx->nColumn; i++){
|
||||
Expr *pOBExpr; /* The expression of the ORDER BY pOBItem */
|
||||
CollSeq *pColl; /* The collating sequence of pOBExpr */
|
||||
int termSortOrder; /* Sort order for this term */
|
||||
int iColumn; /* The i-th column of the index. -1 for rowid */
|
||||
int iSortOrder; /* 1 for DESC, 0 for ASC on the i-th index term */
|
||||
int isEq; /* Subject to an == or IS NULL constraint */
|
||||
int isMatch; /* ORDER BY term matches the index term */
|
||||
const char *zColl; /* Name of collating sequence for i-th index term */
|
||||
WhereTerm *pConstraint; /* A constraint in the WHERE clause */
|
||||
|
||||
assert( i<=pIdx->nColumn );
|
||||
pExpr = pTerm->pExpr;
|
||||
if( pExpr->op!=TK_COLUMN || pExpr->iTable!=base ){
|
||||
/* Can not use an index sort on anything that is not a column in the
|
||||
** left-most table of the FROM clause */
|
||||
/* If the next term of the ORDER BY clause refers to anything other than
|
||||
** a column in the "base" table, then this index will not be of any
|
||||
** further use in handling the ORDER BY. */
|
||||
pOBExpr = pOBItem->pExpr;
|
||||
if( pOBExpr->op!=TK_COLUMN || pOBExpr->iTable!=base ){
|
||||
break;
|
||||
}
|
||||
pColl = sqlite3ExprCollSeq(pParse, pExpr);
|
||||
if( !pColl ){
|
||||
pColl = db->pDfltColl;
|
||||
}
|
||||
|
||||
/* Find column number and collating sequence for the next entry
|
||||
** in the index */
|
||||
if( pIdx->zName && i<pIdx->nColumn ){
|
||||
iColumn = pIdx->aiColumn[i];
|
||||
if( iColumn==pIdx->pTable->iPKey ){
|
||||
@@ -2905,60 +2871,97 @@ static int isSortingIndex(
|
||||
}
|
||||
iSortOrder = pIdx->aSortOrder[i];
|
||||
zColl = pIdx->azColl[i];
|
||||
assert( zColl!=0 );
|
||||
}else{
|
||||
iColumn = -1;
|
||||
iSortOrder = 0;
|
||||
zColl = pColl->zName;
|
||||
zColl = 0;
|
||||
}
|
||||
if( pExpr->iColumn!=iColumn || sqlite3StrICmp(pColl->zName, zColl) ){
|
||||
/* Term j of the ORDER BY clause does not match column i of the index */
|
||||
if( i<nEqCol ){
|
||||
/* If an index column that is constrained by == fails to match an
|
||||
** ORDER BY term, that is OK. Just ignore that column of the index
|
||||
*/
|
||||
continue;
|
||||
}else if( i==pIdx->nColumn ){
|
||||
/* Index column i is the rowid. All other terms match. */
|
||||
break;
|
||||
|
||||
/* Check to see if the column number and collating sequence of the
|
||||
** index match the column number and collating sequence of the ORDER BY
|
||||
** clause entry. Set isMatch to 1 if they both match. */
|
||||
if( pOBExpr->iColumn==iColumn ){
|
||||
if( zColl ){
|
||||
pColl = sqlite3ExprCollSeq(pParse, pOBExpr);
|
||||
if( !pColl ) pColl = db->pDfltColl;
|
||||
isMatch = sqlite3StrICmp(pColl->zName, zColl)==0;
|
||||
}else{
|
||||
/* If an index column fails to match and is not constrained by ==
|
||||
** then the index cannot satisfy the ORDER BY constraint.
|
||||
*/
|
||||
return nPriorSat;
|
||||
}
|
||||
}
|
||||
assert( pIdx->aSortOrder!=0 || iColumn==-1 );
|
||||
assert( pTerm->sortOrder==0 || pTerm->sortOrder==1 );
|
||||
assert( iSortOrder==0 || iSortOrder==1 );
|
||||
termSortOrder = iSortOrder ^ pTerm->sortOrder;
|
||||
if( i>nEqOneRow ){
|
||||
if( termSortOrder!=sortOrder ){
|
||||
/* Indices can only be used if all ORDER BY terms past the
|
||||
** equality constraints have the correct DESC or ASC. */
|
||||
break;
|
||||
isMatch = 1;
|
||||
}
|
||||
}else{
|
||||
sortOrder = termSortOrder;
|
||||
isMatch = 0;
|
||||
}
|
||||
|
||||
/* termSortOrder is 0 or 1 for whether or not the access loop should
|
||||
** run forward or backwards (respectively) in order to satisfy this
|
||||
** term of the ORDER BY clause. */
|
||||
termSortOrder = iSortOrder ^ pOBItem->sortOrder;
|
||||
|
||||
/* If X is the column in the index and ORDER BY clause, check to see
|
||||
** if there are any X= or X IS NULL constraints in the WHERE clause. */
|
||||
pConstraint = findTerm(p->pWC, base, iColumn, p->notReady,
|
||||
WO_EQ|WO_ISNULL|WO_IN, pIdx);
|
||||
if( pConstraint==0 ){
|
||||
isEq = 0;
|
||||
}else if( pConstraint->eOperator==WO_IN ){
|
||||
break;
|
||||
}else if( pConstraint->eOperator==WO_ISNULL ){
|
||||
uniqueNotNull = 0;
|
||||
isEq = 1;
|
||||
}else if( pConstraint->prereqRight==0 ){
|
||||
isEq = 1;
|
||||
}else{
|
||||
Expr *pRight = pConstraint->pExpr->pRight;
|
||||
if( pRight->op==TK_COLUMN ){
|
||||
WHERETRACE((" .. isOrderedColumn(tab=%d,col=%d)",
|
||||
pRight->iTable, pRight->iColumn));
|
||||
isEq = isOrderedColumn(p, pRight->iTable, pRight->iColumn);
|
||||
WHERETRACE((" -> isEq=%d\n", isEq));
|
||||
if( isMatch && isEq>=2 && isEq!=pOBItem->sortOrder+2 ){
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
isEq = 0;
|
||||
}
|
||||
}
|
||||
assert( pOBItem->sortOrder==0 || pOBItem->sortOrder==1 );
|
||||
assert( iSortOrder==0 || iSortOrder==1 );
|
||||
if( !isMatch ){
|
||||
if( isEq==0 ){
|
||||
break;
|
||||
}else{
|
||||
continue;
|
||||
}
|
||||
}else if( isEq!=1 ){
|
||||
if( sortOrder==2 ){
|
||||
sortOrder = termSortOrder;
|
||||
}else if( termSortOrder!=sortOrder ){
|
||||
break;
|
||||
}
|
||||
}
|
||||
j++;
|
||||
pTerm++;
|
||||
pOBItem++;
|
||||
if( iColumn<0 ){
|
||||
seenRowid = 1;
|
||||
break;
|
||||
}else if( pTab->aCol[iColumn].notNull==0 && isEq==0 ){
|
||||
uniqueNotNull = 0;
|
||||
}
|
||||
}
|
||||
*pbRev = sortOrder;
|
||||
|
||||
/* If we have not found at least one ORDER BY term that matches the
|
||||
** index, then show no progress. */
|
||||
if( pOBItem==&pOrderBy->a[nPriorSat] ) return nPriorSat;
|
||||
|
||||
/* Return the necessary scan order back to the caller */
|
||||
*pbRev = sortOrder & 1;
|
||||
|
||||
/* If there was an "ORDER BY rowid" term that matched, or it is only
|
||||
** possible for a single row from this table to match, then skip over
|
||||
** any additional ORDER BY terms dealing with this table.
|
||||
*/
|
||||
if( seenRowid ||
|
||||
( (wsFlags & WHERE_COLUMN_NULL)==0
|
||||
&& i>=pIdx->nColumn
|
||||
&& indexIsUniqueNotNull(pIdx, nEqCol)
|
||||
)
|
||||
){
|
||||
if( seenRowid || (uniqueNotNull && i>=pIdx->nColumn) ){
|
||||
/* Advance j over additional ORDER BY terms associated with base */
|
||||
WhereMaskSet *pMS = p->pWC->pMaskSet;
|
||||
Bitmask m = ~getMask(pMS, base);
|
||||
@@ -3066,6 +3069,11 @@ static void bestBtreeIndex(WhereBestIdx *p){
|
||||
double log10N = (double)1; /* base-10 logarithm of nRow (inexact) */
|
||||
int bRev = 2; /* 0=forward scan. 1=reverse. 2=undecided */
|
||||
|
||||
WHERETRACE((
|
||||
" %s(%s):\n",
|
||||
pSrc->pTab->zName, (pIdx ? pIdx->zName : "ipk")
|
||||
));
|
||||
|
||||
/* The following variables are populated based on the properties of
|
||||
** index being evaluated. They are then used to determine the expected
|
||||
** cost and number of rows returned.
|
||||
@@ -3095,10 +3103,6 @@ static void bestBtreeIndex(WhereBestIdx *p){
|
||||
** the sub-select is assumed to return 25 rows for the purposes of
|
||||
** determining nInMul.
|
||||
**
|
||||
** nOrdered:
|
||||
** The number of equality terms that are constrainted by outer loop
|
||||
** variables that are well-ordered.
|
||||
**
|
||||
** bInEst:
|
||||
** Set to true if there was at least one "x IN (SELECT ...)" term used
|
||||
** in determining the value of nInMul. Note that the RHS of the
|
||||
@@ -3136,7 +3140,6 @@ static void bestBtreeIndex(WhereBestIdx *p){
|
||||
** SELECT a, b FROM tbl WHERE a = 1;
|
||||
** SELECT a, b, c FROM tbl WHERE a = 1;
|
||||
*/
|
||||
int nOrdered; /* Number of ordered terms matching index */
|
||||
int bInEst = 0; /* True if "x IN (SELECT...)" seen */
|
||||
int nInMul = 1; /* Number of distinct equalities to lookup */
|
||||
double rangeDiv = (double)1; /* Estimated reduction in search space */
|
||||
@@ -3164,7 +3167,7 @@ static void bestBtreeIndex(WhereBestIdx *p){
|
||||
}
|
||||
|
||||
/* Determine the values of pc.plan.nEq and nInMul */
|
||||
for(pc.plan.nEq=nOrdered=0; pc.plan.nEq<pProbe->nColumn; pc.plan.nEq++){
|
||||
for(pc.plan.nEq=0; pc.plan.nEq<pProbe->nColumn; pc.plan.nEq++){
|
||||
int j = pProbe->aiColumn[pc.plan.nEq];
|
||||
pTerm = findTerm(pWC, iCur, j, p->notReady, eqTermMask, pIdx);
|
||||
if( pTerm==0 ) break;
|
||||
@@ -3183,10 +3186,6 @@ static void bestBtreeIndex(WhereBestIdx *p){
|
||||
}
|
||||
}else if( pTerm->eOperator & WO_ISNULL ){
|
||||
pc.plan.wsFlags |= WHERE_COLUMN_NULL;
|
||||
if( pc.plan.nEq==nOrdered ) nOrdered++;
|
||||
}else if( bSort && pc.plan.nEq==nOrdered
|
||||
&& isOrderedTerm(p,pTerm,&bRev) ){
|
||||
nOrdered++;
|
||||
}
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
if( pc.plan.nEq==0 && pProbe->aSample ) pFirstTerm = pTerm;
|
||||
@@ -3242,12 +3241,12 @@ static void bestBtreeIndex(WhereBestIdx *p){
|
||||
** the index will scan rows in a different order, set the bSort
|
||||
** variable. */
|
||||
assert( bRev>=0 && bRev<=2 );
|
||||
if( bSort ){
|
||||
testcase( bRev==0 );
|
||||
testcase( bRev==1 );
|
||||
testcase( bRev==2 );
|
||||
pc.plan.nOBSat = isSortingIndex(p, pProbe, iCur, nOrdered,
|
||||
pc.plan.wsFlags, bRev&1, &bRev);
|
||||
if( bSort && (pSrc->jointype & JT_LEFT)==0 ){
|
||||
int bRev = 2;
|
||||
WHERETRACE((" --> before isSortingIndex: nPriorSat=%d\n",nPriorSat));
|
||||
pc.plan.nOBSat = isSortingIndex(p, pProbe, iCur, &bRev);
|
||||
WHERETRACE((" --> after isSortingIndex: bRev=%d nOBSat=%d\n",
|
||||
bRev, pc.plan.nOBSat));
|
||||
if( nPriorSat<pc.plan.nOBSat || (pc.plan.wsFlags & WHERE_UNIQUE)!=0 ){
|
||||
pc.plan.wsFlags |= WHERE_ORDERED;
|
||||
}
|
||||
@@ -3365,7 +3364,10 @@ static void bestBtreeIndex(WhereBestIdx *p){
|
||||
*/
|
||||
pc.rCost = aiRowEst[0]*4;
|
||||
pc.plan.wsFlags &= ~WHERE_IDX_ONLY;
|
||||
if( pIdx ) pc.plan.wsFlags &= ~WHERE_ORDERED;
|
||||
if( pIdx ){
|
||||
pc.plan.wsFlags &= ~WHERE_ORDERED;
|
||||
pc.plan.nOBSat = nPriorSat;
|
||||
}
|
||||
}else{
|
||||
log10N = estLog(aiRowEst[0]);
|
||||
pc.rCost = pc.plan.nRow;
|
||||
@@ -3470,13 +3472,11 @@ static void bestBtreeIndex(WhereBestIdx *p){
|
||||
|
||||
|
||||
WHERETRACE((
|
||||
"%s(%s):\n"
|
||||
" nEq=%d nInMul=%d rangeDiv=%d bSort=%d bLookup=%d wsFlags=0x%08x\n"
|
||||
" notReady=0x%llx log10N=%.1f nRow=%.1f cost=%.1f\n"
|
||||
" used=0x%llx nOrdered=%d nOBSat=%d\n",
|
||||
pSrc->pTab->zName, (pIdx ? pIdx->zName : "ipk"),
|
||||
" nEq=%d nInMul=%d rangeDiv=%d bSort=%d bLookup=%d wsFlags=0x%08x\n"
|
||||
" notReady=0x%llx log10N=%.1f nRow=%.1f cost=%.1f\n"
|
||||
" used=0x%llx nOBSat=%d\n",
|
||||
pc.plan.nEq, nInMul, (int)rangeDiv, bSort, bLookup, pc.plan.wsFlags,
|
||||
p->notReady, log10N, pc.plan.nRow, pc.rCost, pc.used, nOrdered,
|
||||
p->notReady, log10N, pc.plan.nRow, pc.rCost, pc.used,
|
||||
pc.plan.nOBSat
|
||||
));
|
||||
|
||||
@@ -3514,7 +3514,7 @@ static void bestBtreeIndex(WhereBestIdx *p){
|
||||
|| p->cost.plan.u.pIdx==pSrc->pIndex
|
||||
);
|
||||
|
||||
WHERETRACE(("best index is: %s\n",
|
||||
WHERETRACE((" best index is: %s\n",
|
||||
p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk"));
|
||||
|
||||
bestOrClauseIndex(p);
|
||||
@@ -5064,7 +5064,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
sWBI.notReady = (isOptimal ? m : sWBI.notValid);
|
||||
if( sWBI.pSrc->pIndex==0 ) nUnconstrained++;
|
||||
|
||||
WHERETRACE(("=== trying table %d (%s) with isOptimal=%d ===\n",
|
||||
WHERETRACE((" === trying table %d (%s) with isOptimal=%d ===\n",
|
||||
j, sWBI.pSrc->pTab->zName, isOptimal));
|
||||
assert( sWBI.pSrc->pTab );
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
@@ -5117,8 +5117,8 @@ WhereInfo *sqlite3WhereBegin(
|
||||
|| NEVER((sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0))
|
||||
&& (bestJ<0 || compareCost(&sWBI.cost, &bestPlan)) /* (4) */
|
||||
){
|
||||
WHERETRACE(("=== table %d (%s) is best so far\n"
|
||||
" cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=%08x\n",
|
||||
WHERETRACE((" === table %d (%s) is best so far\n"
|
||||
" cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=%08x\n",
|
||||
j, sWBI.pSrc->pTab->zName,
|
||||
sWBI.cost.rCost, sWBI.cost.plan.nRow,
|
||||
sWBI.cost.plan.nOBSat, sWBI.cost.plan.wsFlags));
|
||||
|
||||
Reference in New Issue
Block a user