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:
parent
5ea8b673a3
commit
c1e00ef235
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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(")
|
||||
}
|
||||
|
@ -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"
|
||||
)
|
||||
|
@ -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)
|
||||
}
|
@ -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)"`
|
||||
|
||||
|
@ -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",
|
||||
|
Loading…
x
Reference in New Issue
Block a user