# `Mutare.Ecto.Clause`
[🔗](https://github.com/foxbenjaminfox/mutare_ecto/blob/v0.4.2/lib/mutare/ecto/clause.ex#L1)

Standalone/pipe clause-macro mutations — the composable counterparts of the whole-`from` family
in `Mutare.Ecto.Query`. Where `Query` mutates `from`'s keyword clauses, this mutates the
same kinds of thing written as standalone calls or pipe stages:

  * **Ordering** — `order_by(q, [u], asc: u.name)` / `q |> order_by(asc: u.name)`: flip a
    sort direction (`:asc`↔`:desc`, nulls-placement variants), via the shared
    `Mutare.Ecto.Ordering` catalog.
  * **Aggregate** — `select(q, [u], sum(u.amount))` / `q |> select_merge(%{n: max(u.x)})`, and
    an aggregate written into an `order_by` (`q |> order_by([u], desc: sum(u.amount))`): swap the
    aggregate (`sum`↔`avg`, `min`↔`max`), via the shared `Mutare.Ecto.Aggregate` walker. (A
    `having` aggregate is hosted instead — `Mutare.Ecto.Host`.)
  * **Scalar** — `select(q, [u], u.price * u.qty)` / `q |> order_by([u], desc: u.a + u.b)` /
    `q |> select([u], coalesce(u.score, 0))`: mutate a value-computing form via the shared
    `Mutare.Ecto.Scalar` catalog — the arithmetic swaps (`+`↔`-`, `*`↔`/`) and the coalesce
    fallback drop. (The same forms in a `where`/`having` condition are hosted instead.)
  * **Combination** — `q |> intersect(^other)` / `except_all(q, ^other)`: swap the set
    operation by renaming the macro itself (`intersect`↔`except`, `intersect_all`↔`except_all`),
    via the shared `Mutare.Ecto.Combination` catalog. Unlike the others this mutates the
    call's *name*, not its last argument — the operand queries are untouched.
  * **JoinType** — `join(q, :left, [p], c in Comment, on: …)` / `q |> join(:full, …)`: narrow
    the join's kind by swapping its written **qualifier** (`:left`→`:inner`,
    `:full`→`:left`/`:right`, and `:left`↔`:right` under a `RIGHT`-capable dialect), via the
    shared `Mutare.Ecto.JoinType` catalog — the same flips, under the same policy and
    `dialects:` gate, as `Mutare.Ecto.Query`'s `left_join:` key swap. Only a literal qualifier
    is swapped: a computed one (`join(q, kind, …)`) is a value mutated where it is bound.

These macros route through the `:routing` classifier (`Mutare.Ecto.Host.Routing`), which keeps
their *data* positions raw while the routed node is still offered whole to `mutate/2`; the
mutation uses Mutare's ordinary in-place selector, since the macro call is itself an
expression. The orthogonal **stage removal** (`q |> order_by(…)` → `q`) lives in
`Mutare.Ecto.ClauseDrop`, and the `:bound` ±1 bump of a literal `limit`/`offset` is hosted
pin-only (`Mutare.Ecto.Bound`; NOTES "Bound bump: from whole-call rewrite to pin-only
hosting").

In the normalized call (`Mutare.Ecto.AST.QueryCall`), a value capability mutates the
**last argument** (ordering or selector); a join-kind swap mutates the **second** (qualifier).

# `mutations`

```elixir
@spec mutations(Mutare.Ecto.AST.QueryCall.t(), Mutare.Ecto.Context.t()) :: [
  Mutare.Ecto.SubMutator.tagged()
]
```

Standalone/pipe clause-macro mutations for `node` as self-tagging `Mutare.Ecto.Tag`s
(a swap family — order/aggregate — carries the finer operator/kind label), or `[]`.

---

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