Skip to content

Commit 1ae00d3

Browse files
authored
Merge pull request ClickHouse#396 from vl4deee11/feature/nullable-arrays
Support Array(Nullable(T))
2 parents 183cbf0 + 183c1cb commit 1ae00d3

File tree

8 files changed

+692
-22
lines changed

8 files changed

+692
-22
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ tcp://host1:9000?username=user&password=qwerty&database=clicks&read_timeout=10&w
5252
* UUID
5353
* Nullable(T)
5454
* [Array(T)](https://clickhouse.yandex/reference_en.html#Array(T)) [godoc](https://godoc.org/github.com/ClickHouse/clickhouse-go#Array)
55+
* Array(Nullable(T))
5556
* Tuple(...T)
5657

5758
## TODO

clickhouse_nullable_array_test.go

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
package clickhouse
2+
3+
import (
4+
"database/sql"
5+
"net"
6+
7+
"github.com/stretchr/testify/assert"
8+
9+
"testing"
10+
"time"
11+
)
12+
13+
func Test_NullableArray(t *testing.T) {
14+
const (
15+
ddl = `
16+
CREATE TABLE clickhouse_test_nullable_array
17+
(
18+
arr_decimal Array(Nullable(Decimal(15, 3))),
19+
arr_int8 Array(Nullable(Int8)),
20+
arr_int16 Array(Nullable(Int16)),
21+
arr_int32 Array(Nullable(Int32)),
22+
arr_int64 Array(Nullable(Int64)),
23+
arr_uint8 Array(Nullable(UInt8)),
24+
arr_uint16 Array(Nullable(UInt16)),
25+
arr_uint32 Array(Nullable(UInt32)),
26+
arr_uint64 Array(Nullable(UInt64)),
27+
arr_float32 Array(Nullable(Float32)),
28+
arr_float64 Array(Nullable(Float64)),
29+
arr_ipv6 Array(Nullable(IPv6)),
30+
arr_ipv4 Array(Nullable(IPv4)),
31+
arr_string Array(Nullable(String)),
32+
arr_arr_string Array(Array(Nullable(String))),
33+
arr_date Array(Nullable(Date)),
34+
arr_datetime Array(Nullable(DateTime)),
35+
arr_enum8_str Array(Nullable(Enum8('a8' = 1, 'b8' = 2))),
36+
arr_enum8_int Array(Nullable(Enum8('a8' = 1, 'b8' = 2))),
37+
arr_enum16_str Array(Nullable(Enum16('a16' = 1, 'b16' = 2))),
38+
arr_enum16_int Array(Nullable(Enum16('a16' = 1, 'b16' = 2)))
39+
) Engine = Memory;
40+
`
41+
dml = `
42+
INSERT INTO clickhouse_test_nullable_array (arr_decimal,
43+
arr_int8,
44+
arr_int16,
45+
arr_int32,
46+
arr_int64,
47+
arr_uint8,
48+
arr_uint16,
49+
arr_uint32,
50+
arr_uint64,
51+
arr_float32,
52+
arr_float64,
53+
arr_ipv6,
54+
arr_ipv4,
55+
arr_string,
56+
arr_arr_string,
57+
arr_date,
58+
arr_datetime,
59+
arr_enum8_str,
60+
arr_enum8_int,
61+
arr_enum16_str,
62+
arr_enum16_int)
63+
VALUES (?,
64+
?,
65+
?,
66+
?,
67+
?,
68+
?,
69+
?,
70+
?,
71+
?,
72+
?,
73+
?,
74+
?,
75+
?,
76+
?,
77+
?,
78+
?,
79+
?,
80+
?,
81+
?,
82+
?);
83+
`
84+
query = `
85+
SELECT
86+
*
87+
FROM clickhouse_test_nullable_array
88+
`
89+
)
90+
91+
decV := 16.55
92+
int64Dec := int64(16550)
93+
int8V := int8(123)
94+
int16V := int16(1231)
95+
int32V := int32(12312)
96+
int64V := int64(123123)
97+
98+
uint8V := uint8(123)
99+
uint16V := uint16(1231)
100+
uint32V := uint32(12312)
101+
uint64V := uint64(123123)
102+
103+
float32V := float32(123.123)
104+
float64V := 123123.123123
105+
106+
stringV := "123123"
107+
108+
ipv6V := net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334")
109+
ipv4V := net.ParseIP("123.123.123.123")
110+
111+
timeV, _ := time.Parse("2006-01-02 15:04:05", "2021-07-11 00:00:00")
112+
dateV, _ := time.Parse("2006-01-02", "2021-07-11")
113+
114+
enum8VA := "a8"
115+
enum8VB := "b8"
116+
117+
enum8V1 := int8(1)
118+
enum8V2 := int8(2)
119+
120+
enum16VA := "a16"
121+
enum16VB := "b16"
122+
123+
enum16V1 := int16(1)
124+
enum16V2 := int16(2)
125+
126+
var timeNil *time.Time
127+
128+
if connect, err := sql.Open("clickhouse", "tcp://127.0.0.1:9000?debug=true"); assert.NoError(t, err) {
129+
if tx, err := connect.Begin(); assert.NoError(t, err) {
130+
if _, err := connect.Exec("DROP TABLE IF EXISTS clickhouse_test_nullable_array"); assert.NoError(t, err) {
131+
if _, err := tx.Exec(ddl); assert.NoError(t, err) {
132+
if tx, err := connect.Begin(); assert.NoError(t, err) {
133+
stmt, err := tx.Prepare(dml)
134+
if assert.NoError(t, err) {
135+
for i := 0; i < 10; i++ {
136+
if _, err := stmt.Exec(
137+
[]*float64{&decV, nil, &decV},
138+
[]*int8{&int8V, nil, &int8V},
139+
[]*int16{&int16V, nil, &int16V},
140+
[]*int32{&int32V, nil, &int32V},
141+
[]*int64{&int64V, nil, &int64V},
142+
143+
[]*uint8{&uint8V, nil, &uint8V},
144+
[]*uint16{&uint16V, nil, &uint16V},
145+
[]*uint32{&uint32V, nil, &uint32V},
146+
[]*uint64{&uint64V, nil, &uint64V},
147+
148+
[]*float32{&float32V, nil, &float32V},
149+
[]*float64{&float64V, nil, &float64V},
150+
151+
[]*net.IP{&ipv6V, nil, &ipv6V},
152+
[]*net.IP{&ipv4V, nil, &ipv4V},
153+
154+
[]*string{&stringV, nil, &stringV},
155+
[][]*string{{&stringV, nil, &stringV}},
156+
157+
[]*time.Time{&dateV, nil, &dateV},
158+
[]*time.Time{&timeV, nil, &timeV},
159+
160+
[]*string{&enum8VA, nil, &enum8VB},
161+
[]*int8{&enum8V1, nil, &enum8V2},
162+
[]*string{&enum16VA, nil, &enum16VB},
163+
[]*int16{&enum16V1, nil, &enum16V2},
164+
); !assert.NoError(t, err) {
165+
t.Fatal(err)
166+
}
167+
}
168+
}
169+
if err := tx.Commit(); !assert.NoError(t, err) {
170+
t.Fatal(err)
171+
}
172+
}
173+
if rows, err := connect.Query(query); assert.NoError(t, err) {
174+
for i := 0; i < 10; i++ {
175+
rows.Next()
176+
var (
177+
ArrDecimal = make([]*int64, 0)
178+
ArrInt8 = make([]*int8, 0)
179+
ArrInt16 = make([]*int16, 0)
180+
ArrInt32 = make([]*int32, 0)
181+
ArrInt64 = make([]*int64, 0)
182+
ArrUInt8 = make([]*uint8, 0)
183+
ArrUInt16 = make([]*uint16, 0)
184+
ArrUInt32 = make([]*uint32, 0)
185+
ArrUInt64 = make([]*uint64, 0)
186+
ArrFloat32 = make([]*float32, 0)
187+
ArrFloat64 = make([]*float64, 0)
188+
ArrIpv6 = make([]*net.IP, 0)
189+
ArrIpv4 = make([]*net.IP, 0)
190+
ArrString = make([]*string, 0)
191+
ArrArrString = make([][]*string, 0)
192+
ArrDate = make([]*time.Time, 0)
193+
ArrDateTime = make([]*time.Time, 0)
194+
ArrEnum8Str = make([]*string, 0)
195+
ArrEnum8Int8 = make([]*string, 0)
196+
ArrEnum16Str = make([]*string, 0)
197+
ArrEnum16Int16 = make([]*string, 0)
198+
)
199+
if err := rows.Scan(
200+
&ArrDecimal,
201+
&ArrInt8,
202+
&ArrInt16,
203+
&ArrInt32,
204+
&ArrInt64,
205+
&ArrUInt8,
206+
&ArrUInt16,
207+
&ArrUInt32,
208+
&ArrUInt64,
209+
&ArrFloat32,
210+
&ArrFloat64,
211+
&ArrIpv6,
212+
&ArrIpv4,
213+
&ArrString,
214+
&ArrArrString,
215+
&ArrDate,
216+
&ArrDateTime,
217+
&ArrEnum8Str,
218+
&ArrEnum8Int8,
219+
&ArrEnum16Str,
220+
&ArrEnum16Int16,
221+
); assert.NoError(t, err) {
222+
assert.Equal(t, ArrDecimal, []*int64{&int64Dec, nil, &int64Dec})
223+
assert.Equal(t, ArrInt8, []*int8{&int8V, nil, &int8V})
224+
assert.Equal(t, ArrInt16, []*int16{&int16V, nil, &int16V})
225+
assert.Equal(t, ArrInt32, []*int32{&int32V, nil, &int32V})
226+
assert.Equal(t, ArrInt64, []*int64{&int64V, nil, &int64V})
227+
228+
assert.Equal(t, ArrUInt8, []*uint8{&uint8V, nil, &uint8V})
229+
assert.Equal(t, ArrUInt16, []*uint16{&uint16V, nil, &uint16V})
230+
assert.Equal(t, ArrUInt32, []*uint32{&uint32V, nil, &uint32V})
231+
assert.Equal(t, ArrUInt64, []*uint64{&uint64V, nil, &uint64V})
232+
233+
assert.Equal(t, ArrFloat32, []*float32{&float32V, nil, &float32V})
234+
assert.Equal(t, ArrFloat64, []*float64{&float64V, nil, &float64V})
235+
236+
assert.Equal(t, ArrIpv6, []*net.IP{&ipv6V, nil, &ipv6V})
237+
assert.Equal(t, ArrIpv4, []*net.IP{&ipv4V, nil, &ipv4V})
238+
239+
assert.Equal(t, ArrString, []*string{&stringV, nil, &stringV})
240+
assert.Equal(t, ArrArrString, [][]*string{{&stringV, nil, &stringV}})
241+
242+
assert.True(t, len(ArrDate) == 3)
243+
assert.True(t, len(ArrDateTime) == 3)
244+
assert.Equal(t, ArrDate[1], timeNil)
245+
assert.Equal(t, ArrDateTime[1], timeNil)
246+
247+
assert.Equal(t, ArrEnum8Int8, []*string{&enum8VA, nil, &enum8VB})
248+
assert.Equal(t, ArrEnum16Int16, []*string{&enum16VA, nil, &enum16VB})
249+
assert.Equal(t, ArrEnum8Str, []*string{&enum8VA, nil, &enum8VB})
250+
assert.Equal(t, ArrEnum16Str, []*string{&enum16VA, nil, &enum16VB})
251+
} else {
252+
t.Fatal(err)
253+
}
254+
}
255+
} else {
256+
t.Fatal(err)
257+
}
258+
}
259+
}
260+
}
261+
}
262+
}

lib/codegen/nullable_appender/main.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package main
2+
3+
import (
4+
"flag"
5+
"html/template"
6+
"log"
7+
"os"
8+
"strings"
9+
)
10+
11+
const tmpl = `
12+
// DANGER! This code was autogenerated from template by clickhouse-go/lib/codegen/nullable_appender.
13+
// You shouldn't change it manually.
14+
// For more info check clickhouse-go/lib/codegen/nullable_appender/main.go
15+
16+
package {{.Package}}
17+
18+
import (
19+
"fmt"
20+
"net"
21+
"reflect"
22+
"time"
23+
)
24+
25+
var nullableAppender = map[string]func(v interface{}, slice reflect.Value) (reflect.Value, error){
26+
{{ range .Types }}
27+
"*{{.}}": func(v interface{}, slice reflect.Value) (reflect.Value, error) {
28+
if v != nil {
29+
v, ok := v.({{.}})
30+
if !ok {
31+
return slice, fmt.Errorf("cannot assert to type {{.}}")
32+
}
33+
return reflect.Append(slice, reflect.ValueOf(&v)), nil
34+
}
35+
var vNil *{{.}}
36+
return reflect.Append(slice, reflect.ValueOf(vNil)), nil
37+
},
38+
{{ end }}
39+
}
40+
`
41+
42+
type values struct {
43+
Package string
44+
Types []string
45+
}
46+
47+
func main() {
48+
settings := values{
49+
Types: []string{
50+
// add new types here that can be null and used with Array(Nullable(T))
51+
"int8",
52+
"int16",
53+
"int32",
54+
"int64",
55+
"uint8",
56+
"uint16",
57+
"uint32",
58+
"uint64",
59+
"float32",
60+
"float64",
61+
"string",
62+
"time.Time",
63+
"net.IP",
64+
},
65+
Package: "column",
66+
}
67+
flag.StringVar(&settings.Package, "package", "column", "Package name")
68+
out := flag.String("file", "nullable_appender.go", "Output file")
69+
flag.Parse()
70+
file, err := os.Create(*out)
71+
if err == nil {
72+
defer file.Close()
73+
err = template.Must(
74+
template.New("nullableAppender").Funcs(map[string]interface{}{"toLower": strings.ToLower}).Parse(tmpl),
75+
).Execute(file, &settings)
76+
}
77+
if err != nil {
78+
log.Panic(err)
79+
}
80+
log.Printf("Nullable appender generated for package %s in file %s", settings.Package, *out)
81+
}

0 commit comments

Comments
 (0)