cratestack_sqlx/query/read/
find_many.rs1use cratestack_core::{CoolContext, CoolError};
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 {
78 super::find_many_preview::preview_sql(self)
79 }
80
81 pub fn preview_scoped_sql(&self, ctx: &CoolContext) -> String {
82 super::find_many_preview::preview_scoped_sql(self, ctx)
83 }
84
85 pub async fn run(self, ctx: &CoolContext) -> Result<Vec<M>, CoolError>
86 where
87 for<'r> M: Send + Unpin + sqlx::FromRow<'r, sqlx::postgres::PgRow>,
88 {
89 let order_by = self.effective_order_by();
90 let mut query = sqlx::QueryBuilder::<sqlx::Postgres>::new("SELECT ");
91 query
92 .push(self.descriptor.select_projection())
93 .push(" FROM ")
94 .push(self.descriptor.table_name());
95
96 push_scoped_conditions(
97 &mut query,
98 self.descriptor,
99 &self.filters,
100 None::<(&'static str, i64)>,
101 ctx,
102 ReadPolicyKind::List,
103 );
104 push_order_and_paging(&mut query, &order_by, self.limit, self.offset);
105 if self.for_update {
106 query.push(" FOR UPDATE");
107 }
108
109 query
110 .build_query_as::<M>()
111 .fetch_all(self.runtime.pool())
112 .await
113 .map_err(|error| CoolError::Database(error.to_string()))
114 }
115
116 pub async fn run_in_tx<'tx>(
119 self,
120 tx: &mut sqlx::Transaction<'tx, sqlx::Postgres>,
121 ctx: &CoolContext,
122 ) -> Result<Vec<M>, CoolError>
123 where
124 for<'r> M: Send + Unpin + sqlx::FromRow<'r, sqlx::postgres::PgRow>,
125 {
126 let order_by = self.effective_order_by();
127 let mut query = sqlx::QueryBuilder::<sqlx::Postgres>::new("SELECT ");
128 query
129 .push(self.descriptor.select_projection())
130 .push(" FROM ")
131 .push(self.descriptor.table_name());
132
133 push_scoped_conditions(
134 &mut query,
135 self.descriptor,
136 &self.filters,
137 None::<(&'static str, i64)>,
138 ctx,
139 ReadPolicyKind::List,
140 );
141 push_order_and_paging(&mut query, &order_by, self.limit, self.offset);
142 if self.for_update {
143 query.push(" FOR UPDATE");
144 }
145
146 query
147 .build_query_as::<M>()
148 .fetch_all(&mut **tx)
149 .await
150 .map_err(|error| CoolError::Database(error.to_string()))
151 }
152
153 pub(super) fn effective_order_by(&self) -> Vec<OrderClause> {
154 let mut order_by = self.order_by.clone();
155 let Some(direction) = order_by
156 .iter()
157 .find(|clause| clause.is_relation_scalar())
158 .map(OrderClause::direction)
159 else {
160 return order_by;
161 };
162
163 if order_by
164 .iter()
165 .any(|clause| clause.targets_column(self.descriptor.primary_key()))
166 {
167 return order_by;
168 }
169
170 order_by.push(OrderClause::column(self.descriptor.primary_key(), direction));
171 order_by
172 }
173
174 pub fn include<Rel, RelPK>(
178 self,
179 relation: cratestack_sql::RelationInclude<M, Rel, RelPK>,
180 ) -> super::find_many_with::FindManyWith<'a, M, PK, Rel, RelPK>
181 where
182 Rel: 'static,
183 RelPK: 'static,
184 {
185 super::find_many_with::FindManyWith::new(self, relation)
186 }
187}