Skip to content

Commit 303b18b

Browse files
feat: time and time64 column support
1 parent 757e102 commit 303b18b

File tree

12 files changed

+2028
-22
lines changed

12 files changed

+2028
-22
lines changed

TYPES.md

Lines changed: 50 additions & 10 deletions
Large diffs are not rendered by default.

examples/time_encoding/main.go

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"log"
7+
"time"
8+
9+
"github.com/ClickHouse/clickhouse-go/v2"
10+
"github.com/ClickHouse/clickhouse-go/v2/lib/column"
11+
)
12+
13+
func main() {
14+
// Connect to ClickHouse
15+
conn, err := clickhouse.Open(&clickhouse.Options{
16+
Addr: []string{"localhost:9000"},
17+
Auth: clickhouse.Auth{
18+
Database: "default",
19+
Username: "default",
20+
Password: "",
21+
},
22+
Settings: clickhouse.Settings{
23+
"max_execution_time": 60,
24+
},
25+
Debug: false,
26+
})
27+
if err != nil {
28+
log.Fatal(err)
29+
}
30+
defer conn.Close()
31+
32+
ctx := context.Background()
33+
34+
fmt.Println("=== ClickHouse Time and Time64 Support Demo ===")
35+
36+
// Test 1: Time64 column (fully supported)
37+
fmt.Println("\n--- Testing Time64 Column ---")
38+
39+
// Create table with Time64 column
40+
err = conn.Exec(ctx, "DROP TABLE IF EXISTS test_time64")
41+
if err != nil {
42+
log.Fatal(err)
43+
}
44+
45+
err = conn.Exec(ctx, "CREATE TABLE test_time64 (id UInt32, time64_col Time64(3)) ENGINE = Memory")
46+
if err != nil {
47+
log.Fatal(err)
48+
}
49+
fmt.Println("✓ Time64 table created")
50+
51+
// Insert data using string format
52+
err = conn.Exec(ctx, "INSERT INTO test_time64 VALUES (1, '12:30:45.123')")
53+
if err != nil {
54+
log.Fatal(err)
55+
}
56+
fmt.Println("✓ Time64 data inserted (string format)")
57+
58+
// Insert data using Go-native parameter binding
59+
timeVal64 := time.Date(2024, 1, 1, 15, 16, 17, 123000000, time.UTC)
60+
err = conn.Exec(ctx, "INSERT INTO test_time64 VALUES (?, ?)", 2, timeVal64)
61+
if err != nil {
62+
log.Fatal(err)
63+
}
64+
fmt.Println("✓ Time64 data inserted (Go-native format)")
65+
66+
// Query and display results
67+
var id uint32
68+
var t time.Time
69+
err = conn.QueryRow(ctx, "SELECT id, time64_col FROM test_time64 WHERE id = 1").Scan(&id, &t)
70+
if err != nil {
71+
log.Fatal(err)
72+
}
73+
fmt.Printf("✓ Time64 query result - ID: %d, Time64: %s\n", id, t.Format("15:04:05.000"))
74+
75+
err = conn.QueryRow(ctx, "SELECT id, time64_col FROM test_time64 WHERE id = 2").Scan(&id, &t)
76+
if err != nil {
77+
log.Fatal(err)
78+
}
79+
fmt.Printf("✓ Time64 query result - ID: %d, Time64: %s\n", id, t.Format("15:04:05.000"))
80+
81+
// Test 2: Time column (now fully supported)
82+
fmt.Println("\n--- Testing Time Column ---")
83+
84+
// Create table with Time column
85+
err = conn.Exec(ctx, "DROP TABLE IF EXISTS test_time")
86+
if err != nil {
87+
log.Fatal(err)
88+
}
89+
90+
err = conn.Exec(ctx, "CREATE TABLE test_time (id UInt32, time_col Time) ENGINE = Memory")
91+
if err != nil {
92+
log.Fatal(err)
93+
}
94+
fmt.Println("✓ Time table created")
95+
96+
// Insert data using string format
97+
err = conn.Exec(ctx, "INSERT INTO test_time VALUES (1, '12:30:45')")
98+
if err != nil {
99+
log.Fatal(err)
100+
}
101+
fmt.Println("✓ Time data inserted (string format)")
102+
103+
// Insert data using Go-native parameter binding
104+
timeVal := time.Date(2024, 1, 1, 14, 15, 16, 0, time.UTC)
105+
err = conn.Exec(ctx, "INSERT INTO test_time VALUES (?, ?)", 2, timeVal)
106+
if err != nil {
107+
log.Fatal(err)
108+
}
109+
fmt.Println("✓ Time data inserted (Go-native format)")
110+
111+
// Query and display results
112+
err = conn.QueryRow(ctx, "SELECT id, time_col FROM test_time WHERE id = 1").Scan(&id, &t)
113+
if err != nil {
114+
log.Fatal(err)
115+
}
116+
fmt.Printf("✓ Time query result - ID: %d, Time: %s\n", id, t.Format("15:04:05"))
117+
118+
err = conn.QueryRow(ctx, "SELECT id, time_col FROM test_time WHERE id = 2").Scan(&id, &t)
119+
if err != nil {
120+
log.Fatal(err)
121+
}
122+
fmt.Printf("✓ Time query result - ID: %d, Time: %s\n", id, t.Format("15:04:05"))
123+
124+
// Test 3: Column type verification
125+
fmt.Println("\n--- Column Type Verification ---")
126+
127+
// Verify Time column type
128+
timeCol := &column.Time{}
129+
fmt.Printf("✓ Time column type: %s\n", timeCol.Type())
130+
131+
// Verify Time64 column type
132+
time64Col := &column.Time64{}
133+
fmt.Printf("✓ Time64 column type: %s\n", time64Col.Type())
134+
135+
fmt.Println("\n=== Summary ===")
136+
fmt.Println("✅ Time: Fully supported - insert and query work perfectly")
137+
fmt.Println("✅ Time64: Fully supported - insert and query work perfectly")
138+
fmt.Println("✅ Go-native parameter binding works for both types")
139+
fmt.Println("✅ String format works for both types")
140+
fmt.Println("✅ Column types are properly registered")
141+
fmt.Println("✅ Ready for production use")
142+
}

examples/time_variants/main.go

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"log"
7+
"time"
8+
9+
"github.com/ClickHouse/clickhouse-go/v2"
10+
)
11+
12+
func main() {
13+
// Connect to ClickHouse
14+
conn, err := clickhouse.Open(&clickhouse.Options{
15+
Addr: []string{"localhost:9000"},
16+
Auth: clickhouse.Auth{
17+
Database: "default",
18+
Username: "default",
19+
Password: "",
20+
},
21+
Settings: clickhouse.Settings{
22+
"max_execution_time": 60,
23+
},
24+
Debug: false,
25+
})
26+
if err != nil {
27+
log.Fatal(err)
28+
}
29+
defer conn.Close()
30+
31+
ctx := context.Background()
32+
33+
fmt.Println("=== ClickHouse Time and Time64 Variants Demo ===")
34+
35+
// Step 1: Read and display all existing data
36+
fmt.Println("1. READING EXISTING DATA:")
37+
fmt.Println("==========================")
38+
39+
rows, err := conn.Query(ctx, `
40+
SELECT
41+
id,
42+
time_seconds,
43+
time64_3,
44+
time64_6,
45+
time64_9,
46+
description
47+
FROM time_variants_demo
48+
ORDER BY id
49+
`)
50+
if err != nil {
51+
log.Fatal("Error querying data:", err)
52+
}
53+
defer rows.Close()
54+
55+
fmt.Printf("%-3s %-12s %-15s %-18s %-21s %-25s\n", "ID", "Time", "Time64(3)", "Time64(6)", "Time64(9)", "Description")
56+
fmt.Println(string(make([]byte, 100, 100)))
57+
58+
for rows.Next() {
59+
var (
60+
id uint32
61+
timeSeconds time.Time
62+
time64_3 time.Time
63+
time64_6 time.Time
64+
time64_9 time.Time
65+
description string
66+
)
67+
68+
if err := rows.Scan(&id, &timeSeconds, &time64_3, &time64_6, &time64_9, &description); err != nil {
69+
log.Printf("Error scanning row: %v", err)
70+
continue
71+
}
72+
73+
fmt.Printf("%-3d %-12s %-15s %-18s %-21s %-25s\n",
74+
id,
75+
timeSeconds.Format("15:04:05"),
76+
time64_3.Format("15:04:05.000"),
77+
time64_6.Format("15:04:05.000000"),
78+
time64_9.Format("15:04:05.000000000"),
79+
description)
80+
}
81+
82+
if err := rows.Err(); err != nil {
83+
log.Fatal("Error iterating rows:", err)
84+
}
85+
86+
// Step 2: Modify existing data
87+
fmt.Println("\n2. MODIFYING DATA:")
88+
fmt.Println("===================")
89+
90+
// Update row with ID 1
91+
newTime := time.Date(2024, 1, 1, 14, 25, 30, 0, time.UTC)
92+
newTime64_3 := time.Date(2024, 1, 1, 14, 25, 30, 123000000, time.UTC)
93+
newTime64_6 := time.Date(2024, 1, 1, 14, 25, 30, 123456000, time.UTC)
94+
newTime64_9 := time.Date(2024, 1, 1, 14, 25, 30, 123456789, time.UTC)
95+
96+
err = conn.Exec(ctx, `
97+
ALTER TABLE time_variants_demo
98+
UPDATE
99+
time_seconds = ?,
100+
time64_3 = ?,
101+
time64_6 = ?,
102+
time64_9 = ?,
103+
description = ?
104+
WHERE id = 1
105+
`, newTime, newTime64_3, newTime64_6, newTime64_9, "Updated morning time")
106+
107+
if err != nil {
108+
log.Printf("Error updating data: %v", err)
109+
} else {
110+
fmt.Println("✓ Updated row with ID 1")
111+
}
112+
113+
// Insert new row
114+
newRowTime := time.Date(2024, 1, 1, 18, 45, 15, 0, time.UTC)
115+
newRowTime64_3 := time.Date(2024, 1, 1, 18, 45, 15, 987000000, time.UTC)
116+
newRowTime64_6 := time.Date(2024, 1, 1, 18, 45, 15, 987654000, time.UTC)
117+
newRowTime64_9 := time.Date(2024, 1, 1, 18, 45, 15, 987654321, time.UTC)
118+
119+
err = conn.Exec(ctx, `
120+
INSERT INTO time_variants_demo VALUES (?, ?, ?, ?, ?, ?)
121+
`, 6, newRowTime, newRowTime64_3, newRowTime64_6, newRowTime64_9, "New evening time")
122+
123+
if err != nil {
124+
log.Printf("Error inserting new row: %v", err)
125+
} else {
126+
fmt.Println("✓ Inserted new row with ID 6")
127+
}
128+
129+
// Step 3: Query and display modified data
130+
fmt.Println("\n3. QUERYING MODIFIED DATA:")
131+
fmt.Println("===========================")
132+
133+
rows, err = conn.Query(ctx, `
134+
SELECT
135+
id,
136+
time_seconds,
137+
time64_3,
138+
time64_6,
139+
time64_9,
140+
description
141+
FROM time_variants_demo
142+
ORDER BY id
143+
`)
144+
if err != nil {
145+
log.Fatal("Error querying modified data:", err)
146+
}
147+
defer rows.Close()
148+
149+
fmt.Printf("%-3s %-12s %-15s %-18s %-21s %-25s\n", "ID", "Time", "Time64(3)", "Time64(6)", "Time64(9)", "Description")
150+
fmt.Println(string(make([]byte, 100, 100)))
151+
152+
for rows.Next() {
153+
var (
154+
id uint32
155+
timeSeconds time.Time
156+
time64_3 time.Time
157+
time64_6 time.Time
158+
time64_9 time.Time
159+
description string
160+
)
161+
162+
if err := rows.Scan(&id, &timeSeconds, &time64_3, &time64_6, &time64_9, &description); err != nil {
163+
log.Printf("Error scanning modified row: %v", err)
164+
continue
165+
}
166+
167+
fmt.Printf("%-3d %-12s %-15s %-18s %-21s %-25s\n",
168+
id,
169+
timeSeconds.Format("15:04:05"),
170+
time64_3.Format("15:04:05.000"),
171+
time64_6.Format("15:04:05.000000"),
172+
time64_9.Format("15:04:05.000000000"),
173+
description)
174+
}
175+
176+
if err := rows.Err(); err != nil {
177+
log.Fatal("Error iterating modified rows:", err)
178+
}
179+
180+
// Step 4: Demonstrate different query patterns
181+
fmt.Println("\n4. ADVANCED QUERY PATTERNS:")
182+
fmt.Println("============================")
183+
184+
// Query specific time ranges
185+
fmt.Println("\n--- Times between 12:00 and 16:00 ---")
186+
rows, err = conn.Query(ctx, `
187+
SELECT id, time_seconds, description
188+
FROM time_variants_demo
189+
WHERE time_seconds BETWEEN '12:00:00' AND '16:00:00'
190+
ORDER BY time_seconds
191+
`)
192+
if err != nil {
193+
log.Printf("Error querying time range: %v", err)
194+
} else {
195+
for rows.Next() {
196+
var id uint32
197+
var timeSeconds time.Time
198+
var description string
199+
200+
if err := rows.Scan(&id, &timeSeconds, &description); err != nil {
201+
log.Printf("Error scanning time range row: %v", err)
202+
continue
203+
}
204+
205+
fmt.Printf("ID: %d, Time: %s, Description: %s\n",
206+
id, timeSeconds.Format("15:04:05"), description)
207+
}
208+
rows.Close()
209+
}
210+
211+
// Query with precision comparison
212+
fmt.Println("\n--- Time64 with precision > 500ms ---")
213+
rows, err = conn.Query(ctx, `
214+
SELECT id, time64_3, time64_6, time64_9
215+
FROM time_variants_demo
216+
WHERE time64_3 > '00:00:00.500'
217+
ORDER BY time64_3
218+
`)
219+
if err != nil {
220+
log.Printf("Error querying precision: %v", err)
221+
} else {
222+
for rows.Next() {
223+
var id uint32
224+
var time64_3, time64_6, time64_9 time.Time
225+
226+
if err := rows.Scan(&id, &time64_3, &time64_6, &time64_9); err != nil {
227+
log.Printf("Error scanning precision row: %v", err)
228+
continue
229+
}
230+
231+
fmt.Printf("ID: %d, Time64(3): %s, Time64(6): %s, Time64(9): %s\n",
232+
id,
233+
time64_3.Format("15:04:05.000"),
234+
time64_6.Format("15:04:05.000000"),
235+
time64_9.Format("15:04:05.000000000"))
236+
}
237+
rows.Close()
238+
}
239+
240+
fmt.Println("\n=== Demo Completed Successfully! ===")
241+
}

0 commit comments

Comments
 (0)