Classifies arguments of registered query macros for Mutare.CallRouting.route_arguments/1.
Core follows these routes; Mutare.Ecto.Host supplies the targets at :hosted positions.
Query sources
A query source is the call's first argument, whichever way the call was written: core hands
a pipe stage over as the direct call it is sugar for, so Post |> where(…) is classified as
where(Post, …). The source's shape then determines its treatment, identically for from
and the composable where/join/limit stages:
- A schema alias, table-name string (including interpolation), or
{"table", Schema}pair stays:raw. Replacing a table/schema name makes a broken query. - An
inbinding declaration stays:raw. It is syntax the macro consumes, not an Elixir expression. This also withholds mutations inside its queryable (p in build(n + 1)); the host separately reads the declaration throughMutare.Ecto.Host.Bindings. - A
fragment(...)source stays:raw: its template and arguments are SQL syntax. - A
values(rows, types)source stays:raw: its row keys and declared SQL types describe the source's structure and must agree. - Every other source routes
:expression: a variable, function call, nestedfrom, or upstream pipeline. Core keeps its mutations reachable through the enclosing query stage.
No shape guess locates the source: at classification the arguments are the written syntax, before core resolves the calls inside them, so a source is read by its shape alone.
Query data
fromroutes its clause list per pair. Expression conditions (where/having/on) route:hosted; keyword-shorthand conditions route their scalar values:interpolated. Literallimit:/offset:values route:hostedfor the pin-only bound bumps. Other clause values stay raw for the whole-fromproducers.Mutare.Ecto.AST.FromCallplaces the clauses and reads the source. A binding declaration routed:rawreaches the macro as the syntax it is in each whole-call mutant branch, piped or not.- Composable conditions locate their condition through
Mutare.Ecto.Host.Condition. A predicate routes:hosted; a keyword shorthand routes per pair, exactly as infrom. Written binding lists stay raw. - Plain clauses (
select/order_by/…) keep their data raw. A literal-integer bound (limit/offset) instead routes:hosted, usingMutare.Ecto.Bound's literal-only guard. joinroutes its trailing options per pair:on:uses the condition classification, whileas:/prefix:/hints:stay raw.
A shorthand routes scalar values through ^ interpolation because Ecto rejects a bare
selector case there. Keys, nil values (IS NULL, never = nil), existing pins and compound
values stay raw. Those scalar mutants belong to core's producing family, not :ecto.
Mutare.Ecto.Host.Condition decides whether a value is a predicate or shorthand once, so
the host and classifier agree even when a sibling position causes the whole call to be hosted.
:hosted permits the host to consider a position; it does not promise the host will weave
it. Declaration and receiving-clause constraints remain Mutare.Ecto.StaticCondition's.
Whole nodes are still offered to the plugin's mutators regardless of their argument routes.
dynamic and is_named_binding register :raw instead of using this classifier
(Mutare.Ecto.Surface.macro_registrations/0): neither threads a query. A free-standing
dynamic is still offered whole to Mutare.Ecto.Dynamic.
Summary
Functions
Route the call's arguments — the source, wherever the call was written, is argument 0.
Per-argument treatments for a registered query macro, over the call's complete argument list
(a piped source included, at position 0). Returns [] for a name the plugin doesn't route.
Functions
@spec route_arguments(Mutare.CallRouting.Call.t()) :: Mutare.CallRouting.ArgumentRoutes.t()
Route the call's arguments — the source, wherever the call was written, is argument 0.
@spec treatments(atom(), [Macro.t()]) :: [Mutare.CallRouting.treatment()]
Per-argument treatments for a registered query macro, over the call's complete argument list
(a piped source included, at position 0). Returns [] for a name the plugin doesn't route.