Skip to main content

cratestack_sqlx/query/read/
find_many_with.rs

1//! `FindMany` with one to-one relation side-loaded — see
2//! [`FindMany::include`]. All builder methods delegate to the parent
3//! `FindMany`; only `run` / `run_in_tx` differ to fan out into the
4//! side-load step.
5
6use cratestack_core::{CoolContext, CoolError};
7
8use crate::{FilterExpr, OrderClause, sqlx};
9
10use super::find_many::FindMany;
11use super::side_load::run_side_load;
12
13#[derive(Debug, Clone)]
14pub struct FindManyWith<'a, M: 'static, PK: 'static, Rel: 'static, RelPK: 'static> {
15    parent: FindMany<'a, M, PK>,
16    relation: cratestack_sql::RelationInclude<M, Rel, RelPK>,
17}
18
19impl<'a, M: 'static, PK: 'static, Rel: 'static, RelPK: 'static>
20    FindManyWith<'a, M, PK, Rel, RelPK>
21{
22    pub(super) fn new(
23        parent: FindMany<'a, M, PK>,
24        relation: cratestack_sql::RelationInclude<M, Rel, RelPK>,
25    ) -> Self {
26        Self { parent, relation }
27    }
28
29    pub fn where_(mut self, filter: crate::Filter) -> Self {
30        self.parent = self.parent.where_(filter);
31        self
32    }
33
34    pub fn where_expr(mut self, filter: FilterExpr) -> Self {
35        self.parent = self.parent.where_expr(filter);
36        self
37    }
38
39    pub fn where_any(mut self, filters: impl IntoIterator<Item = FilterExpr>) -> Self {
40        self.parent = self.parent.where_any(filters);
41        self
42    }
43
44    pub fn where_optional<F>(mut self, filter: Option<F>) -> Self
45    where
46        F: Into<FilterExpr>,
47    {
48        self.parent = self.parent.where_optional(filter);
49        self
50    }
51
52    pub fn order_by(mut self, clause: OrderClause) -> Self {
53        self.parent = self.parent.order_by(clause);
54        self
55    }
56
57    pub fn limit(mut self, limit: i64) -> Self {
58        self.parent = self.parent.limit(limit);
59        self
60    }
61
62    pub fn offset(mut self, offset: i64) -> Self {
63        self.parent = self.parent.offset(offset);
64        self
65    }
66
67    /// Apply `FOR UPDATE` to the parent-row SELECT. The related-side
68    /// side-load query is **not** locked — to lock both sides, wrap
69    /// the call in [`Self::run_in_tx`] and issue an explicit
70    /// `SELECT ... FOR UPDATE` against the related table separately.
71    pub fn for_update(mut self) -> Self {
72        self.parent = self.parent.for_update();
73        self
74    }
75
76    pub async fn run(self, ctx: &CoolContext) -> Result<Vec<(M, Option<Rel>)>, CoolError>
77    where
78        M: Clone,
79        for<'r> M: Send + Unpin + sqlx::FromRow<'r, sqlx::postgres::PgRow>,
80        Rel: Clone,
81        for<'r> Rel: Send
82            + Unpin
83            + sqlx::FromRow<'r, sqlx::postgres::PgRow>
84            + cratestack_sql::ModelPrimaryKey<RelPK>,
85        RelPK: Send
86            + Clone
87            + std::cmp::Eq
88            + std::hash::Hash
89            + cratestack_sql::IntoSqlValue
90            + sqlx::Type<sqlx::Postgres>
91            + for<'q> sqlx::Encode<'q, sqlx::Postgres>,
92    {
93        let runtime = self.parent.runtime;
94        let relation = self.relation;
95        let parents = self.parent.run(ctx).await?;
96        run_side_load(
97            runtime,
98            &parents,
99            relation,
100            ctx,
101            None::<&mut sqlx::Transaction<'_, sqlx::Postgres>>,
102        )
103        .await
104    }
105
106    pub async fn run_in_tx<'tx>(
107        self,
108        tx: &mut sqlx::Transaction<'tx, sqlx::Postgres>,
109        ctx: &CoolContext,
110    ) -> Result<Vec<(M, Option<Rel>)>, CoolError>
111    where
112        M: Clone,
113        for<'r> M: Send + Unpin + sqlx::FromRow<'r, sqlx::postgres::PgRow>,
114        Rel: Clone,
115        for<'r> Rel: Send
116            + Unpin
117            + sqlx::FromRow<'r, sqlx::postgres::PgRow>
118            + cratestack_sql::ModelPrimaryKey<RelPK>,
119        RelPK: Send
120            + Clone
121            + std::cmp::Eq
122            + std::hash::Hash
123            + cratestack_sql::IntoSqlValue
124            + sqlx::Type<sqlx::Postgres>
125            + for<'q> sqlx::Encode<'q, sqlx::Postgres>,
126    {
127        let runtime = self.parent.runtime;
128        let relation = self.relation;
129        let parents = self.parent.run_in_tx(tx, ctx).await?;
130        run_side_load(runtime, &parents, relation, ctx, Some(tx)).await
131    }
132}