cratestack_sqlx/query/read/
find_many.rs1use cratestack_core::{CratestackContext, CratestackError};
6use cratestack_sql::ReadSource;
7
8use crate::query::support::{ReadPolicyKind, push_order_and_paging, push_scoped_conditions};
9use crate::{FilterExpr, OrderClause, SqlxRuntime, sqlx};
10
11#[derive(Clone)]
12pub struct FindMany<'a, M: 'static, PK: 'static> {
13 pub(crate) runtime: &'a SqlxRuntime,
14 pub(crate) descriptor: &'static dyn ReadSource<M, PK>,
18 pub(crate) filters: Vec<FilterExpr>,
19 pub(crate) order_by: Vec<OrderClause>,
20 pub(crate) limit: Option<i64>,
21 pub(crate) offset: Option<i64>,
22 pub(crate) for_update: bool,
23}
24
25impl<'a, M: 'static, PK: 'static> FindMany<'a, M, PK> {
26 pub fn where_(mut self, filter: crate::Filter) -> Self {
27 self.filters.push(FilterExpr::from(filter));
28 self
29 }
30
31 pub fn where_expr(mut self, filter: FilterExpr) -> Self {
32 self.filters.push(filter);
33 self
34 }
35
36 pub fn where_any(mut self, filters: impl IntoIterator<Item = FilterExpr>) -> Self {
37 self.filters.push(FilterExpr::any(filters));
38 self
39 }
40
41 pub fn where_optional<F>(mut self, filter: Option<F>) -> Self
45 where
46 F: Into<FilterExpr>,
47 {
48 if let Some(filter) = filter {
49 self.filters.push(filter.into());
50 }
51 self
52 }
53
54 pub fn order_by(mut self, clause: OrderClause) -> Self {
55 self.order_by.push(clause);
56 self
57 }
58
59 pub fn limit(mut self, limit: i64) -> Self {
60 self.limit = Some(limit);
61 self
62 }
63
64 pub fn offset(mut self, offset: i64) -> Self {
65 self.offset = Some(offset);
66 self
67 }
68
69 pub fn for_update(mut self) -> Self {
73 self.for_update = true;
74 self
75 }
76
77 pub fn preview_sql(&self) -> String {
82 super::find_many_preview::preview_sql(self)
83 }
84
85 pub fn preview_scoped_sql(&self, ctx: &CratestackContext) -> String {
88 super::find_many_preview::preview_scoped_sql(self, ctx)
89 }
90
91 pub async fn run(self, ctx: &CratestackContext) -> Result<Vec<M>, CratestackError>
92 where
93 for<'r> M: Send + Unpin + sqlx::FromRow<'r, sqlx::postgres::PgRow>,
94 {
95 if let Some(bound) = self.runtime.bound() {
97 return crate::bound::in_bound_savepoint!(bound, |sp| self.run_in_tx(sp, ctx));
98 }
99 let order_by = self.effective_order_by();
100 let mut query = sqlx::QueryBuilder::<sqlx::Postgres>::new("SELECT ");
101 query
102 .push(self.descriptor.select_projection())
103 .push(" FROM ")
104 .push(self.descriptor.table_name());
105
106 push_scoped_conditions(
107 &mut query,
108 self.descriptor,
109 &self.filters,
110 None::<(&'static str, i64)>,
111 ctx,
112 ReadPolicyKind::List,
113 );
114 push_order_and_paging(&mut query, &order_by, self.limit, self.offset, ctx);
115 if self.for_update {
116 query.push(" FOR UPDATE");
117 }
118
119 query
120 .build_query_as::<M>()
121 .fetch_all(self.runtime.pool())
122 .await
123 .map_err(crate::error::cratestack_error_from_sqlx)
124 }
125
126 pub async fn run_in_tx<'tx>(
129 self,
130 tx: &mut sqlx::Transaction<'tx, sqlx::Postgres>,
131 ctx: &CratestackContext,
132 ) -> Result<Vec<M>, CratestackError>
133 where
134 for<'r> M: Send + Unpin + sqlx::FromRow<'r, sqlx::postgres::PgRow>,
135 {
136 let order_by = self.effective_order_by();
137 let mut query = sqlx::QueryBuilder::<sqlx::Postgres>::new("SELECT ");
138 query
139 .push(self.descriptor.select_projection())
140 .push(" FROM ")
141 .push(self.descriptor.table_name());
142
143 push_scoped_conditions(
144 &mut query,
145 self.descriptor,
146 &self.filters,
147 None::<(&'static str, i64)>,
148 ctx,
149 ReadPolicyKind::List,
150 );
151 push_order_and_paging(&mut query, &order_by, self.limit, self.offset, ctx);
152 if self.for_update {
153 query.push(" FOR UPDATE");
154 }
155
156 query
157 .build_query_as::<M>()
158 .fetch_all(&mut **tx)
159 .await
160 .map_err(crate::error::cratestack_error_from_sqlx)
161 }
162
163 pub(super) fn effective_order_by(&self) -> Vec<OrderClause> {
164 let mut order_by = self.order_by.clone();
165 let Some(direction) = order_by
166 .iter()
167 .find(|clause| clause.is_relation_scalar())
168 .map(OrderClause::direction)
169 else {
170 return order_by;
171 };
172
173 if order_by
174 .iter()
175 .any(|clause| clause.targets_column(self.descriptor.primary_key()))
176 {
177 return order_by;
178 }
179
180 order_by.push(OrderClause::column(
181 self.descriptor.primary_key(),
182 direction,
183 ));
184 order_by
185 }
186
187 pub fn include<Rel, RelPK>(
191 self,
192 relation: cratestack_sql::RelationInclude<M, Rel, RelPK>,
193 ) -> super::find_many_with::FindManyWith<'a, M, PK, Rel, RelPK>
194 where
195 Rel: 'static,
196 RelPK: 'static,
197 {
198 super::find_many_with::FindManyWith::new(self, relation)
199 }
200}