diff --git a/internal/store/postgres/postgres.go b/internal/store/postgres/postgres.go index bb85e16ea..acdf9ecc6 100644 --- a/internal/store/postgres/postgres.go +++ b/internal/store/postgres/postgres.go @@ -29,6 +29,12 @@ func live(table string) exp.BooleanExpression { return goqu.I(table + ".deleted_at").IsNull() } +// softDelete marks rows as deleted instead of removing them. It skips rows +// that are already deleted, so the first delete time is kept. +func softDelete(table string) *goqu.UpdateDataset { + return dialect.Update(table).Set(goqu.Record{"deleted_at": goqu.L("now()")}).Where(live(table)) +} + // fromLive reads only the rows that are not soft-deleted. func fromLive(table string) *goqu.SelectDataset { return dialect.From(table).Where(live(table)) diff --git a/internal/store/postgres/postgres_internal_test.go b/internal/store/postgres/postgres_internal_test.go new file mode 100644 index 000000000..9fe6b3be4 --- /dev/null +++ b/internal/store/postgres/postgres_internal_test.go @@ -0,0 +1,39 @@ +package postgres + +import ( + "testing" + + "github.com/doug-martin/goqu/v9" + "github.com/stretchr/testify/assert" +) + +func TestSoftDelete(t *testing.T) { + tests := []struct { + name string + stmt *goqu.UpdateDataset + wantSQL string + wantParams []any + }{ + { + name: "marks the live rows of the table", + stmt: softDelete(TABLE_DOMAINS), + wantSQL: `UPDATE "domains" SET "deleted_at"=now() WHERE ("domains"."deleted_at" IS NULL)`, + wantParams: []any{}, + }, + { + name: "joins the caller's filter with AND", + stmt: softDelete(TABLE_DOMAINS).Where(goqu.Ex{"id": "8f2d5b1e-3c4a-4d6b-9e7f-1a2b3c4d5e6f"}), + wantSQL: `UPDATE "domains" SET "deleted_at"=now() WHERE (("domains"."deleted_at" IS NULL) AND ("id" = $1))`, + wantParams: []any{"8f2d5b1e-3c4a-4d6b-9e7f-1a2b3c4d5e6f"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotSQL, gotParams, err := tt.stmt.Prepared(true).ToSQL() + assert.NoError(t, err) + assert.Equal(t, tt.wantSQL, gotSQL) + assert.Equal(t, tt.wantParams, gotParams) + }) + } +}