Skip to content

Commit

Permalink
fix(query): make WhereDeleted compatible with ForceDelete
Browse files Browse the repository at this point in the history
As of Bun 1.1.8, `WhereDeleted()` is ignored when used with `ForceDelete()`.

This commit fixes that problem:

```go
db.NewDelete().Model((*Model)(nil)).Where("1 = 1").WhereDeleted().ForceDelete().Exec(ctx)
// Before this commit: DELETE FROM model WHERE 1 = 1
// After this commit:  DELETE FROM model WHERE 1 = 1 AND deleted_at IS NOT NULL
```

Additionally, the `Where("1 = 1")` trick is no longer required as `WhereDeleted()` now counts
as a user-defined `WHERE` clause in `mustAppendWhere`:

```go
db.NewDelete().Model((*Model)(nil)).WhereDeleted().ForceDelete().Exec(ctx)
// Before this commit: Errors with "bun: Update and Delete queries require at least one Where"
// After this commit:  DELETE FROM model WHERE deleted_at IS NOT NULL
```

This gives us an easy and natural way to hard delete all soft deleted records.

Fixes #673.
  • Loading branch information
maximerety committed Nov 22, 2022
1 parent d368bbe commit 299c3fd
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 7 deletions.
41 changes: 37 additions & 4 deletions internal/dbtest/soft_delete_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,15 +111,48 @@ func testSoftDeleteForce(t *testing.T, db *bun.DB) {
err := db.ResetModel(ctx, (*Video)(nil))
require.NoError(t, err)

video1 := &Video{
ID: 1,
videos := []Video{
{Name: "video1"},
{Name: "video2"},
{Name: "video3"},
}
_, err = db.NewInsert().Model(video1).Exec(ctx)

_, err = db.NewInsert().Model(&videos).Exec(ctx)
require.NoError(t, err)

_, err = db.NewDelete().Model(video1).WherePK().ForceDelete().Exec(ctx)
// Force delete video1.
_, err = db.NewDelete().Model((*Video)(nil)).Where("name = ?", "video1").ForceDelete().Exec(ctx)
require.NoError(t, err)

// Soft delete video2.
_, err = db.NewDelete().Model((*Video)(nil)).Where("name = ?", "video2").Exec(ctx)
require.NoError(t, err)

// Check one visible video.
var res []Video
err = db.NewSelect().Model((*Video)(nil)).Column("name").Scan(ctx, &res)
require.NoError(t, err)
require.Equal(t, []Video{{Name: "video3"}}, res)

// Check one soft deleted video.
err = db.NewSelect().Model((*Video)(nil)).Column("name").WhereDeleted().Scan(ctx, &res)
require.NoError(t, err)
require.Equal(t, []Video{{Name: "video2"}}, res)

// Force delete only soft deleted videos.
_, err = db.NewDelete().Model((*Video)(nil)).WhereDeleted().ForceDelete().Exec(ctx)
require.NoError(t, err)

// Check one remaining video.
err = db.NewSelect().Model((*Video)(nil)).Column("name").WhereAllWithDeleted().Scan(ctx, &res)
require.NoError(t, err)
require.Equal(t, []Video{{Name: "video3"}}, res)

// Force delete all videos.
_, err = db.NewDelete().Model((*Video)(nil)).Where("1 = 1").ForceDelete().Exec(ctx)
require.NoError(t, err)

// Check no remaining videos.
count, err := db.NewSelect().Model((*Video)(nil)).WhereAllWithDeleted().Count(ctx)
require.NoError(t, err)
require.Equal(t, 0, count)
Expand Down
4 changes: 2 additions & 2 deletions query_base.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ func (q *baseQuery) isSoftDelete() bool {
if q.table != nil {
return q.table.SoftDeleteField != nil &&
!q.flags.Has(allWithDeletedFlag) &&
!q.flags.Has(forceDeleteFlag)
(!q.flags.Has(forceDeleteFlag) || q.flags.Has(deletedFlag))
}
return false
}
Expand Down Expand Up @@ -773,7 +773,7 @@ func (q *whereBaseQuery) addWhereCols(cols []string) {
func (q *whereBaseQuery) mustAppendWhere(
fmter schema.Formatter, b []byte, withAlias bool,
) ([]byte, error) {
if len(q.where) == 0 && q.whereFields == nil {
if len(q.where) == 0 && q.whereFields == nil && !q.flags.Has(deletedFlag) {
err := errors.New("bun: Update and Delete queries require at least one Where")
return nil, err
}
Expand Down
1 change: 0 additions & 1 deletion query_delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,6 @@ func (q *DeleteQuery) AppendQuery(fmter schema.Formatter, b []byte) (_ []byte, e
return upd.AppendQuery(fmter, b)
}

q = q.WhereDeleted()
withAlias := q.db.features.Has(feature.DeleteTableAlias)

b, err = q.appendWith(fmter, b)
Expand Down

0 comments on commit 299c3fd

Please sign in to comment.