fix: issues with resource query builder w.r.t quotes (#6318)

This commit is contained in:
Nityananda Gohain 2024-11-01 13:52:13 +05:30 committed by GitHub
parent b770fc2457
commit 580f0b816e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 29 additions and 20 deletions

View File

@ -149,7 +149,7 @@ func buildAttributeFilter(item v3.FilterItem) (string, error) {
return fmt.Sprintf(logsOp, keyName, fmtVal), nil return fmt.Sprintf(logsOp, keyName, fmtVal), nil
case v3.FilterOperatorContains, v3.FilterOperatorNotContains: case v3.FilterOperatorContains, v3.FilterOperatorNotContains:
// we also want to treat %, _ as literals for contains // we also want to treat %, _ as literals for contains
val := utils.QuoteEscapedStringForContains(fmt.Sprintf("%s", item.Value)) val := utils.QuoteEscapedStringForContains(fmt.Sprintf("%s", item.Value), false)
// for body the contains is case insensitive // for body the contains is case insensitive
if keyName == BODY { if keyName == BODY {
logsOp = strings.Replace(logsOp, "ILIKE", "LIKE", 1) // removing i from ilike and not ilike logsOp = strings.Replace(logsOp, "ILIKE", "LIKE", 1) // removing i from ilike and not ilike

View File

@ -49,7 +49,7 @@ func buildResourceFilter(logsOp string, key string, op v3.FilterOperator, value
case v3.FilterOperatorContains, v3.FilterOperatorNotContains: case v3.FilterOperatorContains, v3.FilterOperatorNotContains:
// this is required as clickhouseFormattedValue add's quotes to the string // this is required as clickhouseFormattedValue add's quotes to the string
// we also want to treat %, _ as literals for contains // we also want to treat %, _ as literals for contains
escapedStringValue := utils.QuoteEscapedStringForContains(lowerValue) escapedStringValue := utils.QuoteEscapedStringForContains(lowerValue, false)
return fmt.Sprintf("%s %s '%%%s%%'", lowerSearchKey, logsOp, escapedStringValue) return fmt.Sprintf("%s %s '%%%s%%'", lowerSearchKey, logsOp, escapedStringValue)
case v3.FilterOperatorLike, v3.FilterOperatorNotLike: case v3.FilterOperatorLike, v3.FilterOperatorNotLike:
// this is required as clickhouseFormattedValue add's quotes to the string // this is required as clickhouseFormattedValue add's quotes to the string
@ -92,7 +92,7 @@ func buildIndexFilterForInOperator(key string, op v3.FilterOperator, value inter
// if there are no values to filter on, return an empty string // if there are no values to filter on, return an empty string
if len(values) > 0 { if len(values) > 0 {
for _, v := range values { for _, v := range values {
value := utils.QuoteEscapedStringForContains(v) value := utils.QuoteEscapedStringForContains(v, true)
conditions = append(conditions, fmt.Sprintf("labels %s '%%\"%s\":\"%s\"%%'", sqlOp, key, value)) conditions = append(conditions, fmt.Sprintf("labels %s '%%\"%s\":\"%s\"%%'", sqlOp, key, value))
} }
return "(" + strings.Join(conditions, separator) + ")" return "(" + strings.Join(conditions, separator) + ")"
@ -110,24 +110,24 @@ func buildIndexFilterForInOperator(key string, op v3.FilterOperator, value inter
func buildResourceIndexFilter(key string, op v3.FilterOperator, value interface{}) string { func buildResourceIndexFilter(key string, op v3.FilterOperator, value interface{}) string {
// not using clickhouseFormattedValue as we don't wan't the quotes // not using clickhouseFormattedValue as we don't wan't the quotes
strVal := fmt.Sprintf("%s", value) strVal := fmt.Sprintf("%s", value)
formattedValueEscapedForContains := strings.ToLower(utils.QuoteEscapedStringForContains(strVal)) fmtValEscapedForContains := utils.QuoteEscapedStringForContains(strVal, true)
formattedValueEscaped := utils.QuoteEscapedString(strVal) fmtValEscapedForContainsLower := strings.ToLower(fmtValEscapedForContains)
formattedValueEscapedLower := strings.ToLower(formattedValueEscaped) fmtValEscapedLower := strings.ToLower(utils.QuoteEscapedString(strVal))
// add index filters // add index filters
switch op { switch op {
case v3.FilterOperatorContains: case v3.FilterOperatorContains:
return fmt.Sprintf("lower(labels) like '%%%s%%%s%%'", key, formattedValueEscapedForContains) return fmt.Sprintf("lower(labels) like '%%%s%%%s%%'", key, fmtValEscapedForContainsLower)
case v3.FilterOperatorNotContains: case v3.FilterOperatorNotContains:
return fmt.Sprintf("lower(labels) not like '%%%s%%%s%%'", key, formattedValueEscapedForContains) return fmt.Sprintf("lower(labels) not like '%%%s%%%s%%'", key, fmtValEscapedForContainsLower)
case v3.FilterOperatorLike: case v3.FilterOperatorLike:
return fmt.Sprintf("lower(labels) like '%%%s%%%s%%'", key, formattedValueEscapedLower) return fmt.Sprintf("lower(labels) like '%%%s%%%s%%'", key, fmtValEscapedLower)
case v3.FilterOperatorNotLike: case v3.FilterOperatorNotLike:
return fmt.Sprintf("lower(labels) not like '%%%s%%%s%%'", key, formattedValueEscapedLower) return fmt.Sprintf("lower(labels) not like '%%%s%%%s%%'", key, fmtValEscapedLower)
case v3.FilterOperatorEqual: case v3.FilterOperatorEqual:
return fmt.Sprintf("labels like '%%%s%%%s%%'", key, formattedValueEscaped) return fmt.Sprintf("labels like '%%%s%%%s%%'", key, fmtValEscapedForContains)
case v3.FilterOperatorNotEqual: case v3.FilterOperatorNotEqual:
return fmt.Sprintf("labels not like '%%%s%%%s%%'", key, formattedValueEscaped) return fmt.Sprintf("labels not like '%%%s%%%s%%'", key, fmtValEscapedForContains)
case v3.FilterOperatorRegex, v3.FilterOperatorNotRegex: case v3.FilterOperatorRegex, v3.FilterOperatorNotRegex:
// don't try to do anything for regex. // don't try to do anything for regex.
return "" return ""

View File

@ -138,9 +138,9 @@ func Test_buildIndexFilterForInOperator(t *testing.T) {
args: args{ args: args{
key: "service.name", key: "service.name",
op: v3.FilterOperatorNotIn, op: v3.FilterOperatorNotIn,
value: "application'\"_s", value: `application'"_s`,
}, },
want: `(labels not like '%"service.name":"application\'"\_s"%')`, want: `(labels not like '%"service.name":"application\'\\\\"\_s"%')`,
}, },
} }
for _, tt := range tests { for _, tt := range tests {
@ -231,9 +231,9 @@ func Test_buildResourceIndexFilter(t *testing.T) {
args: args{ args: args{
key: "service.name", key: "service.name",
op: v3.FilterOperatorEqual, op: v3.FilterOperatorEqual,
value: "Application", value: `Application"`,
}, },
want: `labels like '%service.name%Application%'`, want: `labels like '%service.name%Application\\\\"%'`,
}, },
} }
for _, tt := range tests { for _, tt := range tests {
@ -319,7 +319,7 @@ func Test_buildResourceFiltersFromFilterItems(t *testing.T) {
Type: v3.AttributeKeyTypeResource, Type: v3.AttributeKeyTypeResource,
}, },
Operator: v3.FilterOperatorContains, Operator: v3.FilterOperatorContains,
Value: "test1", Value: `test1"`,
}, },
}, },
}, },
@ -327,8 +327,8 @@ func Test_buildResourceFiltersFromFilterItems(t *testing.T) {
want: []string{ want: []string{
"simpleJSONExtractString(labels, 'service.name') = 'test'", "simpleJSONExtractString(labels, 'service.name') = 'test'",
"labels like '%service.name%test%'", "labels like '%service.name%test%'",
"simpleJSONExtractString(lower(labels), 'namespace') LIKE '%test1%'", `simpleJSONExtractString(lower(labels), 'namespace') LIKE '%test1"%'`,
"lower(labels) like '%namespace%test1%'", `lower(labels) like '%namespace%test1\\\\"%'`,
}, },
wantErr: false, wantErr: false,
}, },

View File

@ -156,9 +156,18 @@ func QuoteEscapedString(str string) string {
return str return str
} }
func QuoteEscapedStringForContains(str string) string { func QuoteEscapedStringForContains(str string, isIndex bool) string {
// https: //clickhouse.com/docs/en/sql-reference/functions/string-search-functions#like // https: //clickhouse.com/docs/en/sql-reference/functions/string-search-functions#like
str = QuoteEscapedString(str) str = QuoteEscapedString(str)
// we are adding this because if a string contains quote `"` it will be stored as \" in clickhouse
// to query that using like our query should be \\\\"
if isIndex {
// isIndex is true means that the extra slash is present
// [\"a\",\"b\",\"sdf\"]
str = strings.ReplaceAll(str, `"`, `\\\\"`)
}
str = strings.ReplaceAll(str, `%`, `\%`) str = strings.ReplaceAll(str, `%`, `\%`)
str = strings.ReplaceAll(str, `_`, `\_`) str = strings.ReplaceAll(str, `_`, `\_`)
return str return str