cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.67.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/parser/utils/source.go

494lines · modecode

1package utils
2
3import (
4 "fmt"
5 "slices"
6 "strings"
7
8 "github.com/cloudflare/pint/internal/parser"
9
10 "github.com/prometheus/prometheus/model/labels"
11 promParser "github.com/prometheus/prometheus/promql/parser"
12 "github.com/prometheus/prometheus/promql/parser/posrange"
13)
14
15type SourceType int
16
17const (
18 UnknownSource SourceType = iota
19 NumberSource
20 StringSource
21 SelectorSource
22 FuncSource
23 AggregateSource
24)
25
26type ExcludedLabel struct {
27 Reason string
28 Fragment string
29}
30
31type Source struct {
32 Selector *promParser.VectorSelector
33 Call *promParser.Call
34 ExcludeReason map[string]ExcludedLabel // Reason why a label was excluded
35 Operation string
36 Returns promParser.ValueType
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 Alternatives []Source // Alternative lable sources
41 Type SourceType
42 FixedLabels bool // Labels are fixed and only allowed labels can be present.
43}
44
45func LabelsSource(expr string, node *parser.PromQLNode) Source {
46 return walkNode(expr, node.Expr)
47}
48
49func walkNode(expr string, node promParser.Node) (s Source) {
50 switch n := node.(type) {
51 case *promParser.AggregateExpr:
52 switch n.Op {
53 case promParser.SUM:
54 s = parseAggregation(expr, n)
55 s.Operation = "sum"
56 case promParser.MIN:
57 s = parseAggregation(expr, n)
58 s.Operation = "min"
59 case promParser.MAX:
60 s = parseAggregation(expr, n)
61 s.Operation = "max"
62 case promParser.AVG:
63 s = parseAggregation(expr, n)
64 s.Operation = "avg"
65 case promParser.GROUP:
66 s = parseAggregation(expr, n)
67 s.Operation = "group"
68 case promParser.STDDEV:
69 s = parseAggregation(expr, n)
70 s.Operation = "stddev"
71 case promParser.STDVAR:
72 s = parseAggregation(expr, n)
73 s.Operation = "stdvar"
74 case promParser.COUNT:
75 s = parseAggregation(expr, n)
76 s.Operation = "count"
77 case promParser.COUNT_VALUES:
78 s = parseAggregation(expr, n)
79 s.Operation = "count_values"
80 // Param is the label to store the count value in.
81 s.GuaranteedLabels = appendToSlice(s.GuaranteedLabels, n.Param.(*promParser.StringLiteral).Val)
82 s.IncludedLabels = appendToSlice(s.IncludedLabels, n.Param.(*promParser.StringLiteral).Val)
83 s.ExcludedLabels = removeFromSlice(s.ExcludedLabels, n.Param.(*promParser.StringLiteral).Val)
84 delete(s.ExcludeReason, n.Param.(*promParser.StringLiteral).Val)
85 case promParser.QUANTILE:
86 s = parseAggregation(expr, n)
87 s.Operation = "quantile"
88 case promParser.TOPK:
89 s = walkNode(expr, n.Expr)
90 s.Type = AggregateSource
91 s.Operation = "topk"
92 case promParser.BOTTOMK:
93 s = walkNode(expr, n.Expr)
94 s.Type = AggregateSource
95 s.Operation = "bottomk"
96 /*
97 TODO these are experimental and promParser.EnableExperimentalFunctions must be set to true to enable parsing of these.
98 case promParser.LIMITK:
99 s = walkNode(expr, n.Expr)
100 s.Type = AggregateSource
101 s.Operation = "limitk"
102 case promParser.LIMIT_RATIO:
103 s = walkNode(expr, n.Expr)
104 s.Type = AggregateSource
105 s.Operation = "limit_ratio"
106 */
107 }
108
109 case *promParser.BinaryExpr:
110 s = parseBinOps(expr, n)
111
112 case *promParser.Call:
113 s = parseCall(expr, n)
114
115 case *promParser.MatrixSelector:
116 s = walkNode(expr, n.VectorSelector)
117
118 case *promParser.SubqueryExpr:
119 s = walkNode(expr, n.Expr)
120
121 case *promParser.NumberLiteral:
122 s.Type = NumberSource
123 s.Returns = promParser.ValueTypeScalar
124 s.FixedLabels = true
125
126 case *promParser.ParenExpr:
127 s = walkNode(expr, n.Expr)
128
129 case *promParser.StringLiteral:
130 s.Type = StringSource
131 s.Returns = promParser.ValueTypeString
132 s.FixedLabels = true
133
134 case *promParser.UnaryExpr:
135 s = walkNode(expr, n.Expr)
136
137 case *promParser.StepInvariantExpr:
138 // Not possible to get this from the parser.
139
140 case *promParser.VectorSelector:
141 s.Type = SelectorSource
142 s.Returns = promParser.ValueTypeVector
143 s.Selector = n
144 s.GuaranteedLabels = appendToSlice(s.GuaranteedLabels, guaranteedLabelsFromSelector(s.Selector)...)
145
146 default:
147 // unhandled type
148 }
149 return s
150}
151
152func removeFromSlice(sl []string, s ...string) []string {
153 for _, v := range s {
154 idx := slices.Index(sl, v)
155 if idx >= 0 {
156 if len(sl) == 1 {
157 return nil
158 }
159 sl = slices.Delete(sl, idx, idx+1)
160 }
161 }
162 return sl
163}
164
165func appendToSlice(dst []string, values ...string) []string {
166 for _, v := range values {
167 if !slices.Contains(dst, v) {
168 dst = append(dst, v)
169 }
170 }
171 return dst
172}
173
174func setInMap(dst map[string]ExcludedLabel, key string, val ExcludedLabel) map[string]ExcludedLabel {
175 if dst == nil {
176 dst = map[string]ExcludedLabel{}
177 }
178 dst[key] = val
179 return dst
180}
181
182func guaranteedLabelsFromSelector(selector *promParser.VectorSelector) (names []string) {
183 // Any label used in positive filters is gurnateed to be present.
184 for _, lm := range selector.LabelMatchers {
185 if lm.Name == labels.MetricName {
186 continue
187 }
188 if lm.Type == labels.MatchEqual || lm.Type == labels.MatchRegexp {
189 names = appendToSlice(names, lm.Name)
190 }
191 }
192 return names
193}
194
195func getQueryFragment(expr string, pos posrange.PositionRange) string {
196 return expr[pos.Start:pos.End]
197}
198
199func parseAggregation(expr string, n *promParser.AggregateExpr) (s Source) {
200 s = walkNode(expr, n.Expr)
201 if n.Without {
202 s.ExcludedLabels = appendToSlice(s.ExcludedLabels, n.Grouping...)
203 s.IncludedLabels = removeFromSlice(s.IncludedLabels, n.Grouping...)
204 s.GuaranteedLabels = removeFromSlice(s.GuaranteedLabels, n.Grouping...)
205 for _, name := range n.Grouping {
206 s.ExcludeReason = setInMap(
207 s.ExcludeReason,
208 name,
209 ExcludedLabel{
210 Reason: fmt.Sprintf("Query is using aggregation with `without(%s)`, all labels included inside `without(...)` will be removed from the results.",
211 strings.Join(n.Grouping, ", ")),
212 Fragment: getQueryFragment(expr, n.PosRange),
213 },
214 )
215 }
216 } else {
217 s.FixedLabels = true
218 if len(n.Grouping) == 0 {
219 s.IncludedLabels = nil
220 s.GuaranteedLabels = nil
221 s.ExcludeReason = setInMap(
222 s.ExcludeReason,
223 "",
224 ExcludedLabel{
225 Reason: "Query is using aggregation that removes all labels.",
226 Fragment: getQueryFragment(expr, n.PosRange),
227 },
228 )
229 } else {
230 s.IncludedLabels = appendToSlice(s.IncludedLabels, n.Grouping...)
231 for _, name := range n.Grouping {
232 s.ExcludedLabels = removeFromSlice(s.ExcludedLabels, name)
233 }
234 s.ExcludeReason = setInMap(
235 s.ExcludeReason,
236 "",
237 ExcludedLabel{
238 Reason: fmt.Sprintf("Query is using aggregation with `by(%s)`, only labels included inside `by(...)` will be present on the results.",
239 strings.Join(n.Grouping, ", ")),
240 Fragment: getQueryFragment(expr, n.PosRange),
241 },
242 )
243 }
244 }
245 s.Type = AggregateSource
246 s.Returns = promParser.ValueTypeVector
247 s.Call = nil
248 return s
249}
250
251func parseCall(expr string, n *promParser.Call) (s Source) {
252 s.Type = FuncSource
253 s.Operation = n.Func.Name
254 s.Call = n
255
256 var vt promParser.ValueType
257 for i, e := range n.Args {
258 if i >= len(n.Func.ArgTypes) {
259 vt = n.Func.ArgTypes[len(n.Func.ArgTypes)-1]
260 } else {
261 vt = n.Func.ArgTypes[i]
262 }
263
264 // nolint: exhaustive
265 switch vt {
266 case promParser.ValueTypeVector, promParser.ValueTypeMatrix:
267 s.Selector = walkNode(expr, e).Selector
268 }
269 }
270
271 switch n.Func.Name {
272 case "abs", "sgn", "acos", "acosh", "asin", "asinh", "atan", "atanh", "cos", "cosh", "sin", "sinh", "tan", "tanh":
273 // No change to labels.
274 s.Returns = promParser.ValueTypeVector
275 s.GuaranteedLabels = appendToSlice(s.GuaranteedLabels, guaranteedLabelsFromSelector(s.Selector)...)
276
277 case "ceil", "floor", "round":
278 // No change to labels.
279 s.Returns = promParser.ValueTypeVector
280 s.GuaranteedLabels = appendToSlice(s.GuaranteedLabels, guaranteedLabelsFromSelector(s.Selector)...)
281
282 case "changes", "resets":
283 // No change to labels.
284 s.Returns = promParser.ValueTypeVector
285 s.GuaranteedLabels = appendToSlice(s.GuaranteedLabels, guaranteedLabelsFromSelector(s.Selector)...)
286
287 case "clamp", "clamp_max", "clamp_min":
288 // No change to labels.
289 s.Returns = promParser.ValueTypeVector
290 s.GuaranteedLabels = appendToSlice(s.GuaranteedLabels, guaranteedLabelsFromSelector(s.Selector)...)
291
292 case "absent", "absent_over_time":
293 s.Returns = promParser.ValueTypeVector
294 s.FixedLabels = true
295 for _, lm := range s.Selector.LabelMatchers {
296 if lm.Name == labels.MetricName {
297 continue
298 }
299 if lm.Type == labels.MatchEqual {
300 s.IncludedLabels = appendToSlice(s.IncludedLabels, lm.Name)
301 s.GuaranteedLabels = appendToSlice(s.GuaranteedLabels, lm.Name)
302 }
303 }
304
305 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":
306 // No change to labels.
307 s.Returns = promParser.ValueTypeVector
308 s.GuaranteedLabels = appendToSlice(s.GuaranteedLabels, guaranteedLabelsFromSelector(s.Selector)...)
309
310 case "days_in_month", "day_of_month", "day_of_week", "day_of_year", "hour", "minute", "month", "year":
311 s.Returns = promParser.ValueTypeVector
312 // No labels if we don't pass any arguments.
313 // Otherwise no change to labels.
314 if len(s.Call.Args) == 0 {
315 s.FixedLabels = true
316 } else {
317 s.GuaranteedLabels = appendToSlice(s.GuaranteedLabels, guaranteedLabelsFromSelector(s.Selector)...)
318 }
319
320 case "deg", "rad", "ln", "log10", "log2", "sqrt", "exp":
321 // No change to labels.
322 s.Returns = promParser.ValueTypeVector
323 s.GuaranteedLabels = appendToSlice(s.GuaranteedLabels, guaranteedLabelsFromSelector(s.Selector)...)
324
325 case "delta", "idelta", "increase", "deriv", "irate", "rate":
326 // No change to labels.
327 s.Returns = promParser.ValueTypeVector
328 s.GuaranteedLabels = appendToSlice(s.GuaranteedLabels, guaranteedLabelsFromSelector(s.Selector)...)
329
330 case "histogram_avg", "histogram_count", "histogram_sum", "histogram_stddev", "histogram_stdvar", "histogram_fraction", "histogram_quantile":
331 // No change to labels.
332 s.Returns = promParser.ValueTypeVector
333 s.GuaranteedLabels = appendToSlice(s.GuaranteedLabels, guaranteedLabelsFromSelector(s.Selector)...)
334
335 case "holt_winters", "predict_linear":
336 // No change to labels.
337 s.Returns = promParser.ValueTypeVector
338 s.GuaranteedLabels = appendToSlice(s.GuaranteedLabels, guaranteedLabelsFromSelector(s.Selector)...)
339
340 case "label_replace", "label_join":
341 // One label added to the results.
342 s.Returns = promParser.ValueTypeVector
343 s.GuaranteedLabels = appendToSlice(s.GuaranteedLabels, guaranteedLabelsFromSelector(s.Selector)...)
344 s.GuaranteedLabels = appendToSlice(s.GuaranteedLabels, s.Call.Args[1].(*promParser.StringLiteral).Val)
345
346 case "pi":
347 s.Returns = promParser.ValueTypeScalar
348 s.FixedLabels = true
349
350 case "scalar":
351 s.Returns = promParser.ValueTypeScalar
352 s.FixedLabels = true
353
354 case "sort", "sort_desc":
355 // No change to labels.
356 s.Returns = promParser.ValueTypeVector
357
358 case "time":
359 s.Returns = promParser.ValueTypeScalar
360 s.FixedLabels = true
361
362 case "timestamp":
363 // No change to labels.
364 s.Returns = promParser.ValueTypeVector
365 s.GuaranteedLabels = appendToSlice(s.GuaranteedLabels, guaranteedLabelsFromSelector(s.Selector)...)
366
367 case "vector":
368 s.Returns = promParser.ValueTypeVector
369 s.FixedLabels = true
370
371 default:
372 // Unsupported function
373 s.Returns = promParser.ValueTypeNone
374 s.Call = nil
375 }
376
377 return s
378}
379
380func parseBinOps(expr string, n *promParser.BinaryExpr) (s Source) {
381 switch {
382 case n.VectorMatching == nil:
383 s = walkNode(expr, n.LHS)
384 if s.Returns == promParser.ValueTypeScalar || s.Returns == promParser.ValueTypeString {
385 s = walkNode(expr, n.RHS)
386 }
387
388 // foo{} + bar{}
389 // foo{} + on(...) bar{}
390 // foo{} + ignoring(...) bar{}
391 case n.VectorMatching.Card == promParser.CardOneToOne:
392 s = walkNode(expr, n.LHS)
393 if n.VectorMatching.On {
394 s.FixedLabels = true
395 s.IncludedLabels = appendToSlice(s.IncludedLabels, n.VectorMatching.MatchingLabels...)
396 s.ExcludedLabels = removeFromSlice(s.ExcludedLabels, n.VectorMatching.MatchingLabels...)
397 for _, name := range n.VectorMatching.MatchingLabels {
398 delete(s.ExcludeReason, name)
399 }
400 s.ExcludeReason = setInMap(
401 s.ExcludeReason,
402 "",
403 ExcludedLabel{
404 Reason: fmt.Sprintf(
405 "Query is using %s vector matching with `on(%s)`, only labels included inside `on(...)` will be present on the results.",
406 n.VectorMatching.Card, strings.Join(n.VectorMatching.MatchingLabels, ", "),
407 ),
408 Fragment: getQueryFragment(
409 expr,
410 posrange.PositionRange{
411 Start: n.LHS.PositionRange().Start,
412 End: n.RHS.PositionRange().End,
413 },
414 ),
415 },
416 )
417 } else {
418 s.IncludedLabels = removeFromSlice(s.IncludedLabels, n.VectorMatching.MatchingLabels...)
419 s.GuaranteedLabels = removeFromSlice(s.GuaranteedLabels, n.VectorMatching.MatchingLabels...)
420 s.ExcludedLabels = appendToSlice(s.ExcludedLabels, n.VectorMatching.MatchingLabels...)
421 for _, name := range n.VectorMatching.MatchingLabels {
422 s.ExcludeReason = setInMap(
423 s.ExcludeReason,
424 name,
425 ExcludedLabel{
426 Reason: fmt.Sprintf(
427 "Query is using %s vector matching with `ignoring(%s)`, all labels included inside `ignoring(...)` will be removed on the results.",
428 n.VectorMatching.Card, strings.Join(n.VectorMatching.MatchingLabels, ", "),
429 ),
430 Fragment: getQueryFragment(
431 expr,
432 posrange.PositionRange{
433 Start: n.LHS.PositionRange().Start,
434 End: n.RHS.PositionRange().End,
435 },
436 ),
437 },
438 )
439 }
440 }
441
442 // foo{} + on(...) group_left(...) bar{}
443 // foo{} + ignoring(...) group_left(...) bar{}
444 case n.VectorMatching.Card == promParser.CardOneToMany:
445 s = walkNode(expr, n.RHS)
446 s.IncludedLabels = appendToSlice(s.IncludedLabels, n.VectorMatching.Include...)
447 if n.VectorMatching.On {
448 s.IncludedLabels = appendToSlice(s.IncludedLabels, n.VectorMatching.MatchingLabels...)
449 for _, name := range n.VectorMatching.MatchingLabels {
450 delete(s.ExcludeReason, name)
451 }
452 }
453 s.ExcludedLabels = removeFromSlice(s.ExcludedLabels, n.VectorMatching.Include...)
454 for _, name := range n.VectorMatching.Include {
455 delete(s.ExcludeReason, name)
456 }
457
458 // foo{} + on(...) group_right(...) bar{}
459 // foo{} + ignoring(...) group_right(...) bar{}
460 case n.VectorMatching.Card == promParser.CardManyToOne:
461 s = walkNode(expr, n.LHS)
462 s.IncludedLabels = appendToSlice(s.IncludedLabels, n.VectorMatching.Include...)
463 if n.VectorMatching.On {
464 s.IncludedLabels = appendToSlice(s.IncludedLabels, n.VectorMatching.MatchingLabels...)
465 for _, name := range n.VectorMatching.MatchingLabels {
466 delete(s.ExcludeReason, name)
467 }
468 }
469 s.ExcludedLabels = removeFromSlice(s.ExcludedLabels, n.VectorMatching.Include...)
470 for _, name := range n.VectorMatching.Include {
471 delete(s.ExcludeReason, name)
472 }
473
474 // foo{} and on(...) bar{}
475 // foo{} and ignoring(...) bar{}
476 case n.VectorMatching.Card == promParser.CardManyToMany:
477 s = walkNode(expr, n.LHS)
478 if n.VectorMatching.On {
479 s.IncludedLabels = appendToSlice(s.IncludedLabels, n.VectorMatching.MatchingLabels...)
480 for _, name := range n.VectorMatching.MatchingLabels {
481 delete(s.ExcludeReason, name)
482 }
483 }
484 if n.Op == promParser.LOR {
485 s.Alternatives = append(s.Alternatives, walkNode(expr, n.RHS))
486 }
487 }
488
489 if n.VectorMatching != nil && s.Operation == "" {
490 s.Operation = n.VectorMatching.Card.String()
491 }
492
493 return s
494}
495