From 924ce978b734e8a738ef28e2207997a34710cfa6 Mon Sep 17 00:00:00 2001 From: Brian Brazil Date: Tue, 6 Oct 2020 13:16:26 +0100 Subject: [PATCH] Don't do literal regex matching optimisation when case insensitive. (#8013) Fixes #7994 Signed-off-by: Brian Brazil --- pkg/labels/regexp.go | 6 ++--- pkg/labels/regexp_test.go | 7 ++++++ promql/testdata/selectors.test | 45 ++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/pkg/labels/regexp.go b/pkg/labels/regexp.go index 317e39435..eb2b07995 100644 --- a/pkg/labels/regexp.go +++ b/pkg/labels/regexp.go @@ -86,10 +86,10 @@ func optimizeConcatRegex(r *syntax.Regexp) (prefix, suffix, contains string) { // Given Prometheus regex matchers are always anchored to the begin/end // of the text, if the first/last operations are literals, we can safely // treat them as prefix/suffix. - if sub[0].Op == syntax.OpLiteral { + if sub[0].Op == syntax.OpLiteral && (sub[0].Flags&syntax.FoldCase) == 0 { prefix = string(sub[0].Rune) } - if last := len(sub) - 1; sub[last].Op == syntax.OpLiteral { + if last := len(sub) - 1; sub[last].Op == syntax.OpLiteral && (sub[last].Flags&syntax.FoldCase) == 0 { suffix = string(sub[last].Rune) } @@ -97,7 +97,7 @@ func optimizeConcatRegex(r *syntax.Regexp) (prefix, suffix, contains string) { // 1st one. We do not keep the whole list of literals to simplify the // fast path. for i := 1; i < len(sub)-1; i++ { - if sub[i].Op == syntax.OpLiteral { + if sub[i].Op == syntax.OpLiteral && (sub[i].Flags&syntax.FoldCase) == 0 { contains = string(sub[i].Rune) break } diff --git a/pkg/labels/regexp_test.go b/pkg/labels/regexp_test.go index c12b893a9..5c59d5dfd 100644 --- a/pkg/labels/regexp_test.go +++ b/pkg/labels/regexp_test.go @@ -76,6 +76,13 @@ func TestOptimizeConcatRegex(t *testing.T) { {regex: ".*foo.*bar.*", prefix: "", suffix: "", contains: "foo"}, {regex: ".*(foo|bar).*", prefix: "", suffix: "", contains: ""}, {regex: ".*[abc].*", prefix: "", suffix: "", contains: ""}, + {regex: ".*((?i)abc).*", prefix: "", suffix: "", contains: ""}, + {regex: ".*(?i:abc).*", prefix: "", suffix: "", contains: ""}, + {regex: "(?i:abc).*", prefix: "", suffix: "", contains: ""}, + {regex: ".*(?i:abc)", prefix: "", suffix: "", contains: ""}, + {regex: ".*(?i:abc)def.*", prefix: "", suffix: "", contains: "def"}, + {regex: "(?i).*(?-i:abc)def", prefix: "", suffix: "", contains: "abc"}, + {regex: ".*(?msU:abc).*", prefix: "", suffix: "", contains: "abc"}, } for _, c := range cases { diff --git a/promql/testdata/selectors.test b/promql/testdata/selectors.test index bcaffb967..208784796 100644 --- a/promql/testdata/selectors.test +++ b/promql/testdata/selectors.test @@ -58,6 +58,51 @@ eval instant at 0s http_requests{foo!~"bar", job="api-server", instance="1", x!= http_requests{job="api-server", instance="1", group="production"} 0 http_requests{job="api-server", instance="1", group="canary"} 0 +# https://github.com/prometheus/prometheus/issues/7994 +eval instant at 8000s rate(http_requests{group=~"(?i:PRO).*"}[1m]) + {job="api-server", instance="0", group="production"} 1 + {job="api-server", instance="1", group="production"} 2 + +eval instant at 8000s rate(http_requests{group=~".*?(?i:PRO).*"}[1m]) + {job="api-server", instance="0", group="production"} 1 + {job="api-server", instance="1", group="production"} 2 + +eval instant at 8000s rate(http_requests{group=~".*(?i:DUC).*"}[1m]) + {job="api-server", instance="0", group="production"} 1 + {job="api-server", instance="1", group="production"} 2 + +eval instant at 8000s rate(http_requests{group=~".*(?i:TION)"}[1m]) + {job="api-server", instance="0", group="production"} 1 + {job="api-server", instance="1", group="production"} 2 + +eval instant at 8000s rate(http_requests{group=~".*(?i:TION).*?"}[1m]) + {job="api-server", instance="0", group="production"} 1 + {job="api-server", instance="1", group="production"} 2 + + +eval instant at 8000s rate(http_requests{group=~"((?i)PRO).*"}[1m]) + {job="api-server", instance="0", group="production"} 1 + {job="api-server", instance="1", group="production"} 2 + +eval instant at 8000s rate(http_requests{group=~".*((?i)DUC).*"}[1m]) + {job="api-server", instance="0", group="production"} 1 + {job="api-server", instance="1", group="production"} 2 + +eval instant at 8000s rate(http_requests{group=~".*((?i)TION)"}[1m]) + {job="api-server", instance="0", group="production"} 1 + {job="api-server", instance="1", group="production"} 2 + + +eval instant at 8000s rate(http_requests{group=~"(?i:PRODUCTION)"}[1m]) + {job="api-server", instance="0", group="production"} 1 + {job="api-server", instance="1", group="production"} 2 + +eval instant at 8000s rate(http_requests{group=~".*(?i:C).*"}[1m]) + {job="api-server", instance="0", group="production"} 1 + {job="api-server", instance="1", group="production"} 2 + {job="api-server", instance="0", group="canary"} 3 + {job="api-server", instance="1", group="canary"} 4 + clear load 1m metric1{a="a"} 0+1x100