func CanIUseThisIndexForThisWhereClause(index catalog.RangeIndex, where ast.Expression, 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 } // put the where clause into conjunctive normal form ennf := ast.NewExpressionNNF() whereNNF, err := where.Accept(ennf) if err != nil { return false, nil, nil, err } ecnf := ast.NewExpressionCNF() whereCNF, err := whereNNF.Accept(ecnf) if err != nil { return false, nil, nil, err } switch whereCNF := whereCNF.(type) { case *ast.AndOperator: // this is an and, we can try to satisfy individual operands found := false rranges := plan.ScanRanges{} for _, oper := range whereCNF.Operands { // see if the where clause expression is sargable with respect to the index key es := NewExpressionSargable(indexKeyFormal[0]) oper.Accept(es) if es.IsSargable() { found = true for _, ran := range es.ScanRanges() { rranges = MergeRanges(rranges, ran) clog.To(planner.CHANNEL, "now ranges are: %v", rranges) } } } if found { return true, rranges, nil, nil } default: // not an and, we must satisfy the whole expression // see if the where clause expression is sargable with respect to the index key es := NewExpressionSargable(indexKeyFormal[0]) whereCNF.Accept(es) if es.IsSargable() { return true, es.ScanRanges(), nil, nil } } // cannot use this index return false, nil, nil, nil }
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 }