Skip to content
This repository was archived by the owner on Feb 26, 2024. It is now read-only.

Commit 5086663

Browse files
Lucas BaierAlan Braithwaite
Lucas Baier
authored and
Alan Braithwaite
committed
Add type tuple read support
1 parent 98b725a commit 5086663

File tree

5 files changed

+443
-1
lines changed

5 files changed

+443
-1
lines changed

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ tcp://host1:9000?username=user&password=qwerty&database=clicks&read_timeout=10&w
5151
* Enum
5252
* UUID
5353
* Nullable(T)
54+
* Tuple(...T) (one-dimensional)
5455
* [Array(T) (one-dimensional)](https://clickhouse.yandex/reference_en.html#Array(T)) [godoc](https://godoc.org/github.com/ClickHouse/clickhouse-go#Array)
5556

5657
## TODO
@@ -297,4 +298,4 @@ func main() {
297298
log.Fatal(err)
298299
}
299300
}
300-
```
301+
```

clickhouse_tuple_test.go

+324
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,324 @@
1+
package clickhouse_test
2+
3+
import (
4+
"database/sql"
5+
"testing"
6+
"time"
7+
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
func Test_Tuple(t *testing.T) {
12+
const (
13+
ddl = `
14+
CREATE TABLE clickhouse_test_tuple (
15+
int8 Int8,
16+
int16 Int16,
17+
int32 Int32,
18+
int64 Int64,
19+
uint8 UInt8,
20+
uint16 UInt16,
21+
uint32 UInt32,
22+
uint64 UInt64,
23+
float32 Float32,
24+
float64 Float64,
25+
string String,
26+
fString FixedString(2),
27+
date Date,
28+
datetime DateTime,
29+
enum8 Enum8 ('a' = 1, 'b' = 2),
30+
enum16 Enum16('c' = 1, 'd' = 2),
31+
array Array(String),
32+
arrayArray Array(Array(String)),
33+
int8N Nullable(Int8),
34+
int16N Nullable(Int16),
35+
int32N Nullable(Int32),
36+
int64N Nullable(Int64),
37+
uint8N Nullable(UInt8),
38+
uint16N Nullable(UInt16),
39+
uint32N Nullable(UInt32),
40+
uint64N Nullable(UInt64),
41+
float32N Nullable(Float32),
42+
float64N Nullable(Float64),
43+
stringN Nullable(String),
44+
fStringN Nullable(FixedString(2)),
45+
dateN Nullable(Date),
46+
datetimeN Nullable(DateTime),
47+
enum8N Nullable(Enum8 ('a' = 1, 'b' = 2)),
48+
enum16N Nullable(Enum16('c' = 1, 'd' = 2))
49+
) Engine=Memory;
50+
`
51+
dml = `
52+
INSERT INTO clickhouse_test_tuple (
53+
int8,
54+
int16,
55+
int32,
56+
int64,
57+
uint8,
58+
uint16,
59+
uint32,
60+
uint64,
61+
float32,
62+
float64,
63+
string,
64+
fString,
65+
date,
66+
datetime,
67+
enum8,
68+
enum16,
69+
array,
70+
arrayArray,
71+
int8N,
72+
int16N,
73+
int32N,
74+
int64N,
75+
uint8N,
76+
uint16N,
77+
uint32N,
78+
uint64N,
79+
float32N,
80+
float64N,
81+
stringN,
82+
fStringN,
83+
dateN,
84+
datetimeN,
85+
enum8N,
86+
enum16N
87+
) VALUES (
88+
?,
89+
?,
90+
?,
91+
?,
92+
?,
93+
?,
94+
?,
95+
?,
96+
?,
97+
?,
98+
?,
99+
?,
100+
?,
101+
?,
102+
?,
103+
?,
104+
?,
105+
?,
106+
?,
107+
?,
108+
?,
109+
?,
110+
?,
111+
?,
112+
?,
113+
?,
114+
?,
115+
?,
116+
?,
117+
?,
118+
?,
119+
?,
120+
?,
121+
?
122+
)
123+
`
124+
query = `
125+
SELECT
126+
(
127+
int8,
128+
int16,
129+
int32,
130+
int64,
131+
uint8,
132+
uint16,
133+
uint32,
134+
uint64,
135+
float32,
136+
float64,
137+
string,
138+
fString,
139+
date,
140+
datetime,
141+
enum8,
142+
enum16,
143+
array,
144+
(6.2, 'test'),
145+
int8N,
146+
int16N,
147+
int32N,
148+
int64N,
149+
uint8N,
150+
uint16N,
151+
uint32N,
152+
uint64N,
153+
float32N,
154+
float64N,
155+
stringN,
156+
fStringN,
157+
dateN,
158+
datetimeN,
159+
enum8N,
160+
enum16N
161+
)
162+
FROM clickhouse_test_tuple
163+
`
164+
)
165+
166+
if connect, err := sql.Open("clickhouse", "tcp://127.0.0.1:9000?debug=true"); assert.NoError(t, err) {
167+
if tx, err := connect.Begin(); assert.NoError(t, err) {
168+
if _, err := connect.Exec("DROP TABLE IF EXISTS clickhouse_test_tuple"); assert.NoError(t, err) {
169+
if _, err := tx.Exec(ddl); assert.NoError(t, err) {
170+
if tx, err := connect.Begin(); assert.NoError(t, err) {
171+
if stmt, err := tx.Prepare(dml); assert.NoError(t, err) {
172+
for i := 0; i < 10; i++ {
173+
if _, err := stmt.Exec(
174+
8,
175+
16,
176+
32,
177+
64,
178+
18,
179+
116,
180+
132,
181+
165,
182+
1.1,
183+
2.2,
184+
"RU",
185+
"CN",
186+
time.Now(),
187+
time.Now(),
188+
"a",
189+
"c",
190+
[]string{"A", "B", "C"},
191+
[][]string{{"A", "B"}, {"CC", "DD", "EE"}},
192+
new(int8),
193+
16,
194+
new(int32),
195+
64,
196+
18,
197+
116,
198+
132,
199+
165,
200+
1.1,
201+
2.2,
202+
nil,
203+
"CN",
204+
time.Now(),
205+
time.Now(),
206+
"a",
207+
"c",
208+
); !assert.NoError(t, err) {
209+
t.Fatal(err)
210+
}
211+
}
212+
}
213+
if err := tx.Commit(); !assert.NoError(t, err) {
214+
t.Fatal(err)
215+
}
216+
}
217+
if rows, err := connect.Query(query); assert.NoError(t, err) {
218+
for i := 0; rows.Next(); i++ {
219+
var (
220+
tuple []interface{}
221+
)
222+
if err := rows.Scan(
223+
&tuple,
224+
); assert.NoError(t, err) {
225+
if assert.IsType(t, int8(8), tuple[0]) {
226+
assert.Equal(t, int8(8), tuple[0].(int8))
227+
}
228+
if assert.IsType(t, int16(16), tuple[1]) {
229+
assert.Equal(t, int16(16), tuple[1].(int16))
230+
}
231+
if assert.IsType(t, int32(32), tuple[2]) {
232+
assert.Equal(t, int32(32), tuple[2].(int32))
233+
}
234+
if assert.IsType(t, int64(64), tuple[3]) {
235+
assert.Equal(t, int64(64), tuple[3].(int64))
236+
}
237+
if assert.IsType(t, uint8(18), tuple[4]) {
238+
assert.Equal(t, uint8(18), tuple[4].(uint8))
239+
}
240+
if assert.IsType(t, uint16(116), tuple[5]) {
241+
assert.Equal(t, uint16(116), tuple[5].(uint16))
242+
}
243+
if assert.IsType(t, uint32(132), tuple[6]) {
244+
assert.Equal(t, uint32(132), tuple[6].(uint32))
245+
}
246+
if assert.IsType(t, uint64(165), tuple[7]) {
247+
assert.Equal(t, uint64(165), tuple[7].(uint64))
248+
}
249+
if assert.IsType(t, float32(1.1), tuple[8]) {
250+
assert.Equal(t, float32(1.1), tuple[8].(float32))
251+
}
252+
if assert.IsType(t, float64(2.2), tuple[9]) {
253+
assert.Equal(t, float64(2.2), tuple[9].(float64))
254+
}
255+
if assert.IsType(t, "RU", tuple[10]) {
256+
assert.Equal(t, "RU", tuple[10].(string))
257+
}
258+
if assert.IsType(t, "CN", tuple[11]) {
259+
assert.Equal(t, "CN", tuple[11].(string))
260+
}
261+
if assert.IsType(t, time.Now(), tuple[12]) {
262+
// nothing
263+
}
264+
if assert.IsType(t, time.Now(), tuple[13]) {
265+
// nothing
266+
}
267+
if assert.IsType(t, "a", tuple[14]) {
268+
assert.Equal(t, "a", tuple[14].(string))
269+
}
270+
if assert.IsType(t, "c", tuple[15]) {
271+
assert.Equal(t, "c", tuple[15].(string))
272+
}
273+
if assert.IsType(t, []string{"A", "B", "C"}, tuple[16]) {
274+
assert.Equal(t, []string{"A", "B", "C"}, tuple[16].([]string))
275+
}
276+
if assert.IsType(t, []interface{}{}, tuple[17]) {
277+
assert.Equal(t, []interface{}{6.2, "test"}, tuple[17].([]interface{}))
278+
}
279+
280+
if assert.IsType(t, int8(0), tuple[18]) {
281+
assert.Equal(t, int8(0), tuple[18].(int8))
282+
}
283+
if assert.IsType(t, int16(16), tuple[19]) {
284+
assert.Equal(t, int16(16), tuple[19].(int16))
285+
}
286+
if assert.IsType(t, int32(0), tuple[20]) {
287+
assert.Equal(t, int32(0), tuple[20].(int32))
288+
}
289+
if assert.IsType(t, int64(64), tuple[21]) {
290+
assert.Equal(t, int64(64), tuple[21].(int64))
291+
}
292+
if assert.IsType(t, uint8(18), tuple[22]) {
293+
assert.Equal(t, uint8(18), tuple[22].(uint8))
294+
}
295+
if assert.IsType(t, uint16(116), tuple[23]) {
296+
assert.Equal(t, uint16(116), tuple[23].(uint16))
297+
}
298+
if assert.IsType(t, uint32(132), tuple[24]) {
299+
assert.Equal(t, uint32(132), tuple[24].(uint32))
300+
}
301+
if assert.IsType(t, uint64(165), tuple[25]) {
302+
assert.Equal(t, uint64(165), tuple[25].(uint64))
303+
}
304+
if assert.IsType(t, float32(1.1), tuple[26]) {
305+
assert.Equal(t, float32(1.1), tuple[26].(float32))
306+
}
307+
if assert.IsType(t, float64(2.2), tuple[27]) {
308+
assert.Equal(t, float64(2.2), tuple[27].(float64))
309+
}
310+
if assert.Nil(t, tuple[28]) {
311+
if assert.IsType(t, "CN", tuple[29]) {
312+
assert.Equal(t, "CN", tuple[29].(string))
313+
}
314+
}
315+
316+
t.Log(tuple)
317+
}
318+
}
319+
}
320+
}
321+
}
322+
}
323+
}
324+
}

lib/column/column.go

+2
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,8 @@ func Factory(name, chType string, timezone *time.Location) (Column, error) {
180180
} else {
181181
return Factory(name, nestedType, timezone)
182182
}
183+
case strings.HasPrefix(chType, "Tuple"):
184+
return parseTuple(name, chType, timezone)
183185
}
184186
return nil, fmt.Errorf("column: unhandled type %v", chType)
185187
}

0 commit comments

Comments
 (0)