Skip to main content

cratestack_sql/filter/
expr_relations.rs

1//! Relation-subquery constructors for [`FilterExpr`].
2//!
3//! Split from `expr.rs` (200-LoC ceiling): that module owns the enum
4//! itself plus the boolean combinators (`all`/`any`/`not`/`and`/`or`),
5//! this one owns the four `relation*` constructors, which are a
6//! distinct concern — they build a correlated-subquery node rather
7//! than combining existing predicates.
8//!
9//! Each constructor takes the related model's [`RelatedReadScope`] as a
10//! required argument (GHSA-p55v-6xv5-93p3): pass
11//! `<RELATED>_MODEL.related_read_scope()` so the subquery sees only the
12//! related rows the caller could read, or [`RelatedReadScope::Unscoped`]
13//! only when reading the raw related table is the point.
14
15use cratestack_policy::RelationQuantifier;
16
17use super::expr::{FilterExpr, RelationFilter};
18use crate::RelatedReadScope;
19
20impl FilterExpr {
21    /// To-one: the related row exists, is visible under `scope`, and
22    /// matches `filter`.
23    pub fn relation(
24        parent_table: &'static str,
25        parent_column: &'static str,
26        related_table: &'static str,
27        related_column: &'static str,
28        filter: FilterExpr,
29        scope: RelatedReadScope,
30    ) -> Self {
31        Self::Relation(RelationFilter::new(
32            RelationQuantifier::ToOne,
33            parent_table,
34            parent_column,
35            related_table,
36            related_column,
37            filter,
38            scope,
39        ))
40    }
41
42    /// To-many `some`: at least one related row visible under `scope`
43    /// matches `filter`.
44    pub fn relation_some(
45        parent_table: &'static str,
46        parent_column: &'static str,
47        related_table: &'static str,
48        related_column: &'static str,
49        filter: FilterExpr,
50        scope: RelatedReadScope,
51    ) -> Self {
52        Self::Relation(RelationFilter::new(
53            RelationQuantifier::Some,
54            parent_table,
55            parent_column,
56            related_table,
57            related_column,
58            filter,
59            scope,
60        ))
61    }
62
63    /// To-many `every`: no related row visible under `scope` fails
64    /// `filter` (vacuously true when none is visible).
65    pub fn relation_every(
66        parent_table: &'static str,
67        parent_column: &'static str,
68        related_table: &'static str,
69        related_column: &'static str,
70        filter: FilterExpr,
71        scope: RelatedReadScope,
72    ) -> Self {
73        Self::Relation(RelationFilter::new(
74            RelationQuantifier::Every,
75            parent_table,
76            parent_column,
77            related_table,
78            related_column,
79            filter,
80            scope,
81        ))
82    }
83
84    /// To-many `none`: no related row visible under `scope` matches
85    /// `filter`.
86    pub fn relation_none(
87        parent_table: &'static str,
88        parent_column: &'static str,
89        related_table: &'static str,
90        related_column: &'static str,
91        filter: FilterExpr,
92        scope: RelatedReadScope,
93    ) -> Self {
94        Self::Relation(RelationFilter::new(
95            RelationQuantifier::None,
96            parent_table,
97            parent_column,
98            related_table,
99            related_column,
100            filter,
101            scope,
102        ))
103    }
104}