Mutare.Ecto.Host (mutare_ecto v0.3.0)

Copy Markdown View Source

Builds selector-host targets for localized Ecto query conditions.

The companion Mutare.Ecto.Host.Routing identifies hosted argument positions. This module then coordinates four focused components: Host.Condition locates the condition argument a where/having call contains, Host.Bindings interprets Ecto binding declarations, Host.Catalog produces the logical mutants — the plugin's own SQL catalog plus the pin interiors Mutare.Ecto.Island passes to core (which is why context is passed down to the catalog) — and Host.Target constructs the dynamic/2 wrap and selector splice consumed by Mutare core.

The host builds a condition target for a predicate only. Core offers the whole call as soon as any one position routed :hosted, and does not confine the returned targets to those positions — so a from's hosted limit: brings a sibling where: [score: 5] here too. That sibling is a keyword filter, which the classifier already gave to core pair by pair, and which dynamic/2 would refuse. The host therefore reads every condition value through the same classification the classifier used (Mutare.Ecto.Host.Condition.shape/1), wherever the value is written: a from clause, a condition macro's argument, a join's on: option — Host.Condition locates each of the three for a predicate only. Neither the predicate catalog nor the pin sub-contract is offered a keyword filter, so hosting a sibling never changes what happens to it.

A condition that is itself a ^ pin (where: ^filters, where(q, ^cond), a join's on: ^cond — a predicate of kind :root_pin, by the same classification) is woven pin-only, over its bare interior: Ecto treats such a root interpolation according to its runtime value — a keyword list is a field filter, a boolean a literal condition, a dynamic is expanded — and a dynamic/2 wrap would turn the first two into plain parameters. Pinning the selector alone hands Ecto the same kind of value the written pin did, so the instrumented query behaves as the original.

Besides conditions, the host also weaves the :bound ±1 bump of a literal limit/offset value, as a pin-only target with no dynamic/2 wrap and no bindings — see Mutare.Ecto.Bound, the bump catalog and its literal guard. In a from, only the effective occurrence of a repeated bound weaves (Mutare.Ecto.AST.FromCall.effective_clause?/2).

A condition the host cannot weave — a subquery in a having, which Ecto accepts only statically built, or a condition under a binding declaration the plugin cannot re-declare — is delivered as a whole-call rebuild instead, by Mutare.Ecto.StaticCondition — whose delivery/4 decides, for the host and the rebuild alike, which conditions those are.

Summary

Functions

Mutare.Mutator.MacroHost.host/2: the selector-host targets for a resolved Ecto.Query macro call. The call's node is re-read through Mutare.Ecto.AST.QueryCall so the splice transforms rebuild the author's written form. Core's context is unpacked once here, into the plugin's %Mutare.Ecto.Context{} — this is the hosted path's core boundary, as Mutare.Ecto.Dispatcher.mutations/2 is the mutate/2 path's.

Functions

host(call, context)

@spec host(Mutare.CallRouting.Call.t(), Mutare.Mutator.context()) :: [
  Mutare.Ecto.Host.Target.t()
]

Mutare.Mutator.MacroHost.host/2: the selector-host targets for a resolved Ecto.Query macro call. The call's node is re-read through Mutare.Ecto.AST.QueryCall so the splice transforms rebuild the author's written form. Core's context is unpacked once here, into the plugin's %Mutare.Ecto.Context{} — this is the hosted path's core boundary, as Mutare.Ecto.Dispatcher.mutations/2 is the mutate/2 path's.