fix: serializer use default valueOf in assignInterfacesToValue, close #5168
commit 58e1b2bffbc216f2862d040fb545a8a486e473b6
Author: Jinzhu <wosmvp@gmail.com>
Date:   Fri Mar 18 17:06:43 2022 +0800
    Refactor #5168
commit fb9233011d209174e8223e970f0f732412852908
Author: ag9920 <alexgong7@outlook.com>
Date:   Thu Mar 17 21:23:28 2022 +0800
    fix: serializer use default valueOf in assignInterfacesToValue
			
			
This commit is contained in:
		
							parent
							
								
									e6f7da0e0d
								
							
						
					
					
						commit
						3c00980e01
					
				| @ -435,39 +435,7 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field { | ||||
| // create valuer, setter when parse struct
 | ||||
| func (field *Field) setupValuerAndSetter() { | ||||
| 	// Setup NewValuePool
 | ||||
| 	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 { | ||||
| 		// 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 | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if field.NewValuePool == nil { | ||||
| 		field.NewValuePool = poolInitializer(reflect.PtrTo(field.IndirectFieldType)) | ||||
| 	} | ||||
| 	field.setupNewValuePool() | ||||
| 
 | ||||
| 	// ValueOf returns field's value and if it is zero
 | ||||
| 	fieldIndex := field.StructField.Index[0] | ||||
| @ -512,7 +480,7 @@ func (field *Field) setupValuerAndSetter() { | ||||
| 				s = field.Serializer | ||||
| 			} | ||||
| 
 | ||||
| 			return serializer{ | ||||
| 			return &serializer{ | ||||
| 				Field:           field, | ||||
| 				SerializeValuer: s, | ||||
| 				Destination:     v, | ||||
| @ -943,7 +911,9 @@ func (field *Field) setupValuerAndSetter() { | ||||
| 
 | ||||
| 		field.Set = func(ctx context.Context, value reflect.Value, v interface{}) (err error) { | ||||
| 			if s, ok := v.(*serializer); ok { | ||||
| 				if err = s.Serializer.Scan(ctx, field, value, s.value); err == nil { | ||||
| 				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) | ||||
| @ -959,3 +929,43 @@ func (field *Field) setupValuerAndSetter() { | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 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 | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -202,8 +202,6 @@ func TestJoinCount(t *testing.T) { | ||||
| } | ||||
| 
 | ||||
| func TestJoinWithSoftDeleted(t *testing.T) { | ||||
| 	DB = DB.Debug() | ||||
| 
 | ||||
| 	user := GetUser("TestJoinWithSoftDeletedUser", Config{Account: true, NamedPet: true}) | ||||
| 	DB.Create(&user) | ||||
| 
 | ||||
|  | ||||
| @ -42,7 +42,7 @@ func (es *EncryptedString) Scan(ctx context.Context, field *schema.Field, dst re | ||||
| 	case string: | ||||
| 		*es = EncryptedString(strings.TrimPrefix(value, "hello")) | ||||
| 	default: | ||||
| 		return fmt.Errorf("unsupported data %v", dbValue) | ||||
| 		return fmt.Errorf("unsupported data %#v", dbValue) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| @ -83,4 +83,53 @@ func TestSerializer(t *testing.T) { | ||||
| 	} | ||||
| 
 | ||||
| 	AssertEqual(t, result, data) | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func TestSerializerAssignFirstOrCreate(t *testing.T) { | ||||
| 	DB.Migrator().DropTable(&SerializerStruct{}) | ||||
| 	if err := DB.Migrator().AutoMigrate(&SerializerStruct{}); err != nil { | ||||
| 		t.Fatalf("no error should happen when migrate scanner, valuer struct, got error %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	createdAt := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC) | ||||
| 
 | ||||
| 	data := SerializerStruct{ | ||||
| 		Name:            []byte("ag9920"), | ||||
| 		Roles:           []string{"r1", "r2"}, | ||||
| 		Contracts:       map[string]interface{}{"name": "jing1", "age": 11}, | ||||
| 		EncryptedString: EncryptedString("pass"), | ||||
| 		CreatedTime:     createdAt.Unix(), | ||||
| 		JobInfo: Job{ | ||||
| 			Title:    "programmer", | ||||
| 			Number:   9920, | ||||
| 			Location: "Shadyside", | ||||
| 			IsIntern: false, | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	// first time insert record
 | ||||
| 	out := SerializerStruct{} | ||||
| 	if err := DB.Assign(data).FirstOrCreate(&out).Error; err != nil { | ||||
| 		t.Fatalf("failed to FirstOrCreate Assigned data, got error %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	var result SerializerStruct | ||||
| 	if err := DB.First(&result, out.ID).Error; err != nil { | ||||
| 		t.Fatalf("failed to query data, got error %v", err) | ||||
| 	} | ||||
| 	AssertEqual(t, result, out) | ||||
| 
 | ||||
| 	//update record
 | ||||
| 	data.Roles = append(data.Roles, "r3") | ||||
| 	data.JobInfo.Location = "Gates Hillman Complex" | ||||
| 	if err := DB.Assign(data).FirstOrCreate(&out).Error; err != nil { | ||||
| 		t.Fatalf("failed to FirstOrCreate Assigned data, got error %v", err) | ||||
| 	} | ||||
| 	if err := DB.First(&result, out.ID).Error; err != nil { | ||||
| 		t.Fatalf("failed to query data, got error %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	AssertEqual(t, result.Roles, data.Roles) | ||||
| 	AssertEqual(t, result.JobInfo.Location, data.JobInfo.Location) | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 ag9920
						ag9920