In-fragment SQL mutations for a free-standing dynamic/1,2 call — the condition a user
builds ahead of time and splices later:
d = dynamic([p], p.views > 100)
Repo.all(where(query, ^d))The condition body is the same SQL fragment a hosted where/having owns, so the same catalog
walks it (Mutare.Ecto.Fragment, scalar and aggregate swaps folded in — sum↔avg for a
dynamic destined for a having). This closes the loop the where(q, ^d) splice site leaves
open: a top-level ^d is hosted there like any other condition, but the catalog stops at the
pin and the island sub-contract finds only a bare variable inside, which core mutates nowhere
(Mutare.Ecto.Island) — the splice contributes nothing of its own, so the dynamic is
mutated where it is built. This module is that build site.
Delivery differs from the hosted path. dynamic is registered :raw
(Mutare.Ecto.Surface.macro_registrations/0), so core never descends into its DSL arguments —
but core still offers the whole call to mutate/2 (which is why it is :raw, not the
call-level :skip: a skipped call is an inert leaf nobody is offered), and a free-standing dynamic sits in an
ordinary expression position (its value is a runtime %Ecto.Query.DynamicExpr{}, not a spliced
query clause). So each mutant is the whole call rebuilt with exactly one point of the condition
swapped, delivered by Mutare's ordinary in-place selector case — the same Bucket-1 delivery as
the whole-from rewrites in Mutare.Ecto.Query. No host / ^-weaving is needed, each selector
branch invokes the dynamic macro independently, and exactly one DynamicExpr builds per run.
Each mutant is still reported at the mutated expression — the walk anchors it
(Mutare.Ecto.Walk) — so a line-scoped # mutare:ignore reaches one comparison of a
multi-line dynamic.
The written binding list is re-emitted byte-for-byte (its positional reorder is
Mutare.Ecto.BindingReorder's). A ^ pin's interior — nested (p.x > ^(min + 1)) or the whole
body (dynamic([p], ^(if params.sort, do: a, else: b))) — is never this catalog's: it is
sub-contracted to core through the same seam the host uses (Mutare.Ecto.Island), only
delivered as this module's whole-call rewrite instead of a weave.
Summary
Functions
Every single-point in-fragment mutant of a free-standing dynamic/1,2 call, each the whole
call rebuilt with one condition position swapped — the plugin's own catalog mutants as
Mutare.Ecto.Tags, the sub-contracted island mutants as producer-attributed
Mutare.Mutator.Mutations — or [] when there is nothing to mutate. The condition is located
by Mutare.Ecto.Host.Condition.locate/1, exactly as for a standalone where.
Functions
@spec mutations(Mutare.Ecto.AST.QueryCall.t(), Mutare.Ecto.Context.t()) :: [ Mutare.Ecto.SubMutator.tagged() | Mutare.Mutator.Mutation.t() ]
Every single-point in-fragment mutant of a free-standing dynamic/1,2 call, each the whole
call rebuilt with one condition position swapped — the plugin's own catalog mutants as
Mutare.Ecto.Tags, the sub-contracted island mutants as producer-attributed
Mutare.Mutator.Mutations — or [] when there is nothing to mutate. The condition is located
by Mutare.Ecto.Host.Condition.locate/1, exactly as for a standalone where.