func NewExpressionSargable(indexExpression ast.Expression) *ExpressionSargable { return &ExpressionSargable{ indexExpression: indexExpression, equivalenceChecker: ast.NewExpressionEquivalenceChecker(ast.ExpressionList{indexExpression}), constantChecker: ast.NewExpressionFunctionalDependencyCheckerFull(ast.ExpressionList{}), sargable: false, scanRanges: make(plan.ScanRanges, 0), } }
func CanFastCountIndex(index catalog.CountIndex, bucket string, resultExprList ast.ResultExpressionList) ast.Expression { // convert the index key to formal notation indexKeyFormal, err := IndexKeyInFormalNotation(index.Key(), bucket) if err != nil { return nil } deps := ast.ExpressionList{indexKeyFormal[0]} clog.To(planner.CHANNEL, "index deps are: %v", deps) depChecker := ast.NewExpressionFunctionalDependencyCheckerFull(deps) // start looking at the projection for _, resultExpr := range resultExprList { // cannot be * if resultExpr.Star { return nil } switch resultExpr := resultExpr.Expr.(type) { case *ast.FunctionCallCount: // aggregates all take 1 operand operands := resultExpr.GetOperands() if len(operands) < 1 { return nil } aggOperand := operands[0] // must NOT be * if aggOperand.Star { return nil } // look at dependencies inside this operand _, err := depChecker.Visit(aggOperand.Expr) if err != nil { return nil } default: return nil } } // if we made it this far, can do fast count on bucket return indexKeyFormal[0] }
func CanIUseThisIndexForThisProjectionNoWhereNoGroupClause(index catalog.RangeIndex, resultExprList ast.ResultExpressionList, bucket string) (bool, plan.ScanRanges, ast.Expression, error) { // convert the index key to formal notation indexKeyFormal, err := IndexKeyInFormalNotation(index.Key(), bucket) if err != nil { return false, nil, nil, err } // FIXME only looking at first element in key right now deps := ast.ExpressionList{indexKeyFormal[0]} clog.To(planner.CHANNEL, "index deps are: %v", deps) depChecker := ast.NewExpressionFunctionalDependencyCheckerFull(deps) // start looking at the projection allAggregateFunctionsMin := true for _, resultExpr := range resultExprList { // presence of * means we cannot use index on field, must see all (for this particular optimization) if resultExpr.Star { return false, nil, nil, nil } switch expr := resultExpr.Expr.(type) { case ast.AggregateFunctionCallExpression: _, isMin := expr.(*ast.FunctionCallMin) if !isMin { clog.To(planner.CHANNEL, "projection not MIN") allAggregateFunctionsMin = false } // aggregates all take 1 operand operands := expr.GetOperands() if len(operands) < 1 { return false, nil, nil, nil } aggOperand := operands[0] // preence of * means we cannot use this index, must see all (for this particular optimization) if aggOperand.Star { return false, nil, nil, nil } // look at dependencies inside this operand _, err := depChecker.Visit(aggOperand.Expr) if err != nil { return false, nil, nil, nil } default: // all expressions must be aggregates for this particular optimization return false, nil, nil, nil } } // if we made it this far, we can in fact use the index // doing a scan of all non-eliminatable items (non-NULL, non-MISSING) dummyOp := ast.NewIsNotNullOperator(indexKeyFormal[0]) es := NewExpressionSargable(indexKeyFormal[0]) dummyOp.Accept(es) if es.IsSargable() { ranges := es.ScanRanges() if allAggregateFunctionsMin { for _, r := range ranges { r.Limit = 1 } } return true, ranges, nil, nil } clog.Error(fmt.Errorf("expected this to never happen")) // cannot use this index return false, nil, nil, nil }
func DoesIndexCoverStatement(index catalog.RangeIndex, stmt *ast.SelectStatement) bool { if stmt.From.Over != nil { // index cannot cover queries containing OVER right now return false } // convert the index key to formal notation indexKeyFormal, err := IndexKeyInFormalNotation(index.Key(), stmt.From.As) if err != nil { return false } deps := ast.ExpressionList{} for _, indexKey := range indexKeyFormal { deps = append(deps, indexKey) } clog.To(planner.CHANNEL, "index deps are: %v", deps) depChecker := ast.NewExpressionFunctionalDependencyCheckerFull(deps) // first check the projection for _, resultExpr := range stmt.Select { if resultExpr.Star == true || resultExpr.Expr == nil { // currently cannot cover * return false } _, err := depChecker.Visit(resultExpr.Expr) if err != nil { return false } } if stmt.Where != nil { _, err = depChecker.Visit(stmt.Where) if err != nil { return false } } if stmt.GroupBy != nil { for _, groupExpr := range stmt.GroupBy { _, err = depChecker.Visit(groupExpr) if err != nil { return false } } if stmt.Having != nil { _, err = depChecker.Visit(stmt.Having) if err != nil { return false } } } if stmt.OrderBy != nil { for _, orderExpr := range stmt.OrderBy { _, err = depChecker.Visit(orderExpr.Expr) if err != nil { return false } } } // if we go this far it is covered return true }