pub struct UpsertOperation<E: QueryEngine, M: Model> { /* private fields */ }Expand description
An upsert (insert or update) operation.
§Example
let user = client
.user()
.upsert()
.r#where(user::email::equals("test@example.com"))
.create(user::Create { email: "test@example.com".into(), name: Some("Test".into()) })
.update(user::Update { name: Some("Updated".into()), ..Default::default() })
.exec()
.await?;Implementations§
Source§impl<E: QueryEngine, M: Model + FromRow> UpsertOperation<E, M>
impl<E: QueryEngine, M: Model + FromRow> UpsertOperation<E, M>
Sourcepub fn where(self, filter: impl Into<Filter>) -> Self
pub fn where(self, filter: impl Into<Filter>) -> Self
Add a filter condition (identifies the record to upsert).
On the single-statement fast path the filter doubles as the
conflict target: when Self::on_conflict was not called, a
simple equality filter (col = value, or an AND of equalities
for composite keys) supplies the ON CONFLICT (col) columns.
Explicit Self::on_conflict columns take precedence when both
are set. A filter of any other shape cannot name a conflict
target and is rejected by Self::exec with an
invalid_input error unless Self::on_conflict is used. On
the nested-write slow path the filter is instead used directly
as the update branch’s WHERE clause.
Sourcepub fn on_conflict(
self,
columns: impl IntoIterator<Item = impl Into<String>>,
) -> Self
pub fn on_conflict( self, columns: impl IntoIterator<Item = impl Into<String>>, ) -> Self
Set the columns to check for conflict.
Takes precedence over the conflict target derived from the
[Self::r#where] filter when both are set.
Sourcepub fn create(
self,
values: impl IntoIterator<Item = (impl Into<String>, impl Into<FilterValue>)>,
) -> Self
pub fn create( self, values: impl IntoIterator<Item = (impl Into<String>, impl Into<FilterValue>)>, ) -> Self
Set the create data.
Sourcepub fn create_set(
self,
column: impl Into<String>,
value: impl Into<FilterValue>,
) -> Self
pub fn create_set( self, column: impl Into<String>, value: impl Into<FilterValue>, ) -> Self
Set a single create column.
Sourcepub fn update(
self,
values: impl IntoIterator<Item = (impl Into<String>, impl Into<FilterValue>)>,
) -> Self
pub fn update( self, values: impl IntoIterator<Item = (impl Into<String>, impl Into<FilterValue>)>, ) -> Self
Set the update data.
Sourcepub fn update_set(
self,
column: impl Into<String>,
value: impl Into<FilterValue>,
) -> Self
pub fn update_set( self, column: impl Into<String>, value: impl Into<FilterValue>, ) -> Self
Set a single update column.
Sourcepub fn with_where_input<W: WhereUniqueInput<Model = M>>(self, w: W) -> Self
pub fn with_where_input<W: WhereUniqueInput<Model = M>>(self, w: W) -> Self
Apply a typed WhereUniqueInput. Overwrites the existing filter.
Sourcepub fn with_select_input<S: SelectInput<Model = M>>(self, s: S) -> Self
pub fn with_select_input<S: SelectInput<Model = M>>(self, s: S) -> Self
Apply a typed SelectInput.
Sourcepub fn with_create_input<I>(self, input: I) -> Selfwhere
I: CreateInput<Model = M, Data = CreatePayload>,
pub fn with_create_input<I>(self, input: I) -> Selfwhere
I: CreateInput<Model = M, Data = CreatePayload>,
Apply a typed CreateInput to the upsert’s create path.
The columns / values produced by the input are appended to the
existing create_columns / create_values lists. Phase 5a’s
codegen ensures every <Model>CreateInput carries the
model’s @unique conflict column, so Self::on_conflict
remains useful with this method.
Sourcepub fn with_update_input<I>(self, input: I) -> Selfwhere
I: UpdateInput<Model = M, Data = UpdatePayload>,
pub fn with_update_input<I>(self, input: I) -> Selfwhere
I: UpdateInput<Model = M, Data = UpdatePayload>,
Apply a typed UpdateInput to the upsert’s update path.
Atomic operators are preserved — when the update branch fires,
Increment(n) emits col = col + $n in the DO UPDATE SET
clause, etc. Setting any input via this method overrides any
flat update columns recorded by Self::update or
Self::update_set.
Sourcepub fn build_sql(&self, dialect: &dyn SqlDialect) -> (String, Vec<FilterValue>)
pub fn build_sql(&self, dialect: &dyn SqlDialect) -> (String, Vec<FilterValue>)
Build the SQL query.
Conflict-target precedence: Self::on_conflict columns win;
otherwise a where filter that is a simple equality (or an AND
of equalities, for composite keys) supplies the conflict
column(s). Because this method returns SQL unconditionally, the
combinations that would emit invalid SQL (a non-derivable
where filter with no .on_conflict(...), or an update branch
with no conflict target at all) are rejected by Self::exec
instead — direct callers must uphold the same contract.
Sourcepub fn with_create_nested(self, nw: NestedWriteOp) -> Selfwhere
E: SupportsNestedWrites,
pub fn with_create_nested(self, nw: NestedWriteOp) -> Selfwhere
E: SupportsNestedWrites,
Queue a nested write to fire when the create branch runs (i.e. no existing row matched).
Sourcepub fn with_update_nested(self, nw: NestedWriteOp) -> Selfwhere
E: SupportsNestedWrites,
pub fn with_update_nested(self, nw: NestedWriteOp) -> Selfwhere
E: SupportsNestedWrites,
Queue a nested write to fire when the update branch runs (i.e. an existing row was found and updated).
Sourcepub async fn exec(self) -> QueryResult<M>where
M: Send + 'static + ModelWithPk,
pub async fn exec(self) -> QueryResult<M>where
M: Send + 'static + ModelWithPk,
Execute the upsert and return the record.
Fast path (no nested writes queued): runs a single
vendor-specific upsert (INSERT ... ON CONFLICT DO UPDATE on
Postgres, the dialect’s equivalent elsewhere). Enforces the
conflict-target contract first: a where filter that can’t
supply the target (with no .on_conflict(...)), or an update
branch with no target at all, fails with an invalid_input
error before any SQL is sent.
Slow path (nested writes queued via with_create_nested /
with_update_nested): runs a two-statement
engine-agnostic upsert inside a transaction so we can tell which
branch fired:
UPDATEthe row by primary key. Ifaffected > 0, the update branch ran — fireupdate_nestedwith the PK we already have fromwhere:.- Otherwise
INSERTthe row, take the PK from the inserted model, and firecreate_nested.