lode/on_conflict
Upsert policy for inserts — Ecto’s :on_conflict / :conflict_target
options as plain data, rendered to Postgres INSERT ... ON CONFLICT.
Column references are typed Field(row) values (not bare strings), so the
target/action columns are owner-checked against the schema being upserted —
repo.upsert(schema: person_schema(), on_conflict: ...) unifies row, so a
column of another schema won’t type-check. repo erases the policy to its
runtime form (just names) before handing it to the adapter, which is why the
adapter sees OnConflict(Nil).
repo.upsert(r, s, cs, on_conflict: on_conflict.Update( target: on_conflict.Columns([person_email()]), action: on_conflict.Replace([person_name()]), ))
Types
Which uniqueness violation triggers the conflict branch (Ecto’s
:conflict_target). Column references name the database columns
(a field’s source, where they differ).
pub type ConflictTarget(row) {
AnyConstraint
Columns(List(field.Field(row)))
Constraint(String)
}
Constructors
-
AnyConstraintNo explicit target — any unique constraint counts. Postgres allows this only for
DO NOTHING;DO UPDATEmust name its target. -
Columns(List(field.Field(row)))ON CONFLICT (col, ...)— the columns of a unique index. -
Constraint(String)ON CONFLICT ON CONSTRAINT name— a named unique constraint.
The upsert policy (Ecto’s :on_conflict option).
pub type OnConflict(row) {
Raise
Nothing(target: ConflictTarget(row))
Update(target: ConflictTarget(row), action: UpdateAction(row))
}
Constructors
-
RaiseNo
ON CONFLICTclause: a uniqueness violation surfaces as aConstraintError(Ecto’s default,:raise— minus the raising). -
Nothing(target: ConflictTarget(row))ON CONFLICT [target] DO NOTHING: silently skip conflicting rows. -
Update(target: ConflictTarget(row), action: UpdateAction(row))ON CONFLICT target DO UPDATE SET ...: update the existing row instead.
What DO UPDATE sets on the existing row.
pub type UpdateAction(row) {
ReplaceAll
ReplaceAllExcept(List(field.Field(row)))
Replace(List(field.Field(row)))
Set(List(#(field.Field(row), value.Value)))
}
Constructors
-
ReplaceAllSet every inserted column to the incoming (
EXCLUDED) value. The insert payload omits autogenerated and virtual columns, so those are never replaced — but a natural (non-autogenerated) primary key in the payload is, matching Ecto’s:replace_all; useReplaceAllExceptto keep it. -
ReplaceAllExcept(List(field.Field(row)))ReplaceAllminus the listed columns. -
Replace(List(field.Field(row)))Set only the listed columns to their incoming (
EXCLUDED) values (Ecto’s{:replace, fields}). -
Set(List(#(field.Field(row), value.Value)))Set the listed columns to explicit values (Ecto’s
on_conflict: [set: ...]).
Values
pub fn erase(policy: OnConflict(a)) -> OnConflict(b)
Re-type a policy’s phantom row (a no-op at runtime — the phantom has no
runtime representation). repo calls this to hand the schema-checked policy
to the adapter as OnConflict(Nil).
pub fn set_columns(
action action: UpdateAction(row),
insert_columns columns: List(String),
) -> List(String)
Resolve an action to the concrete column names it sets, given the columns the
INSERT provides. Used by adapters to expand ReplaceAll/ReplaceAllExcept
and by repo to reject actions that would set nothing.