use cratestack_core::{CratestackContext, CratestackError};
use crate::query::support::{no_row_error, push_action_policy_query};
use crate::{ModelDescriptor, cratestack_error_from_sqlx, sqlx};
pub(super) async fn delete_returning_record<'e, E, M, PK>(
executor: E,
policy_pool: &sqlx::PgPool,
descriptor: &'static ModelDescriptor<M, PK>,
id: PK,
ctx: &CratestackContext,
if_match: Option<i64>,
) -> Result<M, CratestackError>
where
E: sqlx::Executor<'e, Database = sqlx::Postgres>,
for<'r> M: Send + Unpin + sqlx::FromRow<'r, sqlx::postgres::PgRow>,
PK: Send + Clone + sqlx::Type<sqlx::Postgres> + for<'q> sqlx::Encode<'q, sqlx::Postgres>,
{
let probe_id = id.clone();
match delete_returning_row(executor, descriptor, id, ctx, if_match).await? {
Some(record) => Ok(record),
None => Err(no_row_error(policy_pool, descriptor, probe_id, ctx, if_match, "delete").await),
}
}
pub(super) async fn delete_record_in_conn<M, PK>(
conn: &mut sqlx::PgConnection,
descriptor: &'static ModelDescriptor<M, PK>,
id: PK,
ctx: &CratestackContext,
if_match: Option<i64>,
) -> Result<M, CratestackError>
where
for<'r> M: Send + Unpin + sqlx::FromRow<'r, sqlx::postgres::PgRow>,
PK: Send + Clone + sqlx::Type<sqlx::Postgres> + for<'q> sqlx::Encode<'q, sqlx::Postgres>,
{
let probe_id = id.clone();
match delete_returning_row(&mut *conn, descriptor, id, ctx, if_match).await? {
Some(record) => Ok(record),
None => Err(no_row_error(conn, descriptor, probe_id, ctx, if_match, "delete").await),
}
}
async fn delete_returning_row<'e, E, M, PK>(
executor: E,
descriptor: &'static ModelDescriptor<M, PK>,
id: PK,
ctx: &CratestackContext,
if_match: Option<i64>,
) -> Result<Option<M>, CratestackError>
where
E: sqlx::Executor<'e, Database = sqlx::Postgres>,
for<'r> M: Send + Unpin + sqlx::FromRow<'r, sqlx::postgres::PgRow>,
PK: Send + Clone + sqlx::Type<sqlx::Postgres> + for<'q> sqlx::Encode<'q, sqlx::Postgres>,
{
let version_column = descriptor.version_column;
let mut query = sqlx::QueryBuilder::<sqlx::Postgres>::new("");
match descriptor.soft_delete_column {
Some(col) => {
query.push("UPDATE ").push(descriptor.table_name);
query.push(" SET ").push(col).push(" = NOW()");
if let Some(version_col) = version_column {
query
.push(", ")
.push(version_col)
.push(" = ")
.push(version_col)
.push(" + 1");
}
query.push(" WHERE ");
query.push(col).push(" IS NULL AND ");
query.push(descriptor.primary_key).push(" = ");
}
None => {
query.push("DELETE FROM ").push(descriptor.table_name);
query.push(" WHERE ");
query.push(descriptor.primary_key).push(" = ");
}
}
query.push_bind(id);
if let (Some(version_col), Some(expected)) = (version_column, if_match) {
query.push(" AND ").push(version_col).push(" = ");
query.push_bind(expected);
}
query.push(" AND ");
push_action_policy_query(
&mut query,
descriptor.delete_allow_policies,
descriptor.delete_deny_policies,
ctx,
);
query
.push(" RETURNING ")
.push(descriptor.select_projection());
query
.build_query_as::<M>()
.fetch_optional(executor)
.await
.map_err(cratestack_error_from_sqlx)
}