1
0
mirror of https://github.com/uptrace/go-clickhouse.git synced 2025-06-08 23:26:11 +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.
- [Selecting](https://clickhouse.uptrace.dev/guide/clickhouse-select.html) into scalars, structs,
maps, slices of maps/structs/scalars.
- `Date`, `DateTime`, and `DateTime64`.
- `Array(T)` including nested arrays.
- Enums and `LowCardinality(String)`.
- `Nullable(T)` except `Nullable(Array(T))`.
@ -28,10 +29,6 @@ Main features are:
support.
- In production at [Uptrace](https://uptrace.dev/)
Unsupported:
- `DateTime64`
Resources:
- [**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 {
Int64Column
}

View File

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

View File

@ -1,20 +1,21 @@
package chtype
const (
Any = "_" // for decoding into interface{}
String = "String"
UUID = "UUID"
Int8 = "Int8"
Int16 = "Int16"
Int32 = "Int32"
Int64 = "Int64"
UInt8 = "UInt8"
UInt16 = "UInt16"
UInt32 = "UInt32"
UInt64 = "UInt64"
Float32 = "Float32"
Float64 = "Float64"
DateTime = "DateTime"
Date = "Date"
IPv6 = "IPv6"
Any = "_" // for decoding into interface{}
String = "String"
UUID = "UUID"
Int8 = "Int8"
Int16 = "Int16"
Int32 = "Int32"
Int64 = "Int64"
UInt8 = "UInt8"
UInt16 = "UInt16"
UInt32 = "UInt32"
UInt64 = "UInt64"
Float32 = "Float32"
Float64 = "Float64"
DateTime = "DateTime"
DateTime64 = "DateTime64"
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)
}
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 {
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",
"main": "index.js",
"repository": "git@github.com:uptrace/go-clickhouse.git",