cratestack_sqlx/query/write/
create.rs1use cratestack_core::{AuditOperation, CoolContext, CoolError, ModelEventKind};
6
7use crate::audit::{
8 build_audit_event, dispatch_audit_sink, enqueue_audit_event, ensure_audit_table,
9};
10use crate::descriptor::{enqueue_event_outbox, ensure_event_outbox_table};
11use crate::{CreateModelInput, ModelDescriptor, SqlxRuntime, cool_error_from_sqlx, sqlx};
12
13use super::create_exec::create_record_with_executor;
14
15#[derive(Debug, Clone)]
16pub struct CreateRecord<'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}
21
22impl<'a, M: 'static, PK: 'static, I> CreateRecord<'a, M, PK, I>
23where
24 I: CreateModelInput<M>,
25{
26 pub fn preview_sql(&self) -> String {
27 let values = self.input.sql_values();
28 let placeholders = (1..=values.len())
29 .map(|index| format!("${index}"))
30 .collect::<Vec<_>>()
31 .join(", ");
32 let columns = values
33 .iter()
34 .map(|value| value.column)
35 .collect::<Vec<_>>()
36 .join(", ");
37
38 format!(
39 "INSERT INTO {} ({}) VALUES ({}) RETURNING {}",
40 self.descriptor.table_name,
41 columns,
42 placeholders,
43 self.descriptor.select_projection(),
44 )
45 }
46
47 pub async fn run_in_tx<'tx>(
55 self,
56 tx: &mut sqlx::Transaction<'tx, sqlx::Postgres>,
57 ctx: &CoolContext,
58 ) -> Result<M, CoolError>
59 where
60 for<'r> M: Send + Unpin + sqlx::FromRow<'r, sqlx::postgres::PgRow> + serde::Serialize,
61 {
62 let emits_event = self.descriptor.emits(ModelEventKind::Created);
63 let audit_enabled = self.descriptor.audit_enabled;
64 if emits_event {
65 ensure_event_outbox_table(&mut **tx).await?;
66 }
67 if audit_enabled {
68 ensure_audit_table(self.runtime).await?;
69 }
70 let record = create_record_with_executor(
71 &mut **tx,
72 self.runtime.pool(),
73 self.descriptor,
74 self.input,
75 ctx,
76 )
77 .await?;
78 if emits_event {
79 enqueue_event_outbox(
80 &mut **tx,
81 self.descriptor.schema_name,
82 ModelEventKind::Created,
83 &record,
84 )
85 .await?;
86 }
87 if audit_enabled {
88 let after = serde_json::to_value(&record).ok();
89 let event =
90 build_audit_event(self.descriptor, AuditOperation::Create, None, after, ctx);
91 enqueue_audit_event(&mut **tx, &event).await?;
92 }
93 Ok(record)
94 }
95
96 pub async fn run(self, ctx: &CoolContext) -> Result<M, CoolError>
97 where
98 for<'r> M: Send + Unpin + sqlx::FromRow<'r, sqlx::postgres::PgRow> + serde::Serialize,
99 {
100 let emits_event = self.descriptor.emits(ModelEventKind::Created);
101 let audit_enabled = self.descriptor.audit_enabled;
102 let needs_tx = emits_event || audit_enabled;
103 let mut audit_event = None;
104 let record = if needs_tx {
105 let mut tx = self
106 .runtime
107 .pool()
108 .begin()
109 .await
110 .map_err(cool_error_from_sqlx)?;
111 if emits_event {
112 ensure_event_outbox_table(&mut *tx).await?;
113 }
114 if audit_enabled {
115 ensure_audit_table(self.runtime).await?;
116 }
117 let record = create_record_with_executor(
118 &mut *tx,
119 self.runtime.pool(),
120 self.descriptor,
121 self.input,
122 ctx,
123 )
124 .await?;
125 if emits_event {
126 enqueue_event_outbox(
127 &mut *tx,
128 self.descriptor.schema_name,
129 ModelEventKind::Created,
130 &record,
131 )
132 .await?;
133 }
134 if audit_enabled {
135 let after = serde_json::to_value(&record).ok();
136 let event =
137 build_audit_event(self.descriptor, AuditOperation::Create, None, after, ctx);
138 enqueue_audit_event(&mut *tx, &event).await?;
139 audit_event = Some(event);
140 }
141 tx.commit().await.map_err(cool_error_from_sqlx)?;
142 record
143 } else {
144 create_record_with_executor(
145 self.runtime.pool(),
146 self.runtime.pool(),
147 self.descriptor,
148 self.input,
149 ctx,
150 )
151 .await?
152 };
153
154 if emits_event {
155 let _ = self.runtime.drain_event_outbox().await;
156 }
157 if let Some(event) = &audit_event {
158 dispatch_audit_sink(self.runtime, std::slice::from_ref(event)).await;
159 }
160
161 Ok(record)
162 }
163}