Skip to content

Commit 4d19337

Browse files
committed
parse enum type
1 parent 68535b9 commit 4d19337

5 files changed

+164
-30
lines changed

README.md

+1-3
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,9 @@ Golang SQL database driver for [Yandex ClickHouse](https://clickhouse.yandex/)
2424
* FixedString(N)
2525
* Date
2626
* DateTime
27+
* Enum
2728
* [Array(T) (one-dimensional)](https://clickhouse.yandex/reference_en.html#Array(T)) [godoc](https://godoc.org/github.com/kshvakov/clickhouse#Array)
2829

29-
## TODO
30-
31-
* Compression
3230

3331
example:
3432
```

clickhouse_negative_test.go

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package clickhouse_test
2+
3+
import (
4+
"database/sql"
5+
"testing"
6+
7+
"github.com/kshvakov/clickhouse"
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
func Test_Negative_OpenConnectAndPing(t *testing.T) {
12+
if connect, err := sql.Open("clickhouse", ""); assert.NoError(t, err) {
13+
assert.Error(t, connect.Ping())
14+
}
15+
if connect, err := sql.Open("clickhouse", "tcp://127.0.0.1:10000"); assert.NoError(t, err) {
16+
assert.Error(t, connect.Ping())
17+
}
18+
if connect, err := sql.Open("clickhouse", "tcp://127.0.0.1:9000?username=invalid"); assert.NoError(t, err) {
19+
if err := connect.Ping(); assert.Error(t, err) {
20+
if exception, ok := err.(*clickhouse.Exception); assert.True(t, ok) {
21+
assert.Equal(t, int32(192), exception.Code)
22+
}
23+
}
24+
}
25+
if connect, err := sql.Open("clickhouse", "tcp://127.0.0.1:9000?password=invalid"); assert.NoError(t, err) {
26+
if err := connect.Ping(); assert.Error(t, err) {
27+
if exception, ok := err.(*clickhouse.Exception); assert.True(t, ok) {
28+
assert.Equal(t, int32(193), exception.Code)
29+
}
30+
}
31+
}
32+
if connect, err := sql.Open("clickhouse", "tcp://127.0.0.1:9000?database=invalid"); assert.NoError(t, err) {
33+
if err := connect.Ping(); assert.Error(t, err) {
34+
if exception, ok := err.(*clickhouse.Exception); assert.True(t, ok) {
35+
assert.Equal(t, int32(81), exception.Code)
36+
}
37+
}
38+
}
39+
}

clickhouse_test.go

-27
Original file line numberDiff line numberDiff line change
@@ -15,33 +15,6 @@ func Test_OpenConnectAndPing(t *testing.T) {
1515
if connect, err := sql.Open("clickhouse", "tcp://127.0.0.1:9000?debug=true"); assert.NoError(t, err) {
1616
assert.NoError(t, connect.Ping())
1717
}
18-
if connect, err := sql.Open("clickhouse", ""); assert.NoError(t, err) {
19-
assert.Error(t, connect.Ping())
20-
}
21-
if connect, err := sql.Open("clickhouse", "tcp://127.0.0.1:10000"); assert.NoError(t, err) {
22-
assert.Error(t, connect.Ping())
23-
}
24-
if connect, err := sql.Open("clickhouse", "tcp://127.0.0.1:9000?username=invalid"); assert.NoError(t, err) {
25-
if err := connect.Ping(); assert.Error(t, err) {
26-
if exception, ok := err.(*clickhouse.Exception); assert.True(t, ok) {
27-
assert.Equal(t, int32(192), exception.Code)
28-
}
29-
}
30-
}
31-
if connect, err := sql.Open("clickhouse", "tcp://127.0.0.1:9000?password=invalid"); assert.NoError(t, err) {
32-
if err := connect.Ping(); assert.Error(t, err) {
33-
if exception, ok := err.(*clickhouse.Exception); assert.True(t, ok) {
34-
assert.Equal(t, int32(193), exception.Code)
35-
}
36-
}
37-
}
38-
if connect, err := sql.Open("clickhouse", "tcp://127.0.0.1:9000?database=invalid"); assert.NoError(t, err) {
39-
if err := connect.Ping(); assert.Error(t, err) {
40-
if exception, ok := err.(*clickhouse.Exception); assert.True(t, ok) {
41-
assert.Equal(t, int32(81), exception.Code)
42-
}
43-
}
44-
}
4518
}
4619

4720
func Test_CreateTable(t *testing.T) {

enum.go

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package clickhouse
2+
3+
import (
4+
"fmt"
5+
"strconv"
6+
"strings"
7+
)
8+
9+
type enum struct {
10+
iv map[string]interface{}
11+
vi map[interface{}]string
12+
}
13+
14+
func (e *enum) toIdent(v interface{}) (string, error) {
15+
if ident, found := e.vi[v]; found {
16+
return ident, nil
17+
}
18+
return "", fmt.Errorf("invalid Enum value: %v", v)
19+
}
20+
func (e enum) toValue(ident string) (interface{}, error) {
21+
if value, found := e.iv[ident]; found {
22+
return value, nil
23+
}
24+
return "", fmt.Errorf("invalid Enum ident: %s", ident)
25+
}
26+
27+
func parseEnum(str string) (enum, error) {
28+
var (
29+
data string
30+
isEnum16 bool
31+
)
32+
switch {
33+
case strings.HasPrefix(str, "Enum8"):
34+
data = str[6:]
35+
case strings.HasPrefix(str, "Enum16"):
36+
data = str[7:]
37+
isEnum16 = true
38+
default:
39+
return enum{}, fmt.Errorf("'%s' is not Enum type", str)
40+
}
41+
enum := enum{
42+
iv: make(map[string]interface{}),
43+
vi: make(map[interface{}]string),
44+
}
45+
for _, block := range strings.Split(data[:len(data)-1], ",") {
46+
parts := strings.Split(block, "=")
47+
if len(parts) != 2 {
48+
return enum, fmt.Errorf("invalid Enum format: %s", str)
49+
}
50+
var (
51+
ident = strings.TrimSpace(parts[0])
52+
value, err = strconv.ParseInt(strings.TrimSpace(parts[1]), 10, 16)
53+
)
54+
if err != nil {
55+
return enum, fmt.Errorf("invalid Enum value: %v", err)
56+
}
57+
{
58+
var (
59+
ident = ident[1 : len(ident)-1]
60+
value interface{} = int16(value)
61+
)
62+
if !isEnum16 {
63+
value = int8(value.(int16))
64+
}
65+
enum.iv[ident] = value
66+
enum.vi[value] = ident
67+
}
68+
}
69+
return enum, nil
70+
}

enum_test.go

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package clickhouse
2+
3+
import "testing"
4+
import "github.com/stretchr/testify/assert"
5+
6+
func Test_Parse_Enum(t *testing.T) {
7+
{
8+
if enum, err := parseEnum("Enum8('a' = 2, 'b' = 1)"); assert.NoError(t, err) {
9+
if value, err := enum.toValue("b"); assert.NoError(t, err) {
10+
if v, ok := value.(int8); assert.True(t, ok) {
11+
assert.Equal(t, int8(1), v)
12+
}
13+
}
14+
if value, err := enum.toValue("a"); assert.NoError(t, err) {
15+
if v, ok := value.(int8); assert.True(t, ok) {
16+
assert.Equal(t, int8(2), v)
17+
}
18+
}
19+
}
20+
21+
if enum, err := parseEnum("Enum8('a' = 2, 'b' = 1)"); assert.NoError(t, err) {
22+
if ident, err := enum.toIdent(int8(1)); assert.NoError(t, err) {
23+
assert.Equal(t, "b", ident)
24+
}
25+
if ident, err := enum.toIdent(int8(2)); assert.NoError(t, err) {
26+
assert.Equal(t, "a", ident)
27+
}
28+
}
29+
}
30+
31+
{
32+
if enum, err := parseEnum("Enum16('a' = 2, 'b' = 1)"); assert.NoError(t, err) {
33+
if value, err := enum.toValue("b"); assert.NoError(t, err) {
34+
if v, ok := value.(int16); assert.True(t, ok) {
35+
assert.Equal(t, int16(1), v)
36+
}
37+
}
38+
if value, err := enum.toValue("a"); assert.NoError(t, err) {
39+
if v, ok := value.(int16); assert.True(t, ok) {
40+
assert.Equal(t, int16(2), v)
41+
}
42+
}
43+
}
44+
45+
if enum, err := parseEnum("Enum16('a' = 2, 'b' = 1)"); assert.NoError(t, err) {
46+
if ident, err := enum.toIdent(int16(1)); assert.NoError(t, err) {
47+
assert.Equal(t, "b", ident)
48+
}
49+
if ident, err := enum.toIdent(int16(2)); assert.NoError(t, err) {
50+
assert.Equal(t, "a", ident)
51+
}
52+
}
53+
}
54+
}

0 commit comments

Comments
 (0)