Fix embedded scanner/valuer, close #3283

This commit is contained in:
Jinzhu 2020-08-19 15:47:08 +08:00
parent 3411425d65
commit c1782d60c1
2 changed files with 27 additions and 13 deletions

View File

@ -92,25 +92,30 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field {
valuer, isValuer := fieldValue.Interface().(driver.Valuer)
if isValuer {
if _, ok := fieldValue.Interface().(GormDataTypeInterface); !ok {
var overrideFieldValue bool
if v, err := valuer.Value(); v != nil && err == nil {
overrideFieldValue = true
if v, err := valuer.Value(); reflect.ValueOf(v).IsValid() && err == nil {
fieldValue = reflect.ValueOf(v)
}
if field.IndirectFieldType.Kind() == reflect.Struct {
for i := 0; i < field.IndirectFieldType.NumField(); i++ {
if !overrideFieldValue {
newFieldType := field.IndirectFieldType.Field(i).Type
var getRealFieldValue func(reflect.Value)
getRealFieldValue = func(v reflect.Value) {
rv := reflect.Indirect(v)
if rv.Kind() == reflect.Struct && !rv.Type().ConvertibleTo(reflect.TypeOf(time.Time{})) {
for i := 0; i < rv.Type().NumField(); i++ {
newFieldType := rv.Type().Field(i).Type
for newFieldType.Kind() == reflect.Ptr {
newFieldType = newFieldType.Elem()
}
fieldValue = reflect.New(newFieldType)
overrideFieldValue = true
if rv.Type() != reflect.Indirect(fieldValue).Type() {
getRealFieldValue(fieldValue)
}
if fieldValue.IsValid() {
return
}
// copy tag settings from valuer
for key, value := range ParseTagSetting(field.IndirectFieldType.Field(i).Tag.Get("gorm"), ";") {
if _, ok := field.TagSettings[key]; !ok {
field.TagSettings[key] = value
@ -119,6 +124,9 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field {
}
}
}
getRealFieldValue(fieldValue)
}
}
if dbName, ok := field.TagSettings["COLUMN"]; ok {

View File

@ -27,6 +27,7 @@ func TestScannerValuer(t *testing.T) {
Male: sql.NullBool{Bool: true, Valid: true},
Height: sql.NullFloat64{Float64: 1.8888, Valid: true},
Birthday: sql.NullTime{Time: time.Now(), Valid: true},
Allergen: NullString{sql.NullString{String: "Allergen", Valid: true}},
Password: EncryptedData("pass1"),
Bytes: []byte("byte"),
Num: 18,
@ -143,6 +144,7 @@ type ScannerValuerStruct struct {
Male sql.NullBool
Height sql.NullFloat64
Birthday sql.NullTime
Allergen NullString
Password EncryptedData
Bytes []byte
Num Num
@ -299,3 +301,7 @@ func (t *EmptyTime) Scan(v interface{}) error {
func (t EmptyTime) Value() (driver.Value, error) {
return time.Now() /* pass tests, mysql 8 doesn't support 0000-00-00 by default */, nil
}
type NullString struct {
sql.NullString
}