# `Bylaw.Ecto.Query.Checks.DeterministicOrder`
[🔗](https://github.com/ryanzidago/bylaw/blob/v0.1.0-alpha.1/lib/bylaw/ecto/query/checks/deterministic_order.ex#L1)

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 `c:Ecto.Repo.prepare_query/3` setup.

# `validate`

```elixir
@spec validate(
  Bylaw.Ecto.Query.Check.operation(),
  Bylaw.Ecto.Query.Check.query(),
  opts()
) ::
  Bylaw.Ecto.Query.Check.result()
```

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

---

*Consult [api-reference.md](api-reference.md) for complete listing*
