github.com/m3db/m3@v1.5.0/src/query/parser/promql/resolve_scalars.go (about) 1 // Copyright (c) 2018 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package promql 22 23 import ( 24 "fmt" 25 "math" 26 27 "github.com/m3db/m3/src/query/functions/binary" 28 29 pql "github.com/prometheus/prometheus/promql/parser" 30 ) 31 32 var ( 33 errNilScalarArg = fmt.Errorf("scalar expression is nil") 34 errInvalidNestingFetch = fmt.Errorf("invalid nesting for fetch") 35 errInvalidNestingVector = fmt.Errorf("invalid nesting for vector conversion") 36 ) 37 38 func resolveScalarArgument(expr pql.Expr) (float64, error) { 39 value, nesting, err := resolveScalarArgumentWithNesting(expr, 0) 40 // On a regular error, return error 41 if err != nil { 42 return 0, err 43 } 44 45 if nesting != 0 { 46 return 0, fmt.Errorf("promql.resolveScalarArgument: invalid nesting %d", 47 nesting) 48 } 49 50 return value, nil 51 } 52 53 // resolves an expression which should resolve to a scalar argument 54 func resolveScalarArgumentWithNesting( 55 expr pql.Expr, 56 nesting int, 57 ) (float64, int, error) { 58 if expr == nil { 59 return 0, 0, errNilScalarArg 60 } 61 62 switch n := expr.(type) { 63 case *pql.BinaryExpr: 64 left, nestingLeft, err := resolveScalarArgumentWithNesting(n.LHS, nesting) 65 if err != nil { 66 return 0, 0, err 67 } 68 69 right, nestingRight, err := resolveScalarArgumentWithNesting(n.RHS, nesting) 70 if err != nil { 71 return 0, 0, err 72 } 73 74 if nestingLeft < nestingRight { 75 nesting = nestingLeft 76 } else { 77 nesting = nestingRight 78 } 79 80 op := getBinaryOpType(n.Op) 81 fn, err := binary.ArithmeticFunction(op, n.ReturnBool) 82 if err != nil { 83 return 0, 0, err 84 } 85 86 return fn(left, right), nesting, nil 87 88 case *pql.VectorSelector: 89 // during scalar argument resolution, prom does not expand vectors 90 // and returns NaN as the value instead. 91 if nesting < 1 { 92 return 0, 0, errInvalidNestingFetch 93 } 94 95 return math.NaN(), nesting - 1, nil 96 97 case *pql.Call: 98 // TODO: once these functions exist, use those constants here 99 // If the function called is `scalar`, evaluate and ensure a scalar. 100 if n.Func.Name == "scalar" { 101 return resolveScalarArgumentWithNesting(n.Args[0], nesting+1) 102 } else if n.Func.Name == "vector" { 103 // If the function called is `vector`, evaluate and ensure a vector. 104 if nesting < 1 { 105 return 0, 0, errInvalidNestingVector 106 } 107 108 return resolveScalarArgumentWithNesting(n.Args[0], nesting-1) 109 } 110 111 return 0, 0, nil 112 113 case *pql.NumberLiteral: 114 return n.Val, 0, nil 115 116 case *pql.ParenExpr: 117 // Evaluate inside of paren expressions 118 return resolveScalarArgumentWithNesting(n.Expr, nesting) 119 } 120 121 return 0, 0, fmt.Errorf("resolveScalarArgument: unhandled node type %T, %v", 122 expr, expr) 123 }