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