1
0
mirror of https://github.com/uptrace/go-clickhouse.git synced 2025-06-08 23:26:11 +02:00

374 lines
8.2 KiB
Go
Raw Normal View History

2022-01-23 09:36:24 +02:00
package chschema
import (
"fmt"
"net"
"reflect"
2022-04-29 18:51:14 +03:00
"strconv"
2022-01-23 09:36:24 +02:00
"strings"
"time"
2022-07-14 10:41:44 +03:00
"github.com/uptrace/go-clickhouse/ch/bfloat16"
2022-01-23 09:36:24 +02:00
"github.com/uptrace/go-clickhouse/ch/chtype"
"github.com/uptrace/go-clickhouse/ch/internal"
)
2023-01-21 12:14:00 +02:00
var (
boolType = reflect.TypeOf(false)
int8Type = reflect.TypeOf(int8(0))
int16Type = reflect.TypeOf(int16(0))
int32Type = reflect.TypeOf(int32(0))
int64Type = reflect.TypeOf(int64(0))
uint8Type = reflect.TypeOf(uint8(0))
uint16Type = reflect.TypeOf(uint16(0))
uint32Type = reflect.TypeOf(uint32(0))
uint64Type = reflect.TypeOf(uint64(0))
float32Type = reflect.TypeOf(float32(0))
float64Type = reflect.TypeOf(float64(0))
stringType = reflect.TypeOf("")
bytesType = reflect.TypeOf((*[]byte)(nil)).Elem()
uuidType = reflect.TypeOf((*UUID)(nil)).Elem()
timeType = reflect.TypeOf((*time.Time)(nil)).Elem()
ipType = reflect.TypeOf((*net.IP)(nil)).Elem()
ipNetType = reflect.TypeOf((*net.IPNet)(nil)).Elem()
bfloat16MapType = reflect.TypeOf((*bfloat16.Map)(nil)).Elem()
sliceUint64Type = reflect.TypeOf((*[]uint64)(nil)).Elem()
sliceFloat32Type = reflect.TypeOf((*[]float32)(nil)).Elem()
)
var chTypes = [...]string{
2022-01-23 09:36:24 +02:00
reflect.Bool: chtype.UInt8,
reflect.Int: chtype.Int64,
reflect.Int8: chtype.Int8,
reflect.Int16: chtype.Int16,
reflect.Int32: chtype.Int32,
reflect.Int64: chtype.Int64,
reflect.Uint: chtype.UInt64,
reflect.Uint8: chtype.UInt8,
reflect.Uint16: chtype.UInt16,
reflect.Uint32: chtype.UInt32,
reflect.Uint64: chtype.UInt64,
reflect.Uintptr: "",
reflect.Float32: chtype.Float32,
reflect.Float64: chtype.Float64,
reflect.Complex64: "",
reflect.Complex128: "",
reflect.Array: "",
reflect.Chan: "",
reflect.Func: "",
reflect.Interface: chtype.Any,
reflect.Map: chtype.String,
reflect.Ptr: "",
reflect.Slice: "",
reflect.String: chtype.String,
reflect.Struct: chtype.String,
reflect.UnsafePointer: "",
}
2023-01-21 12:14:00 +02:00
type NewColumnFunc func() Columnar
2022-01-23 09:36:24 +02:00
2023-01-21 12:14:00 +02:00
func NewColumn(chType string, typ reflect.Type) Columnar {
col := ColumnFactory(chType, typ)()
col.Init(chType)
return col
2022-01-23 09:36:24 +02:00
}
2023-01-21 12:14:00 +02:00
func ColumnFactory(chType string, typ reflect.Type) NewColumnFunc {
2022-01-23 09:36:24 +02:00
switch chType {
case chtype.Int8:
return NewInt8Column
case chtype.Int16:
return NewInt16Column
case chtype.Int32:
return NewInt32Column
case chtype.Int64:
return NewInt64Column
case chtype.UInt8:
return NewUInt8Column
2022-01-23 09:36:24 +02:00
case chtype.UInt16:
return NewUInt16Column
2022-01-23 09:36:24 +02:00
case chtype.UInt32:
return NewUInt32Column
2022-01-23 09:36:24 +02:00
case chtype.UInt64:
return NewUInt64Column
2022-01-23 09:36:24 +02:00
case chtype.Float32:
return NewFloat32Column
case chtype.Float64:
return NewFloat64Column
2023-01-21 12:14:00 +02:00
case chtype.String:
if typ == bytesType {
return NewBytesColumn
}
return NewStringColumn
case "LowCardinality(String)":
return NewLCStringColumn
case chtype.Bool:
return NewBoolColumn
case chtype.UUID:
return NewUUIDColumn
case chtype.IPv6:
return NewIPColumn
2022-01-23 09:36:24 +02:00
case chtype.DateTime:
return NewDateTimeColumn
2022-04-29 18:51:14 +03:00
case chtype.DateTime64:
return NewDateTime64Column
2022-01-23 09:36:24 +02:00
case chtype.Date:
return NewDateColumn
2023-01-21 12:14:00 +02:00
case "Array(Int8)":
return NewArrayInt8Column
case "Array(UInt8)":
return NewArrayUInt8Column
case "Array(Int16)":
return NewArrayInt16Column
case "Array(UInt16)":
return NewArrayUInt16Column
case "Array(Int32)":
return NewArrayInt32Column
case "Array(UInt32)":
return NewArrayUInt32Column
case "Array(Int64)":
return NewArrayInt64Column
case "Array(UInt64)":
return NewArrayUInt64Column
case "Array(Float32)":
return NewArrayFloat32Column
case "Array(Float64)":
return NewArrayFloat64Column
case "Array(String)":
return NewArrayStringColumn
case "Array(LowCardinality(String))":
return NewArrayLCStringColumn
case "Array(DateTime)":
return NewArrayDateTimeColumn
case "Array(Array(String))":
return NewArrayArrayStringColumn
case chtype.Any:
2022-01-23 09:36:24 +02:00
return nil
}
2023-01-21 12:14:00 +02:00
if chType := chEnumType(chType); chType != "" {
return NewEnumColumn
2022-01-23 09:36:24 +02:00
}
2023-01-21 12:14:00 +02:00
if chType := chArrayElemType(chType); chType != "" {
if chType := chEnumType(chType); chType != "" {
return NewArrayEnumColumn
}
2022-01-23 09:36:24 +02:00
}
2023-01-21 12:14:00 +02:00
if isDateTime64Type(chType) {
return NewDateTime64Column
2022-01-23 09:36:24 +02:00
}
2023-01-21 12:14:00 +02:00
if chType := chDateTimeType(chType); chType != "" {
return ColumnFactory(chType, typ)
2022-01-23 09:36:24 +02:00
}
2023-01-21 12:14:00 +02:00
if chType := chNullableType(chType); chType != "" {
if typ != nil {
typ = typ.Elem()
}
return NewNullableColumnFunc(ColumnFactory(chType, typ))
2022-01-23 09:36:24 +02:00
}
2023-01-21 12:14:00 +02:00
if chType := chSimpleAggFunc(chType); chType != "" {
return ColumnFactory(chType, typ)
2022-04-29 18:51:14 +03:00
}
2023-01-21 12:14:00 +02:00
if funcName, _ := aggFuncNameAndType(chType); funcName != "" {
switch funcName {
case "quantileBFloat16", "quantilesBFloat16":
return NewBFloat16HistColumn
default:
panic(fmt.Errorf("unsupported ClickHouse type: %s", chType))
}
}
if typ == nil {
panic(fmt.Errorf("unsupported ClickHouse column: %s", chType))
2022-01-23 09:36:24 +02:00
}
2023-01-21 12:14:00 +02:00
kind := typ.Kind()
switch kind {
case reflect.Ptr:
if typ.Elem().Kind() == reflect.Struct {
return NewJSONColumn
}
return NewNullableColumnFunc(ColumnFactory(chNullableType(chType), typ.Elem()))
case reflect.Slice:
switch elem := typ.Elem(); elem.Kind() {
case reflect.Ptr:
if elem.Elem().Kind() == reflect.Struct {
return NewJSONColumn
}
case reflect.Struct:
if elem != timeType {
return NewJSONColumn
}
}
2022-01-23 09:36:24 +02:00
}
2023-01-21 12:14:00 +02:00
panic(fmt.Errorf("unsupported ClickHouse column: %s", chType))
2022-01-23 09:36:24 +02:00
}
2023-01-21 12:14:00 +02:00
func chType(typ reflect.Type) string {
2022-07-14 10:41:44 +03:00
switch typ {
case timeType:
return chtype.DateTime
case ipType:
return chtype.IPv6
}
kind := typ.Kind()
switch kind {
case reflect.Ptr:
if typ.Elem().Kind() == reflect.Struct {
return chtype.String
}
2023-01-21 12:14:00 +02:00
return fmt.Sprintf("Nullable(%s)", chType(typ.Elem()))
2022-07-14 10:41:44 +03:00
case reflect.Slice:
switch elem := typ.Elem(); elem.Kind() {
case reflect.Ptr:
if elem.Elem().Kind() == reflect.Struct {
return chtype.String // json
}
case reflect.Struct:
if elem != timeType {
return chtype.String // json
}
case reflect.Uint8:
return chtype.String // []byte
}
2023-01-21 12:14:00 +02:00
return "Array(" + chType(typ.Elem()) + ")"
2022-07-14 10:41:44 +03:00
case reflect.Array:
if isUUID(typ) {
return chtype.UUID
}
}
2023-01-21 12:14:00 +02:00
if s := chTypes[kind]; s != "" {
2022-07-14 10:41:44 +03:00
return s
}
panic(fmt.Errorf("ch: unsupported Go type: %s", typ))
}
2022-01-23 09:36:24 +02:00
func chArrayElemType(s string) string {
2023-01-21 12:14:00 +02:00
if s := chSubType(s, "SimpleAggregateFunction("); s != "" {
if i := strings.Index(s, ", "); i >= 0 {
s = s[i+2:]
}
return chSubType(s, "Array(")
}
2022-01-23 09:36:24 +02:00
s = chSubType(s, "Array(")
if s == "" {
return ""
}
elemType := s
s = chSubType(s, "SimpleAggregateFunction(")
if s == "" {
return elemType
}
if i := strings.Index(s, ", "); i >= 0 {
return s[i+2:]
}
return s
}
2023-01-21 12:14:00 +02:00
func chEnumType(s string) string {
return chSubType(s, "Enum8(")
2022-01-23 09:36:24 +02:00
}
2023-01-21 12:14:00 +02:00
func chSimpleAggFunc(s string) string {
s = chSubType(s, "SimpleAggregateFunction(")
if s == "" {
return ""
}
i := strings.Index(s, ", ")
if i == -1 {
return ""
}
return s[i+2:]
2022-01-23 09:36:24 +02:00
}
2023-01-21 12:14:00 +02:00
func chDateTimeType(s string) string {
2022-01-23 09:36:24 +02:00
s = chSubType(s, "DateTime(")
if s == "" {
return ""
}
if s != "'UTC'" {
internal.Logger.Printf("DateTime has timezeone=%q, expected UTC", s)
}
return chtype.DateTime
}
2022-04-29 18:51:14 +03:00
func isDateTime64Type(s string) bool {
return chSubType(s, "DateTime64(") != ""
}
func parseDateTime64Prec(s string) int {
s = chSubType(s, "DateTime64(")
if s == "" {
return 0
}
prec, err := strconv.Atoi(s)
if err != nil {
return 0
}
return prec
}
2023-01-21 12:14:00 +02:00
func chNullableType(s string) string {
2022-01-23 09:36:24 +02:00
return chSubType(s, "Nullable(")
}
func aggFuncNameAndType(chType string) (funcName, funcType string) {
2022-07-14 10:41:44 +03:00
var s string
for _, prefix := range []string{"SimpleAggregateFunction(", "AggregateFunction("} {
s = chSubType(chType, prefix)
if s != "" {
break
}
}
2022-01-23 09:36:24 +02:00
if s == "" {
return "", ""
}
const sep = ", "
idx := strings.LastIndex(s, sep)
if idx == -1 {
return "", ""
}
funcName = s[:idx]
funcType = s[idx+len(sep):]
if idx := strings.IndexByte(funcName, '('); idx >= 0 {
funcName = funcName[:idx]
}
return funcName, funcType
}
func chSubType(s, prefix string) string {
if strings.HasPrefix(s, prefix) && strings.HasSuffix(s, ")") {
return s[len(prefix) : len(s)-1]
}
return ""
}
func isUUID(typ reflect.Type) bool {
return typ.Len() == 16 && typ.Elem().Kind() == reflect.Uint8
}