Add on conflict support
This commit is contained in:
		
							parent
							
								
									135d9f8b03
								
							
						
					
					
						commit
						cc064f26ee
					
				| @ -4,6 +4,7 @@ import ( | ||||
| 	"reflect" | ||||
| 
 | ||||
| 	"github.com/jinzhu/gorm" | ||||
| 	"github.com/jinzhu/gorm/clause" | ||||
| 	"github.com/jinzhu/gorm/schema" | ||||
| 	"github.com/jinzhu/gorm/utils" | ||||
| ) | ||||
| @ -282,7 +283,7 @@ func SaveAfterAssociations(db *gorm.DB) { | ||||
| 			} | ||||
| 
 | ||||
| 			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.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...) | ||||
| 
 | ||||
| 			if err == nil { | ||||
| @ -93,7 +93,7 @@ func CreateWithReturning(db *gorm.DB) { | ||||
| 	}) | ||||
| 	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 { | ||||
| 		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 { | ||||
| 			if ref.OwnPrimaryKey { | ||||
| 				foreignFields = append(foreignFields, ref.PrimaryKey) | ||||
| 				relForeignKeys = append(relForeignKeys, ref.PrimaryKey.DBName) | ||||
| 				relForeignKeys = append(relForeignKeys, ref.ForeignKey.DBName) | ||||
| 			} else if ref.PrimaryValue != "" { | ||||
| 				conds = append(conds, clause.Eq{ | ||||
| 					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() | ||||
| 	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