TYPES.md

July 27, 2026 ยท View on GitHub

The following table aims to capture the Golang types supported for each ClickHouse Column Type.

Whilst each ClickHouse 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 ClickHouse 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.

ClickHouse TypeStringDecimalBoolFixedStringUInt8UInt16UInt32UInt64UInt128UInt256Int8Int16Int32Int64Int128Int256Float32Float64UUIDDateDate32DateTimeDateTime64TimeTime64Enum8Enum16PointRingPolygonMultiPolygonLineStringMultiLineString
Golang Type
uint
uint64X
uint32X
uint16X
uint8X
intXX
int64XXX
int32X
int16XX
int8XX
float32X
float64X
stringXXXXXXXXX
boolX
time.TimeXXXX
time.DurationXX
big.IntXXXX
decimal.DecimalX
uuid.UUIDX
orb.PointX
orb.PolygonX
orb.RingX
orb.MultiPolygonX
orb.LineStringX
orb.MultiLineStringX
[]byteXX
fmt.StringerXX
sql.NullStringX
sql.NullTimeXXXX
sql.NullFloat64X
sql.NullInt64X
sql.NullInt32X
sql.NullInt16X
sql.NullBoolX

Scan Support

All types can be read into a pointer or pointer to a pointer.

ClickHouse TypeStringDecimalBoolFixedStringUInt8UInt16UInt32UInt64UInt128UInt256Int8Int16Int32Int64Int128Int256Float32Float64UUIDDateDate32DateTimeDateTime64TimeTime64Enum8Enum16PointRingPolygonMultiPolygonLineStringMultiLineString
Golang Type
uint
uint64X
uint32X
uint16X
uint8X
int
int64XXX
int32X
int16X
int8X
float32X
float64X
stringXXXXX
boolX
time.TimeXXXX
time.DurationXX
big.IntXXXX
decimal.DecimalX
uuid.UUIDX
orb.PointX
orb.PolygonX
orb.RingX
orb.MultiPolygonX
orb.LineStringX
orb.MultiLineStringX
sql.ScanXXXXXXX
sql.NullStringX
sql.NullTimeXXXX
sql.NullFloat64X
sql.NullInt64X
sql.NullInt32X
sql.NullInt16X
sql.NullBoolX

Time and Time64 Example

ClickHouse 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/ClickHouse/clickhouse-go/v2"
)

func main() {
	conn, err := clickhouse.Open(&clickhouse.Options{
		Addr: []string{"localhost:9000"},
	})
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	ctx := clickhouse.Context(context.Background(), clickhouse.WithSettings(clickhouse.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)