cloudflare/pint

Public

mirrored from https://github.com/cloudflare/pintAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
29ed4c2ccc1fb946d416df7635c9bae86ffca5b5

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

internal/parser/utils/source.go

745lines · modecode

1package utils
2
3import (
4 "fmt"
5 "math"
6 "slices"
7 "strings"
8
9 "github.com/prometheus/prometheus/model/labels"
10 promParser "github.com/prometheus/prometheus/promql/parser"
11 "github.com/prometheus/prometheus/promql/parser/posrange"
12)
13
14type SourceType int
15
16const (
17 UnknownSource SourceType = iota
18 NumberSource
19 StringSource
20 SelectorSource
21 FuncSource
22 AggregateSource
23)
24
25type ExcludedLabel struct {
26 Reason string
27 Fragment string
28}
29
30type Source struct {
31 Selectors []*promParser.VectorSelector
32 Call *promParser.Call
33 ExcludeReason map[string]ExcludedLabel // Reason why a label was excluded
34 Operation string
35 Returns promParser.ValueType
36 ReturnedNumbers []float64 // If AlwaysReturns=true this is the number that's returned
37 IncludedLabels []string // Labels that are included by filters, they will be present if exist on source series (by).
38 ExcludedLabels []string // Labels guaranteed to be excluded from the results (without).
39 GuaranteedLabels []string // Labels guaranteed to be present on the results (matchers).
40 Type SourceType
41 FixedLabels bool // Labels are fixed and only allowed labels can be present.
42 IsDead bool // True if this source cannot be reached and is dead code.
43 AlwaysReturns bool // True if this source always returns results.
44}
45
46func LabelsSource(expr string, node promParser.Node) (src []Source) {
47 return walkNode(expr, node)
48}
49
50func walkNode(expr string, node promParser.Node) (src []Source) {
51 var s Source
52 switch n := node.(type) {
53 case *promParser.AggregateExpr:
54 src = append(src, walkAggregation(expr, n)...)
55
56 case *promParser.BinaryExpr:
57 src = append(src, parseBinOps(expr, n)...)
58
59 case *promParser.Call:
60 s = parseCall(expr, n)
61 src = append(src, s)
62
63 case *promParser.MatrixSelector:
64 src = append(src, walkNode(expr, n.VectorSelector)...)
65
66 case *promParser.SubqueryExpr:
67 src = append(src, walkNode(expr, n.Expr)...)
68
69 case *promParser.NumberLiteral:
70 s.Type = NumberSource
71 s.Returns = promParser.ValueTypeScalar
72 s.ReturnedNumbers = append(s.ReturnedNumbers, n.Val)
73 s.IncludedLabels = nil
74 s.GuaranteedLabels = nil
75 s.FixedLabels = true
76 s.AlwaysReturns = true
77 s.ExcludeReason = setInMap(
78 s.ExcludeReason,
79 "",
80 ExcludedLabel{
81 Reason: "This returns a number value with no labels.",
82 Fragment: getQueryFragment(expr, n.PosRange),
83 },
84 )
85 src = append(src, s)
86
87 case *promParser.ParenExpr:
88 src = append(src, walkNode(expr, n.Expr)...)
89
90 case *promParser.StringLiteral:
91 s.Type = StringSource
92 s.Returns = promParser.ValueTypeString
93 s.IncludedLabels = nil
94 s.GuaranteedLabels = nil
95 s.FixedLabels = true
96 s.AlwaysReturns = true
97 s.ExcludeReason = setInMap(
98 s.ExcludeReason,
99 "",
100 ExcludedLabel{
101 Reason: "This returns a string value with no labels.",
102 Fragment: getQueryFragment(expr, n.PosRange),
103 },
104 )
105 src = append(src, s)
106
107 case *promParser.UnaryExpr:
108 src = append(src, walkNode(expr, n.Expr)...)
109
110 case *promParser.StepInvariantExpr:
111 // Not possible to get this from the parser.
112
113 case *promParser.VectorSelector:
114 s.Type = SelectorSource
115 s.Returns = promParser.ValueTypeVector
116 s.Selectors = append(s.Selectors, n)
117 s.GuaranteedLabels = appendToSlice(s.GuaranteedLabels, labelsFromSelectors(guaranteedLabelsMatches, n)...)
118 src = append(src, s)
119
120 default:
121 // unhandled type
122 }
123 return src
124}
125
126func removeFromSlice(sl []string, s ...string) []string {
127 for _, v := range s {
128 idx := slices.Index(sl, v)
129 if idx >= 0 {
130 if len(sl) == 1 {
131 return nil
132 }
133 sl = slices.Delete(sl, idx, idx+1)
134 }
135 }
136 return sl
137}
138
139func appendToSlice(dst []string, values ...string) []string {
140 for _, v := range values {
141 if !slices.Contains(dst, v) {
142 dst = append(dst, v)
143 }
144 }
145 return dst
146}
147
148func setInMap(dst map[string]ExcludedLabel, key string, val ExcludedLabel) map[string]ExcludedLabel {
149 if dst == nil {
150 dst = map[string]ExcludedLabel{}
151 }
152 dst[key] = val
153 return dst
154}
155
156var guaranteedLabelsMatches = []labels.MatchType{labels.MatchEqual, labels.MatchRegexp}
157
158func labelsFromSelectors(matches []labels.MatchType, selectors ...*promParser.VectorSelector) (names []string) {
159 nameCount := map[string]int{}
160 var ok bool
161 for _, selector := range selectors {
162 // Any label used in positive filters is gurnateed to be present.
163 for _, lm := range selector.LabelMatchers {
164 if lm.Name == labels.MetricName {
165 continue
166 }
167
168 if !slices.Contains(matches, lm.Type) {
169 continue
170 }
171
172 names = appendToSlice(names, lm.Name)
173
174 if _, ok = nameCount[lm.Name]; !ok {
175 nameCount[lm.Name] = 0
176 }
177 nameCount[lm.Name]++
178 }
179 }
180 for name, cnt := range nameCount {
181 if cnt != len(selectors) {
182 names = removeFromSlice(names, name)
183 }
184 }
185 return names
186}
187
188func getQueryFragment(expr string, pos posrange.PositionRange) string {
189 return expr[pos.Start:pos.End]
190}
191
192func walkAggregation(expr string, n *promParser.AggregateExpr) (src []Source) {
193 var s Source
194 switch n.Op {
195 case promParser.SUM:
196 for _, s = range parseAggregation(expr, n) {
197 s.Operation = "sum"
198 src = append(src, s)
199 }
200 case promParser.MIN:
201 for _, s = range parseAggregation(expr, n) {
202 s.Operation = "min"
203 src = append(src, s)
204 }
205 case promParser.MAX:
206 for _, s = range parseAggregation(expr, n) {
207 s.Operation = "max"
208 src = append(src, s)
209 }
210 case promParser.AVG:
211 for _, s = range parseAggregation(expr, n) {
212 s.Operation = "avg"
213 src = append(src, s)
214 }
215 case promParser.GROUP:
216 for _, s = range parseAggregation(expr, n) {
217 s.Operation = "group"
218 src = append(src, s)
219 }
220 case promParser.STDDEV:
221 for _, s = range parseAggregation(expr, n) {
222 s.Operation = "stddev"
223 src = append(src, s)
224 }
225 case promParser.STDVAR:
226 for _, s = range parseAggregation(expr, n) {
227 s.Operation = "stdvar"
228 src = append(src, s)
229 }
230 case promParser.COUNT:
231 for _, s = range parseAggregation(expr, n) {
232 s.Operation = "count"
233 src = append(src, s)
234 }
235 case promParser.COUNT_VALUES:
236 for _, s = range parseAggregation(expr, n) {
237 s.Operation = "count_values"
238 // Param is the label to store the count value in.
239 s.GuaranteedLabels = appendToSlice(s.GuaranteedLabels, n.Param.(*promParser.StringLiteral).Val)
240 s.IncludedLabels = appendToSlice(s.IncludedLabels, n.Param.(*promParser.StringLiteral).Val)
241 s.ExcludedLabels = removeFromSlice(s.ExcludedLabels, n.Param.(*promParser.StringLiteral).Val)
242 delete(s.ExcludeReason, n.Param.(*promParser.StringLiteral).Val)
243 src = append(src, s)
244 }
245 case promParser.QUANTILE:
246 for _, s = range parseAggregation(expr, n) {
247 s.Operation = "quantile"
248 src = append(src, s)
249 }
250 case promParser.TOPK:
251 for _, s = range walkNode(expr, n.Expr) {
252 s.Type = AggregateSource
253 s.Operation = "topk"
254 src = append(src, s)
255 }
256 case promParser.BOTTOMK:
257 for _, s = range walkNode(expr, n.Expr) {
258 s.Type = AggregateSource
259 s.Operation = "bottomk"
260 src = append(src, s)
261 }
262 /*
263 TODO these are experimental and promParser.EnableExperimentalFunctions must be set to true to enable parsing of these.
264 case promParser.LIMITK:
265 s = walkNode(expr, n.Expr)
266 s.Type = AggregateSource
267 s.Operation = "limitk"
268 case promParser.LIMIT_RATIO:
269 s = walkNode(expr, n.Expr)
270 s.Type = AggregateSource
271 s.Operation = "limit_ratio"
272 */
273 }
274 return src
275}
276
277func parseAggregation(expr string, n *promParser.AggregateExpr) (src []Source) {
278 var s Source
279 for _, s = range walkNode(expr, n.Expr) {
280 if n.Without {
281 s.ExcludedLabels = appendToSlice(s.ExcludedLabels, n.Grouping...)
282 s.IncludedLabels = removeFromSlice(s.IncludedLabels, n.Grouping...)
283 s.GuaranteedLabels = removeFromSlice(s.GuaranteedLabels, n.Grouping...)
284 for _, name := range n.Grouping {
285 s.ExcludeReason = setInMap(
286 s.ExcludeReason,
287 name,
288 ExcludedLabel{
289 Reason: fmt.Sprintf("Query is using aggregation with `without(%s)`, all labels included inside `without(...)` will be removed from the results.",
290 strings.Join(n.Grouping, ", ")),
291 Fragment: getQueryFragment(expr, n.PosRange),
292 },
293 )
294 }
295 } else {
296 if len(n.Grouping) == 0 {
297 s.IncludedLabels = nil
298 s.GuaranteedLabels = nil
299 s.ExcludeReason = setInMap(
300 s.ExcludeReason,
301 "",
302 ExcludedLabel{
303 Reason: "Query is using aggregation that removes all labels.",
304 Fragment: getQueryFragment(expr, n.PosRange),
305 },
306 )
307 } else {
308 // Check if source of labels already fixes them.
309 if !s.FixedLabels {
310 s.IncludedLabels = appendToSlice(s.IncludedLabels, n.Grouping...)
311 for _, name := range n.Grouping {
312 s.ExcludedLabels = removeFromSlice(s.ExcludedLabels, name)
313 }
314 s.ExcludeReason = setInMap(
315 s.ExcludeReason,
316 "",
317 ExcludedLabel{
318 Reason: fmt.Sprintf("Query is using aggregation with `by(%s)`, only labels included inside `by(...)` will be present on the results.",
319 strings.Join(n.Grouping, ", ")),
320 Fragment: getQueryFragment(expr, n.PosRange),
321 },
322 )
323 }
324 for _, name := range s.GuaranteedLabels {
325 if !slices.Contains(n.Grouping, name) {
326 s.GuaranteedLabels = removeFromSlice(s.GuaranteedLabels, name)
327 }
328 }
329 }
330 s.FixedLabels = true
331 }
332 s.Type = AggregateSource
333 s.Returns = promParser.ValueTypeVector
334 s.Call = nil
335 src = append(src, s)
336 }
337 return src
338}
339
340func parseCall(expr string, n *promParser.Call) (s Source) {
341 s.Type = FuncSource
342 s.Operation = n.Func.Name
343 s.Call = n
344
345 var vt promParser.ValueType
346 for i, e := range n.Args {
347 if i >= len(n.Func.ArgTypes) {
348 vt = n.Func.ArgTypes[len(n.Func.ArgTypes)-1]
349 } else {
350 vt = n.Func.ArgTypes[i]
351 }
352
353 // nolint: exhaustive
354 switch vt {
355 case promParser.ValueTypeVector, promParser.ValueTypeMatrix:
356 for _, es := range walkNode(expr, e) {
357 s.Selectors = append(s.Selectors, es.Selectors...)
358 }
359 }
360 }
361
362 switch n.Func.Name {
363 case "abs", "sgn", "acos", "acosh", "asin", "asinh", "atan", "atanh", "cos", "cosh", "sin", "sinh", "tan", "tanh":
364 // No change to labels.
365 s.Returns = promParser.ValueTypeVector
366 s.GuaranteedLabels = appendToSlice(s.GuaranteedLabels, labelsFromSelectors(guaranteedLabelsMatches, s.Selectors...)...)
367
368 case "ceil", "floor", "round":
369 // No change to labels.
370 s.Returns = promParser.ValueTypeVector
371 s.GuaranteedLabels = appendToSlice(s.GuaranteedLabels, labelsFromSelectors(guaranteedLabelsMatches, s.Selectors...)...)
372
373 case "changes", "resets":
374 // No change to labels.
375 s.Returns = promParser.ValueTypeVector
376 s.GuaranteedLabels = appendToSlice(s.GuaranteedLabels, labelsFromSelectors(guaranteedLabelsMatches, s.Selectors...)...)
377
378 case "clamp", "clamp_max", "clamp_min":
379 // No change to labels.
380 s.Returns = promParser.ValueTypeVector
381 s.GuaranteedLabels = appendToSlice(s.GuaranteedLabels, labelsFromSelectors(guaranteedLabelsMatches, s.Selectors...)...)
382
383 case "absent", "absent_over_time":
384 s.Returns = promParser.ValueTypeVector
385 s.FixedLabels = true
386 for _, name := range labelsFromSelectors([]labels.MatchType{labels.MatchEqual}, s.Selectors...) {
387 s.IncludedLabels = appendToSlice(s.IncludedLabels, name)
388 s.GuaranteedLabels = appendToSlice(s.GuaranteedLabels, name)
389 }
390 s.ExcludeReason = setInMap(
391 s.ExcludeReason,
392 "",
393 ExcludedLabel{
394 Reason: fmt.Sprintf(`The [%s()](https://prometheus.io/docs/prometheus/latest/querying/functions/#%s) function is used to check if provided query doesn't match any time series.
395You will only get any results back if the metric selector you pass doesn't match anything.
396Since there are no matching time series there are also no labels. If some time series is missing you cannot read its labels.
397This means that the only labels you can get back from absent call are the ones you pass to it.
398If you're hoping to get instance specific labels this way and alert when some target is down then that won't work, use the `+"`up`"+` metric instead.`,
399 n.Func.Name, n.Func.Name),
400 Fragment: getQueryFragment(expr, n.PosRange),
401 },
402 )
403
404 case "avg_over_time", "count_over_time", "last_over_time", "max_over_time", "min_over_time", "present_over_time", "quantile_over_time", "stddev_over_time", "stdvar_over_time", "sum_over_time":
405 // No change to labels.
406 s.Returns = promParser.ValueTypeVector
407 s.GuaranteedLabels = appendToSlice(s.GuaranteedLabels, labelsFromSelectors(guaranteedLabelsMatches, s.Selectors...)...)
408
409 case "days_in_month", "day_of_month", "day_of_week", "day_of_year", "hour", "minute", "month", "year":
410 s.Returns = promParser.ValueTypeVector
411 // No labels if we don't pass any arguments.
412 // Otherwise no change to labels.
413 if len(s.Call.Args) == 0 {
414 s.FixedLabels = true
415 s.AlwaysReturns = true
416 s.IncludedLabels = nil
417 s.GuaranteedLabels = nil
418 s.ExcludeReason = setInMap(
419 s.ExcludeReason,
420 "",
421 ExcludedLabel{
422 Reason: fmt.Sprintf("Calling `%s()` with no arguments will return an empty time series with no labels.",
423 n.Func.Name),
424 Fragment: getQueryFragment(expr, n.PosRange),
425 },
426 )
427 } else {
428 s.GuaranteedLabels = appendToSlice(s.GuaranteedLabels, labelsFromSelectors(guaranteedLabelsMatches, s.Selectors...)...)
429 }
430
431 case "deg", "rad", "ln", "log10", "log2", "sqrt", "exp":
432 // No change to labels.
433 s.Returns = promParser.ValueTypeVector
434 s.GuaranteedLabels = appendToSlice(s.GuaranteedLabels, labelsFromSelectors(guaranteedLabelsMatches, s.Selectors...)...)
435
436 case "delta", "idelta", "increase", "deriv", "irate", "rate":
437 // No change to labels.
438 s.Returns = promParser.ValueTypeVector
439 s.GuaranteedLabels = appendToSlice(s.GuaranteedLabels, labelsFromSelectors(guaranteedLabelsMatches, s.Selectors...)...)
440
441 case "histogram_avg", "histogram_count", "histogram_sum", "histogram_stddev", "histogram_stdvar", "histogram_fraction", "histogram_quantile":
442 // No change to labels.
443 s.Returns = promParser.ValueTypeVector
444 s.GuaranteedLabels = appendToSlice(s.GuaranteedLabels, labelsFromSelectors(guaranteedLabelsMatches, s.Selectors...)...)
445
446 case "holt_winters", "predict_linear":
447 // No change to labels.
448 s.Returns = promParser.ValueTypeVector
449 s.GuaranteedLabels = appendToSlice(s.GuaranteedLabels, labelsFromSelectors(guaranteedLabelsMatches, s.Selectors...)...)
450
451 case "label_replace", "label_join":
452 // One label added to the results.
453 s.Returns = promParser.ValueTypeVector
454 s.GuaranteedLabels = appendToSlice(s.GuaranteedLabels, labelsFromSelectors(guaranteedLabelsMatches, s.Selectors...)...)
455 s.GuaranteedLabels = appendToSlice(s.GuaranteedLabels, s.Call.Args[1].(*promParser.StringLiteral).Val)
456
457 case "pi":
458 s.Returns = promParser.ValueTypeScalar
459 s.IncludedLabels = nil
460 s.GuaranteedLabels = nil
461 s.FixedLabels = true
462 s.AlwaysReturns = true
463 s.ExcludeReason = setInMap(
464 s.ExcludeReason,
465 "",
466 ExcludedLabel{
467 Reason: fmt.Sprintf("Calling `%s()` will return a scalar value with no labels.", n.Func.Name),
468 Fragment: getQueryFragment(expr, n.PosRange),
469 },
470 )
471
472 case "scalar":
473 s.Returns = promParser.ValueTypeScalar
474 s.IncludedLabels = nil
475 s.GuaranteedLabels = nil
476 s.FixedLabels = true
477 s.AlwaysReturns = true
478 s.ExcludeReason = setInMap(
479 s.ExcludeReason,
480 "",
481 ExcludedLabel{
482 Reason: fmt.Sprintf("Calling `%s()` will return a scalar value with no labels.", n.Func.Name),
483 Fragment: getQueryFragment(expr, n.PosRange),
484 },
485 )
486
487 case "sort", "sort_desc":
488 // No change to labels.
489 s.Returns = promParser.ValueTypeVector
490
491 case "time":
492 s.Returns = promParser.ValueTypeScalar
493 s.IncludedLabels = nil
494 s.GuaranteedLabels = nil
495 s.FixedLabels = true
496 s.AlwaysReturns = true
497 s.ExcludeReason = setInMap(
498 s.ExcludeReason,
499 "",
500 ExcludedLabel{
501 Reason: fmt.Sprintf("Calling `%s()` will return a scalar value with no labels.", n.Func.Name),
502 Fragment: getQueryFragment(expr, n.PosRange),
503 },
504 )
505
506 case "timestamp":
507 // No change to labels.
508 s.Returns = promParser.ValueTypeVector
509 s.GuaranteedLabels = appendToSlice(s.GuaranteedLabels, labelsFromSelectors(guaranteedLabelsMatches, s.Selectors...)...)
510
511 case "vector":
512 s.Returns = promParser.ValueTypeVector
513 s.IncludedLabels = nil
514 s.GuaranteedLabels = nil
515 s.FixedLabels = true
516 s.AlwaysReturns = true
517 if v, ok := n.Args[0].(*promParser.NumberLiteral); ok {
518 s.ReturnedNumbers = append(s.ReturnedNumbers, v.Val)
519 }
520 s.ExcludeReason = setInMap(
521 s.ExcludeReason,
522 "",
523 ExcludedLabel{
524 Reason: fmt.Sprintf("Calling `%s()` will return a vector value with no labels.", n.Func.Name),
525 Fragment: getQueryFragment(expr, n.PosRange),
526 },
527 )
528
529 default:
530 // Unsupported function
531 s.Returns = promParser.ValueTypeNone
532 s.Call = nil
533 }
534 return s
535}
536
537func parseBinOps(expr string, n *promParser.BinaryExpr) (src []Source) {
538 var s Source
539 switch {
540
541 // foo{} + 1
542 // 1 + foo{}
543 // foo{} > 1
544 // 1 > foo{}
545 case n.VectorMatching == nil:
546 lhs := walkNode(expr, n.LHS)
547 rhs := walkNode(expr, n.RHS)
548 for _, ls := range lhs {
549 for _, rs := range rhs {
550 switch {
551 case ls.AlwaysReturns && rs.AlwaysReturns:
552 // Both sides always return something
553 for i, lv := range ls.ReturnedNumbers {
554 for _, rv := range rs.ReturnedNumbers {
555 ls.ReturnedNumbers[i], ls.IsDead = calculateStaticReturn(lv, rv, n.Op, ls.IsDead)
556 }
557 }
558 src = append(src, ls)
559 case ls.Returns == promParser.ValueTypeVector, ls.Returns == promParser.ValueTypeMatrix:
560 // Use labels from LHS
561 src = append(src, ls)
562 case rs.Returns == promParser.ValueTypeVector, rs.Returns == promParser.ValueTypeMatrix:
563 // Use labels from RHS
564 src = append(src, rs)
565 }
566 }
567 }
568
569 // foo{} + bar{}
570 // foo{} + on(...) bar{}
571 // foo{} + ignoring(...) bar{}
572 case n.VectorMatching.Card == promParser.CardOneToOne:
573 for _, s = range walkNode(expr, n.LHS) {
574 if n.VectorMatching.On {
575 s.FixedLabels = true
576 s.IncludedLabels = appendToSlice(s.IncludedLabels, n.VectorMatching.MatchingLabels...)
577 s.ExcludedLabels = removeFromSlice(s.ExcludedLabels, n.VectorMatching.MatchingLabels...)
578 for _, name := range n.VectorMatching.MatchingLabels {
579 delete(s.ExcludeReason, name)
580 }
581 s.ExcludeReason = setInMap(
582 s.ExcludeReason,
583 "",
584 ExcludedLabel{
585 Reason: fmt.Sprintf(
586 "Query is using %s vector matching with `on(%s)`, only labels included inside `on(...)` will be present on the results.",
587 n.VectorMatching.Card, strings.Join(n.VectorMatching.MatchingLabels, ", "),
588 ),
589 Fragment: getQueryFragment(
590 expr,
591 posrange.PositionRange{
592 Start: n.LHS.PositionRange().Start,
593 End: n.RHS.PositionRange().End,
594 },
595 ),
596 },
597 )
598 } else {
599 s.IncludedLabels = removeFromSlice(s.IncludedLabels, n.VectorMatching.MatchingLabels...)
600 s.GuaranteedLabels = removeFromSlice(s.GuaranteedLabels, n.VectorMatching.MatchingLabels...)
601 s.ExcludedLabels = appendToSlice(s.ExcludedLabels, n.VectorMatching.MatchingLabels...)
602 for _, name := range n.VectorMatching.MatchingLabels {
603 s.ExcludeReason = setInMap(
604 s.ExcludeReason,
605 name,
606 ExcludedLabel{
607 Reason: fmt.Sprintf(
608 "Query is using %s vector matching with `ignoring(%s)`, all labels included inside `ignoring(...)` will be removed on the results.",
609 n.VectorMatching.Card, strings.Join(n.VectorMatching.MatchingLabels, ", "),
610 ),
611 Fragment: getQueryFragment(
612 expr,
613 posrange.PositionRange{
614 Start: n.LHS.PositionRange().Start,
615 End: n.RHS.PositionRange().End,
616 },
617 ),
618 },
619 )
620 }
621 }
622 if s.Operation == "" {
623 s.Operation = n.VectorMatching.Card.String()
624 }
625 src = append(src, s)
626 }
627
628 // foo{} + on(...) group_left(...) bar{}
629 // foo{} + ignoring(...) group_left(...) bar{}
630 case n.VectorMatching.Card == promParser.CardOneToMany:
631 for _, s = range walkNode(expr, n.RHS) {
632 s.IncludedLabels = appendToSlice(s.IncludedLabels, n.VectorMatching.Include...)
633 if n.VectorMatching.On {
634 s.IncludedLabels = appendToSlice(s.IncludedLabels, n.VectorMatching.MatchingLabels...)
635 for _, name := range n.VectorMatching.MatchingLabels {
636 delete(s.ExcludeReason, name)
637 }
638 }
639 s.ExcludedLabels = removeFromSlice(s.ExcludedLabels, n.VectorMatching.Include...)
640 for _, name := range n.VectorMatching.Include {
641 delete(s.ExcludeReason, name)
642 }
643 if s.Operation == "" {
644 s.Operation = n.VectorMatching.Card.String()
645 }
646 src = append(src, s)
647 }
648
649 // foo{} + on(...) group_right(...) bar{}
650 // foo{} + ignoring(...) group_right(...) bar{}
651 case n.VectorMatching.Card == promParser.CardManyToOne:
652 for _, s = range walkNode(expr, n.LHS) {
653 s.IncludedLabels = appendToSlice(s.IncludedLabels, n.VectorMatching.Include...)
654 if n.VectorMatching.On {
655 s.IncludedLabels = appendToSlice(s.IncludedLabels, n.VectorMatching.MatchingLabels...)
656 for _, name := range n.VectorMatching.MatchingLabels {
657 delete(s.ExcludeReason, name)
658 }
659 }
660 s.ExcludedLabels = removeFromSlice(s.ExcludedLabels, n.VectorMatching.Include...)
661 for _, name := range n.VectorMatching.Include {
662 delete(s.ExcludeReason, name)
663 }
664 if s.Operation == "" {
665 s.Operation = n.VectorMatching.Card.String()
666 }
667 src = append(src, s)
668 }
669
670 // foo{} and on(...) bar{}
671 // foo{} and ignoring(...) bar{}
672 case n.VectorMatching.Card == promParser.CardManyToMany:
673 var lhsCanBeEmpty bool // true if any of the LHS query can produce empty results.
674 for _, s = range walkNode(expr, n.LHS) {
675 if n.VectorMatching.On {
676 s.IncludedLabels = appendToSlice(s.IncludedLabels, n.VectorMatching.MatchingLabels...)
677 for _, name := range n.VectorMatching.MatchingLabels {
678 delete(s.ExcludeReason, name)
679 }
680 }
681 if s.Operation == "" {
682 s.Operation = n.VectorMatching.Card.String()
683 }
684 if !s.AlwaysReturns {
685 lhsCanBeEmpty = true
686 }
687 src = append(src, s)
688 }
689 if n.Op == promParser.LOR {
690 for _, s = range walkNode(expr, n.RHS) {
691 if s.Operation == "" {
692 s.Operation = n.VectorMatching.Card.String()
693 }
694 // If LHS can NOT be empty then RHS is dead code.
695 if !lhsCanBeEmpty {
696 s.IsDead = true
697 }
698 src = append(src, s)
699 }
700 }
701 }
702 return src
703}
704
705func calculateStaticReturn(lv, rv float64, op promParser.ItemType, isDead bool) (float64, bool) {
706 switch op {
707 case promParser.EQLC:
708 if lv != rv {
709 return lv, true
710 }
711 case promParser.NEQ:
712 if lv == rv {
713 return lv, true
714 }
715 case promParser.LTE:
716 if lv > rv {
717 return lv, true
718 }
719 case promParser.LSS:
720 if lv >= rv {
721 return lv, true
722 }
723 case promParser.GTE:
724 if lv < rv {
725 return lv, true
726 }
727 case promParser.GTR:
728 if lv <= rv {
729 return lv, true
730 }
731 case promParser.ADD:
732 return lv + rv, isDead
733 case promParser.SUB:
734 return lv - rv, isDead
735 case promParser.MUL:
736 return lv * rv, isDead
737 case promParser.DIV:
738 return lv / rv, isDead
739 case promParser.MOD:
740 return math.Mod(lv, rv), isDead
741 case promParser.POW:
742 return math.Pow(lv, rv), isDead
743 }
744 return lv, isDead
745}
746