github.com/m3db/m3@v1.5.0/src/query/parser/promql/parse_test.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 "testing" 26 "time" 27 28 "github.com/m3db/m3/src/query/functions" 29 "github.com/m3db/m3/src/query/functions/aggregation" 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/linear" 33 "github.com/m3db/m3/src/query/functions/scalar" 34 "github.com/m3db/m3/src/query/functions/tag" 35 "github.com/m3db/m3/src/query/functions/temporal" 36 "github.com/m3db/m3/src/query/models" 37 "github.com/m3db/m3/src/query/parser" 38 39 pql "github.com/prometheus/prometheus/promql/parser" 40 "github.com/stretchr/testify/assert" 41 "github.com/stretchr/testify/require" 42 ) 43 44 func TestDAGWithCountOp(t *testing.T) { 45 q := "count(http_requests_total{method=\"GET\"}) by (service)" 46 p, err := Parse(q, time.Second, models.NewTagOptions(), NewParseOptions()) 47 require.NoError(t, err) 48 transforms, edges, err := p.DAG() 49 require.NoError(t, err) 50 assert.Len(t, transforms, 2) 51 assert.Equal(t, transforms[0].Op.OpType(), functions.FetchType) 52 assert.Equal(t, transforms[0].ID, parser.NodeID("0")) 53 assert.Equal(t, transforms[1].ID, parser.NodeID("1")) 54 assert.Equal(t, transforms[1].Op.OpType(), aggregation.CountType) 55 assert.Len(t, edges, 1) 56 assert.Equal(t, edges[0].ParentID, parser.NodeID("0"), 57 "fetch should be the parent") 58 assert.Equal(t, edges[0].ChildID, parser.NodeID("1"), 59 "aggregation should be the child") 60 } 61 62 func TestDAGWithOffset(t *testing.T) { 63 q := "up offset 2m" 64 p, err := Parse(q, time.Second, models.NewTagOptions(), NewParseOptions()) 65 require.NoError(t, err) 66 transforms, edges, err := p.DAG() 67 require.NoError(t, err) 68 assert.Len(t, transforms, 2) 69 assert.Equal(t, transforms[0].Op.OpType(), functions.FetchType) 70 assert.Equal(t, transforms[0].ID, parser.NodeID("0")) 71 assert.Equal(t, transforms[1].ID, parser.NodeID("1")) 72 assert.Equal(t, transforms[1].Op.OpType(), lazy.OffsetType) 73 assert.Len(t, edges, 1) 74 assert.Equal(t, edges[0].ParentID, parser.NodeID("0"), 75 "fetch should be the parent") 76 assert.Equal(t, edges[0].ChildID, parser.NodeID("1"), 77 "offset should be the child") 78 } 79 80 func TestInvalidOffset(t *testing.T) { 81 q := "up offset -2m" 82 _, err := Parse(q, time.Second, models.NewTagOptions(), NewParseOptions()) 83 require.NoError(t, err) 84 } 85 86 func TestNegativeUnary(t *testing.T) { 87 q := "-up" 88 p, err := Parse(q, time.Second, models.NewTagOptions(), NewParseOptions()) 89 require.NoError(t, err) 90 transforms, edges, err := p.DAG() 91 require.NoError(t, err) 92 assert.Len(t, transforms, 2) 93 assert.Equal(t, transforms[0].Op.OpType(), functions.FetchType) 94 assert.Equal(t, transforms[0].ID, parser.NodeID("0")) 95 assert.Equal(t, transforms[1].Op.OpType(), lazy.UnaryType) 96 assert.Equal(t, transforms[1].ID, parser.NodeID("1")) 97 assert.Len(t, edges, 1) 98 assert.Equal(t, edges[0].ParentID, parser.NodeID("0")) 99 assert.Equal(t, edges[0].ChildID, parser.NodeID("1")) 100 } 101 102 func TestPositiveUnary(t *testing.T) { 103 q := "+up" 104 p, err := Parse(q, time.Second, models.NewTagOptions(), NewParseOptions()) 105 require.NoError(t, err) 106 transforms, edges, err := p.DAG() 107 require.NoError(t, err) 108 assert.Len(t, transforms, 1) // "+" defaults to just a fetch operation 109 assert.Equal(t, transforms[0].Op.OpType(), functions.FetchType) 110 assert.Equal(t, transforms[0].ID, parser.NodeID("0")) 111 assert.Len(t, edges, 0) 112 } 113 114 func TestInvalidUnary(t *testing.T) { 115 q := "*up" 116 _, err := Parse(q, time.Second, models.NewTagOptions(), NewParseOptions()) 117 require.Error(t, err) 118 } 119 120 func TestGetUnaryOpType(t *testing.T) { 121 unaryOpType, err := getUnaryOpType(pql.ADD) 122 require.NoError(t, err) 123 assert.Equal(t, binary.PlusType, unaryOpType) 124 125 _, err = getUnaryOpType(pql.EQL) 126 require.Error(t, err) 127 } 128 129 func TestDAGWithEmptyExpression(t *testing.T) { 130 q := "" 131 _, err := Parse(q, time.Second, models.NewTagOptions(), NewParseOptions()) 132 require.Error(t, err) 133 } 134 135 func TestDAGWithFakeOp(t *testing.T) { 136 q := "fake(http_requests_total{method=\"GET\"})" 137 _, err := Parse(q, time.Second, models.NewTagOptions(), NewParseOptions()) 138 require.Error(t, err) 139 } 140 141 var aggregateParseTests = []struct { 142 q string 143 expectedType string 144 }{ 145 {"sum(up)", aggregation.SumType}, 146 {"min(up)", aggregation.MinType}, 147 {"max(up)", aggregation.MaxType}, 148 {"avg(up)", aggregation.AverageType}, 149 {"stddev(up)", aggregation.StandardDeviationType}, 150 {"stdvar(up)", aggregation.StandardVarianceType}, 151 {"count(up)", aggregation.CountType}, 152 153 {"topk(3, up)", aggregation.TopKType}, 154 {"bottomk(3, up)", aggregation.BottomKType}, 155 {"quantile(3, up)", aggregation.QuantileType}, 156 {"count_values(\"some_name\", up)", aggregation.CountValuesType}, 157 158 {"absent(up)", aggregation.AbsentType}, 159 } 160 161 func TestAggregateParses(t *testing.T) { 162 for _, tt := range aggregateParseTests { 163 t.Run(tt.q, func(t *testing.T) { 164 q := tt.q 165 p, err := Parse(q, time.Second, models.NewTagOptions(), NewParseOptions()) 166 require.NoError(t, err) 167 transforms, edges, err := p.DAG() 168 require.NoError(t, err) 169 assert.Len(t, transforms, 2) 170 assert.Equal(t, transforms[0].Op.OpType(), functions.FetchType) 171 assert.Equal(t, transforms[0].ID, parser.NodeID("0")) 172 assert.Equal(t, transforms[1].Op.OpType(), tt.expectedType) 173 assert.Equal(t, transforms[1].ID, parser.NodeID("1")) 174 assert.Len(t, edges, 1) 175 assert.Equal(t, edges[0].ParentID, parser.NodeID("0")) 176 assert.Equal(t, edges[0].ChildID, parser.NodeID("1")) 177 }) 178 } 179 } 180 181 var aggregationWithTagListTests = []string{ 182 // different number of tags 183 "sum(up) by (t1,)", 184 "sum(up) by (t1,t2)", 185 "sum(up) without (t1)", 186 "sum(up) without (t1, t2, t3)", 187 188 // trailing comma in tag list 189 "sum(up) by (t1,)", 190 "sum(up) without (t1, t2,)", 191 192 // alternative form 193 "sum by (t) (up)", 194 "sum by (t,) (up)", 195 "sum without (t) (up)", 196 "sum without (t,) (up)", 197 } 198 199 func TestAggregationWithTagListDoesNotError(t *testing.T) { 200 for _, q := range aggregationWithTagListTests { 201 t.Run(q, func(t *testing.T) { 202 p, err := Parse(q, time.Second, models.NewTagOptions(), NewParseOptions()) 203 require.NoError(t, err) 204 _, _, err = p.DAG() 205 require.NoError(t, err) 206 }) 207 } 208 } 209 210 var linearParseTests = []struct { 211 q string 212 expectedType string 213 }{ 214 {"abs(up)", linear.AbsType}, 215 {"ceil(up)", linear.CeilType}, 216 {"clamp_min(up, 1)", linear.ClampMinType}, 217 {"clamp_max(up, 1)", linear.ClampMaxType}, 218 {"exp(up)", linear.ExpType}, 219 {"floor(up)", linear.FloorType}, 220 {"ln(up)", linear.LnType}, 221 {"log2(up)", linear.Log2Type}, 222 {"log10(up)", linear.Log10Type}, 223 {"sqrt(up)", linear.SqrtType}, 224 {"round(up)", linear.RoundType}, 225 {"round(up, 10)", linear.RoundType}, 226 227 {"day_of_month(up)", linear.DayOfMonthType}, 228 {"day_of_week(up)", linear.DayOfWeekType}, 229 {"day_of_month(up)", linear.DayOfMonthType}, 230 {"days_in_month(up)", linear.DaysInMonthType}, 231 232 {"hour(up)", linear.HourType}, 233 {"minute(up)", linear.MinuteType}, 234 {"month(up)", linear.MonthType}, 235 {"year(up)", linear.YearType}, 236 237 {"histogram_quantile(1,up)", linear.HistogramQuantileType}, 238 } 239 240 func TestLinearParses(t *testing.T) { 241 for _, tt := range linearParseTests { 242 t.Run(tt.q, func(t *testing.T) { 243 q := tt.q 244 p, err := Parse(q, time.Second, models.NewTagOptions(), NewParseOptions()) 245 require.NoError(t, err) 246 transforms, edges, err := p.DAG() 247 require.NoError(t, err) 248 require.Len(t, transforms, 2) 249 assert.Equal(t, transforms[0].Op.OpType(), functions.FetchType) 250 assert.Equal(t, transforms[0].ID, parser.NodeID("0")) 251 assert.Equal(t, transforms[1].Op.OpType(), tt.expectedType) 252 assert.Equal(t, transforms[1].ID, parser.NodeID("1")) 253 assert.Len(t, edges, 1) 254 assert.Equal(t, edges[0].ParentID, parser.NodeID("0")) 255 assert.Equal(t, edges[0].ChildID, parser.NodeID("1")) 256 }) 257 } 258 } 259 260 var variadicTests = []struct { 261 q string 262 expectedType string 263 }{ 264 {"day_of_month()", linear.DayOfMonthType}, 265 {"day_of_week()", linear.DayOfWeekType}, 266 {"day_of_month()", linear.DayOfMonthType}, 267 {"days_in_month()", linear.DaysInMonthType}, 268 269 {"hour()", linear.HourType}, 270 {"minute()", linear.MinuteType}, 271 {"month()", linear.MonthType}, 272 {"year()", linear.YearType}, 273 } 274 275 func TestVariadicParses(t *testing.T) { 276 for _, tt := range variadicTests { 277 t.Run(tt.q, func(t *testing.T) { 278 q := tt.q 279 p, err := Parse(q, time.Second, models.NewTagOptions(), NewParseOptions()) 280 require.NoError(t, err) 281 transforms, _, err := p.DAG() 282 require.NoError(t, err) 283 require.Len(t, transforms, 1) 284 assert.Equal(t, transforms[0].Op.OpType(), tt.expectedType) 285 assert.Equal(t, transforms[0].ID, parser.NodeID("0")) 286 }) 287 } 288 } 289 290 var sortTests = []struct { 291 q string 292 expectedType string 293 }{ 294 {"sort(up)", linear.SortType}, 295 {"sort_desc(up)", linear.SortDescType}, 296 } 297 298 func TestSort(t *testing.T) { 299 for _, tt := range sortTests { 300 t.Run(tt.q, func(t *testing.T) { 301 q := tt.q 302 p, err := Parse(q, time.Second, models.NewTagOptions(), NewParseOptions()) 303 require.NoError(t, err) 304 transforms, edges, err := p.DAG() 305 require.NoError(t, err) 306 assert.Len(t, transforms, 2) 307 assert.Equal(t, transforms[0].Op.OpType(), functions.FetchType) 308 assert.Equal(t, transforms[0].ID, parser.NodeID("0")) 309 assert.Equal(t, transforms[1].Op.OpType(), tt.expectedType) 310 assert.Equal(t, transforms[1].ID, parser.NodeID("1")) 311 assert.Len(t, edges, 1) 312 }) 313 } 314 } 315 316 func TestScalar(t *testing.T) { 317 p, err := Parse("scalar(up)", time.Second, 318 models.NewTagOptions(), NewParseOptions()) 319 require.NoError(t, err) 320 transforms, edges, err := p.DAG() 321 require.NoError(t, err) 322 assert.Len(t, transforms, 1) 323 assert.Equal(t, transforms[0].Op.OpType(), functions.FetchType) 324 assert.Equal(t, transforms[0].ID, parser.NodeID("0")) 325 assert.Len(t, edges, 0) 326 } 327 328 func TestVector(t *testing.T) { 329 vectorExprs := []string{ 330 "vector(12)", 331 "vector(scalar(up))", 332 "vector(12 - scalar(vector(100)-2))", 333 } 334 335 for _, expr := range vectorExprs { 336 t.Run(expr, func(t *testing.T) { 337 p, err := Parse(expr, time.Second, 338 models.NewTagOptions(), NewParseOptions()) 339 require.NoError(t, err) 340 transforms, edges, err := p.DAG() 341 require.NoError(t, err) 342 assert.Len(t, transforms, 1) 343 assert.Equal(t, transforms[0].Op.OpType(), scalar.ScalarType) 344 assert.Equal(t, transforms[0].ID, parser.NodeID("0")) 345 assert.Len(t, edges, 0) 346 }) 347 } 348 } 349 350 func TestTimeTypeParse(t *testing.T) { 351 q := "time()" 352 p, err := Parse(q, time.Second, models.NewTagOptions(), NewParseOptions()) 353 require.NoError(t, err) 354 transforms, edges, err := p.DAG() 355 require.NoError(t, err) 356 assert.Len(t, transforms, 1) 357 assert.Equal(t, transforms[0].Op.OpType(), scalar.TimeType) 358 assert.Equal(t, transforms[0].ID, parser.NodeID("0")) 359 assert.Len(t, edges, 0) 360 } 361 362 var binaryParseTests = []struct { 363 q string 364 LHSType, RHSType string 365 expectedType string 366 }{ 367 // Arithmetic 368 {"up / up", functions.FetchType, functions.FetchType, binary.DivType}, 369 {"up ^ 10", functions.FetchType, scalar.ScalarType, binary.ExpType}, 370 {"10 - up", scalar.ScalarType, functions.FetchType, binary.MinusType}, 371 {"10 + 10", scalar.ScalarType, scalar.ScalarType, binary.PlusType}, 372 {"up % up", functions.FetchType, functions.FetchType, binary.ModType}, 373 {"up * 10", functions.FetchType, scalar.ScalarType, binary.MultiplyType}, 374 375 // Equality 376 {"up == up", functions.FetchType, functions.FetchType, binary.EqType}, 377 {"up != 10", functions.FetchType, scalar.ScalarType, binary.NotEqType}, 378 {"up > up", functions.FetchType, functions.FetchType, binary.GreaterType}, 379 {"10 < up", scalar.ScalarType, functions.FetchType, binary.LesserType}, 380 {"up >= 10", functions.FetchType, scalar.ScalarType, binary.GreaterEqType}, 381 {"up <= 10", functions.FetchType, scalar.ScalarType, binary.LesserEqType}, 382 383 // Logical 384 {"up and up", functions.FetchType, functions.FetchType, binary.AndType}, 385 {"up or up", functions.FetchType, functions.FetchType, binary.OrType}, 386 {"up unless up", functions.FetchType, functions.FetchType, binary.UnlessType}, 387 388 // Various spacing 389 {"up/ up", functions.FetchType, functions.FetchType, binary.DivType}, 390 {"up-up", functions.FetchType, functions.FetchType, binary.MinusType}, 391 {"10 -up", scalar.ScalarType, functions.FetchType, binary.MinusType}, 392 {"up*10", functions.FetchType, scalar.ScalarType, binary.MultiplyType}, 393 {"up!=10", functions.FetchType, scalar.ScalarType, binary.NotEqType}, 394 {"10 <up", scalar.ScalarType, functions.FetchType, binary.LesserType}, 395 {"up>= 10", functions.FetchType, scalar.ScalarType, binary.GreaterEqType}, 396 } 397 398 func TestBinaryParses(t *testing.T) { 399 for _, tt := range binaryParseTests { 400 t.Run(tt.q, func(t *testing.T) { 401 p, err := Parse(tt.q, time.Second, 402 models.NewTagOptions(), NewParseOptions()) 403 404 require.NoError(t, err) 405 transforms, edges, err := p.DAG() 406 require.NoError(t, err) 407 require.Len(t, transforms, 3) 408 assert.Equal(t, transforms[0].Op.OpType(), tt.LHSType) 409 assert.Equal(t, transforms[0].ID, parser.NodeID("0")) 410 assert.Equal(t, transforms[1].Op.OpType(), tt.RHSType) 411 assert.Equal(t, transforms[1].ID, parser.NodeID("1")) 412 assert.Equal(t, transforms[2].Op.OpType(), tt.expectedType) 413 assert.Equal(t, transforms[2].ID, parser.NodeID("2")) 414 assert.Len(t, edges, 2) 415 assert.Equal(t, edges[0].ParentID, parser.NodeID("0")) 416 assert.Equal(t, edges[0].ChildID, parser.NodeID("2")) 417 assert.Equal(t, edges[1].ParentID, parser.NodeID("1")) 418 assert.Equal(t, edges[1].ChildID, parser.NodeID("2")) 419 }) 420 } 421 } 422 423 func TestParenPrecedenceParses(t *testing.T) { 424 p, err := Parse("(5^(up-6))", time.Second, 425 models.NewTagOptions(), NewParseOptions()) 426 require.NoError(t, err) 427 transforms, edges, err := p.DAG() 428 require.NoError(t, err) 429 require.Len(t, transforms, 5) 430 // 5 431 assert.Equal(t, transforms[0].Op.OpType(), scalar.ScalarType) 432 assert.Equal(t, transforms[0].ID, parser.NodeID("0")) 433 // up 434 assert.Equal(t, transforms[1].Op.OpType(), functions.FetchType) 435 assert.Equal(t, transforms[1].ID, parser.NodeID("1")) 436 // 6 437 assert.Equal(t, transforms[2].Op.OpType(), scalar.ScalarType) 438 assert.Equal(t, transforms[2].ID, parser.NodeID("2")) 439 // - 440 assert.Equal(t, transforms[3].Op.OpType(), binary.MinusType) 441 assert.Equal(t, transforms[3].ID, parser.NodeID("3")) 442 // ^ 443 assert.Equal(t, transforms[4].Op.OpType(), binary.ExpType) 444 assert.Equal(t, transforms[4].ID, parser.NodeID("4")) 445 446 assert.Len(t, edges, 4) 447 // up - 448 assert.Equal(t, edges[0].ParentID, parser.NodeID("1")) 449 assert.Equal(t, edges[0].ChildID, parser.NodeID("3")) 450 // 6 - 451 assert.Equal(t, edges[1].ParentID, parser.NodeID("2")) 452 assert.Equal(t, edges[1].ChildID, parser.NodeID("3")) 453 // 5 ^ 454 assert.Equal(t, edges[2].ParentID, parser.NodeID("0")) 455 assert.Equal(t, edges[2].ChildID, parser.NodeID("4")) 456 // (up -6) ^ 457 assert.Equal(t, edges[3].ParentID, parser.NodeID("3")) 458 assert.Equal(t, edges[3].ChildID, parser.NodeID("4")) 459 } 460 461 var temporalParseTests = []struct { 462 q string 463 expectedType string 464 }{ 465 {"avg_over_time(up[5m])", temporal.AvgType}, 466 {"count_over_time(up[5m])", temporal.CountType}, 467 {"min_over_time(up[5m])", temporal.MinType}, 468 {"max_over_time(up[5m])", temporal.MaxType}, 469 {"sum_over_time(up[5m])", temporal.SumType}, 470 {"stddev_over_time(up[5m])", temporal.StdDevType}, 471 {"stdvar_over_time(up[5m])", temporal.StdVarType}, 472 {"last_over_time(up[5m])", temporal.LastType}, 473 {"quantile_over_time(0.2, up[5m])", temporal.QuantileType}, 474 {"irate(up[5m])", temporal.IRateType}, 475 {"idelta(up[5m])", temporal.IDeltaType}, 476 {"rate(up[5m])", temporal.RateType}, 477 {"delta(up[5m])", temporal.DeltaType}, 478 {"increase(up[5m])", temporal.IncreaseType}, 479 {"resets(up[5m])", temporal.ResetsType}, 480 {"changes(up[5m])", temporal.ChangesType}, 481 {"holt_winters(up[5m], 0.2, 0.3)", temporal.HoltWintersType}, 482 {"predict_linear(up[5m], 100)", temporal.PredictLinearType}, 483 {"deriv(up[5m])", temporal.DerivType}, 484 } 485 486 func TestTemporalParses(t *testing.T) { 487 for _, tt := range temporalParseTests { 488 t.Run(tt.q, func(t *testing.T) { 489 q := tt.q 490 p, err := Parse(q, time.Second, models.NewTagOptions(), NewParseOptions()) 491 require.NoError(t, err) 492 transforms, edges, err := p.DAG() 493 require.NoError(t, err) 494 assert.Len(t, transforms, 2) 495 assert.Equal(t, transforms[0].Op.OpType(), functions.FetchType) 496 assert.Equal(t, transforms[0].ID, parser.NodeID("0")) 497 assert.Equal(t, transforms[1].Op.OpType(), tt.expectedType) 498 assert.Equal(t, transforms[1].ID, parser.NodeID("1")) 499 assert.Len(t, edges, 1) 500 assert.Equal(t, edges[0].ParentID, parser.NodeID("0")) 501 assert.Equal(t, edges[0].ChildID, parser.NodeID("1")) 502 }) 503 } 504 } 505 506 var tagParseTests = []struct { 507 q string 508 expectedType string 509 }{ 510 {`label_join(up, "foo", ",", "s1","s2","s4")`, tag.TagJoinType}, 511 {`label_replace(up, "foo", "$1", "tagname","(.*):.*")`, tag.TagReplaceType}, 512 } 513 514 func TestTagParses(t *testing.T) { 515 for _, tt := range tagParseTests { 516 t.Run(tt.q, func(t *testing.T) { 517 q := tt.q 518 p, err := Parse(q, time.Second, models.NewTagOptions(), NewParseOptions()) 519 require.NoError(t, err) 520 transforms, edges, err := p.DAG() 521 require.NoError(t, err) 522 assert.Len(t, transforms, 2) 523 assert.Equal(t, transforms[0].Op.OpType(), functions.FetchType) 524 assert.Equal(t, transforms[0].ID, parser.NodeID("0")) 525 assert.Equal(t, transforms[1].Op.OpType(), tt.expectedType) 526 assert.Equal(t, transforms[1].ID, parser.NodeID("1")) 527 assert.Len(t, edges, 1) 528 assert.Equal(t, edges[0].ParentID, parser.NodeID("0")) 529 assert.Equal(t, edges[0].ChildID, parser.NodeID("1")) 530 }) 531 } 532 } 533 534 func TestFailedTemporalParse(t *testing.T) { 535 q := "unknown_over_time(http_requests_total[5m])" 536 _, err := Parse(q, time.Second, models.NewTagOptions(), NewParseOptions()) 537 require.Error(t, err) 538 } 539 540 func TestMissingTagsDoNotPanic(t *testing.T) { 541 q := `label_join(up, "foo", ",")` 542 p, err := Parse(q, time.Second, models.NewTagOptions(), NewParseOptions()) 543 require.NoError(t, err) 544 assert.NotPanics(t, func() { _, _, _ = p.DAG() }) 545 } 546 547 var functionArgumentExpressionTests = []struct { 548 name string 549 q string 550 }{ 551 { 552 "scalar argument", 553 "vector(((1)))", 554 }, 555 { 556 "string argument", 557 `label_join(up, ("foo"), ((",")), ((("bar"))))`, 558 }, 559 { 560 "vector argument", 561 "abs(((foo)))", 562 }, 563 { 564 "matrix argument", 565 "stddev_over_time(((metric[1m])))", 566 }, 567 } 568 569 func TestExpressionsInFunctionArgumentsDoNotError(t *testing.T) { 570 for _, tt := range functionArgumentExpressionTests { 571 t.Run(tt.name, func(t *testing.T) { 572 p, err := Parse(tt.q, time.Second, models.NewTagOptions(), NewParseOptions()) 573 require.NoError(t, err) 574 _, _, err = p.DAG() 575 require.NoError(t, err) 576 }) 577 } 578 } 579 580 var invalidFunctionArgumentsTests = []string{ 581 "vector(())", 582 "vector((1)", 583 "vector(metric)", 584 `label_join(up, "f" + "oo", ",", "ba" + "r")`, 585 `label_join(up, 1, ",", 2)`, 586 `label_join("up", "foo", ",", "bar")`, 587 "abs(1)", 588 "abs(())", 589 "stddev_over_time(metric[1m]+1)", 590 "stddev_over_time(metric)", 591 } 592 593 func TestParseInvalidFunctionArgumentsErrors(t *testing.T) { 594 for _, q := range invalidFunctionArgumentsTests { 595 t.Run(q, func(t *testing.T) { 596 _, err := Parse(q, time.Second, models.NewTagOptions(), NewParseOptions()) 597 require.Error(t, err) 598 }) 599 } 600 } 601 602 func TestCustomParseOptions(t *testing.T) { 603 q := "query" 604 v := "foo" 605 called := 0 606 fn := func(query string) (pql.Expr, error) { 607 assert.Equal(t, q, query) 608 called++ 609 return &pql.StringLiteral{Val: v}, nil 610 } 611 612 opts := NewParseOptions().SetParseFn(fn) 613 ex, err := Parse(q, time.Second, models.NewTagOptions(), opts) 614 require.NoError(t, err) 615 assert.Equal(t, 1, called) 616 parse, ok := ex.(*promParser) 617 require.True(t, ok) 618 assert.Equal(t, pql.ValueTypeString, parse.expr.Type()) 619 str, ok := parse.expr.(*pql.StringLiteral) 620 require.True(t, ok) 621 assert.Equal(t, v, str.Val) 622 } 623 624 type customParam struct { 625 prefix string 626 } 627 628 func (c customParam) String() string { 629 return fmt.Sprintf("%s_custom", c.prefix) 630 } 631 632 func (c customParam) OpType() string { 633 return fmt.Sprintf("%s_customOpType", c.prefix) 634 } 635 636 func TestCustomSort(t *testing.T) { 637 tests := []struct { 638 q string 639 ex string 640 }{ 641 {"sort(up)", "sort_customOpType"}, 642 {"clamp_max(up, 0.3)", "clamp_max_customOpType"}, 643 } 644 645 fn := func(s string, _ []interface{}, _ []string, 646 _ bool, _ string, _ models.TagOptions) (parser.Params, bool, error) { 647 return customParam{s}, true, nil 648 } 649 650 opts := NewParseOptions().SetFunctionParseExpr(fn) 651 for _, tt := range tests { 652 p, err := Parse(tt.q, time.Second, models.NewTagOptions(), opts) 653 require.NoError(t, err) 654 transforms, edges, err := p.DAG() 655 require.NoError(t, err) 656 require.Len(t, transforms, 2) 657 assert.Equal(t, functions.FetchType, transforms[0].Op.OpType()) 658 assert.Equal(t, parser.NodeID("0"), transforms[0].ID) 659 assert.Len(t, edges, 1) 660 assert.Equal(t, tt.ex, transforms[1].Op.OpType()) 661 assert.Equal(t, parser.NodeID("1"), transforms[1].ID) 662 } 663 }