use cratestack_core::CratestackContext;
use cratestack_sql::{RelatedReadScope, RelationHop};
use crate::render::relation_from_sql;
use crate::{RelationFilter, RelationQuantifier, sqlx};
use super::filter::push_filter_expr_query;
use super::policy::push_action_policy_query;
pub(super) fn push_relation_filter_query(
query: &mut sqlx::QueryBuilder<sqlx::Postgres>,
relation: &RelationFilter,
ctx: &CratestackContext,
) {
let (open, negate) = match relation.quantifier {
RelationQuantifier::ToOne | RelationQuantifier::Some => ("EXISTS (SELECT 1 ", false),
RelationQuantifier::None => ("NOT EXISTS (SELECT 1 ", false),
RelationQuantifier::Every => ("NOT EXISTS (SELECT 1 ", true),
};
query.push(open);
query.push(relation_from_sql(
relation.parent_table,
relation.parent_column,
relation.related_table,
relation.related_column,
));
push_related_scope(query, relation.related_table, relation.scope, ctx);
query.push(if negate { " AND NOT (" } else { " AND " });
push_filter_expr_query(query, &relation.filter, ctx);
query.push(if negate { "))" } else { ")" });
}
pub(super) fn push_relation_value(
query: &mut sqlx::QueryBuilder<sqlx::Postgres>,
hops: &[RelationHop],
column: &'static str,
ctx: &CratestackContext,
) {
let Some((hop, rest)) = hops.split_first() else {
query.push("NULL");
return;
};
query.push("(SELECT ");
if rest.is_empty() {
query.push(hop.related_table).push(".").push(column);
} else {
push_relation_value(query, rest, column, ctx);
}
query.push(" ");
query.push(relation_from_sql(
hop.parent_table,
hop.parent_column,
hop.related_table,
hop.related_column,
));
push_related_scope(query, hop.related_table, hop.scope, ctx);
query.push(" LIMIT 1)");
}
fn push_related_scope(
query: &mut sqlx::QueryBuilder<sqlx::Postgres>,
related_table: &'static str,
scope: RelatedReadScope,
ctx: &CratestackContext,
) {
let RelatedReadScope::Policy {
allow,
deny,
soft_delete_column,
} = scope
else {
return;
};
if let Some(column) = soft_delete_column {
query
.push(" AND ")
.push(related_table)
.push(".")
.push(column)
.push(" IS NULL");
}
query.push(" AND ");
push_action_policy_query(query, allow, deny, ctx);
}