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()) }