Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix corner AST for brackets #650

Merged
merged 5 commits into from
Oct 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.23
require (
github.com/andybalholm/brotli v1.1.1
github.com/dlclark/regexp2 v1.11.4
github.com/grafana/grafana-plugin-sdk-go v0.255.0
github.com/grafana/grafana-plugin-sdk-go v0.258.0
github.com/klauspost/compress v1.17.11
github.com/stretchr/testify v1.9.0
golang.org/x/sync v0.8.0
Expand All @@ -18,11 +18,11 @@ require (
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cheekybits/genny v1.0.0 // indirect
github.com/chromedp/cdproto v0.0.0-20241014181340-cb3a7a1d51d7 // indirect
github.com/chromedp/cdproto v0.0.0-20241022234722-4d5d5faf59fb // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/elazarl/goproxy v0.0.0-20240909085733-6741dbfc16a1 // indirect
github.com/fatih/color v1.17.0 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/getkin/kin-openapi v0.128.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
Expand All @@ -41,7 +41,7 @@ require (
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect
github.com/hashicorp/go-hclog v1.6.3 // indirect
github.com/hashicorp/go-plugin v1.6.1 // indirect
github.com/hashicorp/go-plugin v1.6.2 // indirect
github.com/hashicorp/yamux v0.1.2 // indirect
github.com/invopop/yaml v0.3.1 // indirect
github.com/josharian/intern v1.0.0 // indirect
Expand All @@ -65,7 +65,7 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.20.5 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.60.0 // indirect
github.com/prometheus/common v0.60.1 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
Expand All @@ -92,8 +92,8 @@ require (
golang.org/x/text v0.19.0 // indirect
golang.org/x/tools v0.26.0 // indirect
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20241021214115-324edc3d5d38 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect
google.golang.org/grpc v1.67.1 // indirect
google.golang.org/protobuf v1.35.1 // indirect
gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect
Expand Down
14 changes: 14 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ github.com/chromedp/cdproto v0.0.0-20240426225625-909263490071 h1:RdCf9hH3xq5vJi
github.com/chromedp/cdproto v0.0.0-20240426225625-909263490071/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
github.com/chromedp/cdproto v0.0.0-20241014181340-cb3a7a1d51d7 h1:VDBgUGgdCBw9lTKwp0KPExhnqmGfGVJQTER2MehoICk=
github.com/chromedp/cdproto v0.0.0-20241014181340-cb3a7a1d51d7/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
github.com/chromedp/cdproto v0.0.0-20241022234722-4d5d5faf59fb h1:noKVm2SsG4v0Yd0lHNtFYc9EUxIVvrr4kJ6hM8wvIYU=
github.com/chromedp/cdproto v0.0.0-20241022234722-4d5d5faf59fb/go.mod h1:4XqMl3iIW08jtieURWL6Tt5924w21pxirC6th662XUM=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
Expand All @@ -47,6 +49,8 @@ github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
Expand Down Expand Up @@ -90,6 +94,8 @@ github.com/grafana/grafana-plugin-sdk-go v0.250.0 h1:9EBucp9jLqMx2b8NTlOXH+4OuQW
github.com/grafana/grafana-plugin-sdk-go v0.250.0/go.mod h1:gCGN9kHY3KeX4qyni3+Kead38Q+85pYOrsDcxZp6AIk=
github.com/grafana/grafana-plugin-sdk-go v0.255.0 h1:e9bmDTdpR6Ho9ru+UhyVoWguVSSGFgInRjOsl4AftBQ=
github.com/grafana/grafana-plugin-sdk-go v0.255.0/go.mod h1:sE25SkFQSj8DwX4qkF7w++6KL6mzOXo1ycDlk9GgFGw=
github.com/grafana/grafana-plugin-sdk-go v0.258.0 h1:rWsaD+5wuGUSNr9fFnSwS6t/jcRtAoEJ51pIR9bbPNs=
github.com/grafana/grafana-plugin-sdk-go v0.258.0/go.mod h1:jN19FbzhAcPTLPIy31X5nvx38rR5eoD/1rASiip0GBY=
github.com/grafana/otel-profiling-go v0.5.1 h1:stVPKAFZSa7eGiqbYuG25VcqYksR6iWvF3YH66t4qL8=
github.com/grafana/otel-profiling-go v0.5.1/go.mod h1:ftN/t5A/4gQI19/8MoWurBEtC6gFw8Dns1sJZ9W4Tls=
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKtUuKQbJqgAIjlnicKg=
Expand All @@ -106,6 +112,8 @@ github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB1
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-plugin v1.6.1 h1:P7MR2UP6gNKGPp+y7EZw2kOiq4IR9WiqLvp0XOsVdwI=
github.com/hashicorp/go-plugin v1.6.1/go.mod h1:XPHFku2tFo3o3QKFgSYo+cghcUhw1NA1hZyMK0PWAw0=
github.com/hashicorp/go-plugin v1.6.2 h1:zdGAEd0V1lCaU0u+MxWQhtSDQmahpkwOun8U8EiRVog=
github.com/hashicorp/go-plugin v1.6.2/go.mod h1:CkgLQ5CZqNmdL9U9JzM532t8ZiYQ35+pj3b1FD37R0Q=
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8=
Expand Down Expand Up @@ -189,6 +197,8 @@ github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
github.com/prometheus/common v0.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA=
github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw=
github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc=
github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
Expand Down Expand Up @@ -359,10 +369,14 @@ google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd h1:
google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd/go.mod h1:fO8wJzT2zbQbAjbIoos1285VfEIYKDDY+Dt+WpTkh6g=
google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53 h1:fVoAXEKA4+yufmbdVYv+SE73+cPZbbbe8paLsHfkK+U=
google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53/go.mod h1:riSXTwQ4+nqmPGtobMFyW5FqVAmIs0St6VPp4Ug7CE4=
google.golang.org/genproto/googleapis/api v0.0.0-20241021214115-324edc3d5d38 h1:2oV8dfuIkM1Ti7DwXc0BJfnwr9csz4TDXI9EmiI+Rbw=
google.golang.org/genproto/googleapis/api v0.0.0-20241021214115-324edc3d5d38/go.mod h1:vuAjtvlwkDKF6L1GQ0SokiRLCGFfeBUXWr/aFFkHACc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 h1:X58yt85/IXCx0Y3ZwN6sEIKZzQtDEYaBWrDvErdXrRE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 h1:zciRKQ4kBpFgpfC5QQCVtnnNAcLIqweL7plyZRQHVpI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c=
google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
Expand Down
65 changes: 53 additions & 12 deletions pkg/eval_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -1105,7 +1105,7 @@ func newEvalAST(isObj bool) *EvalAST {
var obj map[string]interface{}
var arr []interface{}
if isObj {
obj = make(map[string]interface{}, 0)
obj = make(map[string]interface{})
} else {
arr = make([]interface{}, 0)
}
Expand Down Expand Up @@ -1826,21 +1826,62 @@ func toAST(s string) (*EvalAST, error) {
return scanner.toAST()
}

func isClosured(argument string) bool {
var bracketsQueue []rune
for _, v := range argument {
switch v {
case '(':
bracketsQueue = append(bracketsQueue, v)
case ')':
if 0 < len(bracketsQueue) && bracketsQueue[len(bracketsQueue)-1] == '(' {
bracketsQueue = bracketsQueue[:len(bracketsQueue)-1]
} else {
// isClosured checks if a string has properly balanced brackets while ignoring brackets within quotes
// https://github.com/Altinity/clickhouse-grafana/issues/648
func isClosured(str string) bool {
stack := make([]rune, 0)
isInQuote := false
var quoteType rune

openBrackets := map[rune]rune{
'(': ')',
'[': ']',
'{': '}',
}

closeBrackets := map[rune]rune{
')': '(',
']': '[',
'}': '{',
}

runes := []rune(str)
for i := 0; i < len(runes); i++ {
char := runes[i]

// Handle quotes
if (char == '\'' || char == '"' || char == '`') && (i == 0 || runes[i-1] != '\\') {
if !isInQuote {
isInQuote = true
quoteType = char
} else if char == quoteType {
isInQuote = false
quoteType = 0
}
continue
}

// Skip characters inside quotes
if isInQuote {
continue
}

// Handle brackets
if _, ok := openBrackets[char]; ok {
stack = append(stack, char)
} else if closingPair, ok := closeBrackets[char]; ok {
if len(stack) == 0 {
return false
}
lastOpen := stack[len(stack)-1]
stack = stack[:len(stack)-1] // pop
if lastOpen != closingPair {
return false
}
}
}
return len(bracketsQueue) == 0

return len(stack) == 0
}

func betweenBraces(query string) string {
Expand Down
125 changes: 125 additions & 0 deletions pkg/eval_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1850,3 +1850,128 @@ func TestTableMacroProperlyEscaping(t *testing.T) {

r.Equal(expQuery, actualQuery, description+" unexpected result")
}

// https://github.com/Altinity/clickhouse-grafana/issues/648
func TestIsClosured(t *testing.T) {
// Simple brackets test cases
t.Run("handles simple brackets", func(t *testing.T) {
testCases := []struct {
input string
expected bool
}{
{"(test)", true},
{"[test]", true},
{"{test}", true},
}

for _, tc := range testCases {
result := isClosured(tc.input)
if result != tc.expected {
t.Errorf("isClosured(%q) = %v; want %v", tc.input, result, tc.expected)
}
}
})

// Nested brackets test cases
t.Run("handles nested brackets", func(t *testing.T) {
testCases := []struct {
input string
expected bool
}{
{"({[test]})", true},
{"({[test}])", false},
}

for _, tc := range testCases {
result := isClosured(tc.input)
if result != tc.expected {
t.Errorf("isClosured(%q) = %v; want %v", tc.input, result, tc.expected)
}
}
})

// Quotes test cases
t.Run("handles quotes correctly", func(t *testing.T) {
testCases := []struct {
input string
expected bool
}{
{"'(not a bracket)'", true},
{"\"[also not a bracket]\"", true},
{"`{template literal}`", true},
}

for _, tc := range testCases {
result := isClosured(tc.input)
if result != tc.expected {
t.Errorf("isClosured(%q) = %v; want %v", tc.input, result, tc.expected)
}
}
})

// Escaped quotes test cases
t.Run("handles escaped quotes", func(t *testing.T) {
testCases := []struct {
input string
expected bool
}{
{"''(this is a real bracket)'", true},
{"\\'(this is a bracket after escaped quotes)", true},
}

for _, tc := range testCases {
result := isClosured(tc.input)
if result != tc.expected {
t.Errorf("isClosured(%q) = %v; want %v", tc.input, result, tc.expected)
}
}
})

// Provided test cases
t.Run("handles provided test cases", func(t *testing.T) {
testCases := []struct {
input string
expected bool
}{
{"('('+test)", true},
{"[\"(\"+test+\"]]\"] ", true},
{"('('+test+']]')", true},
{"'('+test ]", false},
{"]['('+test]", false},
}

for _, tc := range testCases {
result := isClosured(tc.input)
if result != tc.expected {
t.Errorf("isClosured(%q) = %v; want %v", tc.input, result, tc.expected)
}
}
})

// Empty input test case
t.Run("handles empty input", func(t *testing.T) {
result := isClosured("")
if !result {
t.Error("isClosured(\"\") = false; want true")
}
})

// Unmatched brackets test cases
t.Run("handles unmatched brackets", func(t *testing.T) {
testCases := []struct {
input string
expected bool
}{
{"(((", false},
{")))", false},
{"((())", false},
}

for _, tc := range testCases {
result := isClosured(tc.input)
if result != tc.expected {
t.Errorf("isClosured(%q) = %v; want %v", tc.input, result, tc.expected)
}
}
})
}
Loading
Loading