Bylaw.Ecto.Query.Checks.HardDeleteOnSoftDeleteSchema (bylaw_ecto_query v0.2.0)

Copy Markdown View Source

Validates that soft-delete schemas are not hard-deleted with delete_all.

Schemas that declare a persisted :deleted_at or :archived_at field usually expect lifecycle removal to be represented as an update. A bulk hard delete on that schema is therefore suspicious even when the query is otherwise bounded.

Examples

Bad:

query =
  from(Post, as: :post)
  |> where([post: p], p.status == ^:archived)

Repo.delete_all(query)

Why this is bad:

If Post has a persisted :deleted_at or :archived_at field, hard-deleting rows bypasses the lifecycle model and permanently removes records the schema normally treats as soft-deletable.

Better:

Prefer Repo.update_all/3 setting :deleted_at or :archived_at instead of Repo.delete_all/2 when this check reports an issue.

query =
  from(Post, as: :post)
  |> where([post: p], p.status == ^:archived)

Repo.update_all(query, set: [deleted_at: DateTime.utc_now()])

Why this is better:

Removal is represented in the soft-delete field, preserving the schema's lifecycle convention.

Notes

This check uses persisted root schema fields as the signal. It ignores schema-less queries, virtual fields, source subqueries, and soft-delete fields on joined schemas.

The check is zero-config. Field presence in __schema__(:fields) is the signal.

Options

  • :validate - explicit false disables this check. It can be used in the repo-wide check list or in call-site overrides passed to Bylaw.Ecto.Query.validate/4.

Run globally with defaults:

Bylaw.Ecto.Query.Checks.HardDeleteOnSoftDeleteSchema

Run only for matching rule scopes:

{Bylaw.Ecto.Query.Checks.HardDeleteOnSoftDeleteSchema,
 rules: [
   [where: [ecto_schemas: [Post]]],
   [where: [tables: ["posts"]]]
 ]}

This check has no check-specific rule options.

Usage

Add this module to the explicit check list passed through Bylaw.Ecto.Query. See Bylaw.Ecto.Query for the full Ecto.Repo.prepare_query/3 setup.

The root query and every combination branch are inspected independently. The check ignores schema-less queries, non-query values, virtual fields, source subqueries, and soft-delete fields that appear only on joined schemas.

Summary

Functions

validate(operation, query, opts)

Implements the Bylaw.Ecto.Query.Check validation callback.