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 }