github.com/m3db/m3@v1.5.0/src/query/parser/promql/parse.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  	"time"
    26  
    27  	pql "github.com/prometheus/prometheus/promql/parser"
    28  
    29  	"github.com/m3db/m3/src/query/block"
    30  	"github.com/m3db/m3/src/query/functions/binary"
    31  	"github.com/m3db/m3/src/query/functions/lazy"
    32  	"github.com/m3db/m3/src/query/functions/scalar"
    33  	"github.com/m3db/m3/src/query/models"
    34  	"github.com/m3db/m3/src/query/parser"
    35  	xtime "github.com/m3db/m3/src/x/time"
    36  )
    37  
    38  type promParser struct {
    39  	stepSize          time.Duration
    40  	expr              pql.Expr
    41  	tagOpts           models.TagOptions
    42  	parseFunctionExpr ParseFunctionExpr
    43  }
    44  
    45  // Parse takes a promQL string and converts parses it into a DAG.
    46  func Parse(
    47  	q string,
    48  	stepSize time.Duration,
    49  	tagOpts models.TagOptions,
    50  	parseOptions ParseOptions,
    51  ) (parser.Parser, error) {
    52  	fn := parseOptions.ParseFn()
    53  	expr, err := fn(q)
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  
    58  	return &promParser{
    59  		expr:              expr,
    60  		stepSize:          stepSize,
    61  		tagOpts:           tagOpts,
    62  		parseFunctionExpr: parseOptions.FunctionParseExpr(),
    63  	}, nil
    64  }
    65  
    66  func (p *promParser) DAG() (parser.Nodes, parser.Edges, error) {
    67  	state := &parseState{
    68  		stepSize:          p.stepSize,
    69  		tagOpts:           p.tagOpts,
    70  		parseFunctionExpr: p.parseFunctionExpr,
    71  	}
    72  
    73  	err := state.walk(p.expr)
    74  	if err != nil {
    75  		return nil, nil, err
    76  	}
    77  
    78  	return state.transforms, state.edges, nil
    79  }
    80  
    81  func (p *promParser) String() string {
    82  	return p.expr.String()
    83  }
    84  
    85  type parseState struct {
    86  	stepSize          time.Duration
    87  	edges             parser.Edges
    88  	transforms        parser.Nodes
    89  	tagOpts           models.TagOptions
    90  	parseFunctionExpr ParseFunctionExpr
    91  }
    92  
    93  func (p *parseState) lastTransformID() parser.NodeID {
    94  	if len(p.transforms) == 0 {
    95  		return parser.NodeID(rune(-1))
    96  	}
    97  
    98  	return p.transforms[len(p.transforms)-1].ID
    99  }
   100  
   101  func (p *parseState) transformLen() int {
   102  	return len(p.transforms)
   103  }
   104  
   105  func (p *parseState) addLazyUnaryTransform(unaryOp string) error {
   106  	// NB: if unary type is "+", we do not apply any offsets.
   107  	if unaryOp == binary.PlusType {
   108  		return nil
   109  	}
   110  
   111  	vt := func(val float64) float64 { return val * -1.0 }
   112  	lazyOpts := block.NewLazyOptions().SetValueTransform(vt)
   113  
   114  	op, err := lazy.NewLazyOp(lazy.UnaryType, lazyOpts)
   115  	if err != nil {
   116  		return err
   117  	}
   118  
   119  	opTransform := parser.NewTransformFromOperation(op, p.transformLen())
   120  	p.edges = append(p.edges, parser.Edge{
   121  		ParentID: p.lastTransformID(),
   122  		ChildID:  opTransform.ID,
   123  	})
   124  	p.transforms = append(p.transforms, opTransform)
   125  
   126  	return nil
   127  }
   128  
   129  func (p *parseState) addLazyOffsetTransform(offset time.Duration) error {
   130  	// NB: if offset is <= 0, we do not apply any offsets.
   131  	if offset == 0 {
   132  		return nil
   133  	} else if offset < 0 {
   134  		return fmt.Errorf("offset must be positive, received: %v", offset)
   135  	}
   136  
   137  	var (
   138  		tt = func(t xtime.UnixNano) xtime.UnixNano { return t.Add(offset) }
   139  		mt = func(meta block.Metadata) block.Metadata {
   140  			meta.Bounds.Start = meta.Bounds.Start.Add(offset)
   141  			return meta
   142  		}
   143  	)
   144  
   145  	lazyOpts := block.NewLazyOptions().
   146  		SetTimeTransform(tt).
   147  		SetMetaTransform(mt)
   148  
   149  	op, err := lazy.NewLazyOp(lazy.OffsetType, lazyOpts)
   150  	if err != nil {
   151  		return err
   152  	}
   153  
   154  	opTransform := parser.NewTransformFromOperation(op, p.transformLen())
   155  	p.edges = append(p.edges, parser.Edge{
   156  		ParentID: p.lastTransformID(),
   157  		ChildID:  opTransform.ID,
   158  	})
   159  	p.transforms = append(p.transforms, opTransform)
   160  
   161  	return nil
   162  }
   163  
   164  func adjustOffset(offset time.Duration, step time.Duration) time.Duration {
   165  	// handles case where offset is 0 too.
   166  	align := offset % step
   167  	if align == 0 {
   168  		return offset
   169  	}
   170  
   171  	// NB: Prometheus rounds offsets up to step size, e.g. a 61 second offset with
   172  	// a 1 minute stepsize gets rounded to a 2 minute offset.
   173  	return offset + step - align
   174  }
   175  
   176  func (p *parseState) walk(node pql.Node) error {
   177  	if node == nil {
   178  		return nil
   179  	}
   180  
   181  	switch n := node.(type) {
   182  	case *pql.AggregateExpr:
   183  		err := p.walk(n.Expr)
   184  		if err != nil {
   185  			return err
   186  		}
   187  
   188  		op, err := NewAggregationOperator(n)
   189  		if err != nil {
   190  			return err
   191  		}
   192  
   193  		opTransform := parser.NewTransformFromOperation(op, p.transformLen())
   194  		p.edges = append(p.edges, parser.Edge{
   195  			ParentID: p.lastTransformID(),
   196  			ChildID:  opTransform.ID,
   197  		})
   198  		p.transforms = append(p.transforms, opTransform)
   199  		// TODO: handle labels, params
   200  		return nil
   201  
   202  	case *pql.MatrixSelector:
   203  		// Align offset to stepSize.
   204  		vectorSelector := n.VectorSelector.(*pql.VectorSelector)
   205  		vectorSelector.Offset = adjustOffset(vectorSelector.OriginalOffset, p.stepSize)
   206  		operation, err := NewSelectorFromMatrix(n, p.tagOpts)
   207  		if err != nil {
   208  			return err
   209  		}
   210  
   211  		p.transforms = append(
   212  			p.transforms,
   213  			parser.NewTransformFromOperation(operation, p.transformLen()),
   214  		)
   215  		return p.addLazyOffsetTransform(vectorSelector.OriginalOffset)
   216  
   217  	case *pql.VectorSelector:
   218  		// Align offset to stepSize.
   219  		n.Offset = adjustOffset(n.OriginalOffset, p.stepSize)
   220  		operation, err := NewSelectorFromVector(n, p.tagOpts)
   221  		if err != nil {
   222  			return err
   223  		}
   224  
   225  		p.transforms = append(
   226  			p.transforms,
   227  			parser.NewTransformFromOperation(operation, p.transformLen()),
   228  		)
   229  
   230  		return p.addLazyOffsetTransform(n.OriginalOffset)
   231  
   232  	case *pql.Call:
   233  		if n.Func.Name == scalar.VectorType {
   234  			if len(n.Args) != 1 {
   235  				return fmt.Errorf(
   236  					"vector() operation must be called with 1 argument, got %d",
   237  					len(n.Args),
   238  				)
   239  			}
   240  
   241  			val, err := resolveScalarArgument(n.Args[0])
   242  			if err != nil {
   243  				return err
   244  			}
   245  
   246  			op, err := scalar.NewScalarOp(val, p.tagOpts)
   247  			if err != nil {
   248  				return err
   249  			}
   250  
   251  			opTransform := parser.NewTransformFromOperation(op, p.transformLen())
   252  			p.transforms = append(p.transforms, opTransform)
   253  			return nil
   254  		}
   255  
   256  		for i, expr := range n.Args {
   257  			n.Args[i] = unwrapParenExpr(expr)
   258  		}
   259  
   260  		var (
   261  			// argTypes describes Prom's expected argument types for this call.
   262  			argTypes = n.Func.ArgTypes
   263  			// expressions describes the actual arguments for this call.
   264  			expressions       = n.Args
   265  			argCount          = len(argTypes)
   266  			exprCount         = len(expressions)
   267  			numExpectedValues = argCount
   268  			variadic          = n.Func.Variadic
   269  			hasValue          = false
   270  		)
   271  
   272  		if variadic == 0 {
   273  			if argCount != exprCount {
   274  				return fmt.Errorf("incorrect number of expressions(%d) for %q, "+
   275  					"received %d", exprCount, n.Func.Name, argCount)
   276  			}
   277  		} else {
   278  			hasValue = exprCount > 0
   279  			if argCount-1 > exprCount {
   280  				return fmt.Errorf("incorrect number of expressions(%d) for variadic "+
   281  					"function %q, received %d", exprCount, n.Func.Name, argCount)
   282  			}
   283  
   284  			if argCount != exprCount {
   285  				numExpectedValues--
   286  			}
   287  		}
   288  
   289  		argValues := make([]interface{}, 0, exprCount)
   290  		stringValues := make([]string, 0, exprCount)
   291  		for i := 0; i < numExpectedValues; i++ {
   292  			argType := argTypes[i]
   293  			expr := expressions[i]
   294  			if argType == pql.ValueTypeScalar {
   295  				val, err := resolveScalarArgument(expr)
   296  				if err != nil {
   297  					return err
   298  				}
   299  
   300  				argValues = append(argValues, val)
   301  			} else if argType == pql.ValueTypeString {
   302  				stringValues = append(stringValues, expr.(*pql.StringLiteral).Val)
   303  			} else {
   304  				if e, ok := expr.(*pql.MatrixSelector); ok {
   305  					argValues = append(argValues, e.Range)
   306  				}
   307  
   308  				if err := p.walk(expr); err != nil {
   309  					return err
   310  				}
   311  			}
   312  		}
   313  
   314  		// NB: Variadic function with additional args that are appended to the end
   315  		// of the arg list.
   316  		if variadic != 0 && exprCount > numExpectedValues {
   317  			for _, expr := range expressions[numExpectedValues:] {
   318  				if argTypes[argCount-1] == pql.ValueTypeString {
   319  					stringValues = append(stringValues, expr.(*pql.StringLiteral).Val)
   320  				} else {
   321  					s, err := resolveScalarArgument(expr)
   322  					if err != nil {
   323  						return err
   324  					}
   325  
   326  					argValues = append(argValues, s)
   327  				}
   328  			}
   329  		}
   330  
   331  		op, ok, err := p.parseFunctionExpr(n.Func.Name, argValues,
   332  			stringValues, hasValue, n.Args.String(), p.tagOpts)
   333  		if err != nil {
   334  			return err
   335  		}
   336  
   337  		if !ok {
   338  			return nil
   339  		}
   340  
   341  		opTransform := parser.NewTransformFromOperation(op, p.transformLen())
   342  		if op.OpType() != scalar.TimeType {
   343  			p.edges = append(p.edges, parser.Edge{
   344  				ParentID: p.lastTransformID(),
   345  				ChildID:  opTransform.ID,
   346  			})
   347  		}
   348  
   349  		p.transforms = append(p.transforms, opTransform)
   350  		return nil
   351  
   352  	case *pql.BinaryExpr:
   353  		err := p.walk(n.LHS)
   354  		if err != nil {
   355  			return err
   356  		}
   357  
   358  		lhsID := p.lastTransformID()
   359  		err = p.walk(n.RHS)
   360  		if err != nil {
   361  			return err
   362  		}
   363  
   364  		rhsID := p.lastTransformID()
   365  		op, err := NewBinaryOperator(n, lhsID, rhsID)
   366  		if err != nil {
   367  			return err
   368  		}
   369  
   370  		opTransform := parser.NewTransformFromOperation(op, p.transformLen())
   371  		p.edges = append(p.edges, parser.Edge{
   372  			ParentID: lhsID,
   373  			ChildID:  opTransform.ID,
   374  		})
   375  		p.edges = append(p.edges, parser.Edge{
   376  			ParentID: rhsID,
   377  			ChildID:  opTransform.ID,
   378  		})
   379  		p.transforms = append(p.transforms, opTransform)
   380  		return nil
   381  
   382  	case *pql.NumberLiteral:
   383  		op, err := newScalarOperator(n, p.tagOpts)
   384  		if err != nil {
   385  			return err
   386  		}
   387  
   388  		opTransform := parser.NewTransformFromOperation(op, p.transformLen())
   389  		p.transforms = append(p.transforms, opTransform)
   390  		return nil
   391  
   392  	case *pql.ParenExpr:
   393  		// Evaluate inside of paren expressions
   394  		return p.walk(n.Expr)
   395  
   396  	case *pql.UnaryExpr:
   397  		err := p.walk(n.Expr)
   398  		if err != nil {
   399  			return err
   400  		}
   401  
   402  		unaryOp, err := getUnaryOpType(n.Op)
   403  		if err != nil {
   404  			return err
   405  		}
   406  
   407  		return p.addLazyUnaryTransform(unaryOp)
   408  
   409  	default:
   410  		return fmt.Errorf("promql.Walk: unhandled node type %T, %v", node, node)
   411  	}
   412  }