mirror of
https://github.com/uptrace/go-clickhouse.git
synced 2025-06-08 23:26:11 +02:00
151 lines
2.3 KiB
Go
151 lines
2.3 KiB
Go
package chschema
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
var enumMap sync.Map
|
|
|
|
type enumInfo struct {
|
|
chType string
|
|
dec []string
|
|
enc map[string]int16
|
|
}
|
|
|
|
func (e *enumInfo) Encode(val string) (int16, bool) {
|
|
i, ok := e.enc[val]
|
|
return i, ok
|
|
}
|
|
|
|
func (e *enumInfo) Decode(i int16) string {
|
|
return e.dec[i]
|
|
}
|
|
|
|
func parseEnum(s string) *enumInfo {
|
|
if v, ok := enumMap.Load(s); ok {
|
|
return v.(*enumInfo)
|
|
}
|
|
|
|
enumInfo, err := _parseEnum(s)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
enumInfo.chType = s
|
|
|
|
enumMap.Store(s, enumInfo)
|
|
return enumInfo
|
|
}
|
|
|
|
func _parseEnum(chType string) (*enumInfo, error) {
|
|
s := enumType(chType)
|
|
if s == "" {
|
|
return nil, fmt.Errorf("can't parse enum type: %q", chType)
|
|
}
|
|
|
|
var dec []string
|
|
for s != "" {
|
|
var key, val string
|
|
var ok bool
|
|
|
|
s, key, ok = scanEnumKey(s)
|
|
if !ok {
|
|
return nil, fmt.Errorf("can't parse enum key: %q", s)
|
|
}
|
|
|
|
s, ok = scanEnumChar(s, '=')
|
|
if !ok {
|
|
return nil, fmt.Errorf("can't parse enum '=': %q", s)
|
|
}
|
|
|
|
s, val = scanEnumValue(s)
|
|
if val == "" {
|
|
return nil, fmt.Errorf("can't parse enum value: %q", s)
|
|
}
|
|
|
|
n, err := strconv.ParseInt(val, 10, 16)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ln := int(n + 1)
|
|
if len(dec) < ln {
|
|
dec = append(dec, make([]string, ln-len(dec))...)
|
|
}
|
|
dec[n] = key
|
|
|
|
s, _ = scanEnumChar(s, ',')
|
|
}
|
|
|
|
enc := make(map[string]int16, len(dec))
|
|
for i, s := range dec {
|
|
enc[s] = int16(i)
|
|
}
|
|
|
|
return &enumInfo{
|
|
chType: chType,
|
|
dec: dec,
|
|
enc: enc,
|
|
}, nil
|
|
}
|
|
|
|
func scanEnumKey(s string) (string, string, bool) {
|
|
loop:
|
|
for i := 0; i < len(s); i++ {
|
|
c := s[i]
|
|
switch c {
|
|
case ' ':
|
|
// ignore
|
|
case '\'':
|
|
s = s[i+1:]
|
|
break loop
|
|
default:
|
|
return s, "", false
|
|
}
|
|
}
|
|
|
|
i := strings.IndexByte(s, '\'')
|
|
if i == -1 {
|
|
return s, "", false
|
|
}
|
|
|
|
key := s[:i]
|
|
s = s[i+1:]
|
|
return s, key, true
|
|
}
|
|
|
|
func scanEnumChar(s string, ch byte) (string, bool) {
|
|
var start int
|
|
loop:
|
|
for i := 0; i < len(s); i++ {
|
|
c := s[i]
|
|
switch c {
|
|
case ' ':
|
|
start = i + 1
|
|
case ch:
|
|
return s[i+1:], true
|
|
default:
|
|
break loop
|
|
}
|
|
}
|
|
return s[start:], false
|
|
}
|
|
|
|
func scanEnumValue(s string) (string, string) {
|
|
var start int
|
|
for i := 0; i < len(s); i++ {
|
|
c := s[i]
|
|
switch {
|
|
case c == ' ':
|
|
start = i + 1
|
|
case c >= '0' && c <= '9':
|
|
// continue
|
|
default:
|
|
return s[i:], s[start:i]
|
|
}
|
|
}
|
|
return "", s[start:]
|
|
}
|