Source: the unexported clickhouse struct becomes client, the ch receiver and chproto import alias follow, error message prefixes read datastore, and the JSON serializer interface drops the vendor infix. resources' metadata key and its accessor are renamed together, with a test proving the embedded YAML still unmarshals — a silent drift there would have made every connection take the unsupported-server-version branch. Codegen: all three templates still emitted the upstream module paths, so regenerating would have quietly undone the rename. Note the templates have drifted from the files they produce (regenerating today drops QBit and scanTypeDuration), so the generated files are left as tracked and only the templates are corrected. Dead links: an earlier mechanical pass invented URLs that do not resolve — github.com/ClickHouse/datastore-go, hanzo-ds/driver issue numbers that belong to upstream, and mangled names of independent projects (mailru, uptrace, nikepan, EpicStep). Third-party names are not ours to rewrite, so those directories are removed rather than repaired, and provenance comments now point at real files. Dev infra: compose runs our own server image at the tag production runs, since the vendor's alpine tag does not exist in our registry. Cluster hostnames, nginx upstreams and server configs are renamed as one set. CHANGELOG.md is upstream's release history for versions this module never released; rewriting it would fabricate a lineage, so it is dropped and the upstream base recorded in README instead. The two long-standing vet findings are fixed by keying the struct literals rather than suppressed. LICENSE and contributors/list keep the vendor name on purpose: one is the copyright notice Apache-2.0 requires, the other is real people's names. CI now enforces build, vet, test, gofmt and a grep gate.
27 KiB
The following table aims to capture the Golang types supported for each Datastore Column Type.
Whilst each Datastore type often has a logical Golang type, we aim to support implicit conversions where possible and provided no precision loss will be incurred - thus alleviating the need for users to ensure their data aligns perfectly with Datastore types.
This effort is ongoing and can be separated in to insertion (Append/AppendRow) and read time (via a Scan). Should you need support for a specific conversion, please raise an issue.
Append Support
All types can be inserted as a value or pointer.
| Datastore Type | String | Decimal | Bool | FixedString | UInt8 | UInt16 | UInt32 | UInt64 | UInt128 | UInt256 | Int8 | Int16 | Int32 | Int64 | Int128 | Int256 | Float32 | Float64 | UUID | Date | Date32 | DateTime | DateTime64 | Time | Time64 | Enum8 | Enum16 | Point | Ring | Polygon | MultiPolygon | LineString | MultiLineString | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Golang Type | ||||||||||||||||||||||||||||||||||
| uint | ||||||||||||||||||||||||||||||||||
| unit64 | X | |||||||||||||||||||||||||||||||||
| uint32 | X | |||||||||||||||||||||||||||||||||
| uint16 | X | |||||||||||||||||||||||||||||||||
| uint8 | X | |||||||||||||||||||||||||||||||||
| int | ||||||||||||||||||||||||||||||||||
| int64 | X | X | X | |||||||||||||||||||||||||||||||
| int32 | X | |||||||||||||||||||||||||||||||||
| int16 | X | X | ||||||||||||||||||||||||||||||||
| int8 | X | X | ||||||||||||||||||||||||||||||||
| float32 | X | |||||||||||||||||||||||||||||||||
| float64 | X | |||||||||||||||||||||||||||||||||
| string | X | X | X | X | X | X | X | X | X | |||||||||||||||||||||||||
| bool | X | |||||||||||||||||||||||||||||||||
| time.Time | X | X | X | X | ||||||||||||||||||||||||||||||
| time.Duration | X | X | ||||||||||||||||||||||||||||||||
| big.Int | X | X | X | X | ||||||||||||||||||||||||||||||
| decimal.Decimal | X | |||||||||||||||||||||||||||||||||
| uuid.UUID | X | |||||||||||||||||||||||||||||||||
| orb.Point | X | |||||||||||||||||||||||||||||||||
| orb.Polygon | X | |||||||||||||||||||||||||||||||||
| orb.Ring | X | |||||||||||||||||||||||||||||||||
| orb.MultiPolygon | X | |||||||||||||||||||||||||||||||||
| orb.LineString | X | |||||||||||||||||||||||||||||||||
| orb.MultiLineString | X | |||||||||||||||||||||||||||||||||
| []byte | X | |||||||||||||||||||||||||||||||||
| fmt.Stringer | ||||||||||||||||||||||||||||||||||
| sql.NullString | X | |||||||||||||||||||||||||||||||||
| sql.NullTime | X | X | X | X | ||||||||||||||||||||||||||||||
| sql.NullFloat64 | X | |||||||||||||||||||||||||||||||||
| sql.NullInt64 | X | |||||||||||||||||||||||||||||||||
| sql.NullInt32 | X | |||||||||||||||||||||||||||||||||
| sql.NullInt16 | X | |||||||||||||||||||||||||||||||||
| sql.NullBool | X |
Scan Support
All types can be read into a pointer or pointer to a pointer.
| Datastore Type | String | Decimal | Bool | FixedString | UInt8 | UInt16 | UInt32 | UInt64 | UInt128 | UInt256 | Int8 | Int16 | Int32 | Int64 | Int128 | Int256 | Float32 | Float64 | UUID | Date | Date32 | DateTime | DateTime64 | Time | Time64 | Enum8 | Enum16 | Point | Ring | Polygon | MultiPolygon | LineString | MultiLineString | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Golang Type | ||||||||||||||||||||||||||||||||||
| uint | ||||||||||||||||||||||||||||||||||
| unit64 | X | |||||||||||||||||||||||||||||||||
| uint32 | X | |||||||||||||||||||||||||||||||||
| uint16 | X | |||||||||||||||||||||||||||||||||
| uint8 | X | |||||||||||||||||||||||||||||||||
| int | ||||||||||||||||||||||||||||||||||
| int64 | X | x | x | |||||||||||||||||||||||||||||||
| int32 | X | |||||||||||||||||||||||||||||||||
| int16 | X | |||||||||||||||||||||||||||||||||
| int8 | X | |||||||||||||||||||||||||||||||||
| float32 | X | |||||||||||||||||||||||||||||||||
| float64 | X | |||||||||||||||||||||||||||||||||
| string | X | X | X | X | X | |||||||||||||||||||||||||||||
| bool | X | |||||||||||||||||||||||||||||||||
| time.Time | X | X | X | X | ||||||||||||||||||||||||||||||
| time.Duration | X | X | ||||||||||||||||||||||||||||||||
| big.Int | X | X | X | X | ||||||||||||||||||||||||||||||
| decimal.Decimal | X | |||||||||||||||||||||||||||||||||
| uuid.UUID | X | |||||||||||||||||||||||||||||||||
| orb.Point | X | |||||||||||||||||||||||||||||||||
| orb.Polygon | X | |||||||||||||||||||||||||||||||||
| orb.Ring | X | |||||||||||||||||||||||||||||||||
| orb.MultiPolygon | X | |||||||||||||||||||||||||||||||||
| orb.LineString | X | |||||||||||||||||||||||||||||||||
| orb.MultiLineString | X | |||||||||||||||||||||||||||||||||
| sql.Scan | X | X | X | X | X | |||||||||||||||||||||||||||||
| sql.NullString | X | |||||||||||||||||||||||||||||||||
| sql.NullTime | X | X | X | X | ||||||||||||||||||||||||||||||
| sql.NullFloat64 | X | |||||||||||||||||||||||||||||||||
| sql.NullInt64 | X | |||||||||||||||||||||||||||||||||
| sql.NullInt32 | X | |||||||||||||||||||||||||||||||||
| sql.NullInt16 | X | |||||||||||||||||||||||||||||||||
| sql.NullBool | X |
Time and Time64 Example
Datastore Time and Time64 types represent time-of-day values (without date). In Go, these map to time.Duration representing elapsed time since midnight.
package main
import (
"context"
"fmt"
"time"
"github.com/hanzo-ds/go"
)
func main() {
conn, err := datastore.Open(&datastore.Options{
Addr: []string{"localhost:9000"},
})
if err != nil {
panic(err)
}
defer conn.Close()
ctx := datastore.Context(context.Background(), datastore.WithSettings(datastore.Settings{
"enable_time_time64_type": 1,
}))
// Create table with Time and Time64 columns
err = conn.Exec(ctx, `
CREATE TABLE IF NOT EXISTS time_example (
id UInt32,
t_time Time, -- second precision
t_time64_3 Time64(3), -- millisecond precision
t_time64_6 Time64(6), -- microsecond precision
t_time64_9 Time64(9), -- nanosecond precision
arr_time Array(Time), -- array of Time values
nullable_time Nullable(Time64(9)) -- nullable Time64
) ENGINE = MergeTree() ORDER BY id
`)
if err != nil {
panic(err)
}
// Insert data using time.Duration
batch, err := conn.PrepareBatch(ctx, "INSERT INTO time_example")
if err != nil {
panic(err)
}
// Time values as time.Duration (duration since midnight)
timeValue := 12*time.Hour + 34*time.Minute + 56*time.Second
time64Value := 15*time.Hour + 30*time.Minute + 45*time.Second + 123456789*time.Nanosecond
timeArray := []time.Duration{
6 * time.Hour,
12*time.Hour + 30*time.Minute,
18*time.Hour + 45*time.Minute + 30*time.Second,
}
err = batch.Append(
uint32(1),
timeValue,
time64Value,
time64Value,
time64Value,
timeArray,
&time64Value, // nullable value
)
if err != nil {
panic(err)
}
// Insert NULL for nullable column
err = batch.Append(
uint32(2),
timeValue,
time64Value,
time64Value,
time64Value,
timeArray,
nil, // NULL value
)
if err != nil {
panic(err)
}
err = batch.Send()
if err != nil {
panic(err)
}
// Query data
rows, err := conn.Query(ctx, "SELECT id, t_time, t_time64_9, arr_time, nullable_time FROM time_example ORDER BY id")
if err != nil {
panic(err)
}
defer rows.Close()
for rows.Next() {
var (
id uint32
tTime time.Duration
tTime64 time.Duration
arrTime []time.Duration
nullableTime *time.Duration
)
if err := rows.Scan(&id, &tTime, &tTime64, &arrTime, &nullableTime); err != nil {
panic(err)
}
fmt.Printf("ID: %d\n", id)
fmt.Printf(" Time (second precision): %v\n", tTime)
fmt.Printf(" Time64(9) (nanosecond precision): %v\n", tTime64)
fmt.Printf(" Array(Time): %v\n", arrTime)
if nullableTime != nil {
fmt.Printf(" Nullable Time64: %v\n", *nullableTime)
} else {
fmt.Printf(" Nullable Time64: NULL\n")
}
}
}
// Output:
// ID: 1
// Time (second precision): 12h34m56s
// Time64(9) (nanosecond precision): 15h30m45.123456789s
// Array(Time): [6h0m0s 12h30m0s 18h45m30s]
// Nullable Time64: 15h30m45.123456789s
// ID: 2
// Time (second precision): 12h34m56s
// Time64(9) (nanosecond precision): 15h30m45.123456789s
// Array(Time): [6h0m0s 12h30m0s 18h45m30s]
// Nullable Time64: NULL
// Key points:
// - Time has second precision (truncates to seconds)
// - Time64(N) supports precision from 0 (second) to 9 (nanosecond)
// - Use time.Duration for both Time and Time64 types
// - String input format is also supported: "HH:MM:SS" or "HH:MM:SS.sss..."
// - Time values represent time-of-day only (no date component)
// - Timezone is not applicable (values are timezone-agnostic)