feature: bring custom type and id column name to polymorphism (#6716)
* feature: bring custom type and id column name to polymorphism * relationship: better returns for hasPolymorphicRelation * fix: tests
This commit is contained in:
		
							parent
							
								
									b9ebdb13c7
								
							
						
					
					
						commit
						a2cac75218
					
				| @ -76,8 +76,8 @@ func (schema *Schema) parseRelation(field *Field) *Relationship { | |||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if polymorphic := field.TagSettings["POLYMORPHIC"]; polymorphic != "" { | 	if hasPolymorphicRelation(field.TagSettings) { | ||||||
| 		schema.buildPolymorphicRelation(relation, field, polymorphic) | 		schema.buildPolymorphicRelation(relation, field) | ||||||
| 	} else if many2many := field.TagSettings["MANY2MANY"]; many2many != "" { | 	} else if many2many := field.TagSettings["MANY2MANY"]; many2many != "" { | ||||||
| 		schema.buildMany2ManyRelation(relation, field, many2many) | 		schema.buildMany2ManyRelation(relation, field, many2many) | ||||||
| 	} else if belongsTo := field.TagSettings["BELONGSTO"]; belongsTo != "" { | 	} else if belongsTo := field.TagSettings["BELONGSTO"]; belongsTo != "" { | ||||||
| @ -89,7 +89,8 @@ func (schema *Schema) parseRelation(field *Field) *Relationship { | |||||||
| 		case reflect.Slice: | 		case reflect.Slice: | ||||||
| 			schema.guessRelation(relation, field, guessHas) | 			schema.guessRelation(relation, field, guessHas) | ||||||
| 		default: | 		default: | ||||||
| 			schema.err = fmt.Errorf("unsupported data type %v for %v on field %s", relation.FieldSchema, schema, field.Name) | 			schema.err = fmt.Errorf("unsupported data type %v for %v on field %s", relation.FieldSchema, schema, | ||||||
|  | 				field.Name) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -124,6 +125,20 @@ func (schema *Schema) parseRelation(field *Field) *Relationship { | |||||||
| 	return relation | 	return relation | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // hasPolymorphicRelation check if has polymorphic relation
 | ||||||
|  | // 1. `POLYMORPHIC` tag
 | ||||||
|  | // 2. `POLYMORPHICTYPE` and `POLYMORPHICID` tag
 | ||||||
|  | func hasPolymorphicRelation(tagSettings map[string]string) bool { | ||||||
|  | 	if _, ok := tagSettings["POLYMORPHIC"]; ok { | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	_, hasType := tagSettings["POLYMORPHICTYPE"] | ||||||
|  | 	_, hasId := tagSettings["POLYMORPHICID"] | ||||||
|  | 
 | ||||||
|  | 	return hasType && hasId | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (schema *Schema) setRelation(relation *Relationship) { | func (schema *Schema) setRelation(relation *Relationship) { | ||||||
| 	// set non-embedded relation
 | 	// set non-embedded relation
 | ||||||
| 	if rel := schema.Relationships.Relations[relation.Name]; rel != nil { | 	if rel := schema.Relationships.Relations[relation.Name]; rel != nil { | ||||||
| @ -169,23 +184,41 @@ func (schema *Schema) setRelation(relation *Relationship) { | |||||||
| //	  OwnerID   int
 | //	  OwnerID   int
 | ||||||
| //	  OwnerType string
 | //	  OwnerType string
 | ||||||
| //	}
 | //	}
 | ||||||
| func (schema *Schema) buildPolymorphicRelation(relation *Relationship, field *Field, polymorphic string) { | func (schema *Schema) buildPolymorphicRelation(relation *Relationship, field *Field) { | ||||||
|  | 	polymorphic := field.TagSettings["POLYMORPHIC"] | ||||||
|  | 
 | ||||||
| 	relation.Polymorphic = &Polymorphic{ | 	relation.Polymorphic = &Polymorphic{ | ||||||
| 		Value:           schema.Table, | 		Value: schema.Table, | ||||||
| 		PolymorphicType: relation.FieldSchema.FieldsByName[polymorphic+"Type"], |  | ||||||
| 		PolymorphicID:   relation.FieldSchema.FieldsByName[polymorphic+"ID"], |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	var ( | ||||||
|  | 		typeName = polymorphic + "Type" | ||||||
|  | 		typeId   = polymorphic + "ID" | ||||||
|  | 	) | ||||||
|  | 
 | ||||||
|  | 	if value, ok := field.TagSettings["POLYMORPHICTYPE"]; ok { | ||||||
|  | 		typeName = strings.TrimSpace(value) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if value, ok := field.TagSettings["POLYMORPHICID"]; ok { | ||||||
|  | 		typeId = strings.TrimSpace(value) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	relation.Polymorphic.PolymorphicType = relation.FieldSchema.FieldsByName[typeName] | ||||||
|  | 	relation.Polymorphic.PolymorphicID = relation.FieldSchema.FieldsByName[typeId] | ||||||
|  | 
 | ||||||
| 	if value, ok := field.TagSettings["POLYMORPHICVALUE"]; ok { | 	if value, ok := field.TagSettings["POLYMORPHICVALUE"]; ok { | ||||||
| 		relation.Polymorphic.Value = strings.TrimSpace(value) | 		relation.Polymorphic.Value = strings.TrimSpace(value) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if relation.Polymorphic.PolymorphicType == nil { | 	if relation.Polymorphic.PolymorphicType == nil { | ||||||
| 		schema.err = fmt.Errorf("invalid polymorphic type %v for %v on field %s, missing field %s", relation.FieldSchema, schema, field.Name, polymorphic+"Type") | 		schema.err = fmt.Errorf("invalid polymorphic type %v for %v on field %s, missing field %s", | ||||||
|  | 			relation.FieldSchema, schema, field.Name, polymorphic+"Type") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if relation.Polymorphic.PolymorphicID == nil { | 	if relation.Polymorphic.PolymorphicID == nil { | ||||||
| 		schema.err = fmt.Errorf("invalid polymorphic type %v for %v on field %s, missing field %s", relation.FieldSchema, schema, field.Name, polymorphic+"ID") | 		schema.err = fmt.Errorf("invalid polymorphic type %v for %v on field %s, missing field %s", | ||||||
|  | 			relation.FieldSchema, schema, field.Name, polymorphic+"ID") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if schema.err == nil { | 	if schema.err == nil { | ||||||
| @ -197,12 +230,14 @@ func (schema *Schema) buildPolymorphicRelation(relation *Relationship, field *Fi | |||||||
| 		primaryKeyField := schema.PrioritizedPrimaryField | 		primaryKeyField := schema.PrioritizedPrimaryField | ||||||
| 		if len(relation.foreignKeys) > 0 { | 		if len(relation.foreignKeys) > 0 { | ||||||
| 			if primaryKeyField = schema.LookUpField(relation.foreignKeys[0]); primaryKeyField == nil || len(relation.foreignKeys) > 1 { | 			if primaryKeyField = schema.LookUpField(relation.foreignKeys[0]); primaryKeyField == nil || len(relation.foreignKeys) > 1 { | ||||||
| 				schema.err = fmt.Errorf("invalid polymorphic foreign keys %+v for %v on field %s", relation.foreignKeys, schema, field.Name) | 				schema.err = fmt.Errorf("invalid polymorphic foreign keys %+v for %v on field %s", relation.foreignKeys, | ||||||
|  | 					schema, field.Name) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if primaryKeyField == nil { | 		if primaryKeyField == nil { | ||||||
| 			schema.err = fmt.Errorf("invalid polymorphic type %v for %v on field %s, missing primaryKey field", relation.FieldSchema, schema, field.Name) | 			schema.err = fmt.Errorf("invalid polymorphic type %v for %v on field %s, missing primaryKey field", | ||||||
|  | 				relation.FieldSchema, schema, field.Name) | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| @ -317,7 +352,8 @@ func (schema *Schema) buildMany2ManyRelation(relation *Relationship, field *Fiel | |||||||
| 		Tag:  `gorm:"-"`, | 		Tag:  `gorm:"-"`, | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	if relation.JoinTable, err = Parse(reflect.New(reflect.StructOf(joinTableFields)).Interface(), schema.cacheStore, schema.namer); err != nil { | 	if relation.JoinTable, err = Parse(reflect.New(reflect.StructOf(joinTableFields)).Interface(), schema.cacheStore, | ||||||
|  | 		schema.namer); err != nil { | ||||||
| 		schema.err = err | 		schema.err = err | ||||||
| 	} | 	} | ||||||
| 	relation.JoinTable.Name = many2many | 	relation.JoinTable.Name = many2many | ||||||
| @ -436,7 +472,8 @@ func (schema *Schema) guessRelation(relation *Relationship, field *Field, cgl gu | |||||||
| 			schema.guessRelation(relation, field, guessEmbeddedHas) | 			schema.guessRelation(relation, field, guessEmbeddedHas) | ||||||
| 		// case guessEmbeddedHas:
 | 		// case guessEmbeddedHas:
 | ||||||
| 		default: | 		default: | ||||||
| 			schema.err = fmt.Errorf("invalid field found for struct %v's field %s: define a valid foreign key for relations or implement the Valuer/Scanner interface", schema, field.Name) | 			schema.err = fmt.Errorf("invalid field found for struct %v's field %s: define a valid foreign key for relations or implement the Valuer/Scanner interface", | ||||||
|  | 				schema, field.Name) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -492,7 +529,9 @@ func (schema *Schema) guessRelation(relation *Relationship, field *Field, cgl gu | |||||||
| 
 | 
 | ||||||
| 			lookUpNames := []string{lookUpName} | 			lookUpNames := []string{lookUpName} | ||||||
| 			if len(primaryFields) == 1 { | 			if len(primaryFields) == 1 { | ||||||
| 				lookUpNames = append(lookUpNames, strings.TrimSuffix(lookUpName, primaryField.Name)+"ID", strings.TrimSuffix(lookUpName, primaryField.Name)+"Id", schema.namer.ColumnName(foreignSchema.Table, strings.TrimSuffix(lookUpName, primaryField.Name)+"ID")) | 				lookUpNames = append(lookUpNames, strings.TrimSuffix(lookUpName, primaryField.Name)+"ID", | ||||||
|  | 					strings.TrimSuffix(lookUpName, primaryField.Name)+"Id", schema.namer.ColumnName(foreignSchema.Table, | ||||||
|  | 						strings.TrimSuffix(lookUpName, primaryField.Name)+"ID")) | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			for _, name := range lookUpNames { | 			for _, name := range lookUpNames { | ||||||
|  | |||||||
| @ -577,6 +577,193 @@ func TestEmbeddedHas(t *testing.T) { | |||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func TestPolymorphic(t *testing.T) { | ||||||
|  | 	t.Run("has one", func(t *testing.T) { | ||||||
|  | 		type Toy struct { | ||||||
|  | 			ID        int | ||||||
|  | 			Name      string | ||||||
|  | 			OwnerID   int | ||||||
|  | 			OwnerType string | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		type Cat struct { | ||||||
|  | 			ID   int | ||||||
|  | 			Name string | ||||||
|  | 			Toy  Toy `gorm:"polymorphic:Owner;"` | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		s, err := schema.Parse(&Cat{}, &sync.Map{}, schema.NamingStrategy{}) | ||||||
|  | 		if err != nil { | ||||||
|  | 			t.Fatalf("Failed to parse schema, got error %v", err) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		checkEmbeddedRelations(t, s.Relationships.EmbeddedRelations, map[string]EmbeddedRelations{ | ||||||
|  | 			"Cat": { | ||||||
|  | 				Relations: map[string]Relation{ | ||||||
|  | 					"Toy": { | ||||||
|  | 						Name:        "Toy", | ||||||
|  | 						Type:        schema.HasOne, | ||||||
|  | 						Schema:      "User", | ||||||
|  | 						FieldSchema: "Toy", | ||||||
|  | 						Polymorphic: Polymorphic{ID: "OwnerID", Type: "OwnerType", Value: "users"}, | ||||||
|  | 						References: []Reference{ | ||||||
|  | 							{ForeignKey: "OwnerType", ForeignSchema: "Toy", PrimaryValue: "users"}, | ||||||
|  | 						}, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	t.Run("has one with custom polymorphic type and id", func(t *testing.T) { | ||||||
|  | 		type Toy struct { | ||||||
|  | 			ID    int | ||||||
|  | 			Name  string | ||||||
|  | 			RefId int | ||||||
|  | 			Type  string | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		type Cat struct { | ||||||
|  | 			ID   int | ||||||
|  | 			Name string | ||||||
|  | 			Toy  Toy `gorm:"polymorphic:Owner;polymorphicType:Type;polymorphicId:RefId"` | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		s, err := schema.Parse(&Cat{}, &sync.Map{}, schema.NamingStrategy{}) | ||||||
|  | 		if err != nil { | ||||||
|  | 			t.Fatalf("Failed to parse schema, got error %v", err) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		checkEmbeddedRelations(t, s.Relationships.EmbeddedRelations, map[string]EmbeddedRelations{ | ||||||
|  | 			"Cat": { | ||||||
|  | 				Relations: map[string]Relation{ | ||||||
|  | 					"Toy": { | ||||||
|  | 						Name:        "Toy", | ||||||
|  | 						Type:        schema.HasOne, | ||||||
|  | 						Schema:      "User", | ||||||
|  | 						FieldSchema: "Toy", | ||||||
|  | 						Polymorphic: Polymorphic{ID: "ref_id", Type: "Type", Value: "users"}, | ||||||
|  | 						References: []Reference{ | ||||||
|  | 							{ForeignKey: "Type", ForeignSchema: "Toy", PrimaryValue: "users"}, | ||||||
|  | 						}, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	t.Run("has one with only polymorphic type", func(t *testing.T) { | ||||||
|  | 		type Toy struct { | ||||||
|  | 			ID      int | ||||||
|  | 			Name    string | ||||||
|  | 			OwnerID int | ||||||
|  | 			Type    string | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		type Cat struct { | ||||||
|  | 			ID   int | ||||||
|  | 			Name string | ||||||
|  | 			Toy  Toy `gorm:"polymorphic:Owner;polymorphicType:Type"` | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		s, err := schema.Parse(&Cat{}, &sync.Map{}, schema.NamingStrategy{}) | ||||||
|  | 		if err != nil { | ||||||
|  | 			t.Fatalf("Failed to parse schema, got error %v", err) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		checkEmbeddedRelations(t, s.Relationships.EmbeddedRelations, map[string]EmbeddedRelations{ | ||||||
|  | 			"Cat": { | ||||||
|  | 				Relations: map[string]Relation{ | ||||||
|  | 					"Toy": { | ||||||
|  | 						Name:        "Toy", | ||||||
|  | 						Type:        schema.HasOne, | ||||||
|  | 						Schema:      "User", | ||||||
|  | 						FieldSchema: "Toy", | ||||||
|  | 						Polymorphic: Polymorphic{ID: "owner_id", Type: "Type", Value: "users"}, | ||||||
|  | 						References: []Reference{ | ||||||
|  | 							{ForeignKey: "Type", ForeignSchema: "Toy", PrimaryValue: "users"}, | ||||||
|  | 						}, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	t.Run("has many", func(t *testing.T) { | ||||||
|  | 		type Toy struct { | ||||||
|  | 			ID        int | ||||||
|  | 			Name      string | ||||||
|  | 			OwnerID   int | ||||||
|  | 			OwnerType string | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		type Cat struct { | ||||||
|  | 			ID   int | ||||||
|  | 			Name string | ||||||
|  | 			Toys []Toy `gorm:"polymorphic:Owner;"` | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		s, err := schema.Parse(&Cat{}, &sync.Map{}, schema.NamingStrategy{}) | ||||||
|  | 		if err != nil { | ||||||
|  | 			t.Fatalf("Failed to parse schema, got error %v", err) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		checkEmbeddedRelations(t, s.Relationships.EmbeddedRelations, map[string]EmbeddedRelations{ | ||||||
|  | 			"Cat": { | ||||||
|  | 				Relations: map[string]Relation{ | ||||||
|  | 					"Toys": { | ||||||
|  | 						Name:        "Toys", | ||||||
|  | 						Type:        schema.HasMany, | ||||||
|  | 						Schema:      "User", | ||||||
|  | 						FieldSchema: "Toy", | ||||||
|  | 						Polymorphic: Polymorphic{ID: "OwnerID", Type: "OwnerType", Value: "users"}, | ||||||
|  | 						References: []Reference{ | ||||||
|  | 							{ForeignKey: "OwnerType", ForeignSchema: "Toy", PrimaryValue: "users"}, | ||||||
|  | 						}, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	t.Run("has many with custom polymorphic type and id", func(t *testing.T) { | ||||||
|  | 		type Toy struct { | ||||||
|  | 			ID    int | ||||||
|  | 			Name  string | ||||||
|  | 			RefId int | ||||||
|  | 			Type  string | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		type Cat struct { | ||||||
|  | 			ID   int | ||||||
|  | 			Name string | ||||||
|  | 			Toys []Toy `gorm:"polymorphicType:Type;polymorphicId:RefId"` | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		s, err := schema.Parse(&Cat{}, &sync.Map{}, schema.NamingStrategy{}) | ||||||
|  | 		if err != nil { | ||||||
|  | 			t.Fatalf("Failed to parse schema, got error %v", err) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		checkEmbeddedRelations(t, s.Relationships.EmbeddedRelations, map[string]EmbeddedRelations{ | ||||||
|  | 			"Cat": { | ||||||
|  | 				Relations: map[string]Relation{ | ||||||
|  | 					"Toys": { | ||||||
|  | 						Name:        "Toys", | ||||||
|  | 						Type:        schema.HasMany, | ||||||
|  | 						Schema:      "User", | ||||||
|  | 						FieldSchema: "Toy", | ||||||
|  | 						Polymorphic: Polymorphic{ID: "ref_id", Type: "Type", Value: "users"}, | ||||||
|  | 						References: []Reference{ | ||||||
|  | 							{ForeignKey: "Type", ForeignSchema: "Toy", PrimaryValue: "users"}, | ||||||
|  | 						}, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func TestEmbeddedBelongsTo(t *testing.T) { | func TestEmbeddedBelongsTo(t *testing.T) { | ||||||
| 	type Country struct { | 	type Country struct { | ||||||
| 		ID   int `gorm:"primaryKey"` | 		ID   int `gorm:"primaryKey"` | ||||||
|  | |||||||
| @ -422,7 +422,7 @@ func TestPolymorphicHasManyAssociation(t *testing.T) { | |||||||
| func TestPolymorphicHasManyAssociationForSlice(t *testing.T) { | func TestPolymorphicHasManyAssociationForSlice(t *testing.T) { | ||||||
| 	users := []User{ | 	users := []User{ | ||||||
| 		*GetUser("slice-hasmany-1", Config{Toys: 2}), | 		*GetUser("slice-hasmany-1", Config{Toys: 2}), | ||||||
| 		*GetUser("slice-hasmany-2", Config{Toys: 0}), | 		*GetUser("slice-hasmany-2", Config{Toys: 0, Tools: 2}), | ||||||
| 		*GetUser("slice-hasmany-3", Config{Toys: 4}), | 		*GetUser("slice-hasmany-3", Config{Toys: 4}), | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -430,6 +430,7 @@ func TestPolymorphicHasManyAssociationForSlice(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| 	// Count
 | 	// Count
 | ||||||
| 	AssertAssociationCount(t, users, "Toys", 6, "") | 	AssertAssociationCount(t, users, "Toys", 6, "") | ||||||
|  | 	AssertAssociationCount(t, users, "Tools", 2, "") | ||||||
| 
 | 
 | ||||||
| 	// Find
 | 	// Find
 | ||||||
| 	var toys []Toy | 	var toys []Toy | ||||||
| @ -437,6 +438,14 @@ func TestPolymorphicHasManyAssociationForSlice(t *testing.T) { | |||||||
| 		t.Errorf("toys count should be %v, but got %v", 6, len(toys)) | 		t.Errorf("toys count should be %v, but got %v", 6, len(toys)) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	// Find Tools (polymorphic with custom type and id)
 | ||||||
|  | 	var tools []Tools | ||||||
|  | 	DB.Model(&users).Association("Tools").Find(&tools) | ||||||
|  | 
 | ||||||
|  | 	if len(tools) != 2 { | ||||||
|  | 		t.Errorf("tools count should be %v, but got %v", 2, len(tools)) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	// Append
 | 	// Append
 | ||||||
| 	DB.Model(&users).Association("Toys").Append( | 	DB.Model(&users).Association("Toys").Append( | ||||||
| 		&Toy{Name: "toy-slice-append-1"}, | 		&Toy{Name: "toy-slice-append-1"}, | ||||||
|  | |||||||
| @ -23,6 +23,7 @@ type Config struct { | |||||||
| 	Languages int | 	Languages int | ||||||
| 	Friends   int | 	Friends   int | ||||||
| 	NamedPet  bool | 	NamedPet  bool | ||||||
|  | 	Tools     int | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func GetUser(name string, config Config) *User { | func GetUser(name string, config Config) *User { | ||||||
| @ -47,6 +48,10 @@ func GetUser(name string, config Config) *User { | |||||||
| 		user.Toys = append(user.Toys, Toy{Name: name + "_toy_" + strconv.Itoa(i+1)}) | 		user.Toys = append(user.Toys, Toy{Name: name + "_toy_" + strconv.Itoa(i+1)}) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	for i := 0; i < config.Tools; i++ { | ||||||
|  | 		user.Tools = append(user.Tools, Tools{Name: name + "_tool_" + strconv.Itoa(i+1)}) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	if config.Company { | 	if config.Company { | ||||||
| 		user.Company = Company{Name: "company-" + name} | 		user.Company = Company{Name: "company-" + name} | ||||||
| 	} | 	} | ||||||
| @ -118,11 +123,13 @@ func doCheckUser(t *testing.T, user User, expect User, unscoped bool) { | |||||||
| 		if err := db(unscoped).Where("id = ?", user.ID).First(&newUser).Error; err != nil { | 		if err := db(unscoped).Where("id = ?", user.ID).First(&newUser).Error; err != nil { | ||||||
| 			t.Fatalf("errors happened when query: %v", err) | 			t.Fatalf("errors happened when query: %v", err) | ||||||
| 		} else { | 		} else { | ||||||
| 			AssertObjEqual(t, newUser, user, "ID", "CreatedAt", "UpdatedAt", "DeletedAt", "Name", "Age", "Birthday", "CompanyID", "ManagerID", "Active") | 			AssertObjEqual(t, newUser, user, "ID", "CreatedAt", "UpdatedAt", "DeletedAt", "Name", "Age", "Birthday", | ||||||
|  | 				"CompanyID", "ManagerID", "Active") | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	AssertObjEqual(t, user, expect, "ID", "CreatedAt", "UpdatedAt", "DeletedAt", "Name", "Age", "Birthday", "CompanyID", "ManagerID", "Active") | 	AssertObjEqual(t, user, expect, "ID", "CreatedAt", "UpdatedAt", "DeletedAt", "Name", "Age", "Birthday", "CompanyID", | ||||||
|  | 		"ManagerID", "Active") | ||||||
| 
 | 
 | ||||||
| 	t.Run("Account", func(t *testing.T) { | 	t.Run("Account", func(t *testing.T) { | ||||||
| 		AssertObjEqual(t, user.Account, expect.Account, "ID", "CreatedAt", "UpdatedAt", "DeletedAt", "UserID", "Number") | 		AssertObjEqual(t, user.Account, expect.Account, "ID", "CreatedAt", "UpdatedAt", "DeletedAt", "UserID", "Number") | ||||||
| @ -133,7 +140,8 @@ func doCheckUser(t *testing.T, user User, expect User, unscoped bool) { | |||||||
| 			} else { | 			} else { | ||||||
| 				var account Account | 				var account Account | ||||||
| 				db(unscoped).First(&account, "user_id = ?", user.ID) | 				db(unscoped).First(&account, "user_id = ?", user.ID) | ||||||
| 				AssertObjEqual(t, account, user.Account, "ID", "CreatedAt", "UpdatedAt", "DeletedAt", "UserID", "Number") | 				AssertObjEqual(t, account, user.Account, "ID", "CreatedAt", "UpdatedAt", "DeletedAt", "UserID", | ||||||
|  | 					"Number") | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	}) | 	}) | ||||||
| @ -193,8 +201,10 @@ func doCheckUser(t *testing.T, user User, expect User, unscoped bool) { | |||||||
| 			} else { | 			} else { | ||||||
| 				var manager User | 				var manager User | ||||||
| 				db(unscoped).First(&manager, "id = ?", *user.ManagerID) | 				db(unscoped).First(&manager, "id = ?", *user.ManagerID) | ||||||
| 				AssertObjEqual(t, manager, user.Manager, "ID", "CreatedAt", "UpdatedAt", "DeletedAt", "Name", "Age", "Birthday", "CompanyID", "ManagerID", "Active") | 				AssertObjEqual(t, manager, user.Manager, "ID", "CreatedAt", "UpdatedAt", "DeletedAt", "Name", "Age", | ||||||
| 				AssertObjEqual(t, manager, expect.Manager, "ID", "CreatedAt", "UpdatedAt", "DeletedAt", "Name", "Age", "Birthday", "CompanyID", "ManagerID", "Active") | 					"Birthday", "CompanyID", "ManagerID", "Active") | ||||||
|  | 				AssertObjEqual(t, manager, expect.Manager, "ID", "CreatedAt", "UpdatedAt", "DeletedAt", "Name", "Age", | ||||||
|  | 					"Birthday", "CompanyID", "ManagerID", "Active") | ||||||
| 			} | 			} | ||||||
| 		} else if user.ManagerID != nil { | 		} else if user.ManagerID != nil { | ||||||
| 			t.Errorf("Manager should not be created for zero value, got: %+v", user.ManagerID) | 			t.Errorf("Manager should not be created for zero value, got: %+v", user.ManagerID) | ||||||
| @ -215,7 +225,8 @@ func doCheckUser(t *testing.T, user User, expect User, unscoped bool) { | |||||||
| 		}) | 		}) | ||||||
| 
 | 
 | ||||||
| 		for idx, team := range user.Team { | 		for idx, team := range user.Team { | ||||||
| 			AssertObjEqual(t, team, expect.Team[idx], "ID", "CreatedAt", "UpdatedAt", "DeletedAt", "Name", "Age", "Birthday", "CompanyID", "ManagerID", "Active") | 			AssertObjEqual(t, team, expect.Team[idx], "ID", "CreatedAt", "UpdatedAt", "DeletedAt", "Name", "Age", | ||||||
|  | 				"Birthday", "CompanyID", "ManagerID", "Active") | ||||||
| 		} | 		} | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| @ -250,7 +261,8 @@ func doCheckUser(t *testing.T, user User, expect User, unscoped bool) { | |||||||
| 		}) | 		}) | ||||||
| 
 | 
 | ||||||
| 		for idx, friend := range user.Friends { | 		for idx, friend := range user.Friends { | ||||||
| 			AssertObjEqual(t, friend, expect.Friends[idx], "ID", "CreatedAt", "UpdatedAt", "DeletedAt", "Name", "Age", "Birthday", "CompanyID", "ManagerID", "Active") | 			AssertObjEqual(t, friend, expect.Friends[idx], "ID", "CreatedAt", "UpdatedAt", "DeletedAt", "Name", "Age", | ||||||
|  | 				"Birthday", "CompanyID", "ManagerID", "Active") | ||||||
| 		} | 		} | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
|  | |||||||
| @ -18,7 +18,7 @@ import ( | |||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func TestMigrate(t *testing.T) { | func TestMigrate(t *testing.T) { | ||||||
| 	allModels := []interface{}{&User{}, &Account{}, &Pet{}, &Company{}, &Toy{}, &Language{}} | 	allModels := []interface{}{&User{}, &Account{}, &Pet{}, &Company{}, &Toy{}, &Language{}, &Tools{}} | ||||||
| 	rand.Seed(time.Now().UnixNano()) | 	rand.Seed(time.Now().UnixNano()) | ||||||
| 	rand.Shuffle(len(allModels), func(i, j int) { allModels[i], allModels[j] = allModels[j], allModels[i] }) | 	rand.Shuffle(len(allModels), func(i, j int) { allModels[i], allModels[j] = allModels[j], allModels[i] }) | ||||||
| 	DB.Migrator().DropTable("user_speaks", "user_friends", "ccc") | 	DB.Migrator().DropTable("user_speaks", "user_friends", "ccc") | ||||||
| @ -34,7 +34,7 @@ func TestMigrate(t *testing.T) { | |||||||
| 	if tables, err := DB.Migrator().GetTables(); err != nil { | 	if tables, err := DB.Migrator().GetTables(); err != nil { | ||||||
| 		t.Fatalf("Failed to get database all tables, but got error %v", err) | 		t.Fatalf("Failed to get database all tables, but got error %v", err) | ||||||
| 	} else { | 	} else { | ||||||
| 		for _, t1 := range []string{"users", "accounts", "pets", "companies", "toys", "languages"} { | 		for _, t1 := range []string{"users", "accounts", "pets", "companies", "toys", "languages", "tools"} { | ||||||
| 			hasTable := false | 			hasTable := false | ||||||
| 			for _, t2 := range tables { | 			for _, t2 := range tables { | ||||||
| 				if t2 == t1 { | 				if t2 == t1 { | ||||||
| @ -93,7 +93,8 @@ func TestAutoMigrateInt8PG(t *testing.T) { | |||||||
| 		Test: func(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) { | 		Test: func(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) { | ||||||
| 			sql, _ := fc() | 			sql, _ := fc() | ||||||
| 			if strings.HasPrefix(sql, "ALTER TABLE \"migrate_ints\" ALTER COLUMN \"int8\" TYPE smallint") { | 			if strings.HasPrefix(sql, "ALTER TABLE \"migrate_ints\" ALTER COLUMN \"int8\" TYPE smallint") { | ||||||
| 				t.Fatalf("shouldn't execute ALTER COLUMN TYPE if such type is already existed in DB schema: sql: %s", sql) | 				t.Fatalf("shouldn't execute ALTER COLUMN TYPE if such type is already existed in DB schema: sql: %s", | ||||||
|  | 					sql) | ||||||
| 			} | 			} | ||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
| @ -432,40 +433,50 @@ func TestTiDBMigrateColumns(t *testing.T) { | |||||||
| 			switch columnType.Name() { | 			switch columnType.Name() { | ||||||
| 			case "id": | 			case "id": | ||||||
| 				if v, ok := columnType.PrimaryKey(); !ok || !v { | 				if v, ok := columnType.PrimaryKey(); !ok || !v { | ||||||
| 					t.Fatalf("column id primary key should be correct, name: %v, column: %#v", columnType.Name(), columnType) | 					t.Fatalf("column id primary key should be correct, name: %v, column: %#v", columnType.Name(), | ||||||
|  | 						columnType) | ||||||
| 				} | 				} | ||||||
| 			case "name": | 			case "name": | ||||||
| 				dataType := DB.Dialector.DataTypeOf(stmt.Schema.LookUpField(columnType.Name())) | 				dataType := DB.Dialector.DataTypeOf(stmt.Schema.LookUpField(columnType.Name())) | ||||||
| 				if !strings.Contains(strings.ToUpper(dataType), strings.ToUpper(columnType.DatabaseTypeName())) { | 				if !strings.Contains(strings.ToUpper(dataType), strings.ToUpper(columnType.DatabaseTypeName())) { | ||||||
| 					t.Fatalf("column name type should be correct, name: %v, length: %v, expects: %v, column: %#v", columnType.Name(), columnType.DatabaseTypeName(), dataType, columnType) | 					t.Fatalf("column name type should be correct, name: %v, length: %v, expects: %v, column: %#v", | ||||||
|  | 						columnType.Name(), columnType.DatabaseTypeName(), dataType, columnType) | ||||||
| 				} | 				} | ||||||
| 				if length, ok := columnType.Length(); !ok || length != 100 { | 				if length, ok := columnType.Length(); !ok || length != 100 { | ||||||
| 					t.Fatalf("column name length should be correct, name: %v, length: %v, expects: %v, column: %#v", columnType.Name(), length, 100, columnType) | 					t.Fatalf("column name length should be correct, name: %v, length: %v, expects: %v, column: %#v", | ||||||
|  | 						columnType.Name(), length, 100, columnType) | ||||||
| 				} | 				} | ||||||
| 			case "age": | 			case "age": | ||||||
| 				if v, ok := columnType.DefaultValue(); !ok || v != "18" { | 				if v, ok := columnType.DefaultValue(); !ok || v != "18" { | ||||||
| 					t.Fatalf("column age default value should be correct, name: %v, column: %#v", columnType.Name(), columnType) | 					t.Fatalf("column age default value should be correct, name: %v, column: %#v", columnType.Name(), | ||||||
|  | 						columnType) | ||||||
| 				} | 				} | ||||||
| 				if v, ok := columnType.Comment(); !ok || v != "my age" { | 				if v, ok := columnType.Comment(); !ok || v != "my age" { | ||||||
| 					t.Fatalf("column age comment should be correct, name: %v, column: %#v", columnType.Name(), columnType) | 					t.Fatalf("column age comment should be correct, name: %v, column: %#v", columnType.Name(), | ||||||
|  | 						columnType) | ||||||
| 				} | 				} | ||||||
| 			case "code": | 			case "code": | ||||||
| 				if v, ok := columnType.Unique(); !ok || !v { | 				if v, ok := columnType.Unique(); !ok || !v { | ||||||
| 					t.Fatalf("column code unique should be correct, name: %v, column: %#v", columnType.Name(), columnType) | 					t.Fatalf("column code unique should be correct, name: %v, column: %#v", columnType.Name(), | ||||||
|  | 						columnType) | ||||||
| 				} | 				} | ||||||
| 				if v, ok := columnType.DefaultValue(); !ok || v != "hello" { | 				if v, ok := columnType.DefaultValue(); !ok || v != "hello" { | ||||||
| 					t.Fatalf("column code default value should be correct, name: %v, column: %#v, default value: %v", columnType.Name(), columnType, v) | 					t.Fatalf("column code default value should be correct, name: %v, column: %#v, default value: %v", | ||||||
|  | 						columnType.Name(), columnType, v) | ||||||
| 				} | 				} | ||||||
| 				if v, ok := columnType.Comment(); !ok || v != "my code2" { | 				if v, ok := columnType.Comment(); !ok || v != "my code2" { | ||||||
| 					t.Fatalf("column code comment should be correct, name: %v, column: %#v", columnType.Name(), columnType) | 					t.Fatalf("column code comment should be correct, name: %v, column: %#v", columnType.Name(), | ||||||
|  | 						columnType) | ||||||
| 				} | 				} | ||||||
| 			case "code2": | 			case "code2": | ||||||
| 				// Code2 string `gorm:"comment:my code2;default:hello"`
 | 				// Code2 string `gorm:"comment:my code2;default:hello"`
 | ||||||
| 				if v, ok := columnType.DefaultValue(); !ok || v != "hello" { | 				if v, ok := columnType.DefaultValue(); !ok || v != "hello" { | ||||||
| 					t.Fatalf("column code default value should be correct, name: %v, column: %#v, default value: %v", columnType.Name(), columnType, v) | 					t.Fatalf("column code default value should be correct, name: %v, column: %#v, default value: %v", | ||||||
|  | 						columnType.Name(), columnType, v) | ||||||
| 				} | 				} | ||||||
| 				if v, ok := columnType.Comment(); !ok || v != "my code2" { | 				if v, ok := columnType.Comment(); !ok || v != "my code2" { | ||||||
| 					t.Fatalf("column code comment should be correct, name: %v, column: %#v", columnType.Name(), columnType) | 					t.Fatalf("column code comment should be correct, name: %v, column: %#v", columnType.Name(), | ||||||
|  | 						columnType) | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| @ -497,7 +508,8 @@ func TestTiDBMigrateColumns(t *testing.T) { | |||||||
| 		t.Fatalf("Failed to add column, got %v", err) | 		t.Fatalf("Failed to add column, got %v", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if err := DB.Table("column_structs").Migrator().RenameColumn(&NewColumnStruct{}, "NewName", "new_new_name"); err != nil { | 	if err := DB.Table("column_structs").Migrator().RenameColumn(&NewColumnStruct{}, "NewName", | ||||||
|  | 		"new_new_name"); err != nil { | ||||||
| 		t.Fatalf("Failed to add column, got %v", err) | 		t.Fatalf("Failed to add column, got %v", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -561,36 +573,45 @@ func TestMigrateColumns(t *testing.T) { | |||||||
| 			switch columnType.Name() { | 			switch columnType.Name() { | ||||||
| 			case "id": | 			case "id": | ||||||
| 				if v, ok := columnType.PrimaryKey(); !ok || !v { | 				if v, ok := columnType.PrimaryKey(); !ok || !v { | ||||||
| 					t.Fatalf("column id primary key should be correct, name: %v, column: %#v", columnType.Name(), columnType) | 					t.Fatalf("column id primary key should be correct, name: %v, column: %#v", columnType.Name(), | ||||||
|  | 						columnType) | ||||||
| 				} | 				} | ||||||
| 			case "name": | 			case "name": | ||||||
| 				dataType := DB.Dialector.DataTypeOf(stmt.Schema.LookUpField(columnType.Name())) | 				dataType := DB.Dialector.DataTypeOf(stmt.Schema.LookUpField(columnType.Name())) | ||||||
| 				if !strings.Contains(strings.ToUpper(dataType), strings.ToUpper(columnType.DatabaseTypeName())) { | 				if !strings.Contains(strings.ToUpper(dataType), strings.ToUpper(columnType.DatabaseTypeName())) { | ||||||
| 					t.Fatalf("column name type should be correct, name: %v, length: %v, expects: %v, column: %#v", columnType.Name(), columnType.DatabaseTypeName(), dataType, columnType) | 					t.Fatalf("column name type should be correct, name: %v, length: %v, expects: %v, column: %#v", | ||||||
|  | 						columnType.Name(), columnType.DatabaseTypeName(), dataType, columnType) | ||||||
| 				} | 				} | ||||||
| 				if length, ok := columnType.Length(); !sqlite && (!ok || length != 100) { | 				if length, ok := columnType.Length(); !sqlite && (!ok || length != 100) { | ||||||
| 					t.Fatalf("column name length should be correct, name: %v, length: %v, expects: %v, column: %#v", columnType.Name(), length, 100, columnType) | 					t.Fatalf("column name length should be correct, name: %v, length: %v, expects: %v, column: %#v", | ||||||
|  | 						columnType.Name(), length, 100, columnType) | ||||||
| 				} | 				} | ||||||
| 			case "age": | 			case "age": | ||||||
| 				if v, ok := columnType.DefaultValue(); !ok || v != "18" { | 				if v, ok := columnType.DefaultValue(); !ok || v != "18" { | ||||||
| 					t.Fatalf("column age default value should be correct, name: %v, column: %#v", columnType.Name(), columnType) | 					t.Fatalf("column age default value should be correct, name: %v, column: %#v", columnType.Name(), | ||||||
|  | 						columnType) | ||||||
| 				} | 				} | ||||||
| 				if v, ok := columnType.Comment(); !sqlite && !sqlserver && (!ok || v != "my age") { | 				if v, ok := columnType.Comment(); !sqlite && !sqlserver && (!ok || v != "my age") { | ||||||
| 					t.Fatalf("column age comment should be correct, name: %v, column: %#v", columnType.Name(), columnType) | 					t.Fatalf("column age comment should be correct, name: %v, column: %#v", columnType.Name(), | ||||||
|  | 						columnType) | ||||||
| 				} | 				} | ||||||
| 			case "code": | 			case "code": | ||||||
| 				if v, ok := columnType.Unique(); !ok || !v { | 				if v, ok := columnType.Unique(); !ok || !v { | ||||||
| 					t.Fatalf("column code unique should be correct, name: %v, column: %#v", columnType.Name(), columnType) | 					t.Fatalf("column code unique should be correct, name: %v, column: %#v", columnType.Name(), | ||||||
|  | 						columnType) | ||||||
| 				} | 				} | ||||||
| 				if v, ok := columnType.DefaultValue(); !sqlserver && (!ok || v != "hello") { | 				if v, ok := columnType.DefaultValue(); !sqlserver && (!ok || v != "hello") { | ||||||
| 					t.Fatalf("column code default value should be correct, name: %v, column: %#v, default value: %v", columnType.Name(), columnType, v) | 					t.Fatalf("column code default value should be correct, name: %v, column: %#v, default value: %v", | ||||||
|  | 						columnType.Name(), columnType, v) | ||||||
| 				} | 				} | ||||||
| 				if v, ok := columnType.Comment(); !sqlite && !sqlserver && (!ok || v != "my code2") { | 				if v, ok := columnType.Comment(); !sqlite && !sqlserver && (!ok || v != "my code2") { | ||||||
| 					t.Fatalf("column code comment should be correct, name: %v, column: %#v", columnType.Name(), columnType) | 					t.Fatalf("column code comment should be correct, name: %v, column: %#v", columnType.Name(), | ||||||
|  | 						columnType) | ||||||
| 				} | 				} | ||||||
| 			case "code2": | 			case "code2": | ||||||
| 				if v, ok := columnType.Unique(); !sqlserver && (!ok || !v) { | 				if v, ok := columnType.Unique(); !sqlserver && (!ok || !v) { | ||||||
| 					t.Fatalf("column code2 unique should be correct, name: %v, column: %#v", columnType.Name(), columnType) | 					t.Fatalf("column code2 unique should be correct, name: %v, column: %#v", columnType.Name(), | ||||||
|  | 						columnType) | ||||||
| 				} | 				} | ||||||
| 			case "code3": | 			case "code3": | ||||||
| 				// TODO
 | 				// TODO
 | ||||||
| @ -627,7 +648,8 @@ func TestMigrateColumns(t *testing.T) { | |||||||
| 		t.Fatalf("Failed to add column, got %v", err) | 		t.Fatalf("Failed to add column, got %v", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if err := DB.Table("column_structs").Migrator().RenameColumn(&NewColumnStruct{}, "NewName", "new_new_name"); err != nil { | 	if err := DB.Table("column_structs").Migrator().RenameColumn(&NewColumnStruct{}, "NewName", | ||||||
|  | 		"new_new_name"); err != nil { | ||||||
| 		t.Fatalf("Failed to add column, got %v", err) | 		t.Fatalf("Failed to add column, got %v", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -1555,7 +1577,8 @@ func TestMigrateIgnoreRelations(t *testing.T) { | |||||||
| func TestMigrateView(t *testing.T) { | func TestMigrateView(t *testing.T) { | ||||||
| 	DB.Save(GetUser("joins-args-db", Config{Pets: 2})) | 	DB.Save(GetUser("joins-args-db", Config{Pets: 2})) | ||||||
| 
 | 
 | ||||||
| 	if err := DB.Migrator().CreateView("invalid_users_pets", gorm.ViewOption{Query: nil}); err != gorm.ErrSubQueryRequired { | 	if err := DB.Migrator().CreateView("invalid_users_pets", | ||||||
|  | 		gorm.ViewOption{Query: nil}); err != gorm.ErrSubQueryRequired { | ||||||
| 		t.Fatalf("no view should be created, got %v", err) | 		t.Fatalf("no view should be created, got %v", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -1624,17 +1647,20 @@ func TestMigrateExistingBoolColumnPG(t *testing.T) { | |||||||
| 			switch columnType.Name() { | 			switch columnType.Name() { | ||||||
| 			case "id": | 			case "id": | ||||||
| 				if v, ok := columnType.PrimaryKey(); !ok || !v { | 				if v, ok := columnType.PrimaryKey(); !ok || !v { | ||||||
| 					t.Fatalf("column id primary key should be correct, name: %v, column: %#v", columnType.Name(), columnType) | 					t.Fatalf("column id primary key should be correct, name: %v, column: %#v", columnType.Name(), | ||||||
|  | 						columnType) | ||||||
| 				} | 				} | ||||||
| 			case "string_bool": | 			case "string_bool": | ||||||
| 				dataType := DB.Dialector.DataTypeOf(stmt.Schema.LookUpField(columnType.Name())) | 				dataType := DB.Dialector.DataTypeOf(stmt.Schema.LookUpField(columnType.Name())) | ||||||
| 				if !strings.Contains(strings.ToUpper(dataType), strings.ToUpper(columnType.DatabaseTypeName())) { | 				if !strings.Contains(strings.ToUpper(dataType), strings.ToUpper(columnType.DatabaseTypeName())) { | ||||||
| 					t.Fatalf("column name type should be correct, name: %v, length: %v, expects: %v, column: %#v", columnType.Name(), columnType.DatabaseTypeName(), dataType, columnType) | 					t.Fatalf("column name type should be correct, name: %v, length: %v, expects: %v, column: %#v", | ||||||
|  | 						columnType.Name(), columnType.DatabaseTypeName(), dataType, columnType) | ||||||
| 				} | 				} | ||||||
| 			case "smallint_bool": | 			case "smallint_bool": | ||||||
| 				dataType := DB.Dialector.DataTypeOf(stmt.Schema.LookUpField(columnType.Name())) | 				dataType := DB.Dialector.DataTypeOf(stmt.Schema.LookUpField(columnType.Name())) | ||||||
| 				if !strings.Contains(strings.ToUpper(dataType), strings.ToUpper(columnType.DatabaseTypeName())) { | 				if !strings.Contains(strings.ToUpper(dataType), strings.ToUpper(columnType.DatabaseTypeName())) { | ||||||
| 					t.Fatalf("column name type should be correct, name: %v, length: %v, expects: %v, column: %#v", columnType.Name(), columnType.DatabaseTypeName(), dataType, columnType) | 					t.Fatalf("column name type should be correct, name: %v, length: %v, expects: %v, column: %#v", | ||||||
|  | 						columnType.Name(), columnType.DatabaseTypeName(), dataType, columnType) | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| @ -1659,7 +1685,8 @@ func TestTableType(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| 	DB.Migrator().DropTable(&City{}) | 	DB.Migrator().DropTable(&City{}) | ||||||
| 
 | 
 | ||||||
| 	if err := DB.Set("gorm:table_options", fmt.Sprintf("ENGINE InnoDB COMMENT '%s'", tblComment)).AutoMigrate(&City{}); err != nil { | 	if err := DB.Set("gorm:table_options", | ||||||
|  | 		fmt.Sprintf("ENGINE InnoDB COMMENT '%s'", tblComment)).AutoMigrate(&City{}); err != nil { | ||||||
| 		t.Fatalf("failed to migrate cities tables, got error: %v", err) | 		t.Fatalf("failed to migrate cities tables, got error: %v", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -107,7 +107,7 @@ func OpenTestConnection(cfg *gorm.Config) (db *gorm.DB, err error) { | |||||||
| 
 | 
 | ||||||
| func RunMigrations() { | func RunMigrations() { | ||||||
| 	var err error | 	var err error | ||||||
| 	allModels := []interface{}{&User{}, &Account{}, &Pet{}, &Company{}, &Toy{}, &Language{}, &Coupon{}, &CouponProduct{}, &Order{}, &Parent{}, &Child{}} | 	allModels := []interface{}{&User{}, &Account{}, &Pet{}, &Company{}, &Toy{}, &Language{}, &Coupon{}, &CouponProduct{}, &Order{}, &Parent{}, &Child{}, &Tools{}} | ||||||
| 	rand.Seed(time.Now().UnixNano()) | 	rand.Seed(time.Now().UnixNano()) | ||||||
| 	rand.Shuffle(len(allModels), func(i, j int) { allModels[i], allModels[j] = allModels[j], allModels[i] }) | 	rand.Shuffle(len(allModels), func(i, j int) { allModels[i], allModels[j] = allModels[j], allModels[i] }) | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -20,7 +20,8 @@ type User struct { | |||||||
| 	Account   Account | 	Account   Account | ||||||
| 	Pets      []*Pet | 	Pets      []*Pet | ||||||
| 	NamedPet  *Pet | 	NamedPet  *Pet | ||||||
| 	Toys      []Toy `gorm:"polymorphic:Owner"` | 	Toys      []Toy   `gorm:"polymorphic:Owner"` | ||||||
|  | 	Tools     []Tools `gorm:"polymorphicType:Type;polymorphicId:CustomID"` | ||||||
| 	CompanyID *int | 	CompanyID *int | ||||||
| 	Company   Company | 	Company   Company | ||||||
| 	ManagerID *uint | 	ManagerID *uint | ||||||
| @ -51,6 +52,13 @@ type Toy struct { | |||||||
| 	OwnerType string | 	OwnerType string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | type Tools struct { | ||||||
|  | 	gorm.Model | ||||||
|  | 	Name     string | ||||||
|  | 	CustomID string | ||||||
|  | 	Type     string | ||||||
|  | } | ||||||
|  | 
 | ||||||
| type Company struct { | type Company struct { | ||||||
| 	ID   int | 	ID   int | ||||||
| 	Name string | 	Name string | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Alexis Viscogliosi
						Alexis Viscogliosi