Files
grafana/pkg/services/ngalert/store/json.go
T
Alexander Akhmetov 872ed5dc9d Alerting: Add plugins:hide backend rule search parameter (#113706)
Alerting: Add plugins filter to list rules API
2026-01-05 13:17:51 +01:00

122 lines
3.6 KiB
Go

package store
import (
"encoding/json"
"fmt"
"strings"
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
)
// JSON functions for MySQL/PostgreSQL
func jsonEquals(dialect migrator.Dialect, column, key, value string) (string, []any) {
switch dialect.DriverName() {
case migrator.MySQL:
return fmt.Sprintf("JSON_UNQUOTE(JSON_EXTRACT(NULLIF(%s, ''), CONCAT('$.', ?))) = ?", column), []any{key, value}
case migrator.Postgres:
return fmt.Sprintf("jsonb_extract_path_text(NULLIF(%s, '')::jsonb, ?) = ?", column), []any{key, value}
default:
return "", nil
}
}
func jsonNotEquals(dialect migrator.Dialect, column, key, value string) (string, []any) {
var jx string
switch dialect.DriverName() {
case migrator.MySQL:
jx = fmt.Sprintf("JSON_UNQUOTE(JSON_EXTRACT(NULLIF(%s, ''), CONCAT('$.', ?)))", column)
case migrator.Postgres:
jx = fmt.Sprintf("jsonb_extract_path_text(NULLIF(%s, '')::jsonb, ?)", column)
default:
return "", nil
}
return fmt.Sprintf("(%s IS NULL OR %s != ?)", jx, jx), []any{key, key, value}
}
func jsonKeyMissing(dialect migrator.Dialect, column, key string) (string, []any, error) {
return jsonKeyCondition(dialect, column, key, false)
}
func jsonKeyExists(dialect migrator.Dialect, column, key string) (string, []any, error) {
return jsonKeyCondition(dialect, column, key, true)
}
func jsonKeyCondition(dialect migrator.Dialect, column, key string, exists bool) (string, []any, error) {
nullCheck := "IS NULL"
if exists {
nullCheck = "IS NOT NULL"
}
switch dialect.DriverName() {
case migrator.MySQL:
return fmt.Sprintf("JSON_EXTRACT(NULLIF(%s, ''), CONCAT('$.', ?)) %s", column, nullCheck), []any{key}, nil
case migrator.Postgres:
return fmt.Sprintf("jsonb_extract_path_text(NULLIF(%s, '')::jsonb, ?) %s", column, nullCheck), []any{key}, nil
default:
return "", nil, fmt.Errorf("unsupported dialect for JSON key condition: %s", dialect.DriverName())
}
}
// GLOB functions for SQLite
func globEquals(column, key, value string) (string, []any, error) {
pattern, err := buildGlobPattern(key, value)
if err != nil {
return "", nil, err
}
return column + " GLOB ?", []any{"*" + pattern + "*"}, nil
}
func globNotEquals(column, key, value string) (string, []any, error) {
pattern, err := buildGlobPattern(key, value)
if err != nil {
return "", nil, err
}
return column + " NOT GLOB ?", []any{"*" + pattern + "*"}, nil
}
func globKeyMissing(column, key string) (string, []any, error) {
pattern, err := buildGlobKeyPattern(key)
if err != nil {
return "", nil, err
}
return column + " NOT GLOB ?", []any{"*" + pattern + "*"}, nil
}
func globKeyExists(column, key string) (string, []any, error) {
pattern, err := buildGlobKeyPattern(key)
if err != nil {
return "", nil, err
}
return column + " GLOB ?", []any{"*" + pattern + "*"}, nil
}
// Search for `"key":"value"`
func buildGlobPattern(key, value string) (string, error) {
keyJSON, err := json.Marshal(key)
if err != nil {
return "", fmt.Errorf("failed to marshal key: %w", err)
}
valueJSON, err := json.Marshal(value)
if err != nil {
return "", fmt.Errorf("failed to marshal value: %w", err)
}
return escapeGlobPattern(fmt.Sprintf(`%s:%s`, string(keyJSON), string(valueJSON))), nil
}
// Search for `"key":`
func buildGlobKeyPattern(key string) (string, error) {
keyJSON, err := json.Marshal(key)
if err != nil {
return "", fmt.Errorf("failed to marshal key: %w", err)
}
return escapeGlobPattern(string(keyJSON) + ":"), nil
}
func escapeGlobPattern(pattern string) string {
pattern = strings.ReplaceAll(pattern, "[", "[[]")
pattern = strings.ReplaceAll(pattern, "*", "[*]")
pattern = strings.ReplaceAll(pattern, "?", "[?]")
return pattern
}