cratestack_sqlx/query/write/
create_exec.rs1use cratestack_core::{CoolContext, CoolError};
7
8use crate::query::support::{
9 apply_create_defaults, classify_unique_violation, evaluate_create_policies, find_column_value,
10 push_bind_value,
11};
12use crate::{CreateModelInput, ModelDescriptor, sqlx};
13
14pub async fn create_record_with_executor<'e, E, M, PK, I>(
15 executor: E,
16 policy_pool: &sqlx::PgPool,
17 descriptor: &'static ModelDescriptor<M, PK>,
18 input: I,
19 ctx: &CoolContext,
20) -> Result<M, CoolError>
21where
22 E: sqlx::Executor<'e, Database = sqlx::Postgres>,
23 I: CreateModelInput<M>,
24 for<'r> M: Send + Unpin + sqlx::FromRow<'r, sqlx::postgres::PgRow> + serde::Serialize,
25{
26 input.validate()?;
27 let mut values = apply_create_defaults(input.sql_values(), descriptor.create_defaults, ctx)?;
28 if let Some(version_col) = descriptor.version_column
34 && find_column_value(&values, version_col).is_none()
35 {
36 values.push(crate::SqlColumnValue {
37 column: version_col,
38 value: crate::SqlValue::Int(0),
39 });
40 }
41 if values.is_empty() {
42 return Err(CoolError::Validation(
43 "create input must contain at least one column".to_owned(),
44 ));
45 }
46 if !evaluate_create_policies(
47 policy_pool,
48 descriptor.create_allow_policies,
49 descriptor.create_deny_policies,
50 &values,
51 ctx,
52 )
53 .await?
54 {
55 return Err(CoolError::Forbidden(
56 "create policy denied this operation".to_owned(),
57 ));
58 }
59
60 insert_returning_record(executor, descriptor, &values).await
61}
62
63async fn insert_returning_record<'e, E, M, PK>(
64 executor: E,
65 descriptor: &'static ModelDescriptor<M, PK>,
66 values: &[crate::SqlColumnValue],
67) -> Result<M, CoolError>
68where
69 E: sqlx::Executor<'e, Database = sqlx::Postgres>,
70 for<'r> M: Send + Unpin + sqlx::FromRow<'r, sqlx::postgres::PgRow>,
71{
72 let mut query = sqlx::QueryBuilder::<sqlx::Postgres>::new("INSERT INTO ");
73 query.push(descriptor.table_name).push(" (");
74 for (index, value) in values.iter().enumerate() {
75 if index > 0 {
76 query.push(", ");
77 }
78 query.push(value.column);
79 }
80 query.push(") VALUES (");
81 for (index, value) in values.iter().enumerate() {
82 if index > 0 {
83 query.push(", ");
84 }
85 push_bind_value(&mut query, &value.value);
86 }
87 query
88 .push(") RETURNING ")
89 .push(descriptor.select_projection());
90
91 query
92 .build_query_as::<M>()
93 .fetch_one(executor)
94 .await
95 .map_err(classify_unique_violation)
96}