1
0
mirror of https://github.com/uptrace/go-clickhouse.git synced 2025-06-10 23:27:25 +02:00

feat: add support for DateTime64

This commit is contained in:
Vladimir Mihailenco 2022-04-29 18:51:14 +03:00
parent 5ea8b673a3
commit c1e00ef235
7 changed files with 121 additions and 41 deletions

View File

@ -20,6 +20,7 @@ Main features are:
- [Bun](https://github.com/uptrace/bun/)-like query builder. - [Bun](https://github.com/uptrace/bun/)-like query builder.
- [Selecting](https://clickhouse.uptrace.dev/guide/clickhouse-select.html) into scalars, structs, - [Selecting](https://clickhouse.uptrace.dev/guide/clickhouse-select.html) into scalars, structs,
maps, slices of maps/structs/scalars. maps, slices of maps/structs/scalars.
- `Date`, `DateTime`, and `DateTime64`.
- `Array(T)` including nested arrays. - `Array(T)` including nested arrays.
- Enums and `LowCardinality(String)`. - Enums and `LowCardinality(String)`.
- `Nullable(T)` except `Nullable(Array(T))`. - `Nullable(T)` except `Nullable(Array(T))`.
@ -28,10 +29,6 @@ Main features are:
support. support.
- In production at [Uptrace](https://uptrace.dev/) - In production at [Uptrace](https://uptrace.dev/)
Unsupported:
- `DateTime64`
Resources: Resources:
- [**Get started**](https://clickhouse.uptrace.dev/guide/getting-started.html) - [**Get started**](https://clickhouse.uptrace.dev/guide/getting-started.html)

View File

@ -855,6 +855,54 @@ func (c DateTimeColumn) WriteTo(wr *chproto.Writer) error {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
type DateTime64Column struct {
ColumnOf[time.Time]
prec int
}
var _ Columnar = (*DateTime64Column)(nil)
func NewDateTime64Column(typ reflect.Type, chType string, numRow int) Columnar {
return &DateTime64Column{
ColumnOf: NewColumnOf[time.Time](numRow),
prec: parseDateTime64Prec(chType),
}
}
func (c *DateTime64Column) Type() reflect.Type {
return timeType
}
func (c *DateTime64Column) ConvertAssign(idx int, v reflect.Value) error {
v.Set(reflect.ValueOf(c.Column[idx]))
return nil
}
func (c *DateTime64Column) ReadFrom(rd *chproto.Reader, numRow int) error {
c.Alloc(numRow)
mul := int64(math.Pow10(9 - c.prec))
for i := range c.Column {
n, err := rd.Int64()
if err != nil {
return err
}
c.Column[i] = time.Unix(0, n*mul)
}
return nil
}
func (c *DateTime64Column) WriteTo(wr *chproto.Writer) error {
div := int64(math.Pow10(9 - c.prec))
for i := range c.Column {
wr.Int64(c.Column[i].UnixNano() / div)
}
return nil
}
//------------------------------------------------------------------------------
type Int64TimeColumn struct { type Int64TimeColumn struct {
Int64Column Int64Column
} }

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"net" "net"
"reflect" "reflect"
"strconv"
"strings" "strings"
"time" "time"
@ -132,6 +133,9 @@ func ColumnFactory(typ reflect.Type, chType string) NewColumnFunc {
if s := enumType(chType); s != "" { if s := enumType(chType); s != "" {
return NewEnumColumn return NewEnumColumn
} }
if isDateTime64Type(chType) {
return NewDateTime64Column
}
if strings.HasPrefix(chType, "SimpleAggregateFunction(") { if strings.HasPrefix(chType, "SimpleAggregateFunction(") {
chType = chSubType(chType, "SimpleAggregateFunction(") chType = chSubType(chType, "SimpleAggregateFunction(")
@ -236,6 +240,8 @@ func columnFromCHType(chType string) NewColumnFunc {
return NewFloat64Column return NewFloat64Column
case chtype.DateTime: case chtype.DateTime:
return NewDateTimeColumn return NewDateTimeColumn
case chtype.DateTime64:
return NewDateTime64Column
case chtype.Date: case chtype.Date:
return NewDateColumn return NewDateColumn
case chtype.IPv6: case chtype.IPv6:
@ -258,13 +264,12 @@ var (
float32Type = reflect.TypeOf(float32(0)) float32Type = reflect.TypeOf(float32(0))
float64Type = reflect.TypeOf(float64(0)) float64Type = reflect.TypeOf(float64(0))
stringType = reflect.TypeOf("") stringType = reflect.TypeOf("")
bytesType = reflect.TypeOf((*[]byte)(nil)).Elem() bytesType = reflect.TypeOf((*[]byte)(nil)).Elem()
uuidType = reflect.TypeOf((*UUID)(nil)).Elem() uuidType = reflect.TypeOf((*UUID)(nil)).Elem()
timeType = reflect.TypeOf((*time.Time)(nil)).Elem() timeType = reflect.TypeOf((*time.Time)(nil)).Elem()
ipType = reflect.TypeOf((*net.IP)(nil)).Elem() ipType = reflect.TypeOf((*net.IP)(nil)).Elem()
ipNetType = reflect.TypeOf((*net.IPNet)(nil)).Elem() ipNetType = reflect.TypeOf((*net.IPNet)(nil)).Elem()
bfloat16HistType = reflect.TypeOf((*map[chtype.BFloat16]uint64)(nil)).Elem()
int64SliceType = reflect.TypeOf((*[]int64)(nil)).Elem() int64SliceType = reflect.TypeOf((*[]int64)(nil)).Elem()
uint64SliceType = reflect.TypeOf((*[]uint64)(nil)).Elem() uint64SliceType = reflect.TypeOf((*[]uint64)(nil)).Elem()
@ -318,6 +323,9 @@ func goType(chType string) reflect.Type {
if s := dateTimeType(chType); s != "" { if s := dateTimeType(chType); s != "" {
return timeType return timeType
} }
if isDateTime64Type(chType) {
return timeType
}
if s := nullableType(chType); s != "" { if s := nullableType(chType); s != "" {
return reflect.PtrTo(goType(s)) return reflect.PtrTo(goType(s))
} }
@ -366,6 +374,22 @@ func dateTimeType(s string) string {
return chtype.DateTime return chtype.DateTime
} }
func isDateTime64Type(s string) bool {
return chSubType(s, "DateTime64(") != ""
}
func parseDateTime64Prec(s string) int {
s = chSubType(s, "DateTime64(")
if s == "" {
return 0
}
prec, err := strconv.Atoi(s)
if err != nil {
return 0
}
return prec
}
func nullableType(s string) string { func nullableType(s string) string {
return chSubType(s, "Nullable(") return chSubType(s, "Nullable(")
} }

View File

@ -1,20 +1,21 @@
package chtype package chtype
const ( const (
Any = "_" // for decoding into interface{} Any = "_" // for decoding into interface{}
String = "String" String = "String"
UUID = "UUID" UUID = "UUID"
Int8 = "Int8" Int8 = "Int8"
Int16 = "Int16" Int16 = "Int16"
Int32 = "Int32" Int32 = "Int32"
Int64 = "Int64" Int64 = "Int64"
UInt8 = "UInt8" UInt8 = "UInt8"
UInt16 = "UInt16" UInt16 = "UInt16"
UInt32 = "UInt32" UInt32 = "UInt32"
UInt64 = "UInt64" UInt64 = "UInt64"
Float32 = "Float32" Float32 = "Float32"
Float64 = "Float64" Float64 = "Float64"
DateTime = "DateTime" DateTime = "DateTime"
Date = "Date" DateTime64 = "DateTime64"
IPv6 = "IPv6" Date = "Date"
IPv6 = "IPv6"
) )

View File

@ -1,13 +0,0 @@
package chtype
import "math"
type BFloat16 uint16
func ToBFloat16(f float64) BFloat16 {
return BFloat16(math.Float32bits(float32(f)) >> 16)
}
func (f BFloat16) Float32() float32 {
return math.Float32frombits(uint32(f) << 16)
}

View File

@ -244,6 +244,29 @@ func TestScanArrayUint8(t *testing.T) {
require.Equal(t, map[string]any{"ns": []uint8{0, 1, 2}}, m) require.Equal(t, map[string]any{"ns": []uint8{0, 1, 2}}, m)
} }
func TestDateTime64(t *testing.T) {
type Model struct {
Time time.Time `ch:"type:DateTime64(9)"`
}
ctx := context.Background()
db := chDB()
defer db.Close()
err := db.ResetModel(ctx, (*Model)(nil))
require.NoError(t, err)
in := &Model{Time: time.Unix(0, 12345678912345)}
_, err = db.NewInsert().Model(in).Exec(ctx)
require.NoError(t, err)
out := new(Model)
err = db.NewSelect().Model(out).Scan(ctx)
require.NoError(t, err)
require.Equal(t, in.Time.UnixNano(), out.Time.UnixNano())
}
type Event struct { type Event struct {
ch.CHModel `ch:"goch_events,partition:toYYYYMM(created_at)"` ch.CHModel `ch:"goch_events,partition:toYYYYMM(created_at)"`

View File

@ -1,5 +1,5 @@
{ {
"name": "uptrace/go-clickhouse", "name": "goclickhouse",
"version": "0.2.5", "version": "0.2.5",
"main": "index.js", "main": "index.js",
"repository": "git@github.com:uptrace/go-clickhouse.git", "repository": "git@github.com:uptrace/go-clickhouse.git",