diff --git a/oracle/delete.go b/oracle/delete.go index ba48eb9..8ad65fb 100644 --- a/oracle/delete.go +++ b/oracle/delete.go @@ -93,6 +93,22 @@ func Delete(db *gorm.DB) { addPrimaryKeyWhereClause(db) } + // redirect soft-delete to update clause with bulk returning + if stmt.Schema != nil { + if deletedAtField := stmt.Schema.LookUpField("deleted_at"); deletedAtField != nil && !stmt.Unscoped { + for _, c := range stmt.Schema.DeleteClauses { + stmt.AddClause(c) + } + delete(stmt.Clauses, "DELETE") + delete(stmt.Clauses, "FROM") + stmt.SQL.Reset() + stmt.Vars = stmt.Vars[:0] + stmt.AddClauseIfNotExists(clause.Update{}) + Update(db) + return + } + } + // This prevents soft deletes from bypassing the safety check checkMissingWhereConditions(db) if db.Error != nil { @@ -434,25 +450,7 @@ func executeDelete(db *gorm.DB) { _, hasReturning := stmt.Clauses["RETURNING"] if hasReturning { - // For RETURNING, we need to check if it's a soft delete or hard delete - if stmt.Schema != nil { - if deletedAtField := stmt.Schema.LookUpField("deleted_at"); deletedAtField != nil && !stmt.Unscoped { - // Soft delete with RETURNING - use QueryContext - if rows, err := stmt.ConnPool.QueryContext(stmt.Context, stmt.SQL.String(), stmt.Vars...); err == nil { - defer rows.Close() - gorm.Scan(rows, db, gorm.ScanInitialized) - - if stmt.Result != nil { - stmt.Result.RowsAffected = db.RowsAffected - } - } else { - db.AddError(err) - } - return - } - } - - // Hard delete with RETURNING - use ExecContext (for PL/SQL blocks) + // Hard delete & soft delete with RETURNING - use ExecContext (for PL/SQL blocks) result, err := stmt.ConnPool.ExecContext(stmt.Context, stmt.SQL.String(), stmt.Vars...) if err == nil { db.RowsAffected, _ = result.RowsAffected() diff --git a/tests/delete_test.go b/tests/delete_test.go index ae87e73..fa22f28 100644 --- a/tests/delete_test.go +++ b/tests/delete_test.go @@ -248,7 +248,6 @@ func TestDeleteSliceWithAssociations(t *testing.T) { // only sqlite, postgres, sqlserver support returning func TestSoftDeleteReturning(t *testing.T) { - t.Skip() users := []*User{ GetUser("delete-returning-1", Config{}), GetUser("delete-returning-2", Config{}), @@ -257,13 +256,13 @@ func TestSoftDeleteReturning(t *testing.T) { DB.Create(&users) var results []User - DB.Where("name IN ?", []string{users[0].Name, users[1].Name}).Clauses(clause.Returning{}).Delete(&results) + DB.Where("\"name\" IN ?", []string{users[0].Name, users[1].Name}).Clauses(clause.Returning{}).Delete(&results) if len(results) != 2 { t.Errorf("failed to return delete data, got %v", results) } var count int64 - DB.Model(&User{}).Where("name IN ?", []string{users[0].Name, users[1].Name, users[2].Name}).Count(&count) + DB.Model(&User{}).Where("\"name\" IN ?", []string{users[0].Name, users[1].Name, users[2].Name}).Count(&count) if count != 1 { t.Errorf("failed to delete data, current count %v", count) } diff --git a/tests/joins_test.go b/tests/joins_test.go index 689b6a6..21922b3 100644 --- a/tests/joins_test.go +++ b/tests/joins_test.go @@ -252,8 +252,6 @@ func TestJoinCount(t *testing.T) { } func TestJoinWithSoftDeleted(t *testing.T) { - t.Skip() - user := GetUser("TestJoinWithSoftDeletedUser", Config{Account: true, NamedPet: true}) DB.Create(&user) diff --git a/tests/passed-tests.txt b/tests/passed-tests.txt index f74b330..7bc8e3e 100644 --- a/tests/passed-tests.txt +++ b/tests/passed-tests.txt @@ -79,7 +79,7 @@ TestBlockGlobalDelete TestDeleteWithAssociations TestDeleteAssociationsWithUnscoped TestDeleteSliceWithAssociations -#TestSoftDeleteReturning +TestSoftDeleteReturning TestDeleteReturning TestDeleteByPrimaryKeyOnly TestHardDeleteAfterSoftDelete