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