Bylaw.Ecto.Query.Checks.DeterministicOrder (bylaw_ecto_query v0.1.0-alpha.1)

Copy Markdown View Source

Validates that ordered queries include the root schema primary key.

This is useful when callers page through ordered rows or use helpers such as Repo.one/2 with Ecto.Query.first/2 or Ecto.Query.last/2. Ordering by a non-unique field such as :inserted_at or :name leaves rows with the same value free to move between executions unless the query also orders by a deterministic tie-breaker.

For now, this check only trusts the root Ecto schema primary key. Ecto schemas do not expose arbitrary database unique indexes, and this check should not ask callers to manually assert uniqueness that Bylaw cannot verify. If a query is intentionally ordered by another unique database key, use the explicit escape hatch until a DB-aware check can verify those constraints directly.

Examples

Bad:

from(Post, as: :post)
|> order_by([post: p], desc: p.inserted_at)
|> limit(10)

Why this is bad:

inserted_at is not guaranteed to be unique. Rows with the same timestamp can move between executions, which can make paginated queries skip or duplicate rows.

Better:

from(Post, as: :post)
|> order_by([post: p], desc: p.inserted_at)
|> order_by([post: p], asc: p.id)
|> limit(10)

Why this is better:

The root primary key resolves ties in the visible sort key, so every row has a stable relative position.

Better for a composite primary key:

from(Membership, as: :membership)
|> order_by([membership: mem], asc: mem.inserted_at)
|> order_by([membership: mem], asc: mem.organization_id)
|> order_by([membership: mem], asc: mem.sequence)

Notes

This check only trusts the root Ecto schema primary key. It cannot verify arbitrary unique database indexes or schema-less query sources.

The check is static. It infers root schema primary keys with Ecto schema reflection. Schema-less queries and schemas without primary keys cannot be proven deterministic by this check, so ordered queries in those cases return an issue unless validation is explicitly disabled.

Options

  • :validate - explicit false disables the check. Defaults to true.

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.

Summary

Functions

validate(operation, query, opts)

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