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 let order_by = self.effective_order_by();
96 let mut query = sqlx::QueryBuilder::<sqlx::Postgres>::new("SELECT ");
97 query
98 .push(self.descriptor.select_projection())
99 .push(" FROM ")
100 .push(self.descriptor.table_name());
101
102 push_scoped_conditions(
103 &mut query,
104 self.descriptor,
105 &self.filters,
106 None::<(&'static str, i64)>,
107 ctx,
108 ReadPolicyKind::List,
109 );
110 push_order_and_paging(&mut query, &order_by, self.limit, self.offset, ctx);
111 if self.for_update {
112 query.push(" FOR UPDATE");
113 }
114
115 query
116 .build_query_as::<M>()
117 .fetch_all(self.runtime.pool())
118 .await
119 .map_err(|error| CratestackError::Database(error.to_string()))
120 }
121
122 pub async fn run_in_tx<'tx>(
125 self,
126 tx: &mut sqlx::Transaction<'tx, sqlx::Postgres>,
127 ctx: &CratestackContext,
128 ) -> Result<Vec<M>, CratestackError>
129 where
130 for<'r> M: Send + Unpin + sqlx::FromRow<'r, sqlx::postgres::PgRow>,
131 {
132 let order_by = self.effective_order_by();
133 let mut query = sqlx::QueryBuilder::<sqlx::Postgres>::new("SELECT ");
134 query
135 .push(self.descriptor.select_projection())
136 .push(" FROM ")
137 .push(self.descriptor.table_name());
138
139 push_scoped_conditions(
140 &mut query,
141 self.descriptor,
142 &self.filters,
143 None::<(&'static str, i64)>,
144 ctx,
145 ReadPolicyKind::List,
146 );
147 push_order_and_paging(&mut query, &order_by, self.limit, self.offset, ctx);
148 if self.for_update {
149 query.push(" FOR UPDATE");
150 }
151
152 query
153 .build_query_as::<M>()
154 .fetch_all(&mut **tx)
155 .await
156 .map_err(|error| CratestackError::Database(error.to_string()))
157 }
158
159 pub(super) fn effective_order_by(&self) -> Vec<OrderClause> {
160 let mut order_by = self.order_by.clone();
161 let Some(direction) = order_by
162 .iter()
163 .find(|clause| clause.is_relation_scalar())
164 .map(OrderClause::direction)
165 else {
166 return order_by;
167 };
168
169 if order_by
170 .iter()
171 .any(|clause| clause.targets_column(self.descriptor.primary_key()))
172 {
173 return order_by;
174 }
175
176 order_by.push(OrderClause::column(
177 self.descriptor.primary_key(),
178 direction,
179 ));
180 order_by
181 }
182
183 pub fn include<Rel, RelPK>(
187 self,
188 relation: cratestack_sql::RelationInclude<M, Rel, RelPK>,
189 ) -> super::find_many_with::FindManyWith<'a, M, PK, Rel, RelPK>
190 where
191 Rel: 'static,
192 RelPK: 'static,
193 {
194 super::find_many_with::FindManyWith::new(self, relation)
195 }
196}