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

Validates that query shapes requiring stable row order include `order_by`.

This check only answers whether an `order_by` clause is required and present.
It intentionally does not decide whether the existing order is deterministic;
use `Bylaw.Ecto.Query.Checks.DeterministicOrder` for that separate question.

## Examples

Bad:

    from(Post, as: :post)
    |> limit(10)

Why this is bad:

A limited query without `order_by` asks the database for any matching 10 rows.
Pagination, retries, and repeated calls can see rows appear, disappear, or
move because the selected window has no stable order.

Better:

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

Why this is better:

The selected window is taken from a declared order, so callers can reason
about which rows are first.

Bad:

    from(Post, as: :post)
    |> offset(50)
    |> limit(25)

Why this is bad:

`offset` skips an undefined set of rows when no order exists. Page boundaries
can shift between executions.

Better:

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

Why this is better:

Rows are skipped from a known order, so the page boundary has a defined
meaning.

Bad:

    from(Post, as: :post)
    |> Repo.stream()

Better:

    from(Post, as: :post)
    |> order_by([post: p], asc: p.id)
    |> Repo.stream()

## Notes

This check only requires that some `order_by` exists. It does not prove that
the order is deterministic. If rows can tie on the ordered field, pair this
check with `DeterministicOrder` to require a primary-key tie-breaker:

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

Ecto rewrites `Repo.exists?/2` queries to `select 1` with `limit 1`. This
synthetic limit is ignored because existence checks do not depend on which row
is returned. A preserved `offset` still requires ordering because the skipped
rows are otherwise undefined.

## Options

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

Queries with `limit`, `offset`, or the `:stream` operation require an
`order_by` clause. If any `order_by` exists, this check passes and leaves
deterministic tie-breaker validation to `DeterministicOrder`.

## 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*
