Index: src/wherecode.c ================================================================== --- src/wherecode.c +++ src/wherecode.c @@ -667,22 +667,24 @@ /* ** Insert an OP_CursorHint instruction if it is appropriate to do so. */ static void codeCursorHint( - WhereInfo *pWInfo, - WhereLevel *pLevel + WhereInfo *pWInfo, /* The where clause */ + WhereLevel *pLevel, /* Which loop to provide hints for */ + WhereTerm *pEndRange /* Hint this end-of-scan boundary term if not NULL */ ){ Parse *pParse = pWInfo->pParse; sqlite3 *db = pParse->db; Vdbe *v = pParse->pVdbe; Expr *pExpr = 0; WhereLoop *pLoop = pLevel->pWLoop; int iCur; WhereClause *pWC; WhereTerm *pTerm; - int i; + WhereLoop *pWLoop; + int i, j; struct CCurHint sHint; Walker sWalker; if( OptimizationDisabled(db, SQLITE_CursorHints) ) return; iCur = pLevel->iTabCur; @@ -692,22 +694,37 @@ sHint.pIdx = pLoop->u.btree.pIndex; memset(&sWalker, 0, sizeof(sWalker)); sWalker.pParse = pParse; sWalker.u.pCCurHint = &sHint; pWC = &pWInfo->sWC; + pWLoop = pLevel->pWLoop; for(i=0; inTerm; i++){ pTerm = &pWC->a[i]; if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue; if( pTerm->prereqAll & pLevel->notReady ) continue; if( ExprHasProperty(pTerm->pExpr, EP_FromJoin) ) continue; + + /* All terms in pWLoop->aLTerm[] except pEndRange are used to initialize + ** the cursor. No need to hint initialization terms. */ + if( pTerm!=pEndRange ){ + for(j=0; jnLTerm && pWLoop->aLTerm[j]!=pTerm; j++){} + if( jnLTerm ) continue; + } + + /* No subqueries or non-deterministic functions allowed */ if( sqlite3ExprContainsSubquery(pTerm->pExpr) ) continue; + + /* For an index scan, make sure referenced columns are actually in + ** the index. */ if( sHint.pIdx!=0 ){ sWalker.eCode = 0; sWalker.xExprCallback = codeCursorHintCheckExpr; sqlite3WalkExpr(&sWalker, pTerm->pExpr); if( sWalker.eCode ) continue; } + + /* If we survive all prior tests, that means this term is worth hinting */ pExpr = sqlite3ExprAnd(db, pExpr, sqlite3ExprDup(db, pTerm->pExpr, 0)); } if( pExpr!=0 ){ sWalker.xExprCallback = codeCursorHintFixExpr; sqlite3WalkExpr(&sWalker, pExpr); @@ -715,11 +732,11 @@ (sHint.pIdx ? sHint.iIdxCur : sHint.iTabCur), 0, 0, (const char*)pExpr, P4_EXPR); } } #else -# define codeCursorHint(A,B) /* No-op */ +# define codeCursorHint(A,B,C) /* No-op */ #endif /* SQLITE_ENABLE_CURSOR_HINTS */ /* ** Generate code for the start of the iLevel-th loop in the WHERE clause ** implementation described by pWInfo. @@ -881,11 +898,11 @@ if( bRev ){ pTerm = pStart; pStart = pEnd; pEnd = pTerm; } - codeCursorHint(pWInfo, pLevel); + codeCursorHint(pWInfo, pLevel, pEnd); if( pStart ){ Expr *pX; /* The expression that defines the start bound */ int r1, rTemp; /* Registers for holding the start boundary */ /* The following constant maps TK_xx codes into corresponding @@ -1104,11 +1121,11 @@ startEq = !pRangeStart || pRangeStart->eOperator & (WO_LE|WO_GE); endEq = !pRangeEnd || pRangeEnd->eOperator & (WO_LE|WO_GE); start_constraints = pRangeStart || nEq>0; /* Seek the index cursor to the start of the range. */ - codeCursorHint(pWInfo, pLevel); + codeCursorHint(pWInfo, pLevel, pRangeEnd); nConstraint = nEq; if( pRangeStart ){ Expr *pRight = pRangeStart->pExpr->pRight; sqlite3ExprCode(pParse, pRight, regBase+nEq); whereLikeOptimizationStringFixup(v, pLevel, pRangeStart); @@ -1532,11 +1549,11 @@ if( pTabItem->isRecursive ){ /* Tables marked isRecursive have only a single row that is stored in ** a pseudo-cursor. No need to Rewind or Next such cursors. */ pLevel->op = OP_Noop; }else{ - codeCursorHint(pWInfo, pLevel); + codeCursorHint(pWInfo, pLevel, 0); pLevel->op = aStep[bRev]; pLevel->p1 = iCur; pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev], iCur, addrBrk); VdbeCoverageIf(v, bRev==0); VdbeCoverageIf(v, bRev!=0); Index: test/cursorhint.test ================================================================== --- test/cursorhint.test +++ test/cursorhint.test @@ -92,32 +92,62 @@ } } {AND(AND(LT(c1,15),GT(c2,22)),NE(c0,98))} # Indexed queries # -do_test 4.1 { +do_test 4.1asc { db eval { CREATE INDEX t1bc ON t1(b,c); CREATE INDEX t2yz ON t2(y,z); } p4_of_opcode db CursorHint { - SELECT * FROM t1 WHERE b>11; + SELECT * FROM t1 WHERE b>11 ORDER BY b ASC; + } +} {} +do_test 4.1desc { + p4_of_opcode db CursorHint { + SELECT * FROM t1 WHERE b>11 ORDER BY b DESC; } } {GT(c0,11)} do_test 4.2 { p5_of_opcode db OpenRead . { SELECT * FROM t1 WHERE b>11; } } {02 00} -do_test 4.3 { +do_test 4.3asc { + p4_of_opcode db CursorHint { + SELECT c FROM t1 WHERE b<11 ORDER BY b ASC; + } +} {LT(c0,11)} +do_test 4.3desc { p4_of_opcode db CursorHint { - SELECT c FROM t1 WHERE b>11; + SELECT c FROM t1 WHERE b<11 ORDER BY b DESC; } -} {GT(c0,11)} +} {} do_test 4.4 { p5_of_opcode db OpenRead . { - SELECT c FROM t1 WHERE b>11; + SELECT c FROM t1 WHERE b<11; } } {00} +do_test 4.5asc { + p4_of_opcode db CursorHint { + SELECT c FROM t1 WHERE b>=10 AND b<=20 ORDER BY b ASC; + } +} {LE(c0,20)} +do_test 4.5desc { + p4_of_opcode db CursorHint { + SELECT c FROM t1 WHERE b>=10 AND b<=20 ORDER BY b DESC; + } +} {GE(c0,10)} +do_test 4.6asc { + p4_of_opcode db CursorHint { + SELECT rowid FROM t1 WHERE b=22 AND c>=10 AND c<=20 ORDER BY b,c ASC; + } +} {LE(c1,20)} +do_test 4.6desc { + p4_of_opcode db CursorHint { + SELECT rowid FROM t1 WHERE b=22 AND c>=10 AND c<=20 ORDER BY b,c DESC; + } +} {GE(c1,10)} finish_test