cratestack_sqlx/query/read/
projected_find_many.rs1use cratestack_core::{CoolContext, CoolError};
6use cratestack_sql::IntoColumnName;
7
8use crate::query::support::{ReadPolicyKind, push_order_and_paging, push_scoped_conditions};
9use crate::{FilterExpr, ModelDescriptor, OrderClause, SqlxRuntime, sqlx};
10
11use super::find_many::FindMany;
12
13#[derive(Debug, Clone)]
14pub struct ProjectedFindMany<'a, M: 'static, PK: 'static> {
15 runtime: &'a SqlxRuntime,
16 descriptor: &'static ModelDescriptor<M, PK>,
17 filters: Vec<FilterExpr>,
18 order_by: Vec<OrderClause>,
19 limit: Option<i64>,
20 offset: Option<i64>,
21 for_update: bool,
22 selected: Vec<&'static str>,
23}
24
25impl<'a, M: 'static, PK: 'static> ProjectedFindMany<'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
42 where
43 F: Into<FilterExpr>,
44 {
45 if let Some(filter) = filter {
46 self.filters.push(filter.into());
47 }
48 self
49 }
50
51 pub fn order_by(mut self, clause: OrderClause) -> Self {
52 self.order_by.push(clause);
53 self
54 }
55
56 pub fn limit(mut self, limit: i64) -> Self {
57 self.limit = Some(limit);
58 self
59 }
60
61 pub fn offset(mut self, offset: i64) -> Self {
62 self.offset = Some(offset);
63 self
64 }
65
66 pub fn for_update(mut self) -> Self {
67 self.for_update = true;
68 self
69 }
70
71 fn build_query<'q>(&self, ctx: &CoolContext) -> sqlx::QueryBuilder<'q, sqlx::Postgres> {
72 let mut query = sqlx::QueryBuilder::<sqlx::Postgres>::new("SELECT ");
73 query
74 .push(self.descriptor.select_projection_subset(&self.selected))
75 .push(" FROM ")
76 .push(self.descriptor.table_name);
77 push_scoped_conditions(
78 &mut query,
79 self.descriptor,
80 &self.filters,
81 None::<(&'static str, i64)>,
82 ctx,
83 ReadPolicyKind::List,
84 );
85 push_order_and_paging(&mut query, &self.order_by, self.limit, self.offset);
86 if self.for_update {
87 query.push(" FOR UPDATE");
88 }
89 query
90 }
91
92 pub async fn run(
93 self,
94 ctx: &CoolContext,
95 ) -> Result<Vec<cratestack_sql::Projection<M>>, CoolError>
96 where
97 M: crate::FromPartialPgRow,
98 {
99 let mut query = self.build_query(ctx);
100 let rows = query
101 .build()
102 .fetch_all(self.runtime.pool())
103 .await
104 .map_err(|error| CoolError::Database(error.to_string()))?;
105 decode_many::<M>(rows, &self.selected)
106 }
107
108 pub async fn run_in_tx<'tx>(
109 self,
110 tx: &mut sqlx::Transaction<'tx, sqlx::Postgres>,
111 ctx: &CoolContext,
112 ) -> Result<Vec<cratestack_sql::Projection<M>>, CoolError>
113 where
114 M: crate::FromPartialPgRow,
115 {
116 let mut query = self.build_query(ctx);
117 let rows = query
118 .build()
119 .fetch_all(&mut **tx)
120 .await
121 .map_err(|error| CoolError::Database(error.to_string()))?;
122 decode_many::<M>(rows, &self.selected)
123 }
124}
125
126fn decode_many<M>(
127 rows: Vec<sqlx::postgres::PgRow>,
128 selected: &[&'static str],
129) -> Result<Vec<cratestack_sql::Projection<M>>, CoolError>
130where
131 M: crate::FromPartialPgRow,
132{
133 rows.into_iter()
134 .map(|row| {
135 M::decode_partial_pg_row(&row, selected)
136 .map(|value| cratestack_sql::Projection {
137 value,
138 selected: selected.to_vec(),
139 })
140 .map_err(|error| CoolError::Database(error.to_string()))
141 })
142 .collect()
143}
144
145impl<'a, M: 'static, PK: 'static> FindMany<'a, M, PK> {
146 pub fn select<I, C>(self, columns: I) -> ProjectedFindMany<'a, M, PK>
150 where
151 I: IntoIterator<Item = C>,
152 C: IntoColumnName,
153 {
154 ProjectedFindMany {
155 runtime: self.runtime,
156 descriptor: self.descriptor,
157 filters: self.filters,
158 order_by: self.order_by,
159 limit: self.limit,
160 offset: self.offset,
161 for_update: self.for_update,
162 selected: columns
163 .into_iter()
164 .map(IntoColumnName::into_column_name)
165 .collect(),
166 }
167 }
168}