logs-analyzer/signoz/pkg/query-service/utils/format.go

268 lines
7.1 KiB
Go
Raw Normal View History

2024-09-02 22:47:30 +03:00
package utils
import (
"fmt"
"math"
"reflect"
"strconv"
"strings"
"go.signoz.io/signoz/pkg/query-service/constants"
v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
"go.uber.org/zap"
)
// ValidateAndCastValue validates and casts the value of a key to the corresponding data type of the key
func ValidateAndCastValue(v interface{}, dataType v3.AttributeKeyDataType) (interface{}, error) {
switch dataType {
case v3.AttributeKeyDataTypeString:
switch x := v.(type) {
case string, int, int64, float32, float64, bool:
return fmt.Sprintf("%v", x), nil
case []interface{}:
for i, val := range x {
// if val is not string and it is int, int64, float, bool, convert it to string
if _, ok := val.(string); ok {
continue
} else if _, ok := val.(int); ok {
x[i] = fmt.Sprintf("%v", val)
} else if _, ok := val.(int64); ok {
x[i] = fmt.Sprintf("%v", val)
} else if _, ok := val.(float32); ok {
x[i] = fmt.Sprintf("%v", val)
} else if _, ok := val.(float64); ok {
x[i] = fmt.Sprintf("%v", val)
} else if _, ok := val.(bool); ok {
x[i] = fmt.Sprintf("%v", val)
} else {
return nil, fmt.Errorf("invalid data type, expected string, got %v", reflect.TypeOf(val))
}
}
return x, nil
default:
return nil, fmt.Errorf("invalid data type, expected string, got %v", reflect.TypeOf(v))
}
case v3.AttributeKeyDataTypeBool:
switch x := v.(type) {
case []interface{}:
for i, val := range x {
if _, ok := val.(string); ok {
boolean, err := strconv.ParseBool(val.(string))
if err != nil {
return nil, fmt.Errorf("invalid data type, expected bool, got %v", reflect.TypeOf(val))
}
x[i] = boolean
} else if _, ok := val.(bool); !ok {
return nil, fmt.Errorf("invalid data type, expected bool, got %v", reflect.TypeOf(val))
} else {
x[i] = val.(bool)
}
}
return x, nil
case bool:
return x, nil
case string:
boolean, err := strconv.ParseBool(x)
if err != nil {
return nil, fmt.Errorf("invalid data type, expected bool, got %v", reflect.TypeOf(v))
}
return boolean, nil
default:
return nil, fmt.Errorf("invalid data type, expected bool, got %v", reflect.TypeOf(v))
}
case v3.AttributeKeyDataTypeInt64:
switch x := v.(type) {
case []interface{}:
for i, val := range x {
if _, ok := val.(string); ok {
int64val, err := strconv.ParseInt(val.(string), 10, 64)
if err != nil {
return nil, fmt.Errorf("invalid data type, expected int, got %v", reflect.TypeOf(val))
}
x[i] = int64val
} else if _, ok := val.(int); ok {
x[i] = int64(val.(int))
} else if _, ok := val.(int64); !ok {
return nil, fmt.Errorf("invalid data type, expected int, got %v", reflect.TypeOf(val))
} else {
x[i] = val.(int64)
}
}
return x, nil
case int, int64:
return x, nil
case float32:
return int64(x), nil
case float64:
return int64(x), nil
case string:
int64val, err := strconv.ParseInt(x, 10, 64)
if err != nil {
return nil, fmt.Errorf("invalid data type, expected int, got %v", reflect.TypeOf(v))
}
return int64val, nil
default:
return nil, fmt.Errorf("invalid data type, expected int, got %v", reflect.TypeOf(v))
}
case v3.AttributeKeyDataTypeFloat64:
switch x := v.(type) {
case []interface{}:
for i, val := range x {
if _, ok := val.(string); ok {
float64val, err := strconv.ParseFloat(val.(string), 64)
if err != nil {
return nil, fmt.Errorf("invalid data type, expected float, got %v", reflect.TypeOf(val))
}
x[i] = float64val
} else if _, ok := val.(float32); ok {
x[i] = float64(val.(float32))
} else if _, ok := val.(int); ok {
x[i] = float64(val.(int))
} else if _, ok := val.(int64); ok {
x[i] = float64(val.(int64))
} else if _, ok := val.(float64); !ok {
return nil, fmt.Errorf("invalid data type, expected float, got %v", reflect.TypeOf(val))
} else {
x[i] = val.(float64)
}
}
return x, nil
case float32, float64:
return x, nil
case string:
float64val, err := strconv.ParseFloat(x, 64)
if err != nil {
return nil, fmt.Errorf("invalid data type, expected float, got %v", reflect.TypeOf(v))
}
return float64val, nil
case int:
return float64(x), nil
case int64:
return float64(x), nil
default:
return nil, fmt.Errorf("invalid data type, expected float, got %v", reflect.TypeOf(v))
}
default:
return nil, fmt.Errorf("invalid data type, expected float, bool, int, string or []interface{} but got %v", dataType)
}
}
func QuoteEscapedString(str string) string {
// https://clickhouse.com/docs/en/sql-reference/syntax#string
str = strings.ReplaceAll(str, `\`, `\\`)
str = strings.ReplaceAll(str, `'`, `\'`)
return str
}
// ClickHouseFormattedValue formats the value to be used in clickhouse query
func ClickHouseFormattedValue(v interface{}) string {
// if it's pointer convert it to a value
v = getPointerValue(v)
switch x := v.(type) {
case uint8, uint16, uint32, uint64, int, int8, int16, int32, int64:
return fmt.Sprintf("%d", x)
case float32, float64:
return fmt.Sprintf("%f", x)
case string:
return fmt.Sprintf("'%s'", QuoteEscapedString(x))
case bool:
return fmt.Sprintf("%v", x)
case []interface{}:
if len(x) == 0 {
return "[]"
}
switch x[0].(type) {
case string:
str := "["
for idx, sVal := range x {
str += fmt.Sprintf("'%s'", QuoteEscapedString(sVal.(string)))
if idx != len(x)-1 {
str += ","
}
}
str += "]"
return str
case uint8, uint16, uint32, uint64, int, int8, int16, int32, int64, float32, float64, bool:
return strings.Join(strings.Fields(fmt.Sprint(x)), ",")
default:
zap.L().Error("invalid type for formatted value", zap.Any("type", reflect.TypeOf(x[0])))
return "[]"
}
default:
zap.L().Error("invalid type for formatted value", zap.Any("type", reflect.TypeOf(x)))
return ""
}
}
func getPointerValue(v interface{}) interface{} {
switch x := v.(type) {
case *uint8:
return *x
case *uint16:
return *x
case *uint32:
return *x
case *uint64:
return *x
case *int:
return *x
case *int8:
return *x
case *int16:
return *x
case *int32:
return *x
case *int64:
return *x
case *float32:
return *x
case *float64:
return *x
case *string:
return *x
case *bool:
return *x
case []interface{}:
values := []interface{}{}
for _, val := range x {
values = append(values, getPointerValue(val))
}
return values
default:
return v
}
}
func GetClickhouseColumnName(typeName string, dataType, field string) string {
if typeName == string(v3.AttributeKeyTypeTag) {
typeName = constants.Attributes
}
if typeName != string(v3.AttributeKeyTypeResource) {
typeName = typeName[:len(typeName)-1]
}
// if name contains . replace it with `$$`
field = strings.ReplaceAll(field, ".", "$$")
colName := fmt.Sprintf("`%s_%s_%s`", strings.ToLower(typeName), strings.ToLower(dataType), field)
return colName
}
// GetEpochNanoSecs takes epoch and returns it in ns
func GetEpochNanoSecs(epoch int64) int64 {
temp := epoch
count := 0
if epoch == 0 {
count = 1
} else {
for epoch != 0 {
epoch /= 10
count++
}
}
return temp * int64(math.Pow(10, float64(19-count)))
}