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, ¬FuncErr) 156 isNotFoundErr = goerrors.As(err, ¬FoundErr) 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, ¬FuncErr) 372 isNotFoundErr = goerrors.As(err, ¬FoundErr) 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 }