cratestack_sqlx/query/write/
upsert_do_nothing.rs1use cratestack_core::{CoolContext, CoolError};
7
8use crate::{
9 ConflictTarget, ModelDescriptor, SqlxRuntime, UpsertModelInput, cool_error_from_sqlx, sqlx,
10};
11
12use super::upsert_do_nothing_exec::run_upsert_do_nothing_in_tx;
13use super::upsert_outcome::UpsertOutcome;
14
15#[derive(Debug, Clone)]
16pub struct UpsertRecordDoNothing<'a, M: 'static, PK: 'static, I> {
17 pub(crate) runtime: &'a SqlxRuntime,
18 pub(crate) descriptor: &'static ModelDescriptor<M, PK>,
19 pub(crate) input: I,
20 pub(crate) conflict_target: ConflictTarget,
21}
22
23impl<'a, M: 'static, PK: 'static, I> UpsertRecordDoNothing<'a, M, PK, I>
24where
25 I: UpsertModelInput<M>,
26{
27 pub fn on_conflict(mut self, target: ConflictTarget) -> Self {
31 self.conflict_target = target;
32 self
33 }
34
35 pub fn preview_sql(&self) -> String {
40 let values = self.input.sql_values();
41 let placeholders = (1..=values.len())
42 .map(|index| format!("${index}"))
43 .collect::<Vec<_>>()
44 .join(", ");
45 let columns = values
46 .iter()
47 .map(|value| value.column)
48 .collect::<Vec<_>>()
49 .join(", ");
50 let conflict_tuple = match self.conflict_target {
51 ConflictTarget::PrimaryKey => self.descriptor.primary_key.to_owned(),
52 ConflictTarget::Columns(cols) => cols.join(", "),
53 };
54
55 format!(
56 "INSERT INTO {table} ({columns}) VALUES ({placeholders}) \
57 ON CONFLICT ({conflict_tuple}) DO NOTHING \
58 RETURNING {projection}",
59 table = self.descriptor.table_name,
60 projection = self.descriptor.select_projection(),
61 )
62 }
63
64 pub async fn run(self, ctx: &CoolContext) -> Result<UpsertOutcome<M>, CoolError>
65 where
66 for<'r> M: Send + Unpin + sqlx::FromRow<'r, sqlx::postgres::PgRow> + serde::Serialize,
67 PK: Send + sqlx::Type<sqlx::Postgres> + for<'q> sqlx::Encode<'q, sqlx::Postgres>,
68 {
69 let runtime = self.runtime;
70 let mut tx = runtime.pool().begin().await.map_err(cool_error_from_sqlx)?;
71 let (outcome, emits_event) = run_upsert_do_nothing_in_tx(
72 &mut tx,
73 runtime,
74 self.descriptor,
75 self.input,
76 self.conflict_target,
77 ctx,
78 )
79 .await?;
80 tx.commit().await.map_err(cool_error_from_sqlx)?;
81 if emits_event {
82 let _ = runtime.drain_event_outbox().await;
83 }
84 Ok(outcome)
85 }
86
87 pub async fn run_in_tx<'tx>(
93 self,
94 tx: &mut sqlx::Transaction<'tx, sqlx::Postgres>,
95 ctx: &CoolContext,
96 ) -> Result<UpsertOutcome<M>, CoolError>
97 where
98 for<'r> M: Send + Unpin + sqlx::FromRow<'r, sqlx::postgres::PgRow> + serde::Serialize,
99 PK: Send + sqlx::Type<sqlx::Postgres> + for<'q> sqlx::Encode<'q, sqlx::Postgres>,
100 {
101 let (outcome, _) = run_upsert_do_nothing_in_tx(
102 tx,
103 self.runtime,
104 self.descriptor,
105 self.input,
106 self.conflict_target,
107 ctx,
108 )
109 .await?;
110 Ok(outcome)
111 }
112}