cratestack_sqlx/query/read/
aggregate_count.rs1use cratestack_core::{CratestackContext, CratestackError};
4use cratestack_sql::ReadSource;
5
6use crate::query::support::{ReadPolicyKind, push_scoped_conditions};
7use crate::{FilterExpr, SqlxRuntime, sqlx};
8
9use super::find_many::FindMany;
10
11#[derive(Clone)]
12pub struct AggregateCount<'a, M: 'static, PK: 'static> {
13 runtime: &'a SqlxRuntime,
14 descriptor: &'static dyn ReadSource<M, PK>,
15 filters: Vec<FilterExpr>,
16}
17
18impl<'a, M: 'static, PK: 'static> From<FindMany<'a, M, PK>> for AggregateCount<'a, M, PK> {
29 fn from(find_many: FindMany<'a, M, PK>) -> Self {
30 Self {
31 runtime: find_many.runtime,
32 descriptor: find_many.descriptor,
33 filters: find_many.filters,
34 }
35 }
36}
37
38impl<'a, M: 'static, PK: 'static> AggregateCount<'a, M, PK> {
39 pub(super) fn new(
40 runtime: &'a SqlxRuntime,
41 descriptor: &'static dyn ReadSource<M, PK>,
42 ) -> Self {
43 Self {
44 runtime,
45 descriptor,
46 filters: Vec::new(),
47 }
48 }
49
50 pub fn where_(mut self, filter: crate::Filter) -> Self {
51 self.filters.push(FilterExpr::from(filter));
52 self
53 }
54
55 pub fn where_expr(mut self, filter: FilterExpr) -> Self {
56 self.filters.push(filter);
57 self
58 }
59
60 pub fn where_any(mut self, filters: impl IntoIterator<Item = FilterExpr>) -> Self {
61 self.filters.push(FilterExpr::any(filters));
62 self
63 }
64
65 pub fn where_optional<F>(mut self, filter: Option<F>) -> Self
66 where
67 F: Into<FilterExpr>,
68 {
69 if let Some(filter) = filter {
70 self.filters.push(filter.into());
71 }
72 self
73 }
74
75 fn build_query(&self, ctx: &CratestackContext) -> sqlx::QueryBuilder<sqlx::Postgres> {
76 let mut query = sqlx::QueryBuilder::<sqlx::Postgres>::new("SELECT COUNT(*) FROM ");
77 query.push(self.descriptor.table_name());
78 push_scoped_conditions(
79 &mut query,
80 self.descriptor,
81 &self.filters,
82 None::<(&'static str, i64)>,
83 ctx,
84 ReadPolicyKind::List,
85 );
86 query
87 }
88
89 pub fn preview_scoped_sql(&self, ctx: &CratestackContext) -> String {
96 self.build_query(ctx).into_string()
97 }
98
99 pub async fn run(self, ctx: &CratestackContext) -> Result<i64, CratestackError> {
100 let mut query = self.build_query(ctx);
101 let value: (i64,) = query
102 .build_query_as::<(i64,)>()
103 .fetch_one(self.runtime.pool())
104 .await
105 .map_err(|error| CratestackError::Database(error.to_string()))?;
106 Ok(value.0)
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<i64, CratestackError> {
114 let mut query = self.build_query(ctx);
115 let value: (i64,) = query
116 .build_query_as::<(i64,)>()
117 .fetch_one(&mut **tx)
118 .await
119 .map_err(|error| CratestackError::Database(error.to_string()))?;
120 Ok(value.0)
121 }
122}