github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/query/graphite/native/compiler.go (about)

     1  // Copyright (c) 2019 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 native
    22  
    23  import (
    24  	goerrors "errors"
    25  	"fmt"
    26  	"math"
    27  	"reflect"
    28  	"strconv"
    29  
    30  	"github.com/m3db/m3/src/query/graphite/lexer"
    31  	"github.com/m3db/m3/src/x/errors"
    32  )
    33  
    34  // CompileOptions allows for specifying compile options.
    35  type CompileOptions struct {
    36  	EscapeAllNotOnlyQuotes bool
    37  }
    38  
    39  // Compile converts an input stream into the corresponding Expression.
    40  func Compile(input string, opts CompileOptions) (Expression, error) {
    41  	compiler, closer := newCompiler(input, opts)
    42  	defer closer()
    43  	return compiler.compileExpression()
    44  }
    45  
    46  // getFirstPathExpression extracts the first matching path expression
    47  // in the input.
    48  func getFirstPathExpression(input string) (string, error) {
    49  	compiler, closer := newCompiler(input, CompileOptions{})
    50  	defer closer()
    51  
    52  	// NB(r): Ignore any compilation errors, getFirstPathExpression is meant
    53  	// to be able to compile partial expressions such as "foo.bar, 0.1)" which
    54  	// might be matched as a result of a regular expression match with aliasSub.
    55  	_, _ = compiler.compileExpression()
    56  	if len(compiler.fetches) == 0 {
    57  		return "", compiler.errorf("no fetch expressions")
    58  	}
    59  
    60  	return compiler.fetches[0].pathArg.path, nil
    61  }
    62  
    63  type closer func()
    64  
    65  // A compiler converts an input string into an executable Expression
    66  type compiler struct {
    67  	input   string
    68  	tokens  *tokenLookforward
    69  	fetches []*fetchExpression
    70  }
    71  
    72  func newCompiler(input string, opts CompileOptions) (*compiler, closer) {
    73  	booleanLiterals := map[string]lexer.TokenType{
    74  		"true":  lexer.True,
    75  		"false": lexer.False,
    76  	}
    77  
    78  	lex, tokens := lexer.NewLexer(input, booleanLiterals, lexer.Options{
    79  		EscapeAllNotOnlyQuotes: opts.EscapeAllNotOnlyQuotes,
    80  	})
    81  
    82  	go lex.Run()
    83  
    84  	cleanup := closer(func() {
    85  		// Exhaust all tokens until closed or else lexer won't close.
    86  		for range tokens {
    87  		}
    88  	})
    89  
    90  	return &compiler{
    91  		input:   input,
    92  		tokens:  newTokenLookforward(tokens),
    93  		fetches: make([]*fetchExpression, 0, 4),
    94  	}, cleanup
    95  }
    96  
    97  type tokenLookforward struct {
    98  	lookforward *lexer.Token
    99  	tokens      chan *lexer.Token
   100  }
   101  
   102  func newTokenLookforward(tokens chan *lexer.Token) *tokenLookforward {
   103  	return &tokenLookforward{
   104  		tokens: tokens,
   105  	}
   106  }
   107  
   108  // get advances the lexer tokens.
   109  func (l *tokenLookforward) get() *lexer.Token {
   110  	if token := l.lookforward; token != nil {
   111  		l.lookforward = nil
   112  		return token
   113  	}
   114  
   115  	if token, ok := <-l.tokens; ok {
   116  		return token
   117  	}
   118  
   119  	return nil
   120  }
   121  
   122  func (l *tokenLookforward) peek() (*lexer.Token, bool) {
   123  	if l.lookforward != nil {
   124  		return l.lookforward, true
   125  	}
   126  
   127  	token, ok := <-l.tokens
   128  	if !ok {
   129  		return nil, false
   130  	}
   131  
   132  	l.lookforward = token
   133  	return token, true
   134  }
   135  
   136  // compileExpression compiles a top level expression
   137  func (c *compiler) compileExpression() (Expression, error) {
   138  	token := c.tokens.get()
   139  	if token == nil {
   140  		return noopExpression{}, nil
   141  	}
   142  
   143  	var expr Expression
   144  	switch token.TokenType() {
   145  	case lexer.Pattern:
   146  		expr = c.compileFetchExpression(token.Value())
   147  
   148  	case lexer.Identifier:
   149  		fc, err := c.compileFunctionCall(token.Value())
   150  		fetchCandidate := false
   151  		if err != nil {
   152  			var (
   153  				notFuncErr    *errNotFuncCall
   154  				notFoundErr   *errFuncNotFound
   155  				isNotFuncErr  = goerrors.As(err, &notFuncErr)
   156  				isNotFoundErr = goerrors.As(err, &notFoundErr)
   157  			)
   158  			if (isNotFuncErr || isNotFoundErr) && c.canCompileAsFetch(token.Value()) {
   159  				fetchCandidate = true
   160  				expr = c.compileFetchExpression(token.Value())
   161  			} else {
   162  				return nil, err
   163  			}
   164  		}
   165  
   166  		if !fetchCandidate {
   167  			expr, err = newFuncExpression(fc)
   168  			if err != nil {
   169  				return nil, err
   170  			}
   171  		}
   172  
   173  	default:
   174  		return nil, c.errorf("unexpected value %s", token.Value())
   175  	}
   176  
   177  	if token := c.tokens.get(); token != nil {
   178  		return nil, c.errorf("extra data %s", token.Value())
   179  	}
   180  
   181  	return expr, nil
   182  }
   183  
   184  func (c *compiler) compileFetchExpression(token string) *fetchExpression {
   185  	expr := newFetchExpression(token)
   186  	c.fetches = append(c.fetches, expr)
   187  	return expr
   188  }
   189  
   190  // canCompileAsFetch attempts to see if the given term is a non-delimited
   191  // carbon metric; no dots, without any trailing parentheses.
   192  func (c *compiler) canCompileAsFetch(fname string) bool {
   193  	if nextToken, hasNext := c.tokens.peek(); hasNext {
   194  		return nextToken.TokenType() != lexer.LParenthesis
   195  	}
   196  
   197  	return true
   198  }
   199  
   200  type errFuncNotFound struct{ err error }
   201  
   202  func (e *errFuncNotFound) Error() string { return e.err.Error() }
   203  
   204  type errNotFuncCall struct{ err error }
   205  
   206  func (e *errNotFuncCall) Error() string { return e.err.Error() }
   207  
   208  // compileFunctionCall compiles a function call
   209  func (c *compiler) compileFunctionCall(fname string) (*functionCall, error) {
   210  	fn := findFunction(fname)
   211  	if fn == nil {
   212  		return nil, &errFuncNotFound{c.errorf("could not find function named %s", fname)}
   213  	}
   214  
   215  	if _, err := c.expectToken(lexer.LParenthesis); err != nil {
   216  		// This could be just a pattern or fetch expression, so return
   217  		// with context that there was no opening parenthesis.
   218  		return nil, &errNotFuncCall{err}
   219  	}
   220  
   221  	argTypes := fn.in
   222  	argTypesRequired := len(fn.in)
   223  	if fn.variadic {
   224  		// Variadic can avoid specifying the last arg.
   225  		argTypesRequired--
   226  	}
   227  	var args []funcArg
   228  
   229  	// build up arguments for function call
   230  	for {
   231  		// if not variadic, function should be complete after reading len(argTypes) arguments
   232  		if !fn.variadic && len(args) == len(argTypes) {
   233  			_, err := c.expectToken(lexer.RParenthesis)
   234  			if err != nil {
   235  				return nil, err
   236  			}
   237  			break
   238  		}
   239  
   240  		argType := argTypes[int(math.Min(float64(len(args)), float64(len(argTypes)-1)))]
   241  		nextArg, foundRParen, err := c.compileArg(fn.name, len(args), argType)
   242  		if err != nil {
   243  			return nil, err
   244  		}
   245  		if foundRParen {
   246  			break
   247  		}
   248  
   249  		args = append(args, nextArg)
   250  	}
   251  
   252  	// fill in defaults arguments for those not supplied by user explicitly
   253  	for len(args) < len(argTypes) {
   254  		defaultValue, ok := fn.defaults[uint8(len(args)+1)]
   255  		if !ok {
   256  			break
   257  		}
   258  
   259  		args = append(args, newConstArg(defaultValue))
   260  	}
   261  
   262  	// all required argument types should be filled with values now
   263  	if len(args) < argTypesRequired {
   264  		variadicComment := ""
   265  		if fn.variadic {
   266  			variadicComment = "at least "
   267  		}
   268  		return nil, c.errorf("invalid number of arguments for %s; expected %s%d, received %d",
   269  			fn.name, variadicComment, len(argTypes), len(args))
   270  	}
   271  
   272  	return &functionCall{f: fn, in: args}, nil
   273  }
   274  
   275  // compileArg parses and compiles a single argument
   276  func (c *compiler) compileArg(
   277  	fname string,
   278  	index int,
   279  	reflectType reflect.Type,
   280  ) (arg funcArg, foundRParen bool, err error) {
   281  	token := c.tokens.get()
   282  	if token == nil {
   283  		return nil, false, c.errorf("unexpected eof while parsing %s", fname)
   284  	}
   285  
   286  	if token.TokenType() == lexer.RParenthesis {
   287  		return nil, true, nil
   288  	}
   289  
   290  	if index > 0 {
   291  		if token.TokenType() != lexer.Comma {
   292  			return nil, false, c.errorf("error parsing %s expected ',' received '%s'",
   293  				fname, token.Value())
   294  		}
   295  
   296  		if token = c.tokens.get(); token == nil {
   297  			return nil, false, c.errorf("unexpected eof while parsing %s", fname)
   298  		}
   299  	}
   300  
   301  	arg, err = c.convertTokenToArg(token, reflectType)
   302  	if err != nil {
   303  		return nil, false, c.errorf("invalid function call %s, arg %d: %v", fname, index, err)
   304  	}
   305  
   306  	if !arg.CompatibleWith(reflectType) {
   307  		return nil, false, c.errorf("invalid function call %s, arg %d: expected a %s, received a %s '%s'",
   308  			fname, index, reflectTypeName(reflectType), reflectTypeName(arg.Type()), arg)
   309  	}
   310  
   311  	return arg, false, nil
   312  }
   313  
   314  // reflectTypeName will dereference any pointer types to their base name
   315  // so that function call or fetch expression can be referenced by their name.
   316  func reflectTypeName(reflectType reflect.Type) string {
   317  	for reflectType.Kind() == reflect.Ptr {
   318  		reflectType = reflectType.Elem()
   319  	}
   320  	return reflectType.Name()
   321  }
   322  
   323  // convertTokenToArg converts the given token into the corresponding argument
   324  func (c *compiler) convertTokenToArg(token *lexer.Token, reflectType reflect.Type) (funcArg, error) {
   325  	switch token.TokenType() {
   326  	case lexer.Number:
   327  		n, err := strconv.ParseFloat(token.Value(), 64)
   328  		if err != nil {
   329  			return nil, err
   330  		}
   331  
   332  		if reflectType.Kind() == reflect.Int {
   333  			return newIntConst(int(n)), nil
   334  		}
   335  
   336  		return newFloat64Const(n), nil
   337  	case lexer.String:
   338  		return newStringConst(token.Value()), nil
   339  	case lexer.Pattern:
   340  		return c.compileFetchExpression(token.Value()), nil
   341  	case lexer.True, lexer.False:
   342  		b, err := strconv.ParseBool(token.Value())
   343  		if err != nil {
   344  			return nil, err
   345  		}
   346  		return newBoolConst(b), nil
   347  	case lexer.Identifier:
   348  		currentToken := token.Value()
   349  
   350  		// handle named arguments
   351  		nextToken, hasNextToken := c.tokens.peek()
   352  		if !hasNextToken {
   353  			return nil, c.errorf("unexpected eof, %s should be followed by = or (", currentToken)
   354  		}
   355  
   356  		if nextToken.TokenType() == lexer.Equal {
   357  			// TODO: check if currentToken matches the expected parameter name
   358  			_ = c.tokens.get() // Consume the peeked equal token.
   359  			tokenAfterNext := c.tokens.get()
   360  			if tokenAfterNext == nil {
   361  				return nil, c.errorf("unexpected eof, named argument %s should be followed by its value", currentToken)
   362  			}
   363  			return c.convertTokenToArg(tokenAfterNext, reflectType)
   364  		}
   365  
   366  		fc, err := c.compileFunctionCall(currentToken)
   367  		if err != nil {
   368  			var (
   369  				notFuncErr    *errNotFuncCall
   370  				notFoundErr   *errFuncNotFound
   371  				isNotFuncErr  = goerrors.As(err, &notFuncErr)
   372  				isNotFoundErr = goerrors.As(err, &notFoundErr)
   373  			)
   374  			if (isNotFuncErr || isNotFoundErr) && c.canCompileAsFetch(currentToken) {
   375  				return c.compileFetchExpression(currentToken), nil
   376  			}
   377  			return nil, err
   378  		}
   379  
   380  		return fc, nil
   381  	default:
   382  		return nil, c.errorf("%s not valid", token.Value())
   383  	}
   384  }
   385  
   386  // expectToken reads the next token and confirms it is the expected type before returning it
   387  func (c *compiler) expectToken(expectedType lexer.TokenType) (*lexer.Token, error) {
   388  	token, ok := c.tokens.peek()
   389  	if !ok || token == nil {
   390  		return nil, c.errorf("expected %v but encountered eof", expectedType)
   391  	}
   392  
   393  	if token.TokenType() != expectedType {
   394  		return nil, c.errorf("expected %v but encountered %s", expectedType, token.Value())
   395  	}
   396  
   397  	// Consume the token as it matches.
   398  	_ = c.tokens.get()
   399  	return token, nil
   400  }
   401  
   402  // errorf returns a formatted error vfrom the compiler
   403  func (c *compiler) errorf(msg string, args ...interface{}) error {
   404  	return errors.NewInvalidParamsError(fmt.Errorf("invalid expression '%s': %s", c.input, fmt.Sprintf(msg, args...)))
   405  }
   406  
   407  // ExtractFetchExpressions extracts timeseries fetch expressions from the given query
   408  func ExtractFetchExpressions(s string) ([]string, error) {
   409  	expr, err := Compile(s, CompileOptions{})
   410  	if err != nil {
   411  		return nil, err
   412  	}
   413  
   414  	var targets []string
   415  	extractFetchExpressions(expr, &targets)
   416  	return targets, nil
   417  }
   418  
   419  func extractFetchExpressions(expr Expression, targets *[]string) {
   420  	switch v := expr.(type) {
   421  	case *funcExpression:
   422  		extractFetchExpressionsFromFuncCall(v.call, targets)
   423  	case *fetchExpression:
   424  		*targets = append(*targets, v.pathArg.path)
   425  	}
   426  }
   427  
   428  func extractFetchExpressionsFromFuncCall(call *functionCall, targets *[]string) {
   429  	for _, arg := range call.in {
   430  		switch varg := arg.(type) {
   431  		case *functionCall:
   432  			extractFetchExpressionsFromFuncCall(varg, targets)
   433  		case Expression:
   434  			extractFetchExpressions(varg, targets)
   435  		}
   436  	}
   437  }