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::{CratestackContext, CratestackError};
7
8use crate::{FilterExpr, OrderClause, sqlx};
9
10use super::find_many::FindMany;
11use super::side_load::run_side_load;
12
13#[derive(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(
77        self,
78        ctx: &CratestackContext,
79    ) -> Result<Vec<(M, Option<Rel>)>, CratestackError>
80    where
81        M: Clone,
82        for<'r> M: Send + Unpin + sqlx::FromRow<'r, sqlx::postgres::PgRow>,
83        Rel: Clone,
84        for<'r> Rel: Send
85            + Unpin
86            + sqlx::FromRow<'r, sqlx::postgres::PgRow>
87            + cratestack_sql::ModelPrimaryKey<RelPK>,
88        RelPK: Send
89            + Clone
90            + std::cmp::Eq
91            + std::hash::Hash
92            + cratestack_sql::IntoSqlValue
93            + sqlx::Type<sqlx::Postgres>
94            + for<'q> sqlx::Encode<'q, sqlx::Postgres>,
95    {
96        let runtime = self.parent.runtime;
97        let relation = self.relation;
98        let parents = self.parent.run(ctx).await?;
99        run_side_load(
100            runtime,
101            &parents,
102            relation,
103            ctx,
104            None::<&mut sqlx::Transaction<'_, sqlx::Postgres>>,
105        )
106        .await
107    }
108
109    pub async fn run_in_tx<'tx>(
110        self,
111        tx: &mut sqlx::Transaction<'tx, sqlx::Postgres>,
112        ctx: &CratestackContext,
113    ) -> Result<Vec<(M, Option<Rel>)>, CratestackError>
114    where
115        M: Clone,
116        for<'r> M: Send + Unpin + sqlx::FromRow<'r, sqlx::postgres::PgRow>,
117        Rel: Clone,
118        for<'r> Rel: Send
119            + Unpin
120            + sqlx::FromRow<'r, sqlx::postgres::PgRow>
121            + cratestack_sql::ModelPrimaryKey<RelPK>,
122        RelPK: Send
123            + Clone
124            + std::cmp::Eq
125            + std::hash::Hash
126            + cratestack_sql::IntoSqlValue
127            + sqlx::Type<sqlx::Postgres>
128            + for<'q> sqlx::Encode<'q, sqlx::Postgres>,
129    {
130        let runtime = self.parent.runtime;
131        let relation = self.relation;
132        let parents = self.parent.run_in_tx(tx, ctx).await?;
133        run_side_load(runtime, &parents, relation, ctx, Some(tx)).await
134    }
135}