141 lines
2.8 KiB
Go
141 lines
2.8 KiB
Go
package column
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/kshvakov/clickhouse/lib/binary"
|
|
)
|
|
|
|
type Enum struct {
|
|
iv map[string]interface{}
|
|
vi map[interface{}]string
|
|
base
|
|
baseType interface{}
|
|
}
|
|
|
|
func (enum *Enum) Read(decoder *binary.Decoder) (interface{}, error) {
|
|
var (
|
|
err error
|
|
ident interface{}
|
|
)
|
|
switch enum.baseType.(type) {
|
|
case int16:
|
|
if ident, err = decoder.Int16(); err != nil {
|
|
return nil, err
|
|
}
|
|
default:
|
|
if ident, err = decoder.Int8(); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
if ident, found := enum.vi[ident]; found {
|
|
return ident, nil
|
|
}
|
|
return nil, fmt.Errorf("invalid Enum value: %v", ident)
|
|
}
|
|
|
|
func (enum *Enum) Write(encoder *binary.Encoder, v interface{}) error {
|
|
switch v := v.(type) {
|
|
case string:
|
|
ident, found := enum.iv[v]
|
|
if !found {
|
|
return fmt.Errorf("invalid Enum ident: %s", v)
|
|
}
|
|
switch ident := ident.(type) {
|
|
case int8:
|
|
return encoder.Int8(ident)
|
|
case int16:
|
|
return encoder.Int16(ident)
|
|
}
|
|
case uint8:
|
|
if _, ok := enum.baseType.(int8); ok {
|
|
return encoder.Int8(int8(v))
|
|
}
|
|
case int8:
|
|
if _, ok := enum.baseType.(int8); ok {
|
|
return encoder.Int8(v)
|
|
}
|
|
case uint16:
|
|
if _, ok := enum.baseType.(int16); ok {
|
|
return encoder.Int16(int16(v))
|
|
}
|
|
case int16:
|
|
if _, ok := enum.baseType.(int16); ok {
|
|
return encoder.Int16(v)
|
|
}
|
|
case int64:
|
|
switch enum.baseType.(type) {
|
|
case int8:
|
|
return encoder.Int8(int8(v))
|
|
case int16:
|
|
return encoder.Int16(int16(v))
|
|
}
|
|
}
|
|
return &ErrUnexpectedType{
|
|
T: v,
|
|
Column: enum,
|
|
}
|
|
}
|
|
|
|
func (enum *Enum) defaultValue() interface{} {
|
|
return enum.baseType
|
|
}
|
|
|
|
func parseEnum(name, chType string) (*Enum, error) {
|
|
var (
|
|
data string
|
|
isEnum16 bool
|
|
)
|
|
if len(chType) < 8 {
|
|
return nil, fmt.Errorf("invalid Enum format: %s", chType)
|
|
}
|
|
switch {
|
|
case strings.HasPrefix(chType, "Enum8"):
|
|
data = chType[6:]
|
|
case strings.HasPrefix(chType, "Enum16"):
|
|
data = chType[7:]
|
|
isEnum16 = true
|
|
default:
|
|
return nil, fmt.Errorf("'%s' is not Enum type", chType)
|
|
}
|
|
enum := Enum{
|
|
base: base{
|
|
name: name,
|
|
chType: chType,
|
|
valueOf: baseTypes[string("")],
|
|
},
|
|
iv: make(map[string]interface{}),
|
|
vi: make(map[interface{}]string),
|
|
}
|
|
for _, block := range strings.Split(data[:len(data)-1], ",") {
|
|
parts := strings.Split(block, "=")
|
|
if len(parts) != 2 {
|
|
return nil, fmt.Errorf("invalid Enum format: %s", chType)
|
|
}
|
|
var (
|
|
ident = strings.TrimSpace(parts[0])
|
|
value, err = strconv.ParseInt(strings.TrimSpace(parts[1]), 10, 16)
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid Enum value: %v", chType)
|
|
}
|
|
{
|
|
var (
|
|
ident = ident[1 : len(ident)-1]
|
|
value interface{} = int16(value)
|
|
)
|
|
if !isEnum16 {
|
|
value = int8(value.(int16))
|
|
}
|
|
if enum.baseType == nil {
|
|
enum.baseType = value
|
|
}
|
|
enum.iv[ident] = value
|
|
enum.vi[value] = ident
|
|
}
|
|
}
|
|
return &enum, nil
|
|
}
|