package querybuilder import ( "context" "fmt" "strings" "github.com/SigNoz/signoz/pkg/errors" qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5" "github.com/SigNoz/signoz/pkg/types/telemetrytypes" "github.com/huandu/go-sqlbuilder" "golang.org/x/exp/maps" ) func CollisionHandledFinalExpr( ctx context.Context, field *telemetrytypes.TelemetryFieldKey, fm qbtypes.FieldMapper, cb qbtypes.ConditionBuilder, keys map[string][]*telemetrytypes.TelemetryFieldKey, requiredDataType telemetrytypes.FieldDataType, ) (string, []any, error) { if requiredDataType != telemetrytypes.FieldDataTypeString && requiredDataType != telemetrytypes.FieldDataTypeFloat64 { return "", nil, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "unsupported data type %s", requiredDataType) } var dummyValue any if requiredDataType == telemetrytypes.FieldDataTypeFloat64 { dummyValue = 0.0 } else { dummyValue = "" } var stmts []string var allArgs []any addCondition := func(key *telemetrytypes.TelemetryFieldKey) error { sb := sqlbuilder.NewSelectBuilder() condition, err := cb.ConditionFor(ctx, key, qbtypes.FilterOperatorExists, nil, sb) if err != nil { return err } sb.Where(condition) expr, args := sb.BuildWithFlavor(sqlbuilder.ClickHouse) expr = strings.TrimPrefix(expr, "WHERE ") stmts = append(stmts, expr) allArgs = append(allArgs, args...) return nil } colName, err := fm.FieldFor(ctx, field) if errors.Is(err, qbtypes.ErrColumnNotFound) { // the key didn't have the right context to be added to the query // we try to use the context we know of keysForField := keys[field.Name] if len(keysForField) == 0 { // - the context is not provided // - there are not keys for the field // - it is not a static field // - the next best thing to do is see if there is a typo // and suggest a correction correction, found := telemetrytypes.SuggestCorrection(field.Name, maps.Keys(keys)) if found { // we found a close match, in the error message send the suggestion return "", nil, errors.Wrapf(err, errors.TypeInvalidInput, errors.CodeInvalidInput, correction) } else { // not even a close match, return an error return "", nil, err } } else { for _, key := range keysForField { err := addCondition(key) if err != nil { return "", nil, err } colName, _ = fm.FieldFor(ctx, key) colName, _ = telemetrytypes.DataTypeCollisionHandledFieldName(key, dummyValue, colName) stmts = append(stmts, colName) } } } else { err := addCondition(field) if err != nil { return "", nil, err } colName, _ = telemetrytypes.DataTypeCollisionHandledFieldName(field, dummyValue, colName) stmts = append(stmts, colName) } multiIfStmt := fmt.Sprintf("multiIf(%s, NULL)", strings.Join(stmts, ", ")) return multiIfStmt, allArgs, nil }