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  }