From ce13a2a7bc50d2c23678806acf65dbd589827c77 Mon Sep 17 00:00:00 2001 From: chenrui Date: Mon, 7 Mar 2022 14:39:56 +0800 Subject: [PATCH] fix: soft delete for join.on --- callbacks/query.go | 13 ++++++++++++- soft_delete.go | 18 ++++++++++++++++++ statement.go | 7 +++++++ tests/joins_test.go | 23 +++++++++++++++++++++++ 4 files changed, 60 insertions(+), 1 deletion(-) diff --git a/callbacks/query.go b/callbacks/query.go index 03798859..2c61d6c7 100644 --- a/callbacks/query.go +++ b/callbacks/query.go @@ -107,6 +107,7 @@ func BuildQuerySQL(db *gorm.DB) { } } + joinQueryClauses := make([]clause.Interface, 0) for _, join := range db.Statement.Joins { if db.Statement.Schema == nil { joins = append(joins, clause.Join{ @@ -160,6 +161,10 @@ func BuildQuerySQL(db *gorm.DB) { exprs = append(exprs, clause.Expr{SQL: onSQL, Vars: vars}) } + if len(relation.FieldSchema.QueryClauses) > 0 { + joinQueryClauses = append(joinQueryClauses, relation.FieldSchema.QueryClauses...) + } + joins = append(joins, clause.Join{ Type: clause.LeftJoin, Table: clause.Table{Name: relation.FieldSchema.Table, Alias: tableAliasName}, @@ -172,8 +177,14 @@ func BuildQuerySQL(db *gorm.DB) { } } - db.Statement.Joins = nil db.Statement.AddClause(clause.From{Joins: joins}) + + if len(joinQueryClauses) > 0 { + for _, c := range joinQueryClauses { + db.Statement.UpdateModifierJoinClause(c) + } + } + db.Statement.Joins = nil } else { db.Statement.AddClauseIfNotExists(clause.From{}) } diff --git a/soft_delete.go b/soft_delete.go index 6d646288..9b5982b1 100644 --- a/soft_delete.go +++ b/soft_delete.go @@ -82,6 +82,24 @@ func (sd SoftDeleteQueryClause) ModifyStatement(stmt *Statement) { }}) stmt.Clauses["soft_delete_enabled"] = clause.Clause{} } + + // Modify for Joins[i].ON exprs + if _, ok := stmt.Clauses["soft_delete_join_enabled"]; !ok && !stmt.Statement.Unscoped { + if c, ok := stmt.Clauses["FROM"]; ok && len(stmt.Joins) > 0 { + if fromClause, ok := c.Expression.(clause.From); ok && len(fromClause.Joins) > 0 { + for i, j := range fromClause.Joins { + if sd.Field.Schema != nil && j.Table.Name == sd.Field.Schema.Table { + j.ON.Exprs = append(j.ON.Exprs, clause.Eq{ + Column: clause.Column{Table: j.Table.Alias, Name: sd.Field.DBName}, Value: nil, + }) + } + fromClause.Joins[i] = j + } + } + stmt.Clauses["FROM"] = c + stmt.Clauses["soft_delete_join_enabled"] = clause.Clause{} + } + } } func (DeletedAt) UpdateClauses(f *schema.Field) []clause.Interface { diff --git a/statement.go b/statement.go index cb471776..ed5635bd 100644 --- a/statement.go +++ b/statement.go @@ -271,6 +271,13 @@ func (stmt *Statement) AddClauseIfNotExists(v clause.Interface) { } } +// UpdateModifierJoinClause update join clause for modifier +func (stmt *Statement) UpdateModifierJoinClause(v clause.Interface) { + if optimizer, ok := v.(StatementModifier); ok { + optimizer.ModifyStatement(stmt) + } +} + // BuildCondition build condition func (stmt *Statement) BuildCondition(query interface{}, args ...interface{}) []clause.Expression { if s, ok := query.(string); ok { diff --git a/tests/joins_test.go b/tests/joins_test.go index 4c9cffae..f9d66649 100644 --- a/tests/joins_test.go +++ b/tests/joins_test.go @@ -200,3 +200,26 @@ func TestJoinCount(t *testing.T) { t.Fatalf("result's id, %d, doesn't match user's id, %d", result.ID, user.ID) } } + +// https://github.com/go-gorm/gorm/issues/4918 +func TestJoinWithSoftDeleted(t *testing.T) { + user := User{Name: "TestJoinWithSoftDeleted"} + DB.Create(&user) + + pet := Pet{Name: "A", UserID: &user.ID} + DB.Create(&pet) + + DB = DB.Debug() + + var user1 User + DB.Debug().Model(&User{}).Joins("NamedPet").First(&user1, user.ID) + AssertEqual(t, user1.ID, user.ID) + AssertEqual(t, user1.NamedPet.ID, pet.ID) + + DB.Delete(&pet) + + var user3 User + DB.Model(&User{}).Joins("NamedPet").First(&user3, user.ID) + AssertEqual(t, user3.ID, user.ID) + AssertEqual(t, user3.NamedPet, nil) // soft deleted for join.on +}