mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-21 09:00:59 +03:00
First attempt to get ORDER BY optimization working in NGQP.
FossilOrigin-Name: 9fe20292558bb9422de91e35648cb834cbf3b306
This commit is contained in:
160
src/where.c
160
src/where.c
@@ -84,6 +84,7 @@ struct WhereLoop {
|
||||
*/
|
||||
struct WherePath {
|
||||
Bitmask maskLoop; /* Bitmask of all WhereLoop objects in this path */
|
||||
Bitmask revLoop; /* aLoop[]s that should be reversed for ORDER BY */
|
||||
double nRow; /* Estimated number of rows generated by this path */
|
||||
double rCost; /* Total cost of this path */
|
||||
u8 isOrdered; /* True if this path satisfies ORDER BY */
|
||||
@@ -5078,8 +5079,13 @@ static void whereLoopPrint(WhereLoop *p, SrcList *pTabList){
|
||||
pItem->zAlias ? pItem->zAlias : pTab->zName);
|
||||
if( (p->wsFlags & WHERE_VIRTUALTABLE)==0 ){
|
||||
if( p->u.btree.pIndex ){
|
||||
sqlite3DebugPrintf(".%-12s %2d",
|
||||
p->u.btree.pIndex->zName, p->u.btree.nEq);
|
||||
const char *zName = p->u.btree.pIndex->zName;
|
||||
if( strncmp(zName, "sqlite_autoindex_", 17)==0 ){
|
||||
int i = sqlite3Strlen30(zName) - 1;
|
||||
while( zName[i]!='_' ) i--;
|
||||
zName += i;
|
||||
}
|
||||
sqlite3DebugPrintf(".%-12s %2d", zName, p->u.btree.nEq);
|
||||
}else{
|
||||
sqlite3DebugPrintf("%16s","");
|
||||
}
|
||||
@@ -5734,25 +5740,139 @@ whereLoopAddAll_end:
|
||||
}
|
||||
|
||||
/*
|
||||
** Examine a WherePath to see if it outputs rows in the requested ORDER BY
|
||||
** (or GROUP BY) without requiring a separate source operation. Return 1
|
||||
** if it does and 0 if it does not and -1 if we cannot tell.
|
||||
** Examine a WherePath (with the addition of the extra WhereLoop of the 4th
|
||||
** parameters) to see if it outputs rows in the requested ORDER BY
|
||||
** (or GROUP BY) without requiring a separate source operation. Return:
|
||||
**
|
||||
** 0: ORDER BY is not satisfied. Sorting required
|
||||
** 1: ORDER BY is satisfied. Omit sorting
|
||||
** -1: Unknown at this time
|
||||
**
|
||||
*/
|
||||
static int wherePathSatisfiesOrderBy(
|
||||
WhereInfo *pWInfo, /* The WHERE clause */
|
||||
WherePath *pPath, /* The WherePath to check */
|
||||
int nLoop, /* Number of entries in pPath->aLoop[] */
|
||||
WhereLoop *pLoop /* Add this WhereLoop to the end of pPath->aLoop[] */
|
||||
WhereLoop *pLast, /* Add this WhereLoop to the end of pPath->aLoop[] */
|
||||
Bitmask *pRevMask /* Mask of WhereLoops to run in reverse order */
|
||||
){
|
||||
if( pLoop->wsFlags & WHERE_VIRTUALTABLE ){
|
||||
return nLoop==0 && pLoop->u.vtab.isOrdered;
|
||||
}else{
|
||||
/* TBD: Check to see if pFrom + pWLoop satisfies the ORDER BY.
|
||||
** (1) If yes: set isOrderedValid and isOrdered to 1.
|
||||
** (2) If no: set isOrderedValid to 1 and isOrdered to 0.
|
||||
** (3) unknown: no-op */
|
||||
return 0;
|
||||
u8 revSet;
|
||||
u8 rev;
|
||||
u8 isUnique;
|
||||
u8 requireUnique = 0;
|
||||
u16 nColumn;
|
||||
u16 nOrderBy;
|
||||
int i, j;
|
||||
int nUsed = 0;
|
||||
int iCur;
|
||||
int iColumn;
|
||||
WhereLoop *pLoop;
|
||||
ExprList *pOrderBy = pWInfo->pOrderBy;
|
||||
Expr *pOBExpr;
|
||||
CollSeq *pColl;
|
||||
Index *pIndex;
|
||||
sqlite3 *db = pWInfo->pParse->db;
|
||||
Bitmask revMask = 0;
|
||||
|
||||
/*
|
||||
** We say the WhereLoop is "one-row" if all of the following are true:
|
||||
** (a) All index columns match with WHERE_COLUMN_EQ.
|
||||
** (b) The index is unique
|
||||
**
|
||||
** General rules: (not an algorithm!)
|
||||
**
|
||||
** (1) If the current WhereLoop is one-row, then match over any and all
|
||||
** ORDER BY terms for the current WhereLoop and proceed to the next
|
||||
** WhereLoop.
|
||||
**
|
||||
** (2) If the current WhereLoop is not one-row, then all subsequent
|
||||
** WhereLoops must be one-row.
|
||||
**
|
||||
** (3) Optionally match any ORDER BY terms against the first nEq columns
|
||||
** of the index.
|
||||
**
|
||||
** (4) Index columns past nEq must match ORDER BY terms one-for-one.
|
||||
*/
|
||||
|
||||
assert( pOrderBy!=0 );
|
||||
|
||||
/* Sortability of virtual tables is determined by the xBestIndex method
|
||||
** of the virtual table itself */
|
||||
if( pLast->wsFlags & WHERE_VIRTUALTABLE ){
|
||||
assert( nLoop==0 );
|
||||
return pLast->u.vtab.isOrdered;
|
||||
}
|
||||
|
||||
/* Sorting is always required if any term of the ORDER BY is not a
|
||||
** column reference */
|
||||
nOrderBy = pOrderBy->nExpr;
|
||||
for(i=0; i<nOrderBy; i++){
|
||||
pOBExpr = sqlite3ExprSkipCollate(pOrderBy->a[nUsed].pExpr);
|
||||
if( pOBExpr->op!=TK_COLUMN ) return 0;
|
||||
}
|
||||
|
||||
for(i=0; i<=nLoop && nUsed<nOrderBy; i++){
|
||||
pLoop = i<nLoop ? pPath->aLoop[i] : pLast;
|
||||
assert( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 );
|
||||
isUnique = 1;
|
||||
if( pLoop->wsFlags & WHERE_IPK ){
|
||||
if( (pLoop->wsFlags & WHERE_COLUMN_EQ)!=0 ) isUnique = 0;
|
||||
pIndex = 0;
|
||||
nColumn = 1;
|
||||
}else if( pLoop->u.btree.pIndex==0 ){
|
||||
return 0;
|
||||
}else{
|
||||
pIndex = pLoop->u.btree.pIndex;
|
||||
nColumn = pIndex->nColumn;
|
||||
if( pIndex->onError==OE_None ){
|
||||
isUnique = 0;
|
||||
}else if( (pLoop->wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_RANGE
|
||||
|WHERE_COLUMN_NULL))!=0 ){
|
||||
isUnique = 0;
|
||||
}else if( pLoop->u.btree.nEq < pIndex->nColumn ){
|
||||
isUnique = 0;
|
||||
}
|
||||
}
|
||||
if( !isUnique && requireUnique ) return 0;
|
||||
requireUnique = !isUnique;
|
||||
iCur = pWInfo->pTabList->a[pLoop->iTab].iCursor;
|
||||
j = 0;
|
||||
revSet = rev = 0;
|
||||
for(j=0; j<nColumn && nUsed<nOrderBy; j++, nUsed++){
|
||||
pOBExpr = sqlite3ExprSkipCollate(pOrderBy->a[nUsed].pExpr);
|
||||
assert( pOBExpr->op==TK_COLUMN );
|
||||
if( pOBExpr->iTable!=iCur ) break;
|
||||
if( pIndex==0 ){
|
||||
if( pOBExpr->iColumn<0 && j==0 ){
|
||||
isUnique = 1;
|
||||
rev = pOrderBy->a[nUsed].sortOrder;
|
||||
}else if( isUnique ){
|
||||
continue;
|
||||
}else{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if( isUnique ) continue;
|
||||
iColumn = pIndex->aiColumn[j];
|
||||
if( iColumn==pIndex->pTable->iPKey ) iColumn = -1;
|
||||
if( pOBExpr->iColumn!=iColumn ) return 0;
|
||||
pColl = sqlite3ExprCollSeq(pWInfo->pParse, pOrderBy->a[nUsed].pExpr);
|
||||
if( !pColl ) pColl = db->pDfltColl;
|
||||
if( sqlite3StrICmp(pColl->zName, pIndex->azColl[j])!=0 ) return 0;
|
||||
if( revSet ){
|
||||
if( pIndex->aSortOrder[j]!=rev ) return 0;
|
||||
}else{
|
||||
rev = pIndex->aSortOrder[j];
|
||||
revSet = 1;
|
||||
}
|
||||
}
|
||||
if( rev ) revMask |= ((Bitmask)1)<<i;
|
||||
}
|
||||
if( nUsed==nOrderBy ){
|
||||
*pRevMask = revMask;
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -5831,6 +5951,7 @@ static int wherePathSolver(WhereInfo *pWInfo){
|
||||
for(ii=0, pFrom=aFrom; ii<nFrom; ii++, pFrom++){
|
||||
for(pWLoop=pWInfo->pLoops; pWLoop; pWLoop=pWLoop->pNextLoop){
|
||||
Bitmask maskNew;
|
||||
Bitmask revMask = 0;
|
||||
u8 isOrderedValid = pFrom->isOrderedValid;
|
||||
u8 isOrdered = pFrom->isOrdered;
|
||||
if( (pWLoop->prereq & ~pFrom->maskLoop)!=0 ) continue;
|
||||
@@ -5840,7 +5961,8 @@ static int wherePathSolver(WhereInfo *pWInfo){
|
||||
rCost = pWLoop->rSetup + pWLoop->rRun*pFrom->nRow + pFrom->rCost;
|
||||
maskNew = pFrom->maskLoop | pWLoop->maskSelf;
|
||||
if( !isOrderedValid ){
|
||||
switch( wherePathSatisfiesOrderBy(pWInfo, pFrom, iLoop, pWLoop) ){
|
||||
switch( wherePathSatisfiesOrderBy(pWInfo, pFrom, iLoop,
|
||||
pWLoop, &revMask) ){
|
||||
case 1: /* Yes. pFrom+pWLoop does satisfy the ORDER BY clause */
|
||||
isOrdered = 1;
|
||||
isOrderedValid = 1;
|
||||
@@ -5873,6 +5995,7 @@ static int wherePathSolver(WhereInfo *pWInfo){
|
||||
}
|
||||
/* pWLoop is a winner. Add it to the set of best so far */
|
||||
pTo->maskLoop = pFrom->maskLoop | pWLoop->maskSelf;
|
||||
pTo->revLoop = revMask;
|
||||
pTo->nRow = pFrom->nRow * pWLoop->nOut;
|
||||
pTo->rCost = rCost;
|
||||
pTo->isOrderedValid = isOrderedValid;
|
||||
@@ -6193,7 +6316,12 @@ WhereInfo *sqlite3WhereBegin(
|
||||
&& (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_WHERETRACE))
|
||||
if( sqlite3WhereTrace ){
|
||||
int ii;
|
||||
sqlite3DebugPrintf("------------ Solution ----------------\n");
|
||||
sqlite3DebugPrintf("------------ Solution -------------");
|
||||
if( pWInfo->nOBSat ){
|
||||
sqlite3DebugPrintf(" ORDER BY omitted\n");
|
||||
}else{
|
||||
sqlite3DebugPrintf("\n");
|
||||
}
|
||||
for(ii=0; ii<nTabList; ii++){
|
||||
whereLoopPrint(pWInfo->a[ii].pWLoop, pTabList);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user