Add on conflict support
This commit is contained in:
		
							parent
							
								
									135d9f8b03
								
							
						
					
					
						commit
						cc064f26ee
					
				| @ -4,6 +4,7 @@ import ( | |||||||
| 	"reflect" | 	"reflect" | ||||||
| 
 | 
 | ||||||
| 	"github.com/jinzhu/gorm" | 	"github.com/jinzhu/gorm" | ||||||
|  | 	"github.com/jinzhu/gorm/clause" | ||||||
| 	"github.com/jinzhu/gorm/schema" | 	"github.com/jinzhu/gorm/schema" | ||||||
| 	"github.com/jinzhu/gorm/utils" | 	"github.com/jinzhu/gorm/utils" | ||||||
| ) | ) | ||||||
| @ -282,7 +283,7 @@ func SaveAfterAssociations(db *gorm.DB) { | |||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			if joins.Len() > 0 { | 			if joins.Len() > 0 { | ||||||
| 				db.Session(&gorm.Session{}).Create(joins.Interface()) | 				db.Session(&gorm.Session{}).Clauses(clause.OnConflict{DoNothing: true}).Create(joins.Interface()) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -51,7 +51,7 @@ func Create(config *Config) func(db *gorm.DB) { | |||||||
| 			}) | 			}) | ||||||
| 			db.Statement.AddClause(ConvertToCreateValues(db.Statement)) | 			db.Statement.AddClause(ConvertToCreateValues(db.Statement)) | ||||||
| 
 | 
 | ||||||
| 			db.Statement.Build("INSERT", "VALUES", "ON_CONFLICT") | 			db.Statement.Build("INSERT", "VALUES", "ON CONFLICT") | ||||||
| 			result, err := db.Statement.ConnPool.ExecContext(db.Statement.Context, db.Statement.SQL.String(), db.Statement.Vars...) | 			result, err := db.Statement.ConnPool.ExecContext(db.Statement.Context, db.Statement.SQL.String(), db.Statement.Vars...) | ||||||
| 
 | 
 | ||||||
| 			if err == nil { | 			if err == nil { | ||||||
| @ -93,7 +93,7 @@ func CreateWithReturning(db *gorm.DB) { | |||||||
| 	}) | 	}) | ||||||
| 	db.Statement.AddClause(ConvertToCreateValues(db.Statement)) | 	db.Statement.AddClause(ConvertToCreateValues(db.Statement)) | ||||||
| 
 | 
 | ||||||
| 	db.Statement.Build("INSERT", "VALUES", "ON_CONFLICT") | 	db.Statement.Build("INSERT", "VALUES", "ON CONFLICT") | ||||||
| 
 | 
 | ||||||
| 	if sch := db.Statement.Schema; sch != nil && len(sch.FieldsWithDefaultDBValue) > 0 { | 	if sch := db.Statement.Schema; sch != nil && len(sch.FieldsWithDefaultDBValue) > 0 { | ||||||
| 		db.Statement.WriteString(" RETURNING ") | 		db.Statement.WriteString(" RETURNING ") | ||||||
|  | |||||||
							
								
								
									
										38
									
								
								clause/on_conflict.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								clause/on_conflict.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | package clause | ||||||
|  | 
 | ||||||
|  | type OnConflict struct { | ||||||
|  | 	Columns   []Column | ||||||
|  | 	Where     Where | ||||||
|  | 	DoNothing bool | ||||||
|  | 	DoUpdates Set | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (OnConflict) Name() string { | ||||||
|  | 	return "ON CONFLICT" | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Build build onConflict clause
 | ||||||
|  | func (onConflict OnConflict) Build(builder Builder) { | ||||||
|  | 	if len(onConflict.Columns) > 0 { | ||||||
|  | 		builder.WriteQuoted(onConflict.Columns) // FIXME columns
 | ||||||
|  | 		builder.WriteByte(' ') | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if len(onConflict.Where.Exprs) > 0 { | ||||||
|  | 		builder.WriteString("WHERE ") | ||||||
|  | 		onConflict.Where.Build(builder) | ||||||
|  | 		builder.WriteByte(' ') | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if onConflict.DoNothing { | ||||||
|  | 		builder.WriteString("DO NOTHING") | ||||||
|  | 	} else { | ||||||
|  | 		builder.WriteString("DO UPDATE SET ") | ||||||
|  | 		onConflict.DoUpdates.Build(builder) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // MergeClause merge onConflict clauses
 | ||||||
|  | func (onConflict OnConflict) MergeClause(clause *Clause) { | ||||||
|  | 	clause.Expression = onConflict | ||||||
|  | } | ||||||
| @ -355,7 +355,7 @@ func (rel *Relationship) ToQueryConditions(reflectValue reflect.Value) (conds [] | |||||||
| 		for _, ref := range rel.References { | 		for _, ref := range rel.References { | ||||||
| 			if ref.OwnPrimaryKey { | 			if ref.OwnPrimaryKey { | ||||||
| 				foreignFields = append(foreignFields, ref.PrimaryKey) | 				foreignFields = append(foreignFields, ref.PrimaryKey) | ||||||
| 				relForeignKeys = append(relForeignKeys, ref.PrimaryKey.DBName) | 				relForeignKeys = append(relForeignKeys, ref.ForeignKey.DBName) | ||||||
| 			} else if ref.PrimaryValue != "" { | 			} else if ref.PrimaryValue != "" { | ||||||
| 				conds = append(conds, clause.Eq{ | 				conds = append(conds, clause.Eq{ | ||||||
| 					Column: clause.Column{Table: rel.JoinTable.Table, Name: ref.ForeignKey.DBName}, | 					Column: clause.Column{Table: rel.JoinTable.Table, Name: ref.ForeignKey.DBName}, | ||||||
|  | |||||||
| @ -766,3 +766,90 @@ func TestPolymorphicHasManyAssociationForSlice(t *testing.T) { | |||||||
| 	DB.Model(&users).Association("Toys").Clear() | 	DB.Model(&users).Association("Toys").Clear() | ||||||
| 	AssertAssociationCount(t, users, "Toys", 0, "After Clear") | 	AssertAssociationCount(t, users, "Toys", 0, "After Clear") | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func TestMany2ManyAssociation(t *testing.T) { | ||||||
|  | 	var user = *GetUser("many2many", Config{Languages: 2}) | ||||||
|  | 
 | ||||||
|  | 	if err := DB.Create(&user).Error; err != nil { | ||||||
|  | 		t.Fatalf("errors happened when create: %v", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	CheckUser(t, user, user) | ||||||
|  | 
 | ||||||
|  | 	// Find
 | ||||||
|  | 	var user2 User | ||||||
|  | 	DB.Find(&user2, "id = ?", user.ID) | ||||||
|  | 	DB.Model(&user2).Association("Languages").Find(&user2.Languages) | ||||||
|  | 
 | ||||||
|  | 	CheckUser(t, user2, user) | ||||||
|  | 
 | ||||||
|  | 	// Count
 | ||||||
|  | 	AssertAssociationCount(t, user, "Languages", 2, "") | ||||||
|  | 
 | ||||||
|  | 	// Append
 | ||||||
|  | 	var language = Language{Code: "language-has-many-append", Name: "language-has-many-append"} | ||||||
|  | 	DB.Create(&language) | ||||||
|  | 
 | ||||||
|  | 	if err := DB.Model(&user2).Association("Languages").Append(&language); err != nil { | ||||||
|  | 		t.Fatalf("Error happened when append account, got %v", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	user.Languages = append(user.Languages, language) | ||||||
|  | 	CheckUser(t, user2, user) | ||||||
|  | 
 | ||||||
|  | 	AssertAssociationCount(t, user, "Languages", 3, "AfterAppend") | ||||||
|  | 
 | ||||||
|  | 	var languages = []Language{ | ||||||
|  | 		{Code: "language-has-many-append-1-1", Name: "language-has-many-append-1-1"}, | ||||||
|  | 		{Code: "language-has-many-append-2-1", Name: "language-has-many-append-2-1"}, | ||||||
|  | 	} | ||||||
|  | 	DB.Create(&languages) | ||||||
|  | 
 | ||||||
|  | 	if err := DB.Model(&user2).Association("Languages").Append(&languages); err != nil { | ||||||
|  | 		t.Fatalf("Error happened when append language, got %v", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	user.Languages = append(user.Languages, languages...) | ||||||
|  | 
 | ||||||
|  | 	CheckUser(t, user2, user) | ||||||
|  | 
 | ||||||
|  | 	AssertAssociationCount(t, user, "Languages", 5, "AfterAppendSlice") | ||||||
|  | 
 | ||||||
|  | 	// Replace
 | ||||||
|  | 	var language2 = Language{Code: "language-has-many-replace", Name: "language-has-many-replace"} | ||||||
|  | 	DB.Create(&language2) | ||||||
|  | 
 | ||||||
|  | 	if err := DB.Model(&user2).Association("Languages").Replace(&language2); err != nil { | ||||||
|  | 		t.Fatalf("Error happened when append language, got %v", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	user.Languages = []Language{language2} | ||||||
|  | 	CheckUser(t, user2, user) | ||||||
|  | 
 | ||||||
|  | 	AssertAssociationCount(t, user2, "Languages", 1, "AfterReplace") | ||||||
|  | 
 | ||||||
|  | 	// Delete
 | ||||||
|  | 	if err := DB.Model(&user2).Association("Languages").Delete(&Language{}); err != nil { | ||||||
|  | 		t.Fatalf("Error happened when delete language, got %v", err) | ||||||
|  | 	} | ||||||
|  | 	AssertAssociationCount(t, user2, "Languages", 1, "after delete non-existing data") | ||||||
|  | 
 | ||||||
|  | 	if err := DB.Model(&user2).Association("Languages").Delete(&language2); err != nil { | ||||||
|  | 		t.Fatalf("Error happened when delete Languages, got %v", err) | ||||||
|  | 	} | ||||||
|  | 	AssertAssociationCount(t, user2, "Languages", 0, "after delete") | ||||||
|  | 
 | ||||||
|  | 	// Prepare Data for Clear
 | ||||||
|  | 	if err := DB.Model(&user2).Association("Languages").Append(&language); err != nil { | ||||||
|  | 		t.Fatalf("Error happened when append Languages, got %v", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	AssertAssociationCount(t, user2, "Languages", 1, "after prepare data") | ||||||
|  | 
 | ||||||
|  | 	// Clear
 | ||||||
|  | 	if err := DB.Model(&user2).Association("Languages").Clear(); err != nil { | ||||||
|  | 		t.Errorf("Error happened when clear Languages, got %v", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	AssertAssociationCount(t, user2, "Languages", 0, "after clear") | ||||||
|  | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Jinzhu
						Jinzhu