Builds the Cypher SET clause for a query-scoped bulk write (update_query) and for the
atomic surface of create/upsert, from an Ash changeset's atomic + static changes. Pure —
no transport.
changeset.atomics ([{field, ash_expr}], e.g. [age: %Plus{}]) become n.<field> = <cypher>
fragments — each RHS is HYDRATED (Ash.Filter.hydrate_refs/2, idempotent: fully_atomic_changeset
pre-hydrates the update_query/upsert inputs, but atomic_set-on-create and unit-test-built
atomics arrive as un-hydrated %Ash.Query.Call{}; hydration resolves both — live-verified
scratchpad/probe_hydrate_idempotent.exs) then translated by AshArcadic.Query.Expression
(which emits n.<field> for a stored ref and fails closed value-free on sensitive/non-stored/
:binary/:decimal/relationship/aggregate refs and un-mapped ops). changeset.attributes
(the statically-cast map) become n += $static. A write to the multitenancy discriminator
(atomic OR static) is rejected value-free (a tenant-hop of the row); a fully-empty SET is
rejected value-free. Every value rides a bound $param — the RHS via Expression/add_param,
the static map as $static (Rule 1).
Summary
Functions
Atomic SET fragments (["n.<f> = <cypher>", …]) + params for a list of {field, hydrated_expr}
atomics, seed_params-threaded. Value-free fail-closed on a discriminator target or a
sensitive/non-stored/un-mapped RHS. Shared by update_query (via build_set), atomic create, and
atomic upsert.
Builds {:ok, set_clause, params} from a changeset's atomic + static changes, or
{:error, %UnsupportedFilter{}} fail-closed value-free. seed_params pre-seeds the
$paramN accumulator (the WHERE clause's bound params) so an atomic RHS literal never
collides with a WHERE param. When static changes are present, params["static"] carries
the cast property map (for the caller's encode-gate) and the clause ends …, n += $static.
Functions
@spec atomic_fragments(Ash.Resource.t(), keyword(), map()) :: {:ok, [String.t()], map()} | {:error, Exception.t()}
Atomic SET fragments (["n.<f> = <cypher>", …]) + params for a list of {field, hydrated_expr}
atomics, seed_params-threaded. Value-free fail-closed on a discriminator target or a
sensitive/non-stored/un-mapped RHS. Shared by update_query (via build_set), atomic create, and
atomic upsert.
@spec build_set(Ash.Resource.t(), Ash.Changeset.t(), map()) :: {:ok, String.t(), map()} | {:error, Exception.t()}
Builds {:ok, set_clause, params} from a changeset's atomic + static changes, or
{:error, %UnsupportedFilter{}} fail-closed value-free. seed_params pre-seeds the
$paramN accumulator (the WHERE clause's bound params) so an atomic RHS literal never
collides with a WHERE param. When static changes are present, params["static"] carries
the cast property map (for the caller's encode-gate) and the clause ends …, n += $static.