Validates that queries do not use explicit cartesian joins.
This check catches join shapes that are easy to introduce accidentally and expensive to run.
Examples
Bad:
from(Post, as: :post)
|> join(:inner, [post: p], c in Comment,
as: :comment,
on: true
)Why this is bad:
on: true creates every possible pair of posts and comments. That can
multiply rows, inflate aggregates, and produce a query that is much more
expensive than intended.
Better:
from(Post, as: :post)
|> join(:inner, [post: p], c in Comment,
as: :comment,
on: c.post_id == p.id
)Why this is better:
The join predicate states the relationship between the two tables, so each joined row is tied back to its post.
Bad:
from(Plan, as: :plan)
|> join(:cross, [plan: pl], f in Feature, as: :feature)
|> where([feature: f], f.enabled == true)Notes
This check catches obvious cartesian joins: cross_join, uncorrelated
cross_lateral_join, and non-association joins whose on expression is
literally true. It does not parse SQL fragments or prove general SQL
cardinality.
It rejects cross_join, uncorrelated cross_lateral_join, and
non-association joins whose on expression is literally true. Correlated
lateral joins are treated as constrained when a supported subquery predicate
depends on both a local subquery binding and a previous parent binding, or
when a lateral fragment source exposes a previous parent binding reference.
Options
:validate- explicitfalsedisables the check. Defaults totrue.
Like Bylaw's other Ecto query checks, this intentionally inspects the query
structure produced by Ecto's query macros. It supports the tested join and
lateral subquery shapes exposed by the Ecto query API. Association joins are
not considered literal on: true joins because Ecto stores their association
predicate separately from the on expression.
This check is a guardrail for obvious cartesian joins, not a full SQL cardinality proof. It does not parse fragment SQL. For lateral fragments, an Ecto-visible reference to a previous binding is treated as dependency evidence; opaque SQL that needs stricter review should be handled in the application query or by disabling the check for that call site.
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.