package column import ( "fmt" "reflect" "strings" "time" "github.com/ClickHouse/clickhouse-go/lib/binary" ) type Column interface { Name() string CHType() string ScanType() reflect.Type Read(*binary.Decoder, bool) (interface{}, error) Write(*binary.Encoder, interface{}) error defaultValue() interface{} Depth() int } func Factory(name, chType string, timezone *time.Location) (Column, error) { switch chType { case "Int8": return &Int8{ base: base{ name: name, chType: chType, valueOf: columnBaseTypes[int8(0)], }, }, nil case "Int16": return &Int16{ base: base{ name: name, chType: chType, valueOf: columnBaseTypes[int16(0)], }, }, nil case "Int32": return &Int32{ base: base{ name: name, chType: chType, valueOf: columnBaseTypes[int32(0)], }, }, nil case "Int64": return &Int64{ base: base{ name: name, chType: chType, valueOf: columnBaseTypes[int64(0)], }, }, nil case "UInt8": return &UInt8{ base: base{ name: name, chType: chType, valueOf: columnBaseTypes[uint8(0)], }, }, nil case "UInt16": return &UInt16{ base: base{ name: name, chType: chType, valueOf: columnBaseTypes[uint16(0)], }, }, nil case "UInt32": return &UInt32{ base: base{ name: name, chType: chType, valueOf: columnBaseTypes[uint32(0)], }, }, nil case "UInt64": return &UInt64{ base: base{ name: name, chType: chType, valueOf: columnBaseTypes[uint64(0)], }, }, nil case "Float32": return &Float32{ base: base{ name: name, chType: chType, valueOf: columnBaseTypes[float32(0)], }, }, nil case "Float64": return &Float64{ base: base{ name: name, chType: chType, valueOf: columnBaseTypes[float64(0)], }, }, nil case "String": return &String{ base: base{ name: name, chType: chType, valueOf: columnBaseTypes[string("")], }, }, nil case "UUID": return &UUID{ base: base{ name: name, chType: chType, valueOf: columnBaseTypes[string("")], }, }, nil case "Date": _, offset := time.Unix(0, 0).In(timezone).Zone() return &Date{ base: base{ name: name, chType: chType, valueOf: columnBaseTypes[time.Time{}], }, Timezone: timezone, offset: int64(offset), }, nil case "IPv4": return &IPv4{ base: base{ name: name, chType: chType, valueOf: columnBaseTypes[IPv4{}], }, }, nil case "IPv6": return &IPv6{ base: base{ name: name, chType: chType, valueOf: columnBaseTypes[IPv6{}], }, }, nil } switch { case strings.HasPrefix(chType, "DateTime") && !strings.HasPrefix(chType, "DateTime64"): return &DateTime{ base: base{ name: name, chType: "DateTime", valueOf: columnBaseTypes[time.Time{}], }, Timezone: timezone, }, nil case strings.HasPrefix(chType, "DateTime64"): return &DateTime64{ base: base{ name: name, chType: chType, valueOf: columnBaseTypes[time.Time{}], }, Timezone: timezone, }, nil case strings.HasPrefix(chType, "Array"): return parseArray(name, chType, timezone) case strings.HasPrefix(chType, "Nullable"): return parseNullable(name, chType, timezone) case strings.HasPrefix(chType, "FixedString"): return parseFixedString(name, chType) case strings.HasPrefix(chType, "Enum8"), strings.HasPrefix(chType, "Enum16"): return parseEnum(name, chType) case strings.HasPrefix(chType, "Decimal"): return parseDecimal(name, chType) case strings.HasPrefix(chType, "SimpleAggregateFunction"): if nestedType, err := getNestedType(chType, "SimpleAggregateFunction"); err != nil { return nil, err } else { return Factory(name, nestedType, timezone) } case strings.HasPrefix(chType, "Tuple"): return parseTuple(name, chType, timezone) } return nil, fmt.Errorf("column: unhandled type %v", chType) } func getNestedType(chType string, wrapType string) (string, error) { prefixLen := len(wrapType) + 1 suffixLen := 1 if len(chType) > prefixLen+suffixLen { nested := strings.Split(chType[prefixLen:len(chType)-suffixLen], ",") if len(nested) == 2 { return strings.TrimSpace(nested[1]), nil } if len(nested) == 3 { return strings.TrimSpace(strings.Join(nested[1:], ",")), nil } } return "", fmt.Errorf("column: invalid %s type (%s)", wrapType, chType) }