@@ -164,18 +164,23 @@ type Unless struct {
164164}
165165
166166type Source struct {
167- Labels map [string ]LabelTransform
168- DeadInfo * DeadInfo
169- DeadLabels []DeadLabel
170- Returns promParser.ValueType
171- Operations Operations
172- Joins []Join // Any other sources this source joins with.
173- Unless []Unless // Any other sources this source is suppressed by.
174- ReturnInfo ReturnInfo
175- Position posrange.PositionRange
176- Type Type
177- FixedLabels bool // Labels are fixed and only allowed labels can be present.
178- IsConditional bool // True if this source is guarded by 'foo > 5' or other condition.
167+ Labels map [string ]LabelTransform `yaml:"labels,omitempty"`
168+ DeadInfo * DeadInfo `yaml:"deadInfo,omitempty"`
169+ DeadLabels []DeadLabel `yaml:"deadLabels,omitempty"`
170+ Returns promParser.ValueType `yaml:"returns"`
171+ Operations Operations `yaml:"operations,omitempty"`
172+ // Any other sources this source joins with.
173+ Joins []Join `yaml:"joins,omitempty"`
174+ // Any other sources this source is suppressed by.
175+ Unless []Unless `yaml:"unless,omitempty"`
176+ UsedLabels []string `yaml:"usedLabels,omitempty"`
177+ ReturnInfo ReturnInfo `yaml:"returnInfo,omitempty"`
178+ Position posrange.PositionRange `yaml:"position"`
179+ Type Type `yaml:"type"`
180+ // Labels are fixed and only allowed labels can be present.
181+ FixedLabels bool `yaml:"fixedLabels,omitempty"`
182+ // True if this source is guarded by 'foo > 5' or other condition.
183+ IsConditional bool `yaml:"isConditional,omitempty"`
179184}
180185
181186func (s Source ) Operation () string {
@@ -228,7 +233,11 @@ func (s *Source) excludeAllLabels(expr, reason string, fragment, allFragment pos
228233 }
229234 }
230235 }
236+ s .UsedLabels = slices .DeleteFunc (s .UsedLabels , func (name string ) bool {
237+ return ! slices .Contains (except , name )
238+ })
231239 // Mark except labels as possible, unless they are already guaranteed.
240+ s .UsedLabels = appendToSlice (s .UsedLabels , except ... )
232241 for _ , name := range except {
233242 if l , ok := s .Labels [name ]; ok && l .Kind == GuaranteedLabel {
234243 continue
@@ -265,6 +274,9 @@ func (s *Source) excludeLabel(reason string, fragment posrange.PositionRange, na
265274 Reason : reason ,
266275 Fragment : fragment ,
267276 }
277+ s .UsedLabels = slices .DeleteFunc (s .UsedLabels , func (s string ) bool {
278+ return s == name
279+ })
268280}
269281
270282func (s * Source ) joinLabels (expr string , within posrange.PositionRange , op promParser.ItemType , names []string , outside []posrange.PositionRange ) {
@@ -336,18 +348,6 @@ func (s *Source) checkIncludedLabels(expr string, pos posrange.PositionRange, na
336348 }
337349}
338350
339- func (s * Source ) hasJoinUsingLabel (name string ) bool {
340- for _ , j := range s .Joins {
341- if j .IsOn && slices .Contains (j .MatchingLabels , name ) {
342- return true
343- }
344- if ! j .IsOn && ! slices .Contains (j .MatchingLabels , name ) {
345- return true
346- }
347- }
348- return false
349- }
350-
351351func (s * Source ) checkAggregationLabels (expr string , n * promParser.AggregateExpr ) {
352352 var pos posrange.PositionRange
353353 switch {
@@ -361,8 +361,7 @@ func (s *Source) checkAggregationLabels(expr string, n *promParser.AggregateExpr
361361
362362 for _ , j := range s .Joins {
363363 for _ , name := range j .AddedLabels {
364- if s .hasJoinUsingLabel (name ) {
365- // This label is used for some other join for our source.
364+ if slices .Contains (s .UsedLabels , name ) {
366365 continue
367366 }
368367 if ! n .Without && slices .Contains (n .Grouping , name ) {
@@ -440,6 +439,24 @@ func (s *Source) checkJoinedLabels(expr string, n *promParser.BinaryExpr, dst So
440439 return dead
441440}
442441
442+ func (s * Source ) useLabelsNotExcluded (exluded []string ) {
443+ // Iterating over a map can yield labels in different order each time
444+ // so append labels to an extra slice, sort it, and then append the
445+ // sorted reults to UsedLabels.
446+ // Without this tests might show a diff sometimes.
447+ toAdd := make ([]string , 0 , len (s .Labels ))
448+ for name , lt := range s .Labels {
449+ if lt .Kind == ImpossibleLabel {
450+ continue
451+ }
452+ if ! slices .Contains (exluded , name ) {
453+ toAdd = appendToSlice (toAdd , name )
454+ }
455+ }
456+ slices .Sort (toAdd )
457+ s .UsedLabels = appendToSlice (s .UsedLabels , toAdd ... )
458+ }
459+
443460type Visitor func (s Source , j * Join , u * Unless )
444461
445462func innerWalk (fn Visitor , s Source , j * Join , u * Unless ) {
@@ -701,6 +718,7 @@ func parseAggregation(expr string, n *promParser.AggregateExpr) (src []Source) {
701718 nil ,
702719 )
703720 } else {
721+ s .UsedLabels = appendToSlice (s .UsedLabels , n .Grouping ... )
704722 s .checkIncludedLabels (
705723 expr ,
706724 FindFuncPosition (expr , n .PosRange , promParser .ItemTypeStr [promParser .BY ], nil ),
@@ -859,14 +877,28 @@ If you're hoping to get instance specific labels this way and alert when some ta
859877 n .PosRange ,
860878 labelsFromSelectors (guaranteedLabelsMatches , vs )... ,
861879 )
862- case "label_replace" , "label_join" :
880+ case "label_join" :
881+ // One label added to the results.
882+ // label_join(v instant-vector, dst_label string, separator string, src_label_1 string, src_label_2 string, ...)
883+ s .Returns = promParser .ValueTypeVector
884+ s .guaranteeLabel (
885+ fmt .Sprintf ("This label will be added to the result by %s() call." , n .Func .Name ),
886+ n .PosRange ,
887+ n .Args [1 ].(* promParser.StringLiteral ).Val ,
888+ )
889+ for i := 3 ; i < len (n .Args ); i ++ {
890+ s .UsedLabels = appendToSlice (s .UsedLabels , n .Args [i ].(* promParser.StringLiteral ).Val )
891+ }
892+ case "label_replace" :
863893 // One label added to the results.
894+ // label_replace(v instant-vector, dst_label string, replacement string, src_label string, regex string)
864895 s .Returns = promParser .ValueTypeVector
865896 s .guaranteeLabel (
866897 fmt .Sprintf ("This label will be added to the result by %s() call." , n .Func .Name ),
867898 n .PosRange ,
868899 n .Args [1 ].(* promParser.StringLiteral ).Val ,
869900 )
901+ s .UsedLabels = appendToSlice (s .UsedLabels , n .Args [3 ].(* promParser.StringLiteral ).Val )
870902
871903 case "pi" :
872904 s .Returns = promParser .ValueTypeScalar
@@ -1040,6 +1072,7 @@ func parseBinOps(expr string, n *promParser.BinaryExpr) (src []Source) {
10401072 rhs := walkNode (expr , n .RHS )
10411073 for _ , ls := range walkNode (expr , n .LHS ) {
10421074 if n .VectorMatching .On {
1075+ ls .UsedLabels = appendToSlice (ls .UsedLabels , n .VectorMatching .MatchingLabels ... )
10431076 ls .checkIncludedLabels (expr , pos , n .VectorMatching .MatchingLabels )
10441077 funcPos := FindFuncPosition (expr , pos , promParser .ItemTypeStr [promParser .ON ], []posrange.PositionRange {
10451078 n .LHS .PositionRange (), n .RHS .PositionRange (),
@@ -1055,6 +1088,7 @@ func parseBinOps(expr string, n *promParser.BinaryExpr) (src []Source) {
10551088 n .VectorMatching .MatchingLabels ,
10561089 )
10571090 } else {
1091+ ls .useLabelsNotExcluded (n .VectorMatching .MatchingLabels )
10581092 for _ , name := range n .VectorMatching .MatchingLabels {
10591093 ls .excludeLabel (
10601094 fmt .Sprintf (
@@ -1114,6 +1148,7 @@ func parseBinOps(expr string, n *promParser.BinaryExpr) (src []Source) {
11141148 // foo * on(instance) group_left(a,b) bar{x="y"}
11151149 // then only group_left() labels will be included.
11161150 if n .VectorMatching .On {
1151+ rs .UsedLabels = appendToSlice (rs .UsedLabels , n .VectorMatching .MatchingLabels ... )
11171152 rs .checkIncludedLabels (expr , pos , n .VectorMatching .MatchingLabels )
11181153 for _ , name := range n .VectorMatching .MatchingLabels {
11191154 rs .includeLabel (
@@ -1132,6 +1167,8 @@ func parseBinOps(expr string, n *promParser.BinaryExpr) (src []Source) {
11321167 name ,
11331168 )
11341169 }
1170+ } else {
1171+ rs .useLabelsNotExcluded (n .VectorMatching .MatchingLabels )
11351172 }
11361173 for _ , ls := range lhs {
11371174 ls .checkIncludedLabels (
@@ -1172,6 +1209,7 @@ func parseBinOps(expr string, n *promParser.BinaryExpr) (src []Source) {
11721209 n .LHS .PositionRange (), n .RHS .PositionRange (),
11731210 })
11741211 if n .VectorMatching .On {
1212+ ls .UsedLabels = appendToSlice (ls .UsedLabels , n .VectorMatching .MatchingLabels ... )
11751213 ls .checkIncludedLabels (expr , pos , n .VectorMatching .MatchingLabels )
11761214 for _ , name := range n .VectorMatching .MatchingLabels {
11771215 ls .includeLabel (
@@ -1190,6 +1228,8 @@ func parseBinOps(expr string, n *promParser.BinaryExpr) (src []Source) {
11901228 name ,
11911229 )
11921230 }
1231+ } else {
1232+ ls .useLabelsNotExcluded (n .VectorMatching .MatchingLabels )
11931233 }
11941234 for _ , rs := range rhs {
11951235 rs .checkIncludedLabels (
@@ -1225,12 +1265,16 @@ func parseBinOps(expr string, n *promParser.BinaryExpr) (src []Source) {
12251265 // foo{} and ignoring(...) bar{}
12261266 // foo{} and bar{}
12271267 // foo{} unless bar{}
1268+ // foo{} or bar{}
1269+ // foo{} or on(...) bar{}
1270+ // foo{} or ignoring(...) bar{}
12281271 case n .VectorMatching .Card == promParser .CardManyToMany :
12291272 var lhsCanBeEmpty bool // true if any of the LHS query can produce empty results.
12301273 rhs := walkNode (expr , n .RHS )
12311274 for _ , ls := range walkNode (expr , n .LHS ) {
12321275 var rhsConditional bool
12331276 if n .VectorMatching .On {
1277+ ls .UsedLabels = appendToSlice (ls .UsedLabels , n .VectorMatching .MatchingLabels ... )
12341278 ls .checkIncludedLabels (expr , pos , n .VectorMatching .MatchingLabels )
12351279 for _ , name := range n .VectorMatching .MatchingLabels {
12361280 ls .includeLabel (
@@ -1249,6 +1293,10 @@ func parseBinOps(expr string, n *promParser.BinaryExpr) (src []Source) {
12491293 name ,
12501294 )
12511295 }
1296+ } else if n .Op != promParser .LOR {
1297+ // Mark labels not set inside ignoring(...) as used, but:
1298+ // - skip 'foo OR bar' - OR doesn't do label matching, it works on any results
1299+ ls .useLabelsNotExcluded (n .VectorMatching .MatchingLabels )
12521300 }
12531301 if ! ls .ReturnInfo .AlwaysReturns || ls .IsConditional {
12541302 lhsCanBeEmpty = true
0 commit comments