diff --git a/parser.go b/parser.go index 209ed08..55339f0 100644 --- a/parser.go +++ b/parser.go @@ -1696,22 +1696,7 @@ func (be *BinaryOpExpr) appendStringNoKeepMetricNames(dst []byte) []byte { dst = be.Left.AppendString(dst) } dst = append(dst, ' ') - dst = append(dst, be.Op...) - if be.Bool { - dst = append(dst, "bool"...) - } - if be.GroupModifier.Op != "" { - dst = append(dst, ' ') - dst = be.GroupModifier.AppendString(dst) - } - if be.JoinModifier.Op != "" { - dst = append(dst, ' ') - dst = be.JoinModifier.AppendString(dst) - if prefix := be.JoinModifierPrefix; prefix != nil { - dst = append(dst, " prefix "...) - dst = prefix.AppendString(dst) - } - } + dst = be.appendModifiers(dst) dst = append(dst, ' ') if be.needRightParens() { dst = appendArgInParens(dst, be.Right) @@ -1737,12 +1722,32 @@ func (be *BinaryOpExpr) needRightParens() bool { if isReservedBinaryOpIdent(t.Name) { return true } - return be.KeepMetricNames + return t.KeepMetricNames || be.KeepMetricNames default: return false } } +func (be *BinaryOpExpr) appendModifiers(dst []byte) []byte { + dst = append(dst, be.Op...) + if be.Bool { + dst = append(dst, "bool"...) + } + if be.GroupModifier.Op != "" { + dst = append(dst, ' ') + dst = be.GroupModifier.AppendString(dst) + } + if be.JoinModifier.Op != "" { + dst = append(dst, ' ') + dst = be.JoinModifier.AppendString(dst) + if prefix := be.JoinModifierPrefix; prefix != nil { + dst = append(dst, " prefix "...) + dst = prefix.AppendString(dst) + } + } + return dst +} + func needBinaryOpArgParens(arg Expr) bool { switch t := arg.(type) { case *BinaryOpExpr: @@ -1827,6 +1832,10 @@ type FuncExpr struct { func (fe *FuncExpr) AppendString(dst []byte) []byte { dst = appendEscapedIdent(dst, fe.Name) dst = appendStringArgListExpr(dst, fe.Args) + return fe.appendModifiers(dst) +} + +func (fe *FuncExpr) appendModifiers(dst []byte) []byte { if fe.KeepMetricNames { dst = append(dst, " keep_metric_names"...) } @@ -1855,6 +1864,10 @@ type AggrFuncExpr struct { func (ae *AggrFuncExpr) AppendString(dst []byte) []byte { dst = appendEscapedIdent(dst, ae.Name) dst = appendStringArgListExpr(dst, ae.Args) + return ae.appendModifiers(dst) +} + +func (ae *AggrFuncExpr) appendModifiers(dst []byte) []byte { if ae.Modifier.Op != "" { dst = append(dst, ' ') dst = ae.Modifier.AppendString(dst) @@ -1954,18 +1967,7 @@ func (re *RollupExpr) ForSubquery() bool { // AppendString appends string representation of re to dst and returns the result. func (re *RollupExpr) AppendString(dst []byte) []byte { - needParens := func() bool { - if _, ok := re.Expr.(*RollupExpr); ok { - return true - } - if _, ok := re.Expr.(*BinaryOpExpr); ok { - return true - } - if ae, ok := re.Expr.(*AggrFuncExpr); ok && ae.Modifier.Op != "" { - return true - } - return false - }() + needParens := re.needParens() if needParens { dst = append(dst, '(') } @@ -1973,6 +1975,10 @@ func (re *RollupExpr) AppendString(dst []byte) []byte { if needParens { dst = append(dst, ')') } + return re.appendModifiers(dst) +} + +func (re *RollupExpr) appendModifiers(dst []byte) []byte { if re.Window != nil || re.InheritStep || re.Step != nil { dst = append(dst, '[') dst = re.Window.AppendString(dst) @@ -2002,6 +2008,17 @@ func (re *RollupExpr) AppendString(dst []byte) []byte { return dst } +func (re *RollupExpr) needParens() bool { + switch t := re.Expr.(type) { + case *RollupExpr, *BinaryOpExpr: + return true + case *AggrFuncExpr: + return t.Modifier.Op != "" + default: + return false + } +} + // LabelFilter represents MetricsQL label filter like `foo="bar"`. type LabelFilter struct { // Label contains label name for the filter. diff --git a/parser_test.go b/parser_test.go index 9d0e01b..4194cea 100644 --- a/parser_test.go +++ b/parser_test.go @@ -317,6 +317,7 @@ func TestParseSuccess(t *testing.T) { another(`(a + on(x) group_left(y) b keep_metric_names) offset 5m @ 1235`, `((a + on(x) group_left(y) b) keep_metric_names) offset 5m @ 1235`) another(`(a + on (x) group_left (y) b keep_metric_names) @ 1235 offset 5m`, `((a + on(x) group_left(y) b) keep_metric_names) offset 5m @ 1235`) another(`rate(x) keep_metric_names + (abs(y) keep_metric_names) keep_metric_names`, `(rate(x) keep_metric_names + (abs(y) keep_metric_names)) keep_metric_names`) + same(`a + (rate(b) keep_metric_names)`) // binaryOp with reserved names same(`a + (on)`) diff --git a/prettier.go b/prettier.go deleted file mode 100644 index e425610..0000000 --- a/prettier.go +++ /dev/null @@ -1,167 +0,0 @@ -package metricsql - -import ( - "fmt" - "strings" -) - -const indentString = " " - -func pad(indent int, s string) string { - p := strings.Repeat(indentString, indent) - return p + s -} - -func shouldWrap(expr Expr) bool { - switch expr.(type) { - case *MetricExpr, *NumberExpr, *StringExpr, *FuncExpr: - return false - default: - return true - } -} - -func wrapWithBraces(expr Expr, b *strings.Builder, indent, maxLineLength int) { - if shouldWrap(expr) { - b.WriteString(pad(indent, "(\n")) - b.WriteString(prettify(expr, indent+1, maxLineLength)) - b.WriteString("\n" + pad(indent, ")")) - } else { - b.WriteString(prettify(expr, indent, maxLineLength)) - } -} - -func prettify(expr Expr, indent, maxLineLength int) string { - var str strings.Builder - var b []byte - b = expr.AppendString(b) - if len(b) <= maxLineLength { - str.WriteString(pad(indent, "")) - str.Write(b) - return str.String() - } - switch e := expr.(type) { - case *BinaryOpExpr: - var binaryOpStr strings.Builder - - wrapWithBraces(e.Left, &binaryOpStr, indent+1, maxLineLength) - buildBinaryOp(&binaryOpStr, e, indent) - wrapWithBraces(e.Right, &binaryOpStr, indent+1, maxLineLength) - - str.WriteString(binaryOpStr.String()) - case *RollupExpr: - var rollupStr strings.Builder - - wrapWithBraces(e.Expr, &rollupStr, indent, maxLineLength) - buildRollupFunc(&rollupStr, e) - - str.WriteString(rollupStr.String()) - case *AggrFuncExpr: - var aggrFuncStr strings.Builder - - buildAggrFuncString(&aggrFuncStr, e, indent, maxLineLength) - - str.WriteString(aggrFuncStr.String()) - case *MetricExpr, *NumberExpr, *StringExpr: - var b []byte - str.WriteString(pad(indent, "")) - str.Write(e.AppendString(b)) - case *FuncExpr: - if hasNoArgs(e) { - str.WriteString(pad(indent, e.Name)) - } else { - buildFuncExpr(&str, e, indent, maxLineLength) - } - default: - e.AppendString(b) - str.WriteString(pad(indent, "")) - str.Write(b) - } - return str.String() -} - -func buildRollupFunc(rollupStr *strings.Builder, e *RollupExpr) { - if e.Window != nil || e.InheritStep || e.Step != nil { - rollupStr.WriteString("[") - if e.Window != nil { - var b []byte - b = e.Window.AppendString(b) - rollupStr.Write(b) - } - if e.Step != nil { - rollupStr.WriteString(":") - rollupStr.WriteString(e.Step.s) - } else if e.InheritStep { - rollupStr.WriteString(":") - } - rollupStr.WriteString("]") - } - if e.Offset != nil { - rollupStr.WriteString(fmt.Sprintf(" offset %s", e.Offset.s)) - } - if e.At != nil { - var b []byte - rollupStr.WriteString(fmt.Sprintf(" @ (%s)", e.At.AppendString(b))) - } -} - -func buildBinaryOp(binaryOpStr *strings.Builder, e *BinaryOpExpr, indent int) { - binaryOpStr.WriteString(fmt.Sprintf("\n%s%s", pad(indent, ""), e.Op)) - if e.Bool { - binaryOpStr.WriteString(" bool") - } - if e.GroupModifier.Op != "" { - binaryOpStr.WriteString(" ") - binaryOpStr.Write(e.GroupModifier.AppendString(nil)) - } - if e.JoinModifier.Op != "" { - binaryOpStr.WriteString(" ") - binaryOpStr.Write(e.JoinModifier.AppendString(nil)) - } - binaryOpStr.WriteString("\n") -} - -func buildAggrFuncString(aggrFuncStr *strings.Builder, e *AggrFuncExpr, indent, maxLineLength int) { - aggrFuncStr.WriteString(pad(indent, e.Name)) - if e.Modifier.Op != "" { - aggrFuncStr.WriteString(" ") - aggrFuncStr.Write(e.Modifier.AppendString(nil)) - } - aggrFuncStr.WriteString(" (\n") - for i, a := range e.Args { - aggrFuncStr.WriteString(prettify(a, indent+1, maxLineLength)) - if i < len(e.Args)-1 { - aggrFuncStr.WriteString(",") - } - aggrFuncStr.WriteString("\n") - } - aggrFuncStr.WriteString(pad(indent, ")")) -} - -func hasNoArgs(e *FuncExpr) bool { - return len(e.Args) == 0 -} - -func buildFuncExpr(funcStr *strings.Builder, e *FuncExpr, indent, maxLineLength int) { - var funcExprStr strings.Builder - funcExprStr.WriteString(pad(indent, e.Name) + " (\n") - for i, a := range e.Args { - funcExprStr.WriteString(prettify(a, indent+1, maxLineLength)) - if i < len(e.Args)-1 { - funcExprStr.WriteString(",") - } - funcExprStr.WriteString("\n") - } - funcExprStr.WriteString(pad(indent, ")")) - - funcStr.WriteString(funcExprStr.String()) -} - -func Prettify(s string, maxLineLength int) (string, error) { - expr, err := Parse(s) - if err != nil { - return "", err - } - - return prettify(expr, 0, maxLineLength), nil -} diff --git a/prettier_test.go b/prettier_test.go deleted file mode 100644 index 1f51d53..0000000 --- a/prettier_test.go +++ /dev/null @@ -1,1451 +0,0 @@ -package metricsql - -import "testing" - -func TestPrettier(t *testing.T) { - another := func(s, expected string) { - t.Helper() - - const maxLineLength = 130 - got, err := Prettify(s, maxLineLength) - if err != nil { - t.Fatalf("unexpected error when parsing %q: %s", s, err) - } - - if expected != got { - t.Fatalf("string not prettified;\ngot:%s\nwant\n%q", got, expected) - } - } - same := func(s string) { - t.Helper() - another(s, s) - } - - same(`{}`) - same(`{}[5m]`) - same(`{}[5m:]`) - same(`{}[5M:]`) - same(`{}[:]`) - another(`{}[: ]`, `{}[:]`) - same(`{}[:3s]`) - another(`{}[: 3s ]`, `{}[:3s]`) - same(`{}[5m:3s]`) - another(`{}[ 5m : 3s ]`, `{}[5m:3s]`) - same(`{} offset 5m`) - same(`{} offset -5m`) - same(`{} offset 5M`) - same(`{}[5m] offset 10y`) - same(`{}[5.3m:3.4s] offset 10y`) - same(`{}[:3.4s] offset 10y`) - same(`{}[:3.4s] offset -10y`) - same(`{Foo="bAR"}`) - same(`{foo="bar"}`) - same(`{foo="bar"}[5m]`) - same(`{foo="bar"}[5m:]`) - same(`{foo="bar"}[5m:3s]`) - same(`{foo="bar"} offset 13.4ms`) - same(`{foo="bar"}[5w4h-3.4m13.4ms]`) - same(`{foo="bar"} offset 10y`) - same(`{foo="bar"} offset -10y`) - same(`{foo="bar"}[5m] offset 10y`) - same(`{foo="bar"}[5m:3s] offset 10y`) - another(`{foo="bar"}[5m] oFFSEt 10y`, `{foo="bar"}[5m] offset 10y`) - same("METRIC") - same("metric") - same("m_e:tri44:_c123") - another("-metric", "0 - metric") - same(`metric offset 10h`) - same("metric[5m]") - same("metric[5m:3s]") - same("metric[5m] offset 10h") - same("metric[5m:3s] offset 10h") - same("metric[5i:3i] offset 10i") - same(`metric{foo="bar"}`) - same(`metric{foo="bar"} offset 10h`) - same(`metric{foo!="bar"}[2d]`) - same(`metric{foo="bar"}[2d] offset 10h`) - same(`metric{foo="bar", b="sdfsdf"}[2d:3h] offset 10h`) - same(`metric{foo="bar", b="sdfsdf"}[2d:3h] offset 10`) - same(`metric{foo="bar", b="sdfsdf"}[2d:3] offset 10h`) - same(`metric{foo="bar", b="sdfsdf"}[2:3h] offset 10h`) - same(`metric{foo="bar", b="sdfsdf"}[2.34:5.6] offset 3600.5`) - same(`metric{foo="bar", b="sdfsdf"}[234:56] offset -3600`) - another(` metric { foo = "bar" } [ 2d ] offset 10h `, `metric{foo="bar"}[2d] offset 10h`) - // @ modifier - // See https://prometheus.io/docs/prometheus/latest/querying/basics/#modifier - same(`foo @ 123.45`) - same(`foo\@ @ 123.45`) - same(`{foo=~"bar"} @ end()`) - same(`foo{bar="baz"} @ start()`) - same(`foo{bar="baz"}[5m] @ 12345`) - same(`foo{bar="baz"}[5m:4s] offset 5m @ (end() - 3.5m)`) - another(`foo{bar="baz"}[5m:4s] @ (end() - 3.5m) offset 2.4h`, `foo{bar="baz"}[5m:4s] offset 2.4h @ (end() - 3.5m)`) - another(`foo @ start() + (bar offset 3m @ end()) / baz OFFSET -5m`, `foo @ start() + (bar offset 3m @ end() / baz offset -5m)`) - same(`sum(foo) @ start() + rate(bar @ (end() - 5m))`) - another(`time() @ (start())`, `time() @ start()`) - another(`time() @ (start()+(1+1))`, `time() @ (start() + 2)`) - same(`time() @ (end() - 10m)`) - // metric name matching keywords - same("rate") - same("RATE") - same("by") - same("BY") - same("bool") - same("BOOL") - same("unless") - same("UNLESS") - same("Ignoring") - same("with") - same("WITH") - same("With") - same("offset") - same("keep_metric_names") - same("alias") - same(`alias{foo="bar"}`) - same(`aLIas{alias="aa"}`) - another(`al\ias`, `alias`) - // identifiers with with escape chars - same(`foo\ bar`) - same(`foo\-bar\{{baz\+bar="aa"}`) - another(`\x2E\x2ef\oo{b\xEF\ar="aa"}`, `\..foo{bïar="aa"}`) - same(`温度{房间="水电费"}[5m] offset 10m`) - another(`\温\度{\房\间="水电费"}[5m] offset 10m`, `温度{房间="水电费"}[5m] offset 10m`) - same(`sum(fo\|o) by (b\|a, x)`) - another(`sum(x) by (b\x7Ca)`, `sum(x) by (b\|a)`) - // Duplicate filters - same(`foo{__name__="bar"}`) - same(`foo{a="b", a="c", __name__="aaa", b="d"}`) - // Metric filters ending with comma - another(`m{foo="bar",}`, `m{foo="bar"}`) - // String concat in tag value - another(`m{foo="bar" + "baz"}`, `m{foo="barbaz"}`) - - // Valid regexp - same(`foo{bar=~"x"}`) - same(`foo{bar=~"^x"}`) - same(`foo{bar=~"^x$"}`) - same(`foo{bar=~"^(a[bc]|d)$"}`) - same(`foo{bar!~"x"}`) - same(`foo{bar!~"^x"}`) - same(`foo{bar!~"^x$"}`) - same(`foo{bar!~"^(a[bc]|d)$"}`) - - // stringExpr - same(`""`) - same(`"\n\t\r 12:{}[]()44"`) - another(`''`, `""`) - another("``", `""`) - another(" `foo\"b'ar` ", "\"foo\\\"b'ar\"") - another(` 'foo\'bar"BAZ' `, `"foo'bar\"BAZ"`) - // string concat - another(`"foo"+'bar'`, `"foobar"`) - - // numberExpr - same(`1`) - same(`123.`) - another(`-123.`, `-123`) - same(`foo - 123.`) - same(`12.e+4`) - same(`12Ki`) - same(`1.23Gb`) - same(`foo - 23M`) - another(`-1.23Gb`, `-1.23e+09`) - same(`1.23`) - same(`0.23`) - same(`1.2e+45`) - same(`1.2e-45`) - same(`-1`) - same(`-1.23`) - same(`-0.23`) - same(`-1.2e+45`) - same(`-1.2e-45`) - same(`-1.2e-45`) - same(`12.5E34`) - another(`-.2`, `-0.2`) - another(`-.2E-2`, `-0.002`) - same(`NaN`) - same(`nan`) - same(`NAN`) - same(`nAN`) - same(`Inf`) - same(`INF`) - same(`inf`) - another(`+Inf`, `Inf`) - same(`-Inf`) - another(`-inF`, `-Inf`) - same(`0x12`) - same(`0x3b`) - another(`-0x3b`, `-59`) - another(`+0X3B`, `0X3B`) - same(`0b1011`) - same(`073`) - another(`-0o12`, `-10`) - - // durationExpr - same(`1h`) - another(`-1h`, `0 - 1h`) - same(`0.34h4m5s`) - same(`0.34H4m5S`) - another(`-0.34h4m5s`, `0 - 0.34h4m5s`) - same(`sum_over_tme(m[1h]) / 1h`) - same(`sum_over_time(m[3600]) / 3600`) - - // binaryOpExpr - another(`nan == nan`, `NaN`) - another(`nan ==bool nan`, `1`) - another(`nan !=bool nan`, `0`) - another(`nan !=bool 2`, `1`) - another(`2 !=bool nan`, `1`) - another(`nan >bool nan`, `0`) - another(`nan =bool 2`, `1`) - another(`-1 >bool -inf`, `1`) - another(`-1 2`, `NaN`) - another(`1 > bool 2`, `0`) - another(`3 >= 2`, `3`) - another(`3 <= bool 2`, `0`) - another(`1 + -2 - 3`, `-4`) - another(`1 / 0 + 2`, `+Inf`) - another(`2 + -1 / 0`, `-Inf`) - another(`(-1) ^ 0.5`, `NaN`) - another(`-1 ^ 0.5`, `-1`) - another(`512.5 - (1 + 3) * (2 ^ 2) ^ 3`, `256.5`) - another(`1 == bool 1 != bool 24 < bool 4 > bool -1`, `1`) - another(`1 == bOOl 1 != BOOL 24 < Bool 4 > booL -1`, `1`) - another(`m1+on(foo)group_left m2`, `m1 + on (foo) group_left () m2`) - another(`M1+ON(FOO)GROUP_left M2`, `M1 + on (FOO) group_left () M2`) - same(`m1 + on (foo) group_right () m2`) - same(`m1 + on (foo, bar) group_right (x, y) m2`) - another(`m1 + on (foo, bar,) group_right (x, y,) m2`, `m1 + on (foo, bar) group_right (x, y) m2`) - same(`m1 == bool on (foo, bar) group_right (x, y) m2`) - another(`5 - 1 + 3 * 2 ^ 2 ^ 3 - 2 OR Metric {Bar= "Baz", aaa!="bb",cc=~"dd" ,zz !~"ff" } `, - `770 or Metric{Bar="Baz", aaa!="bb", cc=~"dd", zz!~"ff"}`) - same(`"foo" + bar()`) - same(`"foo" + bar{x="y"}`) - same(`("foo"[3s] + bar{x="y"})[5m:3s] offset 10s`) - same(`("foo"[3s] + bar{x="y"})[5i:3i] offset 10i`) - same(`bar + "foo" offset 3s`) - same(`bar + "foo" offset 3i`) - another(`1+2 if 2>3`, `NaN`) - another(`1+4 if 2<3`, `5`) - another(`2+6 default 3 if 2>3`, `8`) - another(`2+6 if 2>3 default NaN`, `NaN`) - another(`42 if 3>2 if 2+2<5`, `42`) - another(`42 if 3>2 if 2+2>=5`, `NaN`) - another(`1+2 ifnot 2>3`, `3`) - another(`1+4 ifnot 2<3`, `NaN`) - another(`2+6 default 3 ifnot 2>3`, `8`) - another(`2+6 ifnot 2>3 default NaN`, `8`) - another(`42 if 3>2 ifnot 2+2<5`, `NaN`) - another(`42 if 3>2 ifnot 2+2>=5`, `42`) - another(`"foo" + "bar"`, `"foobar"`) - another(`"foo"=="bar"`, `NaN`) - another(`"foo"=="foo"`, `1`) - another(`"foo"!="bar"`, `1`) - another(`"foo"+"bar"+"baz"`, `"foobarbaz"`) - another(`"a">"b"`, `NaN`) - another(`"a">bool"b"`, `0`) - another(`"a"<"b"`, `1`) - another(`"a">="b"`, `NaN`) - another(`"a">=bool"b"`, `0`) - another(`"a"<="b"`, `1`) - same(`"a" - "b"`) - - // parensExpr - another(`(-foo + ((bar) / (baz))) + ((23))`, `((0 - foo) + (bar / baz)) + 23`) - another(`(FOO + ((Bar) / (baZ))) + ((23))`, `(FOO + (Bar / baZ)) + 23`) - same(`(foo, bar)`) - another(`((foo, bar),(baz))`, `((foo, bar), baz)`) - same(`(foo, (bar, baz), ((x, y), (z, y), xx))`) - another(`1+(foo, bar,)`, `1 + (foo, bar)`) - another(`((foo(bar,baz)), (1+(2)+(3,4)+()))`, `(foo(bar, baz), (3 + (3, 4)) + ())`) - same(`()`) - - // funcExpr - same(`f()`) - another(`f(x,)`, `f(x)`) - another(`-f()-Ff()`, `(0 - f()) - Ff()`) - same(`F()`) - another(`+F()`, `F()`) - another(`++F()`, `F()`) - another(`--F()`, `0 - (0 - F())`) - same(`f(http_server_request)`) - same(`f(http_server_request)[4s:5m] offset 10m`) - same(`f(http_server_request)[4i:5i] offset 10i`) - same(`F(HttpServerRequest)`) - same(`f(job, foo)`) - same(`F(Job, Foo)`) - another(` FOO (bar) + f ( m ( ),ff(1 + ( 2.5)) ,M[5m ] , "ff" )`, `FOO(bar) + f(m(), ff(3.5), M[5m], "ff")`) - same(`rate(foo[5m]) keep_metric_names`) - another(`log2(foo) KEEP_metric_names + 1 / increase(bar[5m]) keep_metric_names offset 1h @ 435`, - `log2(foo) keep_metric_names + (1 / increase(bar[5m]) keep_metric_names offset 1h @ 435)`) - // funcName matching keywords - same(`by(2)`) - same(`BY(2)`) - same(`or(2)`) - same(`OR(2)`) - same(`bool(2)`) - same(`BOOL(2)`) - same(`rate(rate(m))`) - same(`rate(rate(m[5m]))`) - same(`rate(rate(m[5m])[1h:])`) - same(`rate(rate(m[5m])[1h:3s])`) - // funcName with escape chars - same(`foo\(ba\-r()`) - - // aggrFuncExpr - same(`sum(http_server_request) by ()`) - same(`sum(http_server_request) by (job)`) - same(`sum(http_server_request) without (job, foo)`) - another(`sum(x,y,) without (a,b,)`, `sum(x, y) without (a, b)`) - another(`sum by () (xx)`, `sum(xx) by ()`) - another(`sum by (s) (xx)[5s]`, `(sum(xx) by (s))[5s]`) - another(`SUM BY (ZZ, aa) (XX)`, `sum(XX) by (ZZ, aa)`) - another(`sum without (a, b) (xx,2+2)`, `sum(xx, 4) without (a, b)`) - another(`Sum WIthout (a, B) (XX,2+2)`, `sum(XX, 4) without (a, B)`) - same(`sum(a) or sum(b)`) - same(`sum(a) by () or sum(b) without (x, y)`) - same(`sum(a) + sum(b)`) - same(`sum(x) * (1 + sum(a))`) - same(`avg(x) limit 10`) - same(`avg(x) without (z, b) limit 1`) - another(`avg by(x) (z) limit 20`, `avg(z) by (x) limit 20`) - - // All the above - another(`Sum(Ff(M) * M{X=""}[5m] Offset 7m - 123, 35) BY (X, y) * F2("Test")`, - `sum((Ff(M) * M{X=""}[5m] offset 7m) - 123, 35) by (X, y) * F2("Test")`) - another(`# comment - Sum(Ff(M) * M{X=""}[5m] Offset 7m - 123, 35) BY (X, y) # yet another comment - * F2("Test")`, - `sum((Ff(M) * M{X=""}[5m] offset 7m) - 123, 35) by (X, y) * F2("Test")`) - - // withExpr - another(`with () x`, `x`) - another(`with (x=1,) x`, `1`) - another(`with (x = m offset 5h) x + x`, `m offset 5h + m offset 5h`) - another(`with (x = m offset 5i) x + x`, `m offset 5i + m offset 5i`) - another(`with (foo = bar{x="x"}) 1`, `1`) - another(`with (foo = bar{x="x"}) "x"`, `"x"`) - another(`with (f="x") f`, `"x"`) - another(`with (foo = bar{x="x"}) x{x="y"}`, `x{x="y"}`) - another(`with (foo = bar{x="x"}) 1+1`, `2`) - another(`with (foo = bar{x="x"}) f()`, `f()`) - another(`with (foo = bar{x="x"}) sum(x)`, `sum(x)`) - another(`with (foo = bar{x="x"}) baz{foo="bar"}`, `baz{foo="bar"}`) - another(`with (foo = bar) baz`, `baz`) - another(`with (foo = bar) foo + foo{a="b"}`, `bar + bar{a="b"}`) - another(`with (foo = bar, bar=baz + f()) test`, `test`) - another(`with (ct={job="test"}) a{ct} + ct() + f({ct="x"})`, `(a{job="test"} + {job="test"}) + f({ct="x"})`) - another(`with (ct={job="test", i="bar"}) ct + {ct, x="d"} + foo{ct, ct} + ctx(1)`, - `(({job="test", i="bar"} + {job="test", i="bar", x="d"}) + foo{job="test", i="bar"}) + ctx(1)`) - another(`with (foo = bar) {__name__=~"foo"}`, `{__name__=~"foo"}`) - another(`with (foo = bar) foo{__name__="foo"}`, `bar`) - another(`with (foo = bar) {__name__="foo", x="y"}`, `bar{x="y"}`) - another(`with (foo(bar) = {__name__!="bar"}) foo(x)`, `{__name__!="bar"}`) - another(`with (foo(bar) = bar{__name__="bar"}) foo(x)`, `x`) - another(`with (foo\-bar(baz) = baz + baz) foo\-bar((x,y))`, `(x, y) + (x, y)`) - another(`with (foo\-bar(baz) = baz + baz) foo\-bar(x*y)`, `(x * y) + (x * y)`) - another(`with (foo\-bar(baz) = baz + baz) foo\-bar(x\*y)`, `x\*y + x\*y`) - another(`with (foo\-bar(b\ az) = b\ az + b\ az) foo\-bar(x\*y)`, `x\*y + x\*y`) - // override ttf to something new. - another(`with (ttf = a) ttf + b`, `a + b`) - // override ttf to ru - another(`with (ttf = ru(m, n)) ttf`, `(clamp_min(n - clamp_min(m, 0), 0) / clamp_min(n, 0)) * 100`) - - // Verify withExpr recursion and forward reference - another(`with (x = x+y, y = x+x) y ^ 2`, `((x + y) + (x + y)) ^ 2`) - another(`with (f1(x)=f2(x), f2(x)=f1(x)^2) f1(foobar)`, `f2(foobar)`) - another(`with (f1(x)=f2(x), f2(x)=f1(x)^2) f2(foobar)`, `f2(foobar) ^ 2`) - - // Verify withExpr funcs - another(`with (x() = y+1) x`, `y + 1`) - another(`with (x(foo) = foo+1) x(a)`, `a + 1`) - another(`with (x(a, b) = a + b) x(foo, bar)`, `foo + bar`) - another(`with (x(a, b) = a + b) x(foo, x(1, 2))`, `foo + 3`) - another(`with (x(a) = sum(a) by (b)) x(xx) / x(y)`, `sum(xx) by (b) / sum(y) by (b)`) - another(`with (f(a,f,x)=ff(x,f,a)) f(f(x,y,z),1,2)`, `ff(2, 1, ff(z, y, x))`) - another(`with (f(x)=1+f(x)) f(foo{bar="baz"})`, `1 + f(foo{bar="baz"})`) - another(`with (a=foo, y=bar, f(a)= a+a+y) f(x)`, `(x + x) + bar`) - another(`with (f(a, b) = m{a, b}) f({a="x", b="y"}, {c="d"})`, `m{a="x", b="y", c="d"}`) - another(`with (xx={a="x"}, f(a, b) = m{a, b}) f({xx, b="y"}, {c="d"})`, `m{a="x", b="y", c="d"}`) - another(`with (x() = {b="c"}) foo{x}`, `foo{b="c"}`) - another(`with (f(x)=x{foo="bar"} offset 5m) f(m offset 10m)`, `(m{foo="bar"} offset 10m) offset 5m`) - another(`with (f(x)=x{foo="bar",bas="a"}[5m]) f(m[10m] offset 3s)`, `(m{foo="bar", bas="a"}[10m] offset 3s)[5m]`) - another(`with (f(x)=x{foo="bar"}[5m] offset 10m) f(m{x="y"})`, `m{x="y", foo="bar"}[5m] offset 10m`) - another(`with (f(x)=x{foo="bar"}[5m] offset 10m) f({x="y", foo="bar", foo="bar"})`, `{x="y", foo="bar"}[5m] offset 10m`) - another(`with (f(m, x)=m{x}[5m] offset 10m) f(foo, {})`, `foo[5m] offset 10m`) - another(`with (f(m, x)=m{x, bar="baz"}[5m] offset 10m) f(foo, {})`, `foo{bar="baz"}[5m] offset 10m`) - another(`with (f(x)=x[5m] offset 3s) f(foo[3m]+bar)`, `(foo[3m] + bar)[5m] offset 3s`) - another(`with (f(x)=x[5m:3s] oFFsEt 1.5m) f(sum(s) by (a,b))`, `(sum(s) by (a, b))[5m:3s] offset 1.5m`) - another(`with (x="a", y=x) y+"bc"`, `"abc"`) - another(`with (x="a", y="b"+x) "we"+y+"z"+f()`, `"webaz" + f()`) - another(`with (f(x) = m{foo=x+"y", bar="y"+x, baz=x} + x) f("qwe")`, `m{foo="qwey", bar="yqwe", baz="qwe"} + "qwe"`) - another(`with (f(a)=a) f`, `f`) - another(`with (f\q(a)=a) f\q`, `fq`) - - // Verify withExpr for aggr func modifiers - another(`with (f(x) = x, y = sum(m) by (f)) y`, `sum(m) by (f)`) - another(`with (f(x) = x, y = sum(m) by (f) limit 20) y`, `sum(m) by (f) limit 20`) - another(`with (f(x) = sum(m) by (x)) f(foo)`, `sum(m) by (foo)`) - another(`with (f(x) = sum(m) by (x) limit 42) f(foo)`, `sum(m) by (foo) limit 42`) - another(`with (f(x) = sum(m) by (x)) f((foo, bar, foo))`, `sum(m) by (foo, bar)`) - another(`with (f(x) = sum(m) without (x,y)) f((a, b))`, `sum(m) without (a, b, y)`) - another(`with (f(x) = sum(m) without (y,x)) f((a, y))`, `sum(m) without (y, a)`) - another(`with (f(x,y) = a + on (x,y) group_left (y,bar) b) f(foo,())`, `a + on (foo) group_left (bar) b`) - another(`with (f(x,y) = a + on (x,y) group_left (y,bar) b) f((foo),())`, `a + on (foo) group_left (bar) b`) - another(`with (f(x,y) = a + on (x,y) group_left (y,bar) b) f((foo,xx),())`, `a + on (foo, xx) group_left (bar) b`) - - // Verify nested with exprs - another(`with (f(x) = (with(x=y) x) + x) f(z)`, `y + z`) - another(`with (x=foo) f(a, with (y=x) y)`, `f(a, foo)`) - another(`with (x=foo) a * x + (with (y=x) y) / y`, `(a * foo) + (foo / y)`) - another(`with (x = with (y = foo) y + x) x/x`, `(foo + x) / (foo + x)`) - another(`with ( - x = {foo="bar"}, - q = m{x, y="1"}, - f(x) = - with ( - z(y) = x + y * q - ) - z(foo) / f(x) - ) - f(a)`, `(a + (foo * m{foo="bar", y="1"})) / f(a)`) - - // complex withExpr - another(`WITH ( - treshold = (0.9), - commonFilters = {job="cacher", instance=~"1.2.3.4"}, - hits = rate(cache{type="hit", commonFilters}[5m]), - miss = rate(cache{type="miss", commonFilters}[5m]), - sumByInstance(arg) = sum(arg) by (instance), - hitRatio = sumByInstance(hits) / sumByInstance(hits + miss) - ) - hitRatio < treshold`, - ` ( - ( - sum(rate(cache{type="hit", job="cacher", instance=~"1.2.3.4"}[5m])) by (instance) - ) - / - ( - sum by (instance) ( - rate(cache{type="hit", job="cacher", instance=~"1.2.3.4"}[5m]) + rate(cache{type="miss", job="cacher", instance=~"1.2.3.4"}[5m]) - ) - ) - ) -< - 0.9`) - another(`WITH ( - x2(x) = x^2, - f(x, y) = x2(x) + x*y + x2(y) - ) - f(a, 3) - `, `((a ^ 2) + (a * 3)) + 9`) - another(`WITH ( - x2(x) = x^2, - f(x, y) = x2(x) + x*y + x2(y) - ) - f(2, 3) - `, `19`) - another(`WITH ( - commonFilters = {instance="foo"}, - timeToFuckup(currv, maxv) = (maxv - currv) / rate(currv) - ) - timeToFuckup(diskUsage{commonFilters}, maxDiskSize{commonFilters})`, - `(maxDiskSize{instance="foo"} - diskUsage{instance="foo"}) / rate(diskUsage{instance="foo"})`) - another(`WITH ( - commonFilters = {job="foo", instance="bar"}, - sumRate(m, cf) = sum(rate(m{cf})) by (job, instance), - hitRate(hits, misses) = sumRate(hits, commonFilters) / (sumRate(hits, commonFilters) + sumRate(misses, commonFilters)) - ) - hitRate(cacheHits, cacheMisses)`, - ` ( - sum(rate(cacheHits{job="foo", instance="bar"})) by (job, instance) - ) -/ - ( - ( - sum(rate(cacheHits{job="foo", instance="bar"})) by (job, instance) - ) - + - ( - sum(rate(cacheMisses{job="foo", instance="bar"})) by (job, instance) - ) - )`) - another(`with(y=123,z=5) union(with(y=3,f(x)=x*y) f(2) + f(3), with(x=5,y=2) x*y*z)`, `union(15, 50)`) - - another(`with(sum=123,now=5) union(with(sum=3,f(x)=x*sum) f(2) + f(3), with(x=5,sum=2) x*sum*now)`, `union(15, 50)`) - another(`WITH(now = sum(rate(my_metric_total)), before = sum(rate(my_metric_total) offset 1h)) now/before*100`, `(sum(rate(my_metric_total)) / sum(rate(my_metric_total) offset 1h)) * 100`) - another(`with (sum = x) sum`, `x`) - another(`with (clamp_min=x) clamp_min`, `x`) - another(`with (now=now(), sum=sum()) now`, `now()`) - another(`with (now=now(), sum=sum()) now()`, `now()`) - another(`with (now(a)=now()+a) now(1)`, `now() + 1`) - another(`with (rate(a,b)=a+b) rate(1,2)`, `3`) - another(`with (now=now(), sum=sum()) x`, `x`) - another(`with (rate(a) = b) c`, `c`) - another(`rate(x) + with (rate(a,b)=a*b) rate(2,b)`, `rate(x) + (2 * b)`) - another(`with (sum(a,b)=a+b) sum(c,d)`, `c + d`) -} - -func TestPrettierShot(t *testing.T) { - another := func(s, expected string) { - t.Helper() - - const maxLineLength = 80 - got, err := Prettify(s, maxLineLength) - if err != nil { - t.Fatalf("unexpected error when parsing %q: %s", s, err) - } - - if expected != got { - t.Fatalf("string not prettified;\ngot:%s\nwant\n%q", got, expected) - } - } - same := func(s string) { - t.Helper() - another(s, s) - } - - same(`{}`) - same(`{}[5m]`) - same(`{}[5m:]`) - same(`{}[5M:]`) - same(`{}[:]`) - another(`{}[: ]`, `{}[:]`) - same(`{}[:3s]`) - another(`{}[: 3s ]`, `{}[:3s]`) - same(`{}[5m:3s]`) - another(`{}[ 5m : 3s ]`, `{}[5m:3s]`) - same(`{} offset 5m`) - same(`{} offset -5m`) - same(`{} offset 5M`) - same(`{}[5m] offset 10y`) - same(`{}[5.3m:3.4s] offset 10y`) - same(`{}[:3.4s] offset 10y`) - same(`{}[:3.4s] offset -10y`) - same(`{Foo="bAR"}`) - same(`{foo="bar"}`) - same(`{foo="bar"}[5m]`) - same(`{foo="bar"}[5m:]`) - same(`{foo="bar"}[5m:3s]`) - same(`{foo="bar"} offset 13.4ms`) - same(`{foo="bar"}[5w4h-3.4m13.4ms]`) - same(`{foo="bar"} offset 10y`) - same(`{foo="bar"} offset -10y`) - same(`{foo="bar"}[5m] offset 10y`) - same(`{foo="bar"}[5m:3s] offset 10y`) - another(`{foo="bar"}[5m] oFFSEt 10y`, `{foo="bar"}[5m] offset 10y`) - same("METRIC") - same("metric") - same("m_e:tri44:_c123") - another("-metric", "0 - metric") - same(`metric offset 10h`) - same("metric[5m]") - same("metric[5m:3s]") - same("metric[5m] offset 10h") - same("metric[5m:3s] offset 10h") - same("metric[5i:3i] offset 10i") - same(`metric{foo="bar"}`) - same(`metric{foo="bar"} offset 10h`) - same(`metric{foo!="bar"}[2d]`) - same(`metric{foo="bar"}[2d] offset 10h`) - same(`metric{foo="bar", b="sdfsdf"}[2d:3h] offset 10h`) - same(`metric{foo="bar", b="sdfsdf"}[2d:3h] offset 10`) - same(`metric{foo="bar", b="sdfsdf"}[2d:3] offset 10h`) - same(`metric{foo="bar", b="sdfsdf"}[2:3h] offset 10h`) - same(`metric{foo="bar", b="sdfsdf"}[2.34:5.6] offset 3600.5`) - same(`metric{foo="bar", b="sdfsdf"}[234:56] offset -3600`) - another(` metric { foo = "bar" } [ 2d ] offset 10h `, `metric{foo="bar"}[2d] offset 10h`) - // @ modifier - // See https://prometheus.io/docs/prometheus/latest/querying/basics/#modifier - same(`foo @ 123.45`) - same(`foo\@ @ 123.45`) - same(`{foo=~"bar"} @ end()`) - same(`foo{bar="baz"} @ start()`) - same(`foo{bar="baz"}[5m] @ 12345`) - same(`foo{bar="baz"}[5m:4s] offset 5m @ (end() - 3.5m)`) - another(`foo{bar="baz"}[5m:4s] @ (end() - 3.5m) offset 2.4h`, `foo{bar="baz"}[5m:4s] offset 2.4h @ (end() - 3.5m)`) - another(`foo @ start() + (bar offset 3m @ end()) / baz OFFSET -5m`, `foo @ start() + (bar offset 3m @ end() / baz offset -5m)`) - same(`sum(foo) @ start() + rate(bar @ (end() - 5m))`) - another(`time() @ (start())`, `time() @ start()`) - another(`time() @ (start()+(1+1))`, `time() @ (start() + 2)`) - same(`time() @ (end() - 10m)`) - // metric name matching keywords - same("rate") - same("RATE") - same("by") - same("BY") - same("bool") - same("BOOL") - same("unless") - same("UNLESS") - same("Ignoring") - same("with") - same("WITH") - same("With") - same("offset") - same("keep_metric_names") - same("alias") - same(`alias{foo="bar"}`) - same(`aLIas{alias="aa"}`) - another(`al\ias`, `alias`) - // identifiers with with escape chars - same(`foo\ bar`) - same(`foo\-bar\{{baz\+bar="aa"}`) - another(`\x2E\x2ef\oo{b\xEF\ar="aa"}`, `\..foo{bïar="aa"}`) - same(`温度{房间="水电费"}[5m] offset 10m`) - another(`\温\度{\房\间="水电费"}[5m] offset 10m`, `温度{房间="水电费"}[5m] offset 10m`) - same(`sum(fo\|o) by (b\|a, x)`) - another(`sum(x) by (b\x7Ca)`, `sum(x) by (b\|a)`) - // Duplicate filters - same(`foo{__name__="bar"}`) - same(`foo{a="b", a="c", __name__="aaa", b="d"}`) - // Metric filters ending with comma - another(`m{foo="bar",}`, `m{foo="bar"}`) - // String concat in tag value - another(`m{foo="bar" + "baz"}`, `m{foo="barbaz"}`) - - // Valid regexp - same(`foo{bar=~"x"}`) - same(`foo{bar=~"^x"}`) - same(`foo{bar=~"^x$"}`) - same(`foo{bar=~"^(a[bc]|d)$"}`) - same(`foo{bar!~"x"}`) - same(`foo{bar!~"^x"}`) - same(`foo{bar!~"^x$"}`) - same(`foo{bar!~"^(a[bc]|d)$"}`) - - // stringExpr - same(`""`) - same(`"\n\t\r 12:{}[]()44"`) - another(`''`, `""`) - another("``", `""`) - another(" `foo\"b'ar` ", "\"foo\\\"b'ar\"") - another(` 'foo\'bar"BAZ' `, `"foo'bar\"BAZ"`) - // string concat - another(`"foo"+'bar'`, `"foobar"`) - - // numberExpr - same(`1`) - same(`123.`) - another(`-123.`, `-123`) - same(`foo - 123.`) - same(`12.e+4`) - same(`12Ki`) - same(`1.23Gb`) - same(`foo - 23M`) - another(`-1.23Gb`, `-1.23e+09`) - same(`1.23`) - same(`0.23`) - same(`1.2e+45`) - same(`1.2e-45`) - same(`-1`) - same(`-1.23`) - same(`-0.23`) - same(`-1.2e+45`) - same(`-1.2e-45`) - same(`-1.2e-45`) - same(`12.5E34`) - another(`-.2`, `-0.2`) - another(`-.2E-2`, `-0.002`) - same(`NaN`) - same(`nan`) - same(`NAN`) - same(`nAN`) - same(`Inf`) - same(`INF`) - same(`inf`) - another(`+Inf`, `Inf`) - same(`-Inf`) - another(`-inF`, `-Inf`) - same(`0x12`) - same(`0x3b`) - another(`-0x3b`, `-59`) - another(`+0X3B`, `0X3B`) - same(`0b1011`) - same(`073`) - another(`-0o12`, `-10`) - - // durationExpr - same(`1h`) - another(`-1h`, `0 - 1h`) - same(`0.34h4m5s`) - same(`0.34H4m5S`) - another(`-0.34h4m5s`, `0 - 0.34h4m5s`) - same(`sum_over_tme(m[1h]) / 1h`) - same(`sum_over_time(m[3600]) / 3600`) - - // binaryOpExpr - another(`nan == nan`, `NaN`) - another(`nan ==bool nan`, `1`) - another(`nan !=bool nan`, `0`) - another(`nan !=bool 2`, `1`) - another(`2 !=bool nan`, `1`) - another(`nan >bool nan`, `0`) - another(`nan =bool 2`, `1`) - another(`-1 >bool -inf`, `1`) - another(`-1 2`, `NaN`) - another(`1 > bool 2`, `0`) - another(`3 >= 2`, `3`) - another(`3 <= bool 2`, `0`) - another(`1 + -2 - 3`, `-4`) - another(`1 / 0 + 2`, `+Inf`) - another(`2 + -1 / 0`, `-Inf`) - another(`(-1) ^ 0.5`, `NaN`) - another(`-1 ^ 0.5`, `-1`) - another(`512.5 - (1 + 3) * (2 ^ 2) ^ 3`, `256.5`) - another(`1 == bool 1 != bool 24 < bool 4 > bool -1`, `1`) - another(`1 == bOOl 1 != BOOL 24 < Bool 4 > booL -1`, `1`) - another(`m1+on(foo)group_left m2`, `m1 + on (foo) group_left () m2`) - another(`M1+ON(FOO)GROUP_left M2`, `M1 + on (FOO) group_left () M2`) - same(`m1 + on (foo) group_right () m2`) - same(`m1 + on (foo, bar) group_right (x, y) m2`) - another(`m1 + on (foo, bar,) group_right (x, y,) m2`, `m1 + on (foo, bar) group_right (x, y) m2`) - same(`m1 == bool on (foo, bar) group_right (x, y) m2`) - another(`5 - 1 + 3 * 2 ^ 2 ^ 3 - 2 OR Metric {Bar= "Baz", aaa!="bb",cc=~"dd" ,zz !~"ff" } `, - `770 or Metric{Bar="Baz", aaa!="bb", cc=~"dd", zz!~"ff"}`) - same(`"foo" + bar()`) - same(`"foo" + bar{x="y"}`) - same(`("foo"[3s] + bar{x="y"})[5m:3s] offset 10s`) - same(`("foo"[3s] + bar{x="y"})[5i:3i] offset 10i`) - same(`bar + "foo" offset 3s`) - same(`bar + "foo" offset 3i`) - another(`1+2 if 2>3`, `NaN`) - another(`1+4 if 2<3`, `5`) - another(`2+6 default 3 if 2>3`, `8`) - another(`2+6 if 2>3 default NaN`, `NaN`) - another(`42 if 3>2 if 2+2<5`, `42`) - another(`42 if 3>2 if 2+2>=5`, `NaN`) - another(`1+2 ifnot 2>3`, `3`) - another(`1+4 ifnot 2<3`, `NaN`) - another(`2+6 default 3 ifnot 2>3`, `8`) - another(`2+6 ifnot 2>3 default NaN`, `8`) - another(`42 if 3>2 ifnot 2+2<5`, `NaN`) - another(`42 if 3>2 ifnot 2+2>=5`, `42`) - another(`"foo" + "bar"`, `"foobar"`) - another(`"foo"=="bar"`, `NaN`) - another(`"foo"=="foo"`, `1`) - another(`"foo"!="bar"`, `1`) - another(`"foo"+"bar"+"baz"`, `"foobarbaz"`) - another(`"a">"b"`, `NaN`) - another(`"a">bool"b"`, `0`) - another(`"a"<"b"`, `1`) - another(`"a">="b"`, `NaN`) - another(`"a">=bool"b"`, `0`) - another(`"a"<="b"`, `1`) - same(`"a" - "b"`) - - // parensExpr - another(`(-foo + ((bar) / (baz))) + ((23))`, `((0 - foo) + (bar / baz)) + 23`) - another(`(FOO + ((Bar) / (baZ))) + ((23))`, `(FOO + (Bar / baZ)) + 23`) - same(`(foo, bar)`) - another(`((foo, bar),(baz))`, `((foo, bar), baz)`) - same(`(foo, (bar, baz), ((x, y), (z, y), xx))`) - another(`1+(foo, bar,)`, `1 + (foo, bar)`) - another(`((foo(bar,baz)), (1+(2)+(3,4)+()))`, `(foo(bar, baz), (3 + (3, 4)) + ())`) - same(`()`) - - // funcExpr - same(`f()`) - another(`f(x,)`, `f(x)`) - another(`-f()-Ff()`, `(0 - f()) - Ff()`) - same(`F()`) - another(`+F()`, `F()`) - another(`++F()`, `F()`) - another(`--F()`, `0 - (0 - F())`) - same(`f(http_server_request)`) - same(`f(http_server_request)[4s:5m] offset 10m`) - same(`f(http_server_request)[4i:5i] offset 10i`) - same(`F(HttpServerRequest)`) - same(`f(job, foo)`) - same(`F(Job, Foo)`) - another(` FOO (bar) + f ( m ( ),ff(1 + ( 2.5)) ,M[5m ] , "ff" )`, `FOO(bar) + f(m(), ff(3.5), M[5m], "ff")`) - same(`rate(foo[5m]) keep_metric_names`) - another(`log2(foo) KEEP_metric_names + 1 / increase(bar[5m]) keep_metric_names offset 1h @ 435`, - ` log2(foo) keep_metric_names -+ - ( - 1 / increase(bar[5m]) keep_metric_names offset 1h @ 435 - )`) - // funcName matching keywords - same(`by(2)`) - same(`BY(2)`) - same(`or(2)`) - same(`OR(2)`) - same(`bool(2)`) - same(`BOOL(2)`) - same(`rate(rate(m))`) - same(`rate(rate(m[5m]))`) - same(`rate(rate(m[5m])[1h:])`) - same(`rate(rate(m[5m])[1h:3s])`) - // funcName with escape chars - same(`foo\(ba\-r()`) - - // aggrFuncExpr - same(`sum(http_server_request) by ()`) - same(`sum(http_server_request) by (job)`) - same(`sum(http_server_request) without (job, foo)`) - another(`sum(x,y,) without (a,b,)`, `sum(x, y) without (a, b)`) - another(`sum by () (xx)`, `sum(xx) by ()`) - another(`sum by (s) (xx)[5s]`, `(sum(xx) by (s))[5s]`) - another(`SUM BY (ZZ, aa) (XX)`, `sum(XX) by (ZZ, aa)`) - another(`sum without (a, b) (xx,2+2)`, `sum(xx, 4) without (a, b)`) - another(`Sum WIthout (a, B) (XX,2+2)`, `sum(XX, 4) without (a, B)`) - same(`sum(a) or sum(b)`) - same(`sum(a) by () or sum(b) without (x, y)`) - same(`sum(a) + sum(b)`) - same(`sum(x) * (1 + sum(a))`) - same(`avg(x) limit 10`) - same(`avg(x) without (z, b) limit 1`) - another(`avg by(x) (z) limit 20`, `avg(z) by (x) limit 20`) - - // All the above - another(`Sum(Ff(M) * M{X=""}[5m] Offset 7m - 123, 35) BY (X, y) * F2("Test")`, - `sum((Ff(M) * M{X=""}[5m] offset 7m) - 123, 35) by (X, y) * F2("Test")`) - another(`# comment - Sum(Ff(M) * M{X=""}[5m] Offset 7m - 123, 35) BY (X, y) # yet another comment - * F2("Test")`, - `sum((Ff(M) * M{X=""}[5m] offset 7m) - 123, 35) by (X, y) * F2("Test")`) - - // withExpr - another(`with () x`, `x`) - another(`with (x=1,) x`, `1`) - another(`with (x = m offset 5h) x + x`, `m offset 5h + m offset 5h`) - another(`with (x = m offset 5i) x + x`, `m offset 5i + m offset 5i`) - another(`with (foo = bar{x="x"}) 1`, `1`) - another(`with (foo = bar{x="x"}) "x"`, `"x"`) - another(`with (f="x") f`, `"x"`) - another(`with (foo = bar{x="x"}) x{x="y"}`, `x{x="y"}`) - another(`with (foo = bar{x="x"}) 1+1`, `2`) - another(`with (foo = bar{x="x"}) f()`, `f()`) - another(`with (foo = bar{x="x"}) sum(x)`, `sum(x)`) - another(`with (foo = bar{x="x"}) baz{foo="bar"}`, `baz{foo="bar"}`) - another(`with (foo = bar) baz`, `baz`) - another(`with (foo = bar) foo + foo{a="b"}`, `bar + bar{a="b"}`) - another(`with (foo = bar, bar=baz + f()) test`, `test`) - another(`with (ct={job="test"}) a{ct} + ct() + f({ct="x"})`, `(a{job="test"} + {job="test"}) + f({ct="x"})`) - another(`with (ct={job="test", i="bar"}) ct + {ct, x="d"} + foo{ct, ct} + ctx(1)`, - ` ( - ( - {job="test", i="bar"} + {job="test", i="bar", x="d"} - ) - + - foo{job="test", i="bar"} - ) -+ - ctx(1)`) - another(`with (foo = bar) {__name__=~"foo"}`, `{__name__=~"foo"}`) - another(`with (foo = bar) foo{__name__="foo"}`, `bar`) - another(`with (foo = bar) {__name__="foo", x="y"}`, `bar{x="y"}`) - another(`with (foo(bar) = {__name__!="bar"}) foo(x)`, `{__name__!="bar"}`) - another(`with (foo(bar) = bar{__name__="bar"}) foo(x)`, `x`) - another(`with (foo\-bar(baz) = baz + baz) foo\-bar((x,y))`, `(x, y) + (x, y)`) - another(`with (foo\-bar(baz) = baz + baz) foo\-bar(x*y)`, `(x * y) + (x * y)`) - another(`with (foo\-bar(baz) = baz + baz) foo\-bar(x\*y)`, `x\*y + x\*y`) - another(`with (foo\-bar(b\ az) = b\ az + b\ az) foo\-bar(x\*y)`, `x\*y + x\*y`) - // override ttf to something new. - another(`with (ttf = a) ttf + b`, `a + b`) - // override ttf to ru - another(`with (ttf = ru(m, n)) ttf`, `(clamp_min(n - clamp_min(m, 0), 0) / clamp_min(n, 0)) * 100`) - - // Verify withExpr recursion and forward reference - another(`with (x = x+y, y = x+x) y ^ 2`, `((x + y) + (x + y)) ^ 2`) - another(`with (f1(x)=f2(x), f2(x)=f1(x)^2) f1(foobar)`, `f2(foobar)`) - another(`with (f1(x)=f2(x), f2(x)=f1(x)^2) f2(foobar)`, `f2(foobar) ^ 2`) - - // Verify withExpr funcs - another(`with (x() = y+1) x`, `y + 1`) - another(`with (x(foo) = foo+1) x(a)`, `a + 1`) - another(`with (x(a, b) = a + b) x(foo, bar)`, `foo + bar`) - another(`with (x(a, b) = a + b) x(foo, x(1, 2))`, `foo + 3`) - another(`with (x(a) = sum(a) by (b)) x(xx) / x(y)`, `sum(xx) by (b) / sum(y) by (b)`) - another(`with (f(a,f,x)=ff(x,f,a)) f(f(x,y,z),1,2)`, `ff(2, 1, ff(z, y, x))`) - another(`with (f(x)=1+f(x)) f(foo{bar="baz"})`, `1 + f(foo{bar="baz"})`) - another(`with (a=foo, y=bar, f(a)= a+a+y) f(x)`, `(x + x) + bar`) - another(`with (f(a, b) = m{a, b}) f({a="x", b="y"}, {c="d"})`, `m{a="x", b="y", c="d"}`) - another(`with (xx={a="x"}, f(a, b) = m{a, b}) f({xx, b="y"}, {c="d"})`, `m{a="x", b="y", c="d"}`) - another(`with (x() = {b="c"}) foo{x}`, `foo{b="c"}`) - another(`with (f(x)=x{foo="bar"} offset 5m) f(m offset 10m)`, `(m{foo="bar"} offset 10m) offset 5m`) - another(`with (f(x)=x{foo="bar",bas="a"}[5m]) f(m[10m] offset 3s)`, `(m{foo="bar", bas="a"}[10m] offset 3s)[5m]`) - another(`with (f(x)=x{foo="bar"}[5m] offset 10m) f(m{x="y"})`, `m{x="y", foo="bar"}[5m] offset 10m`) - another(`with (f(x)=x{foo="bar"}[5m] offset 10m) f({x="y", foo="bar", foo="bar"})`, `{x="y", foo="bar"}[5m] offset 10m`) - another(`with (f(m, x)=m{x}[5m] offset 10m) f(foo, {})`, `foo[5m] offset 10m`) - another(`with (f(m, x)=m{x, bar="baz"}[5m] offset 10m) f(foo, {})`, `foo{bar="baz"}[5m] offset 10m`) - another(`with (f(x)=x[5m] offset 3s) f(foo[3m]+bar)`, `(foo[3m] + bar)[5m] offset 3s`) - another(`with (f(x)=x[5m:3s] oFFsEt 1.5m) f(sum(s) by (a,b))`, `(sum(s) by (a, b))[5m:3s] offset 1.5m`) - another(`with (x="a", y=x) y+"bc"`, `"abc"`) - another(`with (x="a", y="b"+x) "we"+y+"z"+f()`, `"webaz" + f()`) - another(`with (f(x) = m{foo=x+"y", bar="y"+x, baz=x} + x) f("qwe")`, `m{foo="qwey", bar="yqwe", baz="qwe"} + "qwe"`) - another(`with (f(a)=a) f`, `f`) - another(`with (f\q(a)=a) f\q`, `fq`) - - // Verify withExpr for aggr func modifiers - another(`with (f(x) = x, y = sum(m) by (f)) y`, `sum(m) by (f)`) - another(`with (f(x) = x, y = sum(m) by (f) limit 20) y`, `sum(m) by (f) limit 20`) - another(`with (f(x) = sum(m) by (x)) f(foo)`, `sum(m) by (foo)`) - another(`with (f(x) = sum(m) by (x) limit 42) f(foo)`, `sum(m) by (foo) limit 42`) - another(`with (f(x) = sum(m) by (x)) f((foo, bar, foo))`, `sum(m) by (foo, bar)`) - another(`with (f(x) = sum(m) without (x,y)) f((a, b))`, `sum(m) without (a, b, y)`) - another(`with (f(x) = sum(m) without (y,x)) f((a, y))`, `sum(m) without (y, a)`) - another(`with (f(x,y) = a + on (x,y) group_left (y,bar) b) f(foo,())`, `a + on (foo) group_left (bar) b`) - another(`with (f(x,y) = a + on (x,y) group_left (y,bar) b) f((foo),())`, `a + on (foo) group_left (bar) b`) - another(`with (f(x,y) = a + on (x,y) group_left (y,bar) b) f((foo,xx),())`, `a + on (foo, xx) group_left (bar) b`) - - // Verify nested with exprs - another(`with (f(x) = (with(x=y) x) + x) f(z)`, `y + z`) - another(`with (x=foo) f(a, with (y=x) y)`, `f(a, foo)`) - another(`with (x=foo) a * x + (with (y=x) y) / y`, `(a * foo) + (foo / y)`) - another(`with (x = with (y = foo) y + x) x/x`, `(foo + x) / (foo + x)`) - another(`with ( - x = {foo="bar"}, - q = m{x, y="1"}, - f(x) = - with ( - z(y) = x + y * q - ) - z(foo) / f(x) - ) - f(a)`, `(a + (foo * m{foo="bar", y="1"})) / f(a)`) - - // complex withExpr - another(`WITH ( - treshold = (0.9), - commonFilters = {job="cacher", instance=~"1.2.3.4"}, - hits = rate(cache{type="hit", commonFilters}[5m]), - miss = rate(cache{type="miss", commonFilters}[5m]), - sumByInstance(arg) = sum(arg) by (instance), - hitRatio = sumByInstance(hits) / sumByInstance(hits + miss) - ) - hitRatio < treshold`, - ` ( - ( - sum by (instance) ( - rate(cache{type="hit", job="cacher", instance=~"1.2.3.4"}[5m]) - ) - ) - / - ( - sum by (instance) ( - rate(cache{type="hit", job="cacher", instance=~"1.2.3.4"}[5m]) - + - rate(cache{type="miss", job="cacher", instance=~"1.2.3.4"}[5m]) - ) - ) - ) -< - 0.9`) - another(`WITH ( - x2(x) = x^2, - f(x, y) = x2(x) + x*y + x2(y) - ) - f(a, 3) - `, `((a ^ 2) + (a * 3)) + 9`) - another(`WITH ( - x2(x) = x^2, - f(x, y) = x2(x) + x*y + x2(y) - ) - f(2, 3) - `, `19`) - another(`WITH ( - commonFilters = {instance="foo"}, - timeToFuckup(currv, maxv) = (maxv - currv) / rate(currv) - ) - timeToFuckup(diskUsage{commonFilters}, maxDiskSize{commonFilters})`, - ` ( - maxDiskSize{instance="foo"} - diskUsage{instance="foo"} - ) -/ - rate(diskUsage{instance="foo"})`) - another(`WITH ( - commonFilters = {job="foo", instance="bar"}, - sumRate(m, cf) = sum(rate(m{cf})) by (job, instance), - hitRate(hits, misses) = sumRate(hits, commonFilters) / (sumRate(hits, commonFilters) + sumRate(misses, commonFilters)) - ) - hitRate(cacheHits, cacheMisses)`, - ` ( - sum(rate(cacheHits{job="foo", instance="bar"})) by (job, instance) - ) -/ - ( - ( - sum(rate(cacheHits{job="foo", instance="bar"})) by (job, instance) - ) - + - ( - sum(rate(cacheMisses{job="foo", instance="bar"})) by (job, instance) - ) - )`) - another(`with(y=123,z=5) union(with(y=3,f(x)=x*y) f(2) + f(3), with(x=5,y=2) x*y*z)`, `union(15, 50)`) - - another(`with(sum=123,now=5) union(with(sum=3,f(x)=x*sum) f(2) + f(3), with(x=5,sum=2) x*sum*now)`, `union(15, 50)`) - another(`WITH(now = sum(rate(my_metric_total)), before = sum(rate(my_metric_total) offset 1h)) now/before*100`, `(sum(rate(my_metric_total)) / sum(rate(my_metric_total) offset 1h)) * 100`) - another(`with (sum = x) sum`, `x`) - another(`with (clamp_min=x) clamp_min`, `x`) - another(`with (now=now(), sum=sum()) now`, `now()`) - another(`with (now=now(), sum=sum()) now()`, `now()`) - another(`with (now(a)=now()+a) now(1)`, `now() + 1`) - another(`with (rate(a,b)=a+b) rate(1,2)`, `3`) - another(`with (now=now(), sum=sum()) x`, `x`) - another(`with (rate(a) = b) c`, `c`) - another(`rate(x) + with (rate(a,b)=a*b) rate(2,b)`, `rate(x) + (2 * b)`) - another(`with (sum(a,b)=a+b) sum(c,d)`, `c + d`) - -} - -func TestLongExpressions(t *testing.T) { - another := func(s, expected string) { - t.Helper() - - const maxLineLength = 10 - got, err := Prettify(s, maxLineLength) - if err != nil { - t.Fatalf("unexpected error when parsing %q: %s", s, err) - } - - if expected != got { - t.Fatalf("string not prettified;\ngot:%s\nwant\n%q", got, expected) - } - } - - another(`((node_memory_MemTotal_bytes{instance=~"node:port", job=~"job"}-node_memory_MemFree_bytes{instance=~"node:port", job=~"job"})/node_memory_MemTotal_bytes{instance=~"node:port", job=~"job"})*100`, ` ( - ( - node_memory_MemTotal_bytes{instance=~"node:port", job=~"job"} - - - node_memory_MemFree_bytes{instance=~"node:port", job=~"job"} - ) - / - node_memory_MemTotal_bytes{instance=~"node:port", job=~"job"} - ) -* - 100`) - another(`(((count(count(node_cpu_seconds_total{instance=~"node:port",job=~"job"}) by (cpu)))-avg(sum by (mode) (rate(node_cpu_seconds_total{mode='idle',instance=~"node:port",job=~"job"}[5m]))))*100)/count(count(node_cpu_seconds_total{instance=~"node:port",job=~"job"}) by (cpu))`, ` ( - ( - ( - count ( - count by (cpu) ( - node_cpu_seconds_total{instance=~"node:port", job=~"job"} - ) - ) - ) - - - ( - avg ( - sum by (mode) ( - rate ( - node_cpu_seconds_total{mode="idle", instance=~"node:port", job=~"job"}[5m] - ) - ) - ) - ) - ) - * - 100 - ) -/ - ( - count ( - count by (cpu) ( - node_cpu_seconds_total{instance=~"node:port", job=~"job"} - ) - ) - )`) - another(`sum by(job,foo) (sum by(job,foo) (sum by(job,foo) (task:errors:rate10s{job="s"})))`, `sum by (job, foo) ( - sum by (job, foo) ( - sum by (job, foo) ( - task:errors:rate10s{job="s"} - ) - ) -)`) - another(`foo_1 + ignoring(foo) foo_2 + ignoring(job) group_left foo_3 + on(instance) group_right foo_4`, ` ( - ( - foo_1 - + ignoring (foo) - foo_2 - ) - + ignoring (job) group_left () - foo_3 - ) -+ on (instance) group_right () - foo_4`) - another(`irate(very_long_vector_selector[10m:1m] @ start() offset 1m)`, `irate ( - very_long_vector_selector[10m:1m] offset 1m @ (start()) -)`) - another(`histogram_quantile(0.9, rate(instance_cpu_time_seconds{app="webapp", job="agent", instance="cluster-admin"}[5m]))`, `histogram_quantile ( - 0.9, - rate ( - instance_cpu_time_seconds{app="webapp", job="agent", instance="cluster-admin"}[5m] - ) -)`) - another(`topk(10, (sum without(env) (instance_cpu_time_ns{app="app", job="admin", revision="14d2e34", env="dev", job="cluster-admin"})))`, `topk ( - 10, - sum without (env) ( - instance_cpu_time_ns{app="app", job="admin", revision="14d2e34", env="dev", job="cluster-admin"} - ) -)`) - another(`max_over_time(rate(new_http_request_duration_seconds_count[1m])[1m:] @ start() offset 1m)`, `max_over_time ( - rate ( - new_http_request_duration_seconds_count[1m] - )[1m:] offset 1m @ (start()) -)`) - another(`label_replace(label_replace(up{job="vmadmin",service="host:port"}, "foo", "$1", "service", "(.*):.*"), "foo", "$1", "service", "(.*):.*")`, `label_replace ( - label_replace ( - up{job="vmadmin", service="host:port"}, - "foo", - "$1", - "service", - "(.*):.*" - ), - "foo", - "$1", - "service", - "(.*):.*" -)`) - another(`min(vm_free_disk_space_bytes{job=~"job_storage", instance=~"instance"}/ignoring(path) ((rate(vm_rows_added_to_storage_total{job=~"job_storage", instance=~"instance"}[1d])-ignoring(type) rate(vm_deduplicated_samples_total{job=~"job_storage", instance=~"instance", type="merge"}[1d])) * scalar(sum(vm_data_size_bytes{job=~"job_storage", instance=~"instance", type!~"indexdb.*"})/sum(vm_rows{job=~"job_storage", instance=~"instance", type!~"indexdb.*"}))))`, `min ( - vm_free_disk_space_bytes{job=~"job_storage", instance=~"instance"} - / ignoring (path) - ( - ( - rate ( - vm_rows_added_to_storage_total{job=~"job_storage", instance=~"instance"}[1d] - ) - - ignoring (type) - rate ( - vm_deduplicated_samples_total{job=~"job_storage", instance=~"instance", type="merge"}[1d] - ) - ) - * - scalar ( - ( - sum ( - vm_data_size_bytes{job=~"job_storage", instance=~"instance", type!~"indexdb.*"} - ) - ) - / - ( - sum ( - vm_rows{job=~"job_storage", instance=~"instance", type!~"indexdb.*"} - ) - ) - ) - ) -)`) - another(`max(rate(process_cpu_seconds_total{job=~"job_storage", instance=~"instance"}[10m])/process_cpu_cores_available{job=~"job_storage", instance=~"instance"})`, `max ( - rate ( - process_cpu_seconds_total{job=~"job_storage", instance=~"instance"}[10m] - ) - / - process_cpu_cores_available{job=~"job_storage", instance=~"instance"} -)`) - another(`max(sum(vm_data_size_bytes{job=~"$job", instance=~"$instance"}) by(job, instance) /(sum(vm_free_disk_space_bytes{job=~"$job", instance=~"$instance"}) by(job, instance) +sum(vm_data_size_bytes{job=~"$job", instance=~"$instance"}) by(job, instance)))`, `max ( - ( - sum by (job, instance) ( - vm_data_size_bytes{job=~"$job", instance=~"$instance"} - ) - ) - / - ( - ( - sum by (job, instance) ( - vm_free_disk_space_bytes{job=~"$job", instance=~"$instance"} - ) - ) - + - ( - sum by (job, instance) ( - vm_data_size_bytes{job=~"$job", instance=~"$instance"} - ) - ) - ) -)`) - another(`max without (endpoint) (sum without (instance) (up{job=~".*etcd.*"} == bool 0) or count without (To) (sum without (instance) (rate(etcd_network_peer_sent_failures_total{job=~".*etcd.*"}[120s])) > 0.01))> 0`, ` ( - max without (endpoint) ( - ( - sum without (instance) ( - up{job=~".*etcd.*"} - == bool - 0 - ) - ) - or - ( - count without (To) ( - ( - sum without (instance) ( - rate ( - etcd_network_peer_sent_failures_total{job=~".*etcd.*"}[120s] - ) - ) - ) - > - 0.01 - ) - ) - ) - ) -> - 0`) - another(`histogram_quantile(0.99, sum(rate(grpc_server_handling_seconds_bucket{job=~".*etcd.*", grpc_method!="Defragment", grpc_type="unary"}[5m])) without(grpc_type))> 0.15`, ` histogram_quantile ( - 0.99, - sum without (grpc_type) ( - rate ( - grpc_server_handling_seconds_bucket{job=~".*etcd.*", grpc_method!="Defragment", grpc_type="unary"}[5m] - ) - ) - ) -> - 0.15`) - another(`(rate(prometheus_tsdb_head_samples_appended_total{job="prometheus"}[5m]) <= 0 and (sum without(scrape_job) (prometheus_target_metadata_cache_entries{job="prometheus"}) > 0 or sum without(rule_group) (prometheus_rule_group_rules{job="prometheus"}) > 0))`, ` ( - rate ( - prometheus_tsdb_head_samples_appended_total{job="prometheus"}[5m] - ) - <= - 0 - ) -and - ( - ( - ( - sum without (scrape_job) ( - prometheus_target_metadata_cache_entries{job="prometheus"} - ) - ) - > - 0 - ) - or - ( - ( - sum without (rule_group) ( - prometheus_rule_group_rules{job="prometheus"} - ) - ) - > - 0 - ) - )`) - another(`(histogram_quantile(0.90, sum by (job, instance, namespace, type, le) (rate(promscale_ingest_duration_seconds_bucket[5m]))) > 10 and sum by (job, instance, namespace, type) (rate(promscale_ingest_duration_seconds_bucket[5m]))) > 0`, ` ( - ( - histogram_quantile ( - 0.90, - sum by (job, instance, namespace, type, le) ( - rate ( - promscale_ingest_duration_seconds_bucket[5m] - ) - ) - ) - > - 10 - ) - and - ( - sum by (job, instance, namespace, type) ( - rate ( - promscale_ingest_duration_seconds_bucket[5m] - ) - ) - ) - ) -> - 0`) - another(`1 - ((node_memory_MemAvailable_bytes{job="node"} or (node_memory_Buffers_bytes{job="node"} + node_memory_Cached_bytes{job="node"} + node_memory_MemFree_bytes{job="node"} + node_memory_Slab_bytes{job="node"}) ) / node_memory_MemTotal_bytes{job="node"})`, ` 1 -- - ( - ( - node_memory_MemAvailable_bytes{job="node"} - or - ( - ( - ( - node_memory_Buffers_bytes{job="node"} - + - node_memory_Cached_bytes{job="node"} - ) - + - node_memory_MemFree_bytes{job="node"} - ) - + - node_memory_Slab_bytes{job="node"} - ) - ) - / - node_memory_MemTotal_bytes{job="node"} - )`) - another(`(node_timex_offset_seconds{job="node"} > 0.05 and deriv(node_timex_offset_seconds{job="node"}[5m]) >= 0) or (node_timex_offset_seconds{job="node"} < -0.05 and deriv(node_timex_offset_seconds{job="node"}[5m]) <= 0)`, ` ( - ( - node_timex_offset_seconds{job="node"} - > - 0.05 - ) - and - ( - deriv ( - node_timex_offset_seconds{job="node"}[5m] - ) - >= - 0 - ) - ) -or - ( - ( - node_timex_offset_seconds{job="node"} - < - -0.05 - ) - and - ( - deriv ( - node_timex_offset_seconds{job="node"}[5m] - ) - <= - 0 - ) - )`) - another(`(count by (job) (changes(process_start_time_seconds{job="alertmanager"}[10m]) > 4) / count by (job) (up{job="alertmanager"})) >= 0.5`, ` ( - ( - count by (job) ( - changes ( - process_start_time_seconds{job="alertmanager"}[10m] - ) - > - 4 - ) - ) - / - ( - count by (job) ( - up{job="alertmanager"} - ) - ) - ) ->= - 0.5`) - another(`WITH (commonFilters = {instance=~"$node:$port",job=~"$job"}) (((count(count(node_cpu_seconds_total{commonFilters}) by (cpu)))-avg(sum by (mode) (rate(node_cpu_seconds_total{mode='idle',commonFilters}[5m]))))*100)/count(count(node_cpu_seconds_total{commonFilters}) by (cpu))`, ` ( - ( - ( - count ( - count by (cpu) ( - node_cpu_seconds_total{instance=~"$node:$port", job=~"$job"} - ) - ) - ) - - - ( - avg ( - sum by (mode) ( - rate ( - node_cpu_seconds_total{mode="idle", instance=~"$node:$port", job=~"$job"}[5m] - ) - ) - ) - ) - ) - * - 100 - ) -/ - ( - count ( - count by (cpu) ( - node_cpu_seconds_total{instance=~"$node:$port", job=~"$job"} - ) - ) - )`) - another(`WITH (commonFilters = {instance=~"$node:$port",job=~"$job"}, cpuCount = count(count(node_cpu_seconds_total{commonFilters}) by (cpu)))((cpuCount-avg(sum by (mode) (rate(node_cpu_seconds_total{mode='idle',commonFilters}[5m]))))*100) / cpuCount`, ` ( - ( - ( - count ( - count by (cpu) ( - node_cpu_seconds_total{instance=~"$node:$port", job=~"$job"} - ) - ) - ) - - - ( - avg ( - sum by (mode) ( - rate ( - node_cpu_seconds_total{mode="idle", instance=~"$node:$port", job=~"$job"}[5m] - ) - ) - ) - ) - ) - * - 100 - ) -/ - ( - count ( - count by (cpu) ( - node_cpu_seconds_total{instance=~"$node:$port", job=~"$job"} - ) - ) - )`) - another(`rate(remote_storage_samples_in_total{cluster=~"$cluster", instance=~"$instance"}[5m])- ignoring(remote_name, url) group_right(instance) (rate(remote_storage_succeeded_samples_total{cluster=~"$cluster", instance=~"$instance"}[5m]) or rate(remote_storage_samples_total{cluster=~"$cluster", instance=~"$instance"}[5m]))- (rate(remote_storage_dropped_samples_total{cluster=~"$cluster", instance=~"$instance"}[5m]) or rate(remote_storage_samples_dropped_total{cluster=~"$cluster", instance=~"$instance"}[5m]))`, ` ( - rate ( - remote_storage_samples_in_total{cluster=~"$cluster", instance=~"$instance"}[5m] - ) - - ignoring (remote_name, url) group_right (instance) - ( - rate ( - remote_storage_succeeded_samples_total{cluster=~"$cluster", instance=~"$instance"}[5m] - ) - or - rate ( - remote_storage_samples_total{cluster=~"$cluster", instance=~"$instance"}[5m] - ) - ) - ) -- - ( - rate ( - remote_storage_dropped_samples_total{cluster=~"$cluster", instance=~"$instance"}[5m] - ) - or - rate ( - remote_storage_samples_dropped_total{cluster=~"$cluster", instance=~"$instance"}[5m] - ) - )`) -} diff --git a/prettifier.go b/prettifier.go new file mode 100644 index 0000000..486a224 --- /dev/null +++ b/prettifier.go @@ -0,0 +1,201 @@ +package metricsql + +// Prettify returns prettified representation of MetricsQL query q. +func Prettify(q string) (string, error) { + e, err := Parse(q) + if err != nil { + return "", err + } + b := appendPrettifiedExpr(nil, e, 0, false) + return string(b), nil +} + +// maxPrettifiedLineLen is the maximum length of a single line returned by Prettify(). +// +// Actual lines may exceed the maximum length in some cases. +const maxPrettifiedLineLen = 80 + +func appendPrettifiedExpr(dst []byte, e Expr, indent int, needParens bool) []byte { + dstLen := len(dst) + + // Try appending e to dst and check whether its length exceeds the maximum allowed line length. + dst = appendIndent(dst, indent) + if needParens { + dst = append(dst, '(') + } + dst = e.AppendString(dst) + if needParens { + dst = append(dst, ')') + } + if len(dst)-dstLen <= maxPrettifiedLineLen { + // There is no need in splitting the e string representation, since its' length doesn't exceed. + return dst + } + + // The e string representation exceeds maxPrettifiedLineLen. Split it into multiple lines + dst = dst[:dstLen] + if needParens { + dst = appendIndent(dst, indent) + dst = append(dst, "(\n"...) + indent++ + } + switch t := e.(type) { + case *BinaryOpExpr: + // Split: + // + // a op b + // + // into: + // + // foo + // op + // bar + if t.KeepMetricNames { + dst = appendIndent(dst, indent) + dst = append(dst, "(\n"...) + indent++ + } + dst = appendPrettifiedExpr(dst, t.Left, indent, t.needLeftParens()) + dst = append(dst, '\n') + dst = appendIndent(dst, indent+1) + dst = t.appendModifiers(dst) + dst = append(dst, '\n') + dst = appendPrettifiedExpr(dst, t.Right, indent, t.needRightParens()) + if t.KeepMetricNames { + indent-- + dst = append(dst, '\n') + dst = appendIndent(dst, indent) + dst = append(dst, ") keep_metric_names"...) + } + case *RollupExpr: + // Split: + // + // q[d:s] offset off @ x + // + // into: + // + // ( + // q + // )[d:s] offset off @ x + dst = appendPrettifiedExpr(dst, t.Expr, indent, t.needParens()) + dst = t.appendModifiers(dst) + case *AggrFuncExpr: + // Split: + // + // aggr_func(arg1, ..., argN) modifiers + // + // into: + // + // aggr_func( + // arg1, + // ... + // argN + // ) modifiers + dst = appendIndent(dst, indent) + dst = appendEscapedIdent(dst, t.Name) + dst = appendPrettifiedFuncArgs(dst, indent, t.Args) + dst = t.appendModifiers(dst) + case *FuncExpr: + // Split: + // + // func(arg1, ..., argN) modifiers + // + // into: + // + // func( + // arg1, + // ... + // argN + // ) modifiers + dst = appendIndent(dst, indent) + dst = appendEscapedIdent(dst, t.Name) + dst = appendPrettifiedFuncArgs(dst, indent, t.Args) + dst = t.appendModifiers(dst) + case *MetricExpr: + // Split: + // + // metric{filters1 or ... or filtersN} + // + // into: + // + // metric{ + // filters1 + // or + // ... + // or + // filtersN + // } + offset := 0 + metricName := t.getMetricName() + if metricName != "" { + offset = 1 + } + dst = appendIndent(dst, indent) + dst = appendEscapedIdent(dst, metricName) + dst = append(dst, "{\n"...) + lfss := t.LabelFilterss + for i, lfs := range lfss { + dst = appendPrettifiedLabelFilters(dst, indent+1, lfs[offset:]) + dst = append(dst, '\n') + if i+1 < len(lfss) { + dst = appendIndent(dst, indent+2) + dst = append(dst, "or\n"...) + } + } + dst = appendIndent(dst, indent) + dst = append(dst, '}') + default: + // marshal other expressions as is + dst = t.AppendString(dst) + } + if needParens { + indent-- + dst = append(dst, '\n') + dst = appendIndent(dst, indent) + dst = append(dst, ')') + } + return dst +} + +func appendPrettifiedFuncArgs(dst []byte, indent int, args []Expr) []byte { + dst = append(dst, "(\n"...) + for i, arg := range args { + dst = appendPrettifiedExpr(dst, arg, indent+1, false) + if i+1 < len(args) { + dst = append(dst, ',') + } + dst = append(dst, '\n') + } + dst = appendIndent(dst, indent) + dst = append(dst, ')') + return dst +} + +func appendPrettifiedLabelFilters(dst []byte, indent int, lfs []LabelFilter) []byte { + dstLen := len(dst) + + // Try marshaling lfs into a single line + dst = appendIndent(dst, indent) + dst = appendLabelFilters(dst, lfs) + if len(dst)-dstLen <= maxPrettifiedLineLen { + return dst + } + + // Too long line - split it into multiple lines + dst = dst[:dstLen] + for i := range lfs { + dst = appendIndent(dst, indent) + dst = lfs[i].AppendString(dst) + if i+1 < len(lfs) { + dst = append(dst, ",\n"...) + } + } + return dst +} + +func appendIndent(dst []byte, indent int) []byte { + for i := 0; i < indent; i++ { + dst = append(dst, " "...) + } + return dst +} diff --git a/prettifier_test.go b/prettifier_test.go new file mode 100644 index 0000000..395361b --- /dev/null +++ b/prettifier_test.go @@ -0,0 +1,146 @@ +package metricsql + +import ( + "testing" +) + +func TestPrettifyError(t *testing.T) { + f := func(s string) { + t.Helper() + + result, err := Prettify(s) + if err == nil { + t.Fatalf("expecting non-nil error") + } + if result != "" { + t.Fatalf("expecting empty result; got %q", result) + } + } + + f(`foo{`) + f(`invalid query`) +} + +func TestPrettifySuccess(t *testing.T) { + another := func(s, resultExpected string) { + t.Helper() + + result, err := Prettify(s) + if err != nil { + t.Fatalf("unexpected error when parsing %q: %s", s, err) + } + if result != resultExpected { + t.Fatalf("unexpected query after prettifying;\ngot\n%s\nwant\n%s", result, resultExpected) + } + + // Verify that the result is successfully parsed and prettified into the same string + if _, err := Parse(result); err != nil { + t.Fatalf("unexpected error when parsing prettified result: %s", err) + } + result2, err := Prettify(result) + if err != nil { + t.Fatalf("unexpected error when parsing prettified %q: %s", s, err) + } + if result2 != result { + t.Fatalf("unexpected result after prettifying already prettified result;\ngot\n%s\nwant\n%s", result2, result) + } + } + same := func(s string) { + t.Helper() + another(s, s) + } + + // Verify that short queries remain single-line + same(`foo`) + same(`foo{bar="baz"}`) + same(`foo{bar="baz",x="y" or q="w",r="t"}`) + same(`foo{bar="baz"} + rate(x{y="x"}[5m] offset 1h)`) + + // Verify that long label filters are split into multiple lines + another(`process_cpu_seconds_total{foo="bar",xjljljlkjopiwererrewre="asdfdsfdsfsdfdsfjkljlk"}`, + `process_cpu_seconds_total{ + foo="bar",xjljljlkjopiwererrewre="asdfdsfdsfsdfdsfjkljlk" +}`) + another(`process_cpu_seconds_total{foo="bar",xjljljlkjopiwererrewre="asdfdsfdsfsdfdsfjkljlk",very_long_label_aaaaaaaaaaaaaaa="fdsfdsffdsfs"}`, + `process_cpu_seconds_total{ + foo="bar", + xjljljlkjopiwererrewre="asdfdsfdsfsdfdsfjkljlk", + very_long_label_aaaaaaaaaaaaaaa="fdsfdsffdsfs" +}`) + another(`{foo="bar",xjljljlkjopiwererrewre="asdfdsfdsfsdfdsfjkljlk",very_long_label_aaaaaaaaaaaaaaa="fdsfdsffdsfs"}`, + `{ + foo="bar", + xjljljlkjopiwererrewre="asdfdsfdsfsdfdsfjkljlk", + very_long_label_aaaaaaaaaaaaaaa="fdsfdsffdsfs" +}`) + another(`process_cpu_seconds_total{instance="foobar-baz",job="job1234567" or instance="lkjlkjlkjlkjlkjlkjlkjlkjlkjlk",job="lkjljlkjalkadsfdsffdsfdsfd", + some_very_long_label="very_very_very_long_value_12397787_dfdfdfsds_dsffdfsf"}`, + `process_cpu_seconds_total{ + instance="foobar-baz",job="job1234567" + or + instance="lkjlkjlkjlkjlkjlkjlkjlkjlkjlk", + job="lkjljlkjalkadsfdsffdsfdsfd", + some_very_long_label="very_very_very_long_value_12397787_dfdfdfsds_dsffdfsf" +}`) + + // Verify that long binary operations are split into multiple lines + another(`(sum(rate(process_cpu_seconds_total{instance="foo",job="bar"}[5m] offset 1h @ start())) by (x) / on(x) group_right(y) prefix "x" sum(rate(node_cpu_seconds_total{mode!="idle"}[5m]) keep_metric_names)) keep_metric_names`, + `( + sum( + rate( + process_cpu_seconds_total{instance="foo",job="bar"}[5m] offset 1h @ start() + ) + ) by(x) + / on(x) group_right(y) prefix "x" + sum(rate(node_cpu_seconds_total{mode!="idle"}[5m]) keep_metric_names) +) keep_metric_names`) + + another(`process_cpu_seconds_total{aaaaaaaaaaaaaaaaaa="bbbbbb"} offset 5m + (rate(xxxxxxxxxxxxxxxx{yyyyyyyy="aaaaaaa"}) keep_metric_names)`, + `(process_cpu_seconds_total{aaaaaaaaaaaaaaaaaa="bbbbbb"} offset 5m) + + +(rate(xxxxxxxxxxxxxxxx{yyyyyyyy="aaaaaaa"}) keep_metric_names)`) + another(`process_cpu_seconds_total{aaaaaaaaaaaaaaaaaa="bbbbbb",cccccccccccccccccccccc!~"ddddddddddddddddddddddd"} offset 5m + (rate(xxxxxxxxxxxxxxxx{yyyyyyyy="aaaaaaa"}) keep_metric_names)`, + `( + process_cpu_seconds_total{ + aaaaaaaaaaaaaaaaaa="bbbbbb", + cccccccccccccccccccccc!~"ddddddddddddddddddddddd" + } offset 5m +) + + +(rate(xxxxxxxxxxxxxxxx{yyyyyyyy="aaaaaaa"}) keep_metric_names)`) + + // Verify that long rollup expression is properly split into multiple lines + another(`process_cpu_seconds_total{foo="bar",aaaaaaaaaaaaaaaaaaaaaaaa="bbbbbbbbbbbbbbbbbbbb",c="dddddddddddd"}[5m:3s] offset 5h3m @ 12345`, + `process_cpu_seconds_total{ + foo="bar",aaaaaaaaaaaaaaaaaaaaaaaa="bbbbbbbbbbbbbbbbbbbb",c="dddddddddddd" +}[5m:3s] offset 5h3m @ 12345`) + another(`process_cpu_seconds_total{foo="bar",aaaaaaaaaaaaaaaaaaaaaaaa="bbbbbbbbbbbbbbbbbbbb",ccccccccccccccc="dddddddddddd"}[5m:3s] offset 5h3m @ 12345`, + `process_cpu_seconds_total{ + foo="bar", + aaaaaaaaaaaaaaaaaaaaaaaa="bbbbbbbbbbbbbbbbbbbb", + ccccccccccccccc="dddddddddddd" +}[5m:3s] offset 5h3m @ 12345`) + + // Verify that aggregate expression is properly split into multiple lines + another(`sum without(x,y) (process_cpu_seconds_total{foo="bar",aaaaaaaaaaaaaaaaaaaaaaaa="bbbbbbbbbbbbbbbbbbbb",c="dddddddddddd"}[5m:3s] offset 5h3m @ 12345)`, + `sum( + process_cpu_seconds_total{ + foo="bar",aaaaaaaaaaaaaaaaaaaaaaaa="bbbbbbbbbbbbbbbbbbbb",c="dddddddddddd" + }[5m:3s] offset 5h3m @ 12345 +) without(x,y)`) + + // Verify that an ordinary function args are split into multiple lines + another(`clamp_min(process_cpu_seconds_total{aaaaaaaaaaaaaaaaaaaaaaaaa="bbbb",cccccc="dddd",ppppppppppppppppppppppppp=~"xxxxxxx"}, 123, "456")`, + `clamp_min( + process_cpu_seconds_total{ + aaaaaaaaaaaaaaaaaaaaaaaaa="bbbb", + cccccc="dddd", + ppppppppppppppppppppppppp=~"xxxxxxx" + }, + 123, + "456" +)`) + + // Verify how prettifier works with very long string + same(`"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"`) +}