You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
972 lines
31 KiB
972 lines
31 KiB
package schema |
|
|
|
import ( |
|
"context" |
|
"database/sql" |
|
"database/sql/driver" |
|
"fmt" |
|
"reflect" |
|
"strconv" |
|
"strings" |
|
"sync" |
|
"time" |
|
|
|
"github.com/jinzhu/now" |
|
"gorm.io/gorm/clause" |
|
"gorm.io/gorm/utils" |
|
) |
|
|
|
// special types' reflect type |
|
var ( |
|
TimeReflectType = reflect.TypeOf(time.Time{}) |
|
TimePtrReflectType = reflect.TypeOf(&time.Time{}) |
|
ByteReflectType = reflect.TypeOf(uint8(0)) |
|
) |
|
|
|
type ( |
|
// DataType GORM data type |
|
DataType string |
|
// TimeType GORM time type |
|
TimeType int64 |
|
) |
|
|
|
// GORM time types |
|
const ( |
|
UnixTime TimeType = 1 |
|
UnixSecond TimeType = 2 |
|
UnixMillisecond TimeType = 3 |
|
UnixNanosecond TimeType = 4 |
|
) |
|
|
|
// GORM fields types |
|
const ( |
|
Bool DataType = "bool" |
|
Int DataType = "int" |
|
Uint DataType = "uint" |
|
Float DataType = "float" |
|
String DataType = "string" |
|
Time DataType = "time" |
|
Bytes DataType = "bytes" |
|
) |
|
|
|
// Field is the representation of model schema's field |
|
type Field struct { |
|
Name string |
|
DBName string |
|
BindNames []string |
|
DataType DataType |
|
GORMDataType DataType |
|
PrimaryKey bool |
|
AutoIncrement bool |
|
AutoIncrementIncrement int64 |
|
Creatable bool |
|
Updatable bool |
|
Readable bool |
|
AutoCreateTime TimeType |
|
AutoUpdateTime TimeType |
|
HasDefaultValue bool |
|
DefaultValue string |
|
DefaultValueInterface interface{} |
|
NotNull bool |
|
Unique bool |
|
Comment string |
|
Size int |
|
Precision int |
|
Scale int |
|
IgnoreMigration bool |
|
FieldType reflect.Type |
|
IndirectFieldType reflect.Type |
|
StructField reflect.StructField |
|
Tag reflect.StructTag |
|
TagSettings map[string]string |
|
Schema *Schema |
|
EmbeddedSchema *Schema |
|
OwnerSchema *Schema |
|
ReflectValueOf func(context.Context, reflect.Value) reflect.Value |
|
ValueOf func(context.Context, reflect.Value) (value interface{}, zero bool) |
|
Set func(context.Context, reflect.Value, interface{}) error |
|
Serializer SerializerInterface |
|
NewValuePool FieldNewValuePool |
|
} |
|
|
|
// ParseField parses reflect.StructField to Field |
|
func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field { |
|
var ( |
|
err error |
|
tagSetting = ParseTagSetting(fieldStruct.Tag.Get("gorm"), ";") |
|
) |
|
|
|
field := &Field{ |
|
Name: fieldStruct.Name, |
|
DBName: tagSetting["COLUMN"], |
|
BindNames: []string{fieldStruct.Name}, |
|
FieldType: fieldStruct.Type, |
|
IndirectFieldType: fieldStruct.Type, |
|
StructField: fieldStruct, |
|
Tag: fieldStruct.Tag, |
|
TagSettings: tagSetting, |
|
Schema: schema, |
|
Creatable: true, |
|
Updatable: true, |
|
Readable: true, |
|
PrimaryKey: utils.CheckTruth(tagSetting["PRIMARYKEY"], tagSetting["PRIMARY_KEY"]), |
|
AutoIncrement: utils.CheckTruth(tagSetting["AUTOINCREMENT"]), |
|
HasDefaultValue: utils.CheckTruth(tagSetting["AUTOINCREMENT"]), |
|
NotNull: utils.CheckTruth(tagSetting["NOT NULL"], tagSetting["NOTNULL"]), |
|
Unique: utils.CheckTruth(tagSetting["UNIQUE"]), |
|
Comment: tagSetting["COMMENT"], |
|
AutoIncrementIncrement: 1, |
|
} |
|
|
|
for field.IndirectFieldType.Kind() == reflect.Ptr { |
|
field.IndirectFieldType = field.IndirectFieldType.Elem() |
|
} |
|
|
|
fieldValue := reflect.New(field.IndirectFieldType) |
|
// if field is valuer, used its value or first field as data type |
|
valuer, isValuer := fieldValue.Interface().(driver.Valuer) |
|
if isValuer { |
|
if _, ok := fieldValue.Interface().(GormDataTypeInterface); !ok { |
|
if v, err := valuer.Value(); reflect.ValueOf(v).IsValid() && err == nil { |
|
fieldValue = reflect.ValueOf(v) |
|
} |
|
|
|
// Use the field struct's first field type as data type, e.g: use `string` for sql.NullString |
|
var getRealFieldValue func(reflect.Value) |
|
getRealFieldValue = func(v reflect.Value) { |
|
var ( |
|
rv = reflect.Indirect(v) |
|
rvType = rv.Type() |
|
) |
|
|
|
if rv.Kind() == reflect.Struct && !rvType.ConvertibleTo(TimeReflectType) { |
|
for i := 0; i < rvType.NumField(); i++ { |
|
for key, value := range ParseTagSetting(rvType.Field(i).Tag.Get("gorm"), ";") { |
|
if _, ok := field.TagSettings[key]; !ok { |
|
field.TagSettings[key] = value |
|
} |
|
} |
|
} |
|
|
|
for i := 0; i < rvType.NumField(); i++ { |
|
newFieldType := rvType.Field(i).Type |
|
for newFieldType.Kind() == reflect.Ptr { |
|
newFieldType = newFieldType.Elem() |
|
} |
|
|
|
fieldValue = reflect.New(newFieldType) |
|
if rvType != reflect.Indirect(fieldValue).Type() { |
|
getRealFieldValue(fieldValue) |
|
} |
|
|
|
if fieldValue.IsValid() { |
|
return |
|
} |
|
} |
|
} |
|
} |
|
|
|
getRealFieldValue(fieldValue) |
|
} |
|
} |
|
|
|
if v, isSerializer := fieldValue.Interface().(SerializerInterface); isSerializer { |
|
field.DataType = String |
|
field.Serializer = v |
|
} else { |
|
var serializerName = field.TagSettings["JSON"] |
|
if serializerName == "" { |
|
serializerName = field.TagSettings["SERIALIZER"] |
|
} |
|
if serializerName != "" { |
|
if serializer, ok := GetSerializer(serializerName); ok { |
|
// Set default data type to string for serializer |
|
field.DataType = String |
|
field.Serializer = serializer |
|
} else { |
|
schema.err = fmt.Errorf("invalid serializer type %v", serializerName) |
|
} |
|
} |
|
} |
|
|
|
if num, ok := field.TagSettings["AUTOINCREMENTINCREMENT"]; ok { |
|
field.AutoIncrementIncrement, _ = strconv.ParseInt(num, 10, 64) |
|
} |
|
|
|
if v, ok := field.TagSettings["DEFAULT"]; ok { |
|
field.HasDefaultValue = true |
|
field.DefaultValue = v |
|
} |
|
|
|
if num, ok := field.TagSettings["SIZE"]; ok { |
|
if field.Size, err = strconv.Atoi(num); err != nil { |
|
field.Size = -1 |
|
} |
|
} |
|
|
|
if p, ok := field.TagSettings["PRECISION"]; ok { |
|
field.Precision, _ = strconv.Atoi(p) |
|
} |
|
|
|
if s, ok := field.TagSettings["SCALE"]; ok { |
|
field.Scale, _ = strconv.Atoi(s) |
|
} |
|
|
|
// default value is function or null or blank (primary keys) |
|
field.DefaultValue = strings.TrimSpace(field.DefaultValue) |
|
skipParseDefaultValue := strings.Contains(field.DefaultValue, "(") && |
|
strings.Contains(field.DefaultValue, ")") || strings.ToLower(field.DefaultValue) == "null" || field.DefaultValue == "" |
|
switch reflect.Indirect(fieldValue).Kind() { |
|
case reflect.Bool: |
|
field.DataType = Bool |
|
if field.HasDefaultValue && !skipParseDefaultValue { |
|
if field.DefaultValueInterface, err = strconv.ParseBool(field.DefaultValue); err != nil { |
|
schema.err = fmt.Errorf("failed to parse %s as default value for bool, got error: %v", field.DefaultValue, err) |
|
} |
|
} |
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: |
|
field.DataType = Int |
|
if field.HasDefaultValue && !skipParseDefaultValue { |
|
if field.DefaultValueInterface, err = strconv.ParseInt(field.DefaultValue, 0, 64); err != nil { |
|
schema.err = fmt.Errorf("failed to parse %s as default value for int, got error: %v", field.DefaultValue, err) |
|
} |
|
} |
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: |
|
field.DataType = Uint |
|
if field.HasDefaultValue && !skipParseDefaultValue { |
|
if field.DefaultValueInterface, err = strconv.ParseUint(field.DefaultValue, 0, 64); err != nil { |
|
schema.err = fmt.Errorf("failed to parse %s as default value for uint, got error: %v", field.DefaultValue, err) |
|
} |
|
} |
|
case reflect.Float32, reflect.Float64: |
|
field.DataType = Float |
|
if field.HasDefaultValue && !skipParseDefaultValue { |
|
if field.DefaultValueInterface, err = strconv.ParseFloat(field.DefaultValue, 64); err != nil { |
|
schema.err = fmt.Errorf("failed to parse %s as default value for float, got error: %v", field.DefaultValue, err) |
|
} |
|
} |
|
case reflect.String: |
|
field.DataType = String |
|
if field.HasDefaultValue && !skipParseDefaultValue { |
|
field.DefaultValue = strings.Trim(field.DefaultValue, "'") |
|
field.DefaultValue = strings.Trim(field.DefaultValue, `"`) |
|
field.DefaultValueInterface = field.DefaultValue |
|
} |
|
case reflect.Struct: |
|
if _, ok := fieldValue.Interface().(*time.Time); ok { |
|
field.DataType = Time |
|
} else if fieldValue.Type().ConvertibleTo(TimeReflectType) { |
|
field.DataType = Time |
|
} else if fieldValue.Type().ConvertibleTo(TimePtrReflectType) { |
|
field.DataType = Time |
|
} |
|
if field.HasDefaultValue && !skipParseDefaultValue && field.DataType == Time { |
|
if t, err := now.Parse(field.DefaultValue); err == nil { |
|
field.DefaultValueInterface = t |
|
} |
|
} |
|
case reflect.Array, reflect.Slice: |
|
if reflect.Indirect(fieldValue).Type().Elem() == ByteReflectType && field.DataType == "" { |
|
field.DataType = Bytes |
|
} |
|
} |
|
|
|
if dataTyper, ok := fieldValue.Interface().(GormDataTypeInterface); ok { |
|
field.DataType = DataType(dataTyper.GormDataType()) |
|
} |
|
|
|
if v, ok := field.TagSettings["AUTOCREATETIME"]; (ok && utils.CheckTruth(v)) || (!ok && field.Name == "CreatedAt" && (field.DataType == Time || field.DataType == Int || field.DataType == Uint)) { |
|
if field.DataType == Time { |
|
field.AutoCreateTime = UnixTime |
|
} else if strings.ToUpper(v) == "NANO" { |
|
field.AutoCreateTime = UnixNanosecond |
|
} else if strings.ToUpper(v) == "MILLI" { |
|
field.AutoCreateTime = UnixMillisecond |
|
} else { |
|
field.AutoCreateTime = UnixSecond |
|
} |
|
} |
|
|
|
if v, ok := field.TagSettings["AUTOUPDATETIME"]; (ok && utils.CheckTruth(v)) || (!ok && field.Name == "UpdatedAt" && (field.DataType == Time || field.DataType == Int || field.DataType == Uint)) { |
|
if field.DataType == Time { |
|
field.AutoUpdateTime = UnixTime |
|
} else if strings.ToUpper(v) == "NANO" { |
|
field.AutoUpdateTime = UnixNanosecond |
|
} else if strings.ToUpper(v) == "MILLI" { |
|
field.AutoUpdateTime = UnixMillisecond |
|
} else { |
|
field.AutoUpdateTime = UnixSecond |
|
} |
|
} |
|
|
|
if field.GORMDataType == "" { |
|
field.GORMDataType = field.DataType |
|
} |
|
|
|
if val, ok := field.TagSettings["TYPE"]; ok { |
|
switch DataType(strings.ToLower(val)) { |
|
case Bool, Int, Uint, Float, String, Time, Bytes: |
|
field.DataType = DataType(strings.ToLower(val)) |
|
default: |
|
field.DataType = DataType(val) |
|
} |
|
} |
|
|
|
if field.Size == 0 { |
|
switch reflect.Indirect(fieldValue).Kind() { |
|
case reflect.Int, reflect.Int64, reflect.Uint, reflect.Uint64, reflect.Float64: |
|
field.Size = 64 |
|
case reflect.Int8, reflect.Uint8: |
|
field.Size = 8 |
|
case reflect.Int16, reflect.Uint16: |
|
field.Size = 16 |
|
case reflect.Int32, reflect.Uint32, reflect.Float32: |
|
field.Size = 32 |
|
} |
|
} |
|
|
|
// setup permission |
|
if val, ok := field.TagSettings["-"]; ok { |
|
val = strings.ToLower(strings.TrimSpace(val)) |
|
switch val { |
|
case "-": |
|
field.Creatable = false |
|
field.Updatable = false |
|
field.Readable = false |
|
field.DataType = "" |
|
case "all": |
|
field.Creatable = false |
|
field.Updatable = false |
|
field.Readable = false |
|
field.DataType = "" |
|
field.IgnoreMigration = true |
|
case "migration": |
|
field.IgnoreMigration = true |
|
} |
|
} |
|
|
|
if v, ok := field.TagSettings["->"]; ok { |
|
field.Creatable = false |
|
field.Updatable = false |
|
if strings.ToLower(v) == "false" { |
|
field.Readable = false |
|
} else { |
|
field.Readable = true |
|
} |
|
} |
|
|
|
if v, ok := field.TagSettings["<-"]; ok { |
|
field.Creatable = true |
|
field.Updatable = true |
|
|
|
if v != "<-" { |
|
if !strings.Contains(v, "create") { |
|
field.Creatable = false |
|
} |
|
|
|
if !strings.Contains(v, "update") { |
|
field.Updatable = false |
|
} |
|
} |
|
} |
|
|
|
// Normal anonymous field or having `EMBEDDED` tag |
|
if _, ok := field.TagSettings["EMBEDDED"]; ok || (field.GORMDataType != Time && field.GORMDataType != Bytes && !isValuer && |
|
fieldStruct.Anonymous && (field.Creatable || field.Updatable || field.Readable)) { |
|
kind := reflect.Indirect(fieldValue).Kind() |
|
switch kind { |
|
case reflect.Struct: |
|
var err error |
|
field.Creatable = false |
|
field.Updatable = false |
|
field.Readable = false |
|
|
|
cacheStore := &sync.Map{} |
|
cacheStore.Store(embeddedCacheKey, true) |
|
if field.EmbeddedSchema, err = getOrParse(fieldValue.Interface(), cacheStore, embeddedNamer{Table: schema.Table, Namer: schema.namer}); err != nil { |
|
schema.err = err |
|
} |
|
|
|
for _, ef := range field.EmbeddedSchema.Fields { |
|
ef.Schema = schema |
|
ef.OwnerSchema = field.EmbeddedSchema |
|
ef.BindNames = append([]string{fieldStruct.Name}, ef.BindNames...) |
|
// index is negative means is pointer |
|
if field.FieldType.Kind() == reflect.Struct { |
|
ef.StructField.Index = append([]int{fieldStruct.Index[0]}, ef.StructField.Index...) |
|
} else { |
|
ef.StructField.Index = append([]int{-fieldStruct.Index[0] - 1}, ef.StructField.Index...) |
|
} |
|
|
|
if prefix, ok := field.TagSettings["EMBEDDEDPREFIX"]; ok && ef.DBName != "" { |
|
ef.DBName = prefix + ef.DBName |
|
} |
|
|
|
if ef.PrimaryKey { |
|
if val, ok := ef.TagSettings["PRIMARYKEY"]; ok && utils.CheckTruth(val) { |
|
ef.PrimaryKey = true |
|
} else if val, ok := ef.TagSettings["PRIMARY_KEY"]; ok && utils.CheckTruth(val) { |
|
ef.PrimaryKey = true |
|
} else { |
|
ef.PrimaryKey = false |
|
|
|
if val, ok := ef.TagSettings["AUTOINCREMENT"]; !ok || !utils.CheckTruth(val) { |
|
ef.AutoIncrement = false |
|
} |
|
|
|
if ef.DefaultValue == "" { |
|
ef.HasDefaultValue = false |
|
} |
|
} |
|
} |
|
|
|
for k, v := range field.TagSettings { |
|
ef.TagSettings[k] = v |
|
} |
|
} |
|
case reflect.Invalid, reflect.Uintptr, reflect.Array, reflect.Chan, reflect.Func, reflect.Interface, |
|
reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer, reflect.Complex64, reflect.Complex128: |
|
schema.err = fmt.Errorf("invalid embedded struct for %s's field %s, should be struct, but got %v", field.Schema.Name, field.Name, field.FieldType) |
|
} |
|
} |
|
|
|
return field |
|
} |
|
|
|
// create valuer, setter when parse struct |
|
func (field *Field) setupValuerAndSetter() { |
|
// Setup NewValuePool |
|
field.setupNewValuePool() |
|
|
|
// ValueOf returns field's value and if it is zero |
|
fieldIndex := field.StructField.Index[0] |
|
switch { |
|
case len(field.StructField.Index) == 1 && fieldIndex > 0: |
|
field.ValueOf = func(ctx context.Context, value reflect.Value) (interface{}, bool) { |
|
fieldValue := reflect.Indirect(value).Field(fieldIndex) |
|
return fieldValue.Interface(), fieldValue.IsZero() |
|
} |
|
default: |
|
field.ValueOf = func(ctx context.Context, v reflect.Value) (interface{}, bool) { |
|
v = reflect.Indirect(v) |
|
for _, fieldIdx := range field.StructField.Index { |
|
if fieldIdx >= 0 { |
|
v = v.Field(fieldIdx) |
|
} else { |
|
v = v.Field(-fieldIdx - 1) |
|
|
|
if !v.IsNil() { |
|
v = v.Elem() |
|
} else { |
|
return nil, true |
|
} |
|
} |
|
} |
|
|
|
fv, zero := v.Interface(), v.IsZero() |
|
return fv, zero |
|
} |
|
} |
|
|
|
if field.Serializer != nil { |
|
oldValuerOf := field.ValueOf |
|
field.ValueOf = func(ctx context.Context, v reflect.Value) (interface{}, bool) { |
|
value, zero := oldValuerOf(ctx, v) |
|
if zero { |
|
return value, zero |
|
} |
|
|
|
s, ok := value.(SerializerValuerInterface) |
|
if !ok { |
|
s = field.Serializer |
|
} |
|
|
|
return &serializer{ |
|
Field: field, |
|
SerializeValuer: s, |
|
Destination: v, |
|
Context: ctx, |
|
fieldValue: value, |
|
}, false |
|
} |
|
} |
|
|
|
// ReflectValueOf returns field's reflect value |
|
switch { |
|
case len(field.StructField.Index) == 1 && fieldIndex > 0: |
|
field.ReflectValueOf = func(ctx context.Context, value reflect.Value) reflect.Value { |
|
return reflect.Indirect(value).Field(fieldIndex) |
|
} |
|
default: |
|
field.ReflectValueOf = func(ctx context.Context, v reflect.Value) reflect.Value { |
|
v = reflect.Indirect(v) |
|
for idx, fieldIdx := range field.StructField.Index { |
|
if fieldIdx >= 0 { |
|
v = v.Field(fieldIdx) |
|
} else { |
|
v = v.Field(-fieldIdx - 1) |
|
|
|
if v.IsNil() { |
|
v.Set(reflect.New(v.Type().Elem())) |
|
} |
|
|
|
if idx < len(field.StructField.Index)-1 { |
|
v = v.Elem() |
|
} |
|
} |
|
} |
|
return v |
|
} |
|
} |
|
|
|
fallbackSetter := func(ctx context.Context, value reflect.Value, v interface{}, setter func(context.Context, reflect.Value, interface{}) error) (err error) { |
|
if v == nil { |
|
field.ReflectValueOf(ctx, value).Set(reflect.New(field.FieldType).Elem()) |
|
} else { |
|
reflectV := reflect.ValueOf(v) |
|
// Optimal value type acquisition for v |
|
reflectValType := reflectV.Type() |
|
|
|
if reflectValType.AssignableTo(field.FieldType) { |
|
field.ReflectValueOf(ctx, value).Set(reflectV) |
|
return |
|
} else if reflectValType.ConvertibleTo(field.FieldType) { |
|
field.ReflectValueOf(ctx, value).Set(reflectV.Convert(field.FieldType)) |
|
return |
|
} else if field.FieldType.Kind() == reflect.Ptr { |
|
fieldValue := field.ReflectValueOf(ctx, value) |
|
fieldType := field.FieldType.Elem() |
|
|
|
if reflectValType.AssignableTo(fieldType) { |
|
if !fieldValue.IsValid() { |
|
fieldValue = reflect.New(fieldType) |
|
} else if fieldValue.IsNil() { |
|
fieldValue.Set(reflect.New(fieldType)) |
|
} |
|
fieldValue.Elem().Set(reflectV) |
|
return |
|
} else if reflectValType.ConvertibleTo(fieldType) { |
|
if fieldValue.IsNil() { |
|
fieldValue.Set(reflect.New(fieldType)) |
|
} |
|
|
|
fieldValue.Elem().Set(reflectV.Convert(fieldType)) |
|
return |
|
} |
|
} |
|
|
|
if reflectV.Kind() == reflect.Ptr { |
|
if reflectV.IsNil() { |
|
field.ReflectValueOf(ctx, value).Set(reflect.New(field.FieldType).Elem()) |
|
} else if reflectV.Type().Elem().AssignableTo(field.FieldType) { |
|
field.ReflectValueOf(ctx, value).Set(reflectV.Elem()) |
|
return |
|
} else { |
|
err = setter(ctx, value, reflectV.Elem().Interface()) |
|
} |
|
} else if valuer, ok := v.(driver.Valuer); ok { |
|
if v, err = valuer.Value(); err == nil { |
|
err = setter(ctx, value, v) |
|
} |
|
} else if _, ok := v.(clause.Expr); !ok { |
|
return fmt.Errorf("failed to set value %#v to field %s", v, field.Name) |
|
} |
|
} |
|
|
|
return |
|
} |
|
|
|
// Set |
|
switch field.FieldType.Kind() { |
|
case reflect.Bool: |
|
field.Set = func(ctx context.Context, value reflect.Value, v interface{}) error { |
|
switch data := v.(type) { |
|
case **bool: |
|
if data != nil && *data != nil { |
|
field.ReflectValueOf(ctx, value).SetBool(**data) |
|
} |
|
case bool: |
|
field.ReflectValueOf(ctx, value).SetBool(data) |
|
case int64: |
|
field.ReflectValueOf(ctx, value).SetBool(data > 0) |
|
case string: |
|
b, _ := strconv.ParseBool(data) |
|
field.ReflectValueOf(ctx, value).SetBool(b) |
|
default: |
|
return fallbackSetter(ctx, value, v, field.Set) |
|
} |
|
return nil |
|
} |
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: |
|
field.Set = func(ctx context.Context, value reflect.Value, v interface{}) (err error) { |
|
switch data := v.(type) { |
|
case **int64: |
|
if data != nil && *data != nil { |
|
field.ReflectValueOf(ctx, value).SetInt(**data) |
|
} |
|
case int64: |
|
field.ReflectValueOf(ctx, value).SetInt(data) |
|
case int: |
|
field.ReflectValueOf(ctx, value).SetInt(int64(data)) |
|
case int8: |
|
field.ReflectValueOf(ctx, value).SetInt(int64(data)) |
|
case int16: |
|
field.ReflectValueOf(ctx, value).SetInt(int64(data)) |
|
case int32: |
|
field.ReflectValueOf(ctx, value).SetInt(int64(data)) |
|
case uint: |
|
field.ReflectValueOf(ctx, value).SetInt(int64(data)) |
|
case uint8: |
|
field.ReflectValueOf(ctx, value).SetInt(int64(data)) |
|
case uint16: |
|
field.ReflectValueOf(ctx, value).SetInt(int64(data)) |
|
case uint32: |
|
field.ReflectValueOf(ctx, value).SetInt(int64(data)) |
|
case uint64: |
|
field.ReflectValueOf(ctx, value).SetInt(int64(data)) |
|
case float32: |
|
field.ReflectValueOf(ctx, value).SetInt(int64(data)) |
|
case float64: |
|
field.ReflectValueOf(ctx, value).SetInt(int64(data)) |
|
case []byte: |
|
return field.Set(ctx, value, string(data)) |
|
case string: |
|
if i, err := strconv.ParseInt(data, 0, 64); err == nil { |
|
field.ReflectValueOf(ctx, value).SetInt(i) |
|
} else { |
|
return err |
|
} |
|
case time.Time: |
|
if field.AutoCreateTime == UnixNanosecond || field.AutoUpdateTime == UnixNanosecond { |
|
field.ReflectValueOf(ctx, value).SetInt(data.UnixNano()) |
|
} else if field.AutoCreateTime == UnixMillisecond || field.AutoUpdateTime == UnixMillisecond { |
|
field.ReflectValueOf(ctx, value).SetInt(data.UnixNano() / 1e6) |
|
} else { |
|
field.ReflectValueOf(ctx, value).SetInt(data.Unix()) |
|
} |
|
case *time.Time: |
|
if data != nil { |
|
if field.AutoCreateTime == UnixNanosecond || field.AutoUpdateTime == UnixNanosecond { |
|
field.ReflectValueOf(ctx, value).SetInt(data.UnixNano()) |
|
} else if field.AutoCreateTime == UnixMillisecond || field.AutoUpdateTime == UnixMillisecond { |
|
field.ReflectValueOf(ctx, value).SetInt(data.UnixNano() / 1e6) |
|
} else { |
|
field.ReflectValueOf(ctx, value).SetInt(data.Unix()) |
|
} |
|
} else { |
|
field.ReflectValueOf(ctx, value).SetInt(0) |
|
} |
|
default: |
|
return fallbackSetter(ctx, value, v, field.Set) |
|
} |
|
return err |
|
} |
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: |
|
field.Set = func(ctx context.Context, value reflect.Value, v interface{}) (err error) { |
|
switch data := v.(type) { |
|
case **uint64: |
|
if data != nil && *data != nil { |
|
field.ReflectValueOf(ctx, value).SetUint(**data) |
|
} |
|
case uint64: |
|
field.ReflectValueOf(ctx, value).SetUint(data) |
|
case uint: |
|
field.ReflectValueOf(ctx, value).SetUint(uint64(data)) |
|
case uint8: |
|
field.ReflectValueOf(ctx, value).SetUint(uint64(data)) |
|
case uint16: |
|
field.ReflectValueOf(ctx, value).SetUint(uint64(data)) |
|
case uint32: |
|
field.ReflectValueOf(ctx, value).SetUint(uint64(data)) |
|
case int64: |
|
field.ReflectValueOf(ctx, value).SetUint(uint64(data)) |
|
case int: |
|
field.ReflectValueOf(ctx, value).SetUint(uint64(data)) |
|
case int8: |
|
field.ReflectValueOf(ctx, value).SetUint(uint64(data)) |
|
case int16: |
|
field.ReflectValueOf(ctx, value).SetUint(uint64(data)) |
|
case int32: |
|
field.ReflectValueOf(ctx, value).SetUint(uint64(data)) |
|
case float32: |
|
field.ReflectValueOf(ctx, value).SetUint(uint64(data)) |
|
case float64: |
|
field.ReflectValueOf(ctx, value).SetUint(uint64(data)) |
|
case []byte: |
|
return field.Set(ctx, value, string(data)) |
|
case time.Time: |
|
if field.AutoCreateTime == UnixNanosecond || field.AutoUpdateTime == UnixNanosecond { |
|
field.ReflectValueOf(ctx, value).SetUint(uint64(data.UnixNano())) |
|
} else if field.AutoCreateTime == UnixMillisecond || field.AutoUpdateTime == UnixMillisecond { |
|
field.ReflectValueOf(ctx, value).SetUint(uint64(data.UnixNano() / 1e6)) |
|
} else { |
|
field.ReflectValueOf(ctx, value).SetUint(uint64(data.Unix())) |
|
} |
|
case string: |
|
if i, err := strconv.ParseUint(data, 0, 64); err == nil { |
|
field.ReflectValueOf(ctx, value).SetUint(i) |
|
} else { |
|
return err |
|
} |
|
default: |
|
return fallbackSetter(ctx, value, v, field.Set) |
|
} |
|
return err |
|
} |
|
case reflect.Float32, reflect.Float64: |
|
field.Set = func(ctx context.Context, value reflect.Value, v interface{}) (err error) { |
|
switch data := v.(type) { |
|
case **float64: |
|
if data != nil && *data != nil { |
|
field.ReflectValueOf(ctx, value).SetFloat(**data) |
|
} |
|
case float64: |
|
field.ReflectValueOf(ctx, value).SetFloat(data) |
|
case float32: |
|
field.ReflectValueOf(ctx, value).SetFloat(float64(data)) |
|
case int64: |
|
field.ReflectValueOf(ctx, value).SetFloat(float64(data)) |
|
case int: |
|
field.ReflectValueOf(ctx, value).SetFloat(float64(data)) |
|
case int8: |
|
field.ReflectValueOf(ctx, value).SetFloat(float64(data)) |
|
case int16: |
|
field.ReflectValueOf(ctx, value).SetFloat(float64(data)) |
|
case int32: |
|
field.ReflectValueOf(ctx, value).SetFloat(float64(data)) |
|
case uint: |
|
field.ReflectValueOf(ctx, value).SetFloat(float64(data)) |
|
case uint8: |
|
field.ReflectValueOf(ctx, value).SetFloat(float64(data)) |
|
case uint16: |
|
field.ReflectValueOf(ctx, value).SetFloat(float64(data)) |
|
case uint32: |
|
field.ReflectValueOf(ctx, value).SetFloat(float64(data)) |
|
case uint64: |
|
field.ReflectValueOf(ctx, value).SetFloat(float64(data)) |
|
case []byte: |
|
return field.Set(ctx, value, string(data)) |
|
case string: |
|
if i, err := strconv.ParseFloat(data, 64); err == nil { |
|
field.ReflectValueOf(ctx, value).SetFloat(i) |
|
} else { |
|
return err |
|
} |
|
default: |
|
return fallbackSetter(ctx, value, v, field.Set) |
|
} |
|
return err |
|
} |
|
case reflect.String: |
|
field.Set = func(ctx context.Context, value reflect.Value, v interface{}) (err error) { |
|
switch data := v.(type) { |
|
case **string: |
|
if data != nil && *data != nil { |
|
field.ReflectValueOf(ctx, value).SetString(**data) |
|
} |
|
case string: |
|
field.ReflectValueOf(ctx, value).SetString(data) |
|
case []byte: |
|
field.ReflectValueOf(ctx, value).SetString(string(data)) |
|
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: |
|
field.ReflectValueOf(ctx, value).SetString(utils.ToString(data)) |
|
case float64, float32: |
|
field.ReflectValueOf(ctx, value).SetString(fmt.Sprintf("%."+strconv.Itoa(field.Precision)+"f", data)) |
|
default: |
|
return fallbackSetter(ctx, value, v, field.Set) |
|
} |
|
return err |
|
} |
|
default: |
|
fieldValue := reflect.New(field.FieldType) |
|
switch fieldValue.Elem().Interface().(type) { |
|
case time.Time: |
|
field.Set = func(ctx context.Context, value reflect.Value, v interface{}) error { |
|
switch data := v.(type) { |
|
case **time.Time: |
|
if data != nil && *data != nil { |
|
field.Set(ctx, value, *data) |
|
} |
|
case time.Time: |
|
field.ReflectValueOf(ctx, value).Set(reflect.ValueOf(v)) |
|
case *time.Time: |
|
if data != nil { |
|
field.ReflectValueOf(ctx, value).Set(reflect.ValueOf(data).Elem()) |
|
} else { |
|
field.ReflectValueOf(ctx, value).Set(reflect.ValueOf(time.Time{})) |
|
} |
|
case string: |
|
if t, err := now.Parse(data); err == nil { |
|
field.ReflectValueOf(ctx, value).Set(reflect.ValueOf(t)) |
|
} else { |
|
return fmt.Errorf("failed to set string %v to time.Time field %s, failed to parse it as time, got error %v", v, field.Name, err) |
|
} |
|
default: |
|
return fallbackSetter(ctx, value, v, field.Set) |
|
} |
|
return nil |
|
} |
|
case *time.Time: |
|
field.Set = func(ctx context.Context, value reflect.Value, v interface{}) error { |
|
switch data := v.(type) { |
|
case **time.Time: |
|
if data != nil { |
|
field.ReflectValueOf(ctx, value).Set(reflect.ValueOf(*data)) |
|
} |
|
case time.Time: |
|
fieldValue := field.ReflectValueOf(ctx, value) |
|
if fieldValue.IsNil() { |
|
fieldValue.Set(reflect.New(field.FieldType.Elem())) |
|
} |
|
fieldValue.Elem().Set(reflect.ValueOf(v)) |
|
case *time.Time: |
|
field.ReflectValueOf(ctx, value).Set(reflect.ValueOf(v)) |
|
case string: |
|
if t, err := now.Parse(data); err == nil { |
|
fieldValue := field.ReflectValueOf(ctx, value) |
|
if fieldValue.IsNil() { |
|
if v == "" { |
|
return nil |
|
} |
|
fieldValue.Set(reflect.New(field.FieldType.Elem())) |
|
} |
|
fieldValue.Elem().Set(reflect.ValueOf(t)) |
|
} else { |
|
return fmt.Errorf("failed to set string %v to time.Time field %s, failed to parse it as time, got error %v", v, field.Name, err) |
|
} |
|
default: |
|
return fallbackSetter(ctx, value, v, field.Set) |
|
} |
|
return nil |
|
} |
|
default: |
|
if _, ok := fieldValue.Elem().Interface().(sql.Scanner); ok { |
|
// pointer scanner |
|
field.Set = func(ctx context.Context, value reflect.Value, v interface{}) (err error) { |
|
reflectV := reflect.ValueOf(v) |
|
if !reflectV.IsValid() { |
|
field.ReflectValueOf(ctx, value).Set(reflect.New(field.FieldType).Elem()) |
|
} else if reflectV.Type().AssignableTo(field.FieldType) { |
|
field.ReflectValueOf(ctx, value).Set(reflectV) |
|
} else if reflectV.Kind() == reflect.Ptr { |
|
if reflectV.IsNil() || !reflectV.IsValid() { |
|
field.ReflectValueOf(ctx, value).Set(reflect.New(field.FieldType).Elem()) |
|
} else { |
|
return field.Set(ctx, value, reflectV.Elem().Interface()) |
|
} |
|
} else { |
|
fieldValue := field.ReflectValueOf(ctx, value) |
|
if fieldValue.IsNil() { |
|
fieldValue.Set(reflect.New(field.FieldType.Elem())) |
|
} |
|
|
|
if valuer, ok := v.(driver.Valuer); ok { |
|
v, _ = valuer.Value() |
|
} |
|
|
|
err = fieldValue.Interface().(sql.Scanner).Scan(v) |
|
} |
|
return |
|
} |
|
} else if _, ok := fieldValue.Interface().(sql.Scanner); ok { |
|
// struct scanner |
|
field.Set = func(ctx context.Context, value reflect.Value, v interface{}) (err error) { |
|
reflectV := reflect.ValueOf(v) |
|
if !reflectV.IsValid() { |
|
field.ReflectValueOf(ctx, value).Set(reflect.New(field.FieldType).Elem()) |
|
} else if reflectV.Type().AssignableTo(field.FieldType) { |
|
field.ReflectValueOf(ctx, value).Set(reflectV) |
|
} else if reflectV.Kind() == reflect.Ptr { |
|
if reflectV.IsNil() || !reflectV.IsValid() { |
|
field.ReflectValueOf(ctx, value).Set(reflect.New(field.FieldType).Elem()) |
|
} else { |
|
return field.Set(ctx, value, reflectV.Elem().Interface()) |
|
} |
|
} else { |
|
if valuer, ok := v.(driver.Valuer); ok { |
|
v, _ = valuer.Value() |
|
} |
|
|
|
err = field.ReflectValueOf(ctx, value).Addr().Interface().(sql.Scanner).Scan(v) |
|
} |
|
return |
|
} |
|
} else { |
|
field.Set = func(ctx context.Context, value reflect.Value, v interface{}) (err error) { |
|
return fallbackSetter(ctx, value, v, field.Set) |
|
} |
|
} |
|
} |
|
} |
|
|
|
if field.Serializer != nil { |
|
var ( |
|
oldFieldSetter = field.Set |
|
sameElemType bool |
|
sameType = field.FieldType == reflect.ValueOf(field.Serializer).Type() |
|
) |
|
|
|
if reflect.ValueOf(field.Serializer).Kind() == reflect.Ptr { |
|
sameElemType = field.FieldType == reflect.ValueOf(field.Serializer).Type().Elem() |
|
} |
|
|
|
field.Set = func(ctx context.Context, value reflect.Value, v interface{}) (err error) { |
|
if s, ok := v.(*serializer); ok { |
|
if s.fieldValue != nil { |
|
err = oldFieldSetter(ctx, value, s.fieldValue) |
|
} else if err = s.Serializer.Scan(ctx, field, value, s.value); err == nil { |
|
if sameElemType { |
|
field.ReflectValueOf(ctx, value).Set(reflect.ValueOf(s.Serializer).Elem()) |
|
s.Serializer = reflect.New(reflect.Indirect(reflect.ValueOf(field.Serializer)).Type()).Interface().(SerializerInterface) |
|
} else if sameType { |
|
field.ReflectValueOf(ctx, value).Set(reflect.ValueOf(s.Serializer)) |
|
s.Serializer = reflect.New(reflect.Indirect(reflect.ValueOf(field.Serializer)).Type()).Interface().(SerializerInterface) |
|
} |
|
} |
|
} else { |
|
err = oldFieldSetter(ctx, value, v) |
|
} |
|
return |
|
} |
|
} |
|
} |
|
|
|
func (field *Field) setupNewValuePool() { |
|
var fieldValue = reflect.New(field.FieldType).Interface() |
|
if field.Serializer != nil { |
|
field.NewValuePool = &sync.Pool{ |
|
New: func() interface{} { |
|
return &serializer{ |
|
Field: field, |
|
Serializer: reflect.New(reflect.Indirect(reflect.ValueOf(field.Serializer)).Type()).Interface().(SerializerInterface), |
|
} |
|
}, |
|
} |
|
} else if _, ok := fieldValue.(sql.Scanner); !ok { |
|
field.setupDefaultNewValuePool() |
|
} |
|
|
|
if field.NewValuePool == nil { |
|
field.NewValuePool = poolInitializer(reflect.PtrTo(field.IndirectFieldType)) |
|
} |
|
} |
|
|
|
func (field *Field) setupDefaultNewValuePool() { |
|
// set default NewValuePool |
|
switch field.IndirectFieldType.Kind() { |
|
case reflect.String: |
|
field.NewValuePool = stringPool |
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: |
|
field.NewValuePool = intPool |
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: |
|
field.NewValuePool = uintPool |
|
case reflect.Float32, reflect.Float64: |
|
field.NewValuePool = floatPool |
|
case reflect.Bool: |
|
field.NewValuePool = boolPool |
|
default: |
|
if field.IndirectFieldType == TimeReflectType { |
|
field.NewValuePool = timePool |
|
} |
|
} |
|
}
|
|
|