Test ForeignKeyConstraints
This commit is contained in:
		
							parent
							
								
									d4d339f3b5
								
							
						
					
					
						commit
						4f19e2a7b3
					
				| @ -137,6 +137,32 @@ func ConvertToAssignments(stmt *gorm.Statement) (set clause.Set) { | ||||
| 		updatingValue = updatingValue.Elem() | ||||
| 	} | ||||
| 
 | ||||
| 	if !updatingValue.CanAddr() || stmt.Dest != stmt.Model { | ||||
| 		switch stmt.ReflectValue.Kind() { | ||||
| 		case reflect.Slice, reflect.Array: | ||||
| 			var priamryKeyExprs []clause.Expression | ||||
| 			for i := 0; i < stmt.ReflectValue.Len(); i++ { | ||||
| 				var exprs = make([]clause.Expression, len(stmt.Schema.PrimaryFields)) | ||||
| 				var notZero bool | ||||
| 				for idx, field := range stmt.Schema.PrimaryFields { | ||||
| 					value, isZero := field.ValueOf(stmt.ReflectValue.Index(i)) | ||||
| 					exprs[idx] = clause.Eq{Column: field.DBName, Value: value} | ||||
| 					notZero = notZero || !isZero | ||||
| 				} | ||||
| 				if notZero { | ||||
| 					priamryKeyExprs = append(priamryKeyExprs, clause.And(exprs...)) | ||||
| 				} | ||||
| 			} | ||||
| 			stmt.AddClause(clause.Where{Exprs: []clause.Expression{clause.Or(priamryKeyExprs...)}}) | ||||
| 		case reflect.Struct: | ||||
| 			for _, field := range stmt.Schema.PrimaryFields { | ||||
| 				if value, isZero := field.ValueOf(stmt.ReflectValue); !isZero { | ||||
| 					stmt.AddClause(clause.Where{Exprs: []clause.Expression{clause.Eq{Column: field.DBName, Value: value}}}) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	switch value := updatingValue.Interface().(type) { | ||||
| 	case map[string]interface{}: | ||||
| 		set = make([]clause.Assignment, 0, len(value)) | ||||
| @ -218,31 +244,5 @@ func ConvertToAssignments(stmt *gorm.Statement) (set clause.Set) { | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if !updatingValue.CanAddr() || stmt.Dest != stmt.Model { | ||||
| 		switch stmt.ReflectValue.Kind() { | ||||
| 		case reflect.Slice, reflect.Array: | ||||
| 			var priamryKeyExprs []clause.Expression | ||||
| 			for i := 0; i < stmt.ReflectValue.Len(); i++ { | ||||
| 				var exprs = make([]clause.Expression, len(stmt.Schema.PrimaryFields)) | ||||
| 				var notZero bool | ||||
| 				for idx, field := range stmt.Schema.PrimaryFields { | ||||
| 					value, isZero := field.ValueOf(stmt.ReflectValue.Index(i)) | ||||
| 					exprs[idx] = clause.Eq{Column: field.DBName, Value: value} | ||||
| 					notZero = notZero || !isZero | ||||
| 				} | ||||
| 				if notZero { | ||||
| 					priamryKeyExprs = append(priamryKeyExprs, clause.And(exprs...)) | ||||
| 				} | ||||
| 			} | ||||
| 			stmt.AddClause(clause.Where{Exprs: []clause.Expression{clause.Or(priamryKeyExprs...)}}) | ||||
| 		case reflect.Struct: | ||||
| 			for _, field := range stmt.Schema.PrimaryFields { | ||||
| 				if value, isZero := field.ValueOf(stmt.ReflectValue); !isZero { | ||||
| 					stmt.AddClause(clause.Where{Exprs: []clause.Expression{clause.Eq{Column: field.DBName, Value: value}}}) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
|  | ||||
| @ -103,12 +103,14 @@ func (m Migrator) AutoMigrate(values ...interface{}) error { | ||||
| 
 | ||||
| 				for _, rel := range stmt.Schema.Relationships.Relations { | ||||
| 					if constraint := rel.ParseConstraint(); constraint != nil { | ||||
| 						if constraint.Schema == stmt.Schema { | ||||
| 							if !tx.Migrator().HasConstraint(value, constraint.Name) { | ||||
| 								if err := tx.Migrator().CreateConstraint(value, constraint.Name); err != nil { | ||||
| 									return err | ||||
| 								} | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 
 | ||||
| 					for _, chk := range stmt.Schema.ParseCheckConstraints() { | ||||
| 						if !tx.Migrator().HasConstraint(value, chk.Name) { | ||||
| @ -177,10 +179,12 @@ func (m Migrator) CreateTable(values ...interface{}) error { | ||||
| 
 | ||||
| 			for _, rel := range stmt.Schema.Relationships.Relations { | ||||
| 				if constraint := rel.ParseConstraint(); constraint != nil { | ||||
| 					if constraint.Schema == stmt.Schema { | ||||
| 						sql, vars := buildConstraint(constraint) | ||||
| 						createTableSQL += sql + "," | ||||
| 						values = append(values, vars...) | ||||
| 					} | ||||
| 				} | ||||
| 
 | ||||
| 				// create join table
 | ||||
| 				if rel.JoinTable != nil { | ||||
| @ -550,7 +554,7 @@ func (m Migrator) ReorderModels(values []interface{}, autoAdd bool) (results []i | ||||
| 		dep.Parse(value) | ||||
| 
 | ||||
| 		for _, rel := range dep.Schema.Relationships.Relations { | ||||
| 			if c := rel.ParseConstraint(); c != nil && c.Schema != c.ReferenceSchema { | ||||
| 			if c := rel.ParseConstraint(); c != nil && c.Schema == dep.Statement.Schema && c.Schema != c.ReferenceSchema { | ||||
| 				dep.Depends = append(dep.Depends, c.ReferenceSchema) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| @ -85,6 +85,10 @@ func (schema *Schema) parseRelation(field *Field) { | ||||
| 	} | ||||
| 
 | ||||
| 	if relation.Type == "has" { | ||||
| 		if relation.FieldSchema != relation.Schema && relation.Polymorphic == nil { | ||||
| 			relation.FieldSchema.Relationships.Relations["_"+relation.Schema.Name+"_"+relation.Name] = relation | ||||
| 		} | ||||
| 
 | ||||
| 		switch field.IndirectFieldType.Kind() { | ||||
| 		case reflect.Struct: | ||||
| 			relation.Type = HasOne | ||||
| @ -384,18 +388,24 @@ func (rel *Relationship) ParseConstraint() *Constraint { | ||||
| 		Field:    rel.Field, | ||||
| 		OnUpdate: settings["ONUPDATE"], | ||||
| 		OnDelete: settings["ONDELETE"], | ||||
| 		Schema:   rel.Schema, | ||||
| 	} | ||||
| 
 | ||||
| 	for _, ref := range rel.References { | ||||
| 		if ref.PrimaryKey != nil && !ref.OwnPrimaryKey { | ||||
| 		if ref.PrimaryKey != nil { | ||||
| 			constraint.ForeignKeys = append(constraint.ForeignKeys, ref.ForeignKey) | ||||
| 			constraint.References = append(constraint.References, ref.PrimaryKey) | ||||
| 
 | ||||
| 			if ref.OwnPrimaryKey { | ||||
| 				constraint.Schema = ref.ForeignKey.Schema | ||||
| 				constraint.ReferenceSchema = rel.Schema | ||||
| 			} else { | ||||
| 				constraint.Schema = rel.Schema | ||||
| 				constraint.ReferenceSchema = ref.PrimaryKey.Schema | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if rel.JoinTable != nil || constraint.ReferenceSchema == nil { | ||||
| 	if rel.JoinTable != nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
|  | ||||
| @ -31,3 +31,112 @@ func TestInvalidAssociation(t *testing.T) { | ||||
| 		t.Fatalf("should return errors for invalid association, but got nil") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestForeignKeyConstraints(t *testing.T) { | ||||
| 	type Profile struct { | ||||
| 		ID       uint | ||||
| 		Name     string | ||||
| 		MemberID uint | ||||
| 	} | ||||
| 
 | ||||
| 	type Member struct { | ||||
| 		ID      uint | ||||
| 		Refer   uint `gorm:"unique_index"` | ||||
| 		Name    string | ||||
| 		Profile Profile `gorm:"Constraint:OnUpdate:CASCADE,OnDelete:CASCADE;FOREIGNKEY:MemberID;References:Refer"` | ||||
| 	} | ||||
| 
 | ||||
| 	DB.Migrator().DropTable(&Profile{}, &Member{}) | ||||
| 
 | ||||
| 	if err := DB.AutoMigrate(&Profile{}, &Member{}); err != nil { | ||||
| 		t.Fatalf("Failed to migrate, got error: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	member := Member{Refer: 1, Name: "foreign_key_constraints", Profile: Profile{Name: "my_profile"}} | ||||
| 
 | ||||
| 	DB.Create(&member) | ||||
| 
 | ||||
| 	var profile Profile | ||||
| 	if err := DB.First(&profile, "id = ?", member.Profile.ID).Error; err != nil { | ||||
| 		t.Fatalf("failed to find profile, got error: %v", err) | ||||
| 	} else if profile.MemberID != member.ID { | ||||
| 		t.Fatalf("member id is not equal: expects: %v, got: %v", member.ID, profile.MemberID) | ||||
| 	} | ||||
| 
 | ||||
| 	member.Profile = Profile{} | ||||
| 	DB.Model(&member).Update("Refer", 100) | ||||
| 
 | ||||
| 	var profile2 Profile | ||||
| 	if err := DB.First(&profile2, "id = ?", profile.ID).Error; err != nil { | ||||
| 		t.Fatalf("failed to find profile, got error: %v", err) | ||||
| 	} else if profile2.MemberID != 100 { | ||||
| 		t.Fatalf("member id is not equal: expects: %v, got: %v", 100, profile2.MemberID) | ||||
| 	} | ||||
| 
 | ||||
| 	if r := DB.Delete(&member); r.Error != nil || r.RowsAffected != 1 { | ||||
| 		t.Fatalf("Should delete member, got error: %v, affected: %v", r.Error, r.RowsAffected) | ||||
| 	} | ||||
| 
 | ||||
| 	var result Member | ||||
| 	if err := DB.First(&result, member.ID).Error; err == nil { | ||||
| 		t.Fatalf("Should not find deleted member") | ||||
| 	} | ||||
| 
 | ||||
| 	if err := DB.First(&profile2, profile.ID).Error; err == nil { | ||||
| 		t.Fatalf("Should not find deleted profile") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestForeignKeyConstraintsBelongsTo(t *testing.T) { | ||||
| 	type Profile struct { | ||||
| 		ID    uint | ||||
| 		Name  string | ||||
| 		Refer uint `gorm:"unique_index"` | ||||
| 	} | ||||
| 
 | ||||
| 	type Member struct { | ||||
| 		ID        uint | ||||
| 		Name      string | ||||
| 		ProfileID uint | ||||
| 		Profile   Profile `gorm:"Constraint:OnUpdate:CASCADE,OnDelete:CASCADE;FOREIGNKEY:ProfileID;References:Refer"` | ||||
| 	} | ||||
| 
 | ||||
| 	DB.Migrator().DropTable(&Profile{}, &Member{}) | ||||
| 
 | ||||
| 	if err := DB.AutoMigrate(&Profile{}, &Member{}); err != nil { | ||||
| 		t.Fatalf("Failed to migrate, got error: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	member := Member{Name: "foreign_key_constraints_belongs_to", Profile: Profile{Name: "my_profile_belongs_to", Refer: 1}} | ||||
| 
 | ||||
| 	DB.Create(&member) | ||||
| 
 | ||||
| 	var profile Profile | ||||
| 	if err := DB.First(&profile, "id = ?", member.Profile.ID).Error; err != nil { | ||||
| 		t.Fatalf("failed to find profile, got error: %v", err) | ||||
| 	} else if profile.Refer != member.ProfileID { | ||||
| 		t.Fatalf("member id is not equal: expects: %v, got: %v", profile.Refer, member.ProfileID) | ||||
| 	} | ||||
| 
 | ||||
| 	DB.Model(&profile).Update("Refer", 100) | ||||
| 
 | ||||
| 	var member2 Member | ||||
| 	if err := DB.First(&member2, "id = ?", member.ID).Error; err != nil { | ||||
| 		t.Fatalf("failed to find member, got error: %v", err) | ||||
| 	} else if member2.ProfileID != 100 { | ||||
| 		t.Fatalf("member id is not equal: expects: %v, got: %v", 100, member2.ProfileID) | ||||
| 	} | ||||
| 
 | ||||
| 	if r := DB.Delete(&profile); r.Error != nil || r.RowsAffected != 1 { | ||||
| 		t.Fatalf("Should delete member, got error: %v, affected: %v", r.Error, r.RowsAffected) | ||||
| 	} | ||||
| 
 | ||||
| 	var result Member | ||||
| 	if err := DB.First(&result, member.ID).Error; err == nil { | ||||
| 		t.Fatalf("Should not find deleted member") | ||||
| 	} | ||||
| 
 | ||||
| 	if err := DB.First(&profile, profile.ID).Error; err == nil { | ||||
| 		t.Fatalf("Should not find deleted profile") | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -433,8 +433,8 @@ func TestNestedPreload9(t *testing.T) { | ||||
| 		Level1 struct { | ||||
| 			ID         uint | ||||
| 			Value      string | ||||
| 			Level2ID   uint | ||||
| 			Level2_1ID uint | ||||
| 			Level2ID   *uint | ||||
| 			Level2_1ID *uint | ||||
| 			Level0s    []Level0 `json:",omitempty"` | ||||
| 		} | ||||
| 		Level2 struct { | ||||
|  | ||||
| @ -66,6 +66,7 @@ func OpenTestConnection() (db *gorm.DB, err error) { | ||||
| 	default: | ||||
| 		log.Println("testing sqlite3...") | ||||
| 		db, err = gorm.Open(sqlite.Open(filepath.Join(os.TempDir(), "gorm.db")), &gorm.Config{}) | ||||
| 		db.Exec("PRAGMA foreign_keys = ON") | ||||
| 	} | ||||
| 
 | ||||
| 	if debug := os.Getenv("DEBUG"); debug == "true" { | ||||
|  | ||||
| @ -37,7 +37,7 @@ type Account struct { | ||||
| 
 | ||||
| type Pet struct { | ||||
| 	gorm.Model | ||||
| 	UserID uint | ||||
| 	UserID *uint | ||||
| 	Name   string | ||||
| 	Toy    Toy `gorm:"polymorphic:Owner;"` | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Jinzhu
						Jinzhu