use cratestack_core::{CratestackContext, CratestackError};
use crate::query::support::{push_action_policy_query, push_bind_value};
use crate::{ModelDescriptor, SqlValue, cratestack_error_from_sqlx, sqlx};
pub(super) async fn select_for_update_by_conflict_target<'e, E, M, PK>(
executor: E,
descriptor: &'static ModelDescriptor<M, PK>,
conflict: &[(&'static str, SqlValue)],
predicate: Option<&'static str>,
) -> Result<Option<M>, CratestackError>
where
E: sqlx::Executor<'e, Database = sqlx::Postgres>,
for<'r> M: Send + Unpin + sqlx::FromRow<'r, sqlx::postgres::PgRow>,
{
let mut query = sqlx::QueryBuilder::<sqlx::Postgres>::new("SELECT ");
query.push(descriptor.select_projection());
query.push(" FROM ").push(descriptor.table_name);
query.push(" WHERE ");
for (idx, (column, value)) in conflict.iter().enumerate() {
if idx > 0 {
query.push(" AND ");
}
query.push(*column).push(" = ");
push_bind_value(&mut query, value);
}
if let Some(predicate) = predicate {
query.push(" AND (").push(predicate).push(")");
}
if let Some(col) = descriptor.soft_delete_column {
query.push(" AND ").push(col).push(" IS NULL");
}
query.push(" FOR UPDATE");
query
.build_query_as::<M>()
.fetch_optional(executor)
.await
.map_err(cratestack_error_from_sqlx)
}
pub(super) async fn row_passes_update_policy<M, PK>(
policy_pool: &sqlx::PgPool,
descriptor: &'static ModelDescriptor<M, PK>,
conflict: &[(&'static str, SqlValue)],
predicate: Option<&'static str>,
ctx: &CratestackContext,
) -> Result<bool, CratestackError> {
let mut query = sqlx::QueryBuilder::<sqlx::Postgres>::new("SELECT 1 FROM ");
query.push(descriptor.table_name);
query.push(" WHERE ");
for (idx, (column, value)) in conflict.iter().enumerate() {
if idx > 0 {
query.push(" AND ");
}
query.push(*column).push(" = ");
push_bind_value(&mut query, value);
}
if let Some(predicate) = predicate {
query.push(" AND (").push(predicate).push(")");
}
query.push(" AND ");
push_action_policy_query(
&mut query,
descriptor.update_allow_policies,
descriptor.update_deny_policies,
ctx,
);
let row: Option<(i32,)> = query
.build_query_as::<(i32,)>()
.fetch_optional(policy_pool)
.await
.map_err(cratestack_error_from_sqlx)?;
Ok(row.is_some())
}