mirror of
https://github.com/uptrace/go-clickhouse.git
synced 2025-06-08 23:26:11 +02:00
118 lines
2.2 KiB
Go
118 lines
2.2 KiB
Go
|
package ch
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"reflect"
|
||
|
"time"
|
||
|
|
||
|
"github.com/uptrace/go-clickhouse/ch/chschema"
|
||
|
)
|
||
|
|
||
|
var errNilModel = errors.New("ch: Model(nil)")
|
||
|
|
||
|
var (
|
||
|
timeType = reflect.TypeOf((*time.Time)(nil)).Elem()
|
||
|
mapType = reflect.TypeOf((*map[string]any)(nil)).Elem()
|
||
|
)
|
||
|
|
||
|
type (
|
||
|
Query = chschema.Query
|
||
|
Model = chschema.Model
|
||
|
)
|
||
|
|
||
|
func newModel(db *DB, values ...any) (Model, error) {
|
||
|
if len(values) > 1 {
|
||
|
return scan(values...), nil
|
||
|
}
|
||
|
|
||
|
v0 := values[0]
|
||
|
switch v0 := v0.(type) {
|
||
|
case Model:
|
||
|
return v0, nil
|
||
|
}
|
||
|
|
||
|
v := reflect.ValueOf(v0)
|
||
|
if !v.IsValid() {
|
||
|
return nil, errNilModel
|
||
|
}
|
||
|
if v.Kind() != reflect.Ptr {
|
||
|
return nil, fmt.Errorf("ch: Model(non-pointer %T)", v0)
|
||
|
}
|
||
|
v = v.Elem()
|
||
|
|
||
|
switch v.Kind() {
|
||
|
case reflect.Struct:
|
||
|
if v.Type() != timeType {
|
||
|
return newStructTableModelValue(db, v), nil
|
||
|
}
|
||
|
case reflect.Slice:
|
||
|
typ := v.Type()
|
||
|
elemType := indirectType(typ.Elem())
|
||
|
if elemType == mapType {
|
||
|
return newSliceMapModel(v), nil
|
||
|
}
|
||
|
if elemType.Kind() == reflect.Struct && elemType != timeType {
|
||
|
return newSliceTableModel(db, v, elemType), nil
|
||
|
}
|
||
|
case reflect.Map:
|
||
|
if v.Type() == mapType {
|
||
|
return newMapModel(v), nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return scan(v0), nil
|
||
|
}
|
||
|
|
||
|
func scan(values ...any) Model {
|
||
|
m := &scanModel{
|
||
|
values: make([]reflect.Value, len(values)),
|
||
|
}
|
||
|
for i, v := range values {
|
||
|
m.values[i] = reflect.ValueOf(v).Elem()
|
||
|
}
|
||
|
return m
|
||
|
}
|
||
|
|
||
|
type TableModel interface {
|
||
|
Model
|
||
|
|
||
|
Table() *chschema.Table
|
||
|
Block(fields []*chschema.Field) *chschema.Block
|
||
|
}
|
||
|
|
||
|
func newTableModel(db *DB, value any) (TableModel, error) {
|
||
|
if value, ok := value.(TableModel); ok {
|
||
|
return value, nil
|
||
|
}
|
||
|
|
||
|
v := reflect.ValueOf(value)
|
||
|
if !v.IsValid() {
|
||
|
return nil, errNilModel
|
||
|
}
|
||
|
if v.Kind() != reflect.Ptr {
|
||
|
return nil, fmt.Errorf("ch: Model(non-pointer %T)", value)
|
||
|
}
|
||
|
|
||
|
if v.IsNil() {
|
||
|
typ := v.Type().Elem()
|
||
|
if typ.Kind() == reflect.Struct {
|
||
|
return newStructTableModel(db, chschema.TableForType(typ)), nil
|
||
|
}
|
||
|
return nil, errNilModel
|
||
|
}
|
||
|
|
||
|
v = v.Elem()
|
||
|
|
||
|
switch v.Kind() {
|
||
|
case reflect.Struct:
|
||
|
return newStructTableModelValue(db, v), nil
|
||
|
case reflect.Slice:
|
||
|
elemType := sliceElemType(v)
|
||
|
if elemType.Kind() == reflect.Struct {
|
||
|
return newSliceTableModel(db, v, elemType), nil
|
||
|
}
|
||
|
}
|
||
|
return nil, fmt.Errorf("ch: Model(unsupported %s)", v.Type())
|
||
|
}
|