From 5b299ef99eb14b6b99d3fbc2af828c85eb763e4d Mon Sep 17 00:00:00 2001 From: ADITYA TIWARI Date: Sun, 14 Dec 2025 20:18:06 +0000 Subject: [PATCH 1/4] fix/promql/parser: Fix utf-8 label quoting in format_query endpoint Signed-off-by: ADITYA TIWARI --- promql/parser/printer.go | 19 ++++++++++++--- promql/parser/printer_test.go | 46 +++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/promql/parser/printer.go b/promql/parser/printer.go index 961167428b..67b13eaf12 100644 --- a/promql/parser/printer.go +++ b/promql/parser/printer.go @@ -109,7 +109,7 @@ func writeLabels(b *bytes.Buffer, ss []string) { if i > 0 { b.WriteString(", ") } - if !model.LegacyValidation.IsValidMetricName(s) { + if !model.LegacyValidation.IsValidLabelName(s) { b.Write(strconv.AppendQuote(b.AvailableBuffer(), s)) } else { b.WriteString(s) @@ -145,6 +145,19 @@ func (node *BinaryExpr) ShortString() string { return node.Op.String() + node.returnBool() + node.getMatchingStr() } +// joinLabels joins label names, quoting them if they are not valid legacy label names. +func joinLabels(labels []string) string { + quoted := make([]string, 0, len(labels)) + for _, label := range labels { + if model.LegacyValidation.IsValidLabelName(label) { + quoted = append(quoted, label) + } else { + quoted = append(quoted, strconv.Quote(label)) + } + } + return strings.Join(quoted, ", ") +} + func (node *BinaryExpr) getMatchingStr() string { matching := "" vm := node.VectorMatching @@ -154,7 +167,7 @@ func (node *BinaryExpr) getMatchingStr() string { if vm.On { vmTag = "on" } - matching = fmt.Sprintf(" %s (%s)", vmTag, strings.Join(vm.MatchingLabels, ", ")) + matching = fmt.Sprintf(" %s (%s)", vmTag, joinLabels(vm.MatchingLabels)) } if vm.Card == CardManyToOne || vm.Card == CardOneToMany { @@ -162,7 +175,7 @@ func (node *BinaryExpr) getMatchingStr() string { if vm.Card == CardManyToOne { vmCard = "left" } - matching += fmt.Sprintf(" group_%s (%s)", vmCard, strings.Join(vm.Include, ", ")) + matching += fmt.Sprintf(" group_%s (%s)", vmCard, joinLabels(vm.Include)) } } return matching diff --git a/promql/parser/printer_test.go b/promql/parser/printer_test.go index b28da988da..c9a3cb35e8 100644 --- a/promql/parser/printer_test.go +++ b/promql/parser/printer_test.go @@ -269,6 +269,10 @@ func TestExprString(t *testing.T) { { in: `predict_linear(foo[1h], 3000)`, }, + { + in: `sum by("üüü") (foo)`, + out: `sum by ("üüü") (foo)`, + }, } EnableExtendedRangeSelectors = true @@ -394,3 +398,45 @@ func TestVectorSelector_String(t *testing.T) { }) } } + +func TestBinaryExprUTF8Labels(t *testing.T) { + testCases := []struct { + name string + input string + expected string + }{ + { + name: "UTF-8 labels in on clause", + input: `foo / on("äää") bar`, + expected: `foo / on ("äää") bar`, + }, + { + name: "UTF-8 labels in group_left clause", + input: `foo / on("äää") group_left("ööö") bar`, + expected: `foo / on ("äää") group_left ("ööö") bar`, + }, + { + name: "Mixed legacy and UTF-8 labels", + input: `foo / on(legacy, "üüü") bar`, + expected: `foo / on (legacy, "üüü") bar`, + }, + { + name: "Legacy labels only (should not quote)", + input: `foo / on(job, instance) bar`, + expected: `foo / on (job, instance) bar`, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + expr, err := ParseExpr(tc.input) + if err != nil { + t.Fatalf("Failed to parse: %v", err) + } + result := expr.String() + if result != tc.expected { + t.Errorf("Expected: %s\nGot: %s", tc.expected, result) + } + }) + } +} From 2e4f5e8cfc966fe29cac87dac337acd70ac3e15c Mon Sep 17 00:00:00 2001 From: ADITYA TIWARI Date: Mon, 15 Dec 2025 12:38:12 +0000 Subject: [PATCH 2/4] promql/parser: consolidate label quoting logic refactors binary expression formatting to reuse writeLabels() instead of maintaining separate joinLabels() function. adds comprehensive UTF-8 label tests for all expression types Signed-off-by: ADITYA TIWARI --- promql/parser/printer.go | 32 +++++++++++++++++--------------- promql/parser/printer_test.go | 18 ++++++++++++++++++ 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/promql/parser/printer.go b/promql/parser/printer.go index 67b13eaf12..cca04ae222 100644 --- a/promql/parser/printer.go +++ b/promql/parser/printer.go @@ -145,21 +145,9 @@ func (node *BinaryExpr) ShortString() string { return node.Op.String() + node.returnBool() + node.getMatchingStr() } -// joinLabels joins label names, quoting them if they are not valid legacy label names. -func joinLabels(labels []string) string { - quoted := make([]string, 0, len(labels)) - for _, label := range labels { - if model.LegacyValidation.IsValidLabelName(label) { - quoted = append(quoted, label) - } else { - quoted = append(quoted, strconv.Quote(label)) - } - } - return strings.Join(quoted, ", ") -} - func (node *BinaryExpr) getMatchingStr() string { matching := "" + var b bytes.Buffer vm := node.VectorMatching if vm != nil { if len(vm.MatchingLabels) > 0 || vm.On || vm.Card == CardManyToOne || vm.Card == CardOneToMany { @@ -167,7 +155,14 @@ func (node *BinaryExpr) getMatchingStr() string { if vm.On { vmTag = "on" } - matching = fmt.Sprintf(" %s (%s)", vmTag, joinLabels(vm.MatchingLabels)) + // Use writeLabels() instead of joinLabels() + b.Reset() + b.WriteString(" ") + b.WriteString(vmTag) + b.WriteString(" (") + writeLabels(&b, vm.MatchingLabels) + b.WriteString(")") + matching = b.String() } if vm.Card == CardManyToOne || vm.Card == CardOneToMany { @@ -175,7 +170,14 @@ func (node *BinaryExpr) getMatchingStr() string { if vm.Card == CardManyToOne { vmCard = "left" } - matching += fmt.Sprintf(" group_%s (%s)", vmCard, joinLabels(vm.Include)) + // Use writeLabels() instead of joinLabels() + b.Reset() + b.WriteString(" group_") + b.WriteString(vmCard) + b.WriteString(" (") + writeLabels(&b, vm.Include) + b.WriteString(")") + matching += b.String() } } return matching diff --git a/promql/parser/printer_test.go b/promql/parser/printer_test.go index c9a3cb35e8..31e707ee96 100644 --- a/promql/parser/printer_test.go +++ b/promql/parser/printer_test.go @@ -273,6 +273,14 @@ func TestExprString(t *testing.T) { in: `sum by("üüü") (foo)`, out: `sum by ("üüü") (foo)`, }, + { + in: `sum without("äää") (foo)`, + out: `sum without ("äää") (foo)`, + }, + { + in: `count by("ööö", job) (foo)`, + out: `count by ("ööö", job) (foo)`, + }, } EnableExtendedRangeSelectors = true @@ -410,11 +418,21 @@ func TestBinaryExprUTF8Labels(t *testing.T) { input: `foo / on("äää") bar`, expected: `foo / on ("äää") bar`, }, + { + name: "UTF-8 labels in ignoring clause", + input: `foo / ignoring("üüü") bar`, + expected: `foo / ignoring ("üüü") bar`, + }, { name: "UTF-8 labels in group_left clause", input: `foo / on("äää") group_left("ööö") bar`, expected: `foo / on ("äää") group_left ("ööö") bar`, }, + { + name: "UTF-8 labels in group_right clause", + input: `foo / on("äää") group_right("ööö") bar`, + expected: `foo / on ("äää") group_right ("ööö") bar`, + }, { name: "Mixed legacy and UTF-8 labels", input: `foo / on(legacy, "üüü") bar`, From 301b9eff446800abdb0de2ee9776621c9708e75d Mon Sep 17 00:00:00 2001 From: Aditya Tiwari <142050150+ADITYATIWARI342005@users.noreply.github.com> Date: Mon, 15 Dec 2025 18:25:09 +0530 Subject: [PATCH 3/4] Update comments to clarify label formatting method Signed-off-by: Aditya Tiwari <142050150+ADITYATIWARI342005@users.noreply.github.com> --- promql/parser/printer.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/promql/parser/printer.go b/promql/parser/printer.go index cca04ae222..9869cc5a30 100644 --- a/promql/parser/printer.go +++ b/promql/parser/printer.go @@ -155,7 +155,7 @@ func (node *BinaryExpr) getMatchingStr() string { if vm.On { vmTag = "on" } - // Use writeLabels() instead of joinLabels() + // Format labels with proper UTF-8 quoting using writeLabels() b.Reset() b.WriteString(" ") b.WriteString(vmTag) @@ -170,7 +170,7 @@ func (node *BinaryExpr) getMatchingStr() string { if vm.Card == CardManyToOne { vmCard = "left" } - // Use writeLabels() instead of joinLabels() + // Format labels with proper UTF-8 quoting using writeLabels() b.Reset() b.WriteString(" group_") b.WriteString(vmCard) From 3a82dcc6c5dc3f98fd4b646872d2448a6b53695a Mon Sep 17 00:00:00 2001 From: ADITYA TIWARI Date: Mon, 15 Dec 2025 13:39:15 +0000 Subject: [PATCH 4/4] promql/parser: simplify BinaryExpr label formatting Signed-off-by: ADITYA TIWARI --- promql/parser/printer.go | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/promql/parser/printer.go b/promql/parser/printer.go index 9869cc5a30..f30ad5a778 100644 --- a/promql/parser/printer.go +++ b/promql/parser/printer.go @@ -155,11 +155,7 @@ func (node *BinaryExpr) getMatchingStr() string { if vm.On { vmTag = "on" } - // Format labels with proper UTF-8 quoting using writeLabels() - b.Reset() - b.WriteString(" ") - b.WriteString(vmTag) - b.WriteString(" (") + b.WriteString(" " + vmTag + " (") writeLabels(&b, vm.MatchingLabels) b.WriteString(")") matching = b.String() @@ -170,11 +166,8 @@ func (node *BinaryExpr) getMatchingStr() string { if vm.Card == CardManyToOne { vmCard = "left" } - // Format labels with proper UTF-8 quoting using writeLabels() b.Reset() - b.WriteString(" group_") - b.WriteString(vmCard) - b.WriteString(" (") + b.WriteString(" group_" + vmCard + " (") writeLabels(&b, vm.Include) b.WriteString(")") matching += b.String()