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- explicitfalsedisables the check. Defaults totrue.
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 Ecto.Repo.prepare_query/3 setup.
Summary
Functions
Implements the Bylaw.Ecto.Query.Check validation callback.
Functions
@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.