Skip to main content

rust_ef/query/
select.rs

1//! `SelectQueryBuilder<T>` — projection query builder.
2//!
3//! Produced by `QueryBuilder::select_internal` when the user projects to
4//! named columns. Executes the projection query and returns either raw
5//! `Vec<Vec<DbValue>>` rows or strongly-typed tuples via `to_list_typed_n`
6//! methods (converting each column via `TryFrom<DbValue>`).
7
8use std::marker::PhantomData;
9use std::sync::Arc;
10
11use crate::entity::IEntityType;
12use crate::error::EFResult;
13use crate::provider::{DbValue, DbValueConvertError, IDatabaseProvider};
14
15use super::state::QueryState;
16
17/// A query builder for projected column results.
18#[derive(Clone)]
19pub struct SelectQueryBuilder<T: IEntityType> {
20    pub(crate) state: QueryState,
21    pub(crate) provider: Option<Arc<dyn IDatabaseProvider>>,
22    pub(crate) _phantom: PhantomData<T>,
23}
24
25impl<T: IEntityType> SelectQueryBuilder<T> {
26    /// Returns the generated SQL.
27    pub fn to_sql(&self) -> String {
28        if let Some(provider) = &self.provider {
29            let gen = provider.sql_generator();
30            self.state.to_sql_with(gen)
31        } else {
32            self.state.to_sql()
33        }
34    }
35
36    /// Executes the projection query and returns raw column values per row.
37    pub async fn to_list(self) -> EFResult<Vec<Vec<DbValue>>> {
38        let provider = self.provider.as_ref().ok_or_else(|| {
39            crate::error::EFError::configuration(
40                "No provider attached to SelectQueryBuilder.".to_string(),
41            )
42        })?;
43        let gen = provider.sql_generator();
44        let sql = self.state.to_sql_with(gen);
45        let params = self.state.all_params();
46        let mut conn = provider.get_connection().await?;
47        conn.query(&sql, &params).await
48    }
49
50    // -------------------------------------------------------------------
51    // G3: Strongly-typed projection terminal methods.
52    //
53    // Each `to_list_typed_n::<V0, ...>` method executes the projection
54    // query, then converts each column value via `TryFrom<DbValue>` into the
55    // corresponding type parameter, returning `Vec<(V0, ...)>`.
56    // -------------------------------------------------------------------
57
58    async fn fetch_rows(self) -> EFResult<Vec<Vec<DbValue>>> {
59        self.to_list().await
60    }
61
62    /// Single-column typed projection → `Vec<V0>`.
63    pub async fn to_list_typed_1<V0>(self) -> EFResult<Vec<V0>>
64    where
65        V0: TryFrom<DbValue, Error = DbValueConvertError>,
66    {
67        let rows = self.fetch_rows().await?;
68        rows.into_iter()
69            .map(|row| {
70                let cell = row
71                    .first()
72                    .ok_or_else(|| crate::error::EFError::query("projection row has no columns"))?;
73                V0::try_from(cell.clone()).map_err(crate::error::EFError::from)
74            })
75            .collect()
76    }
77
78    /// Two-column typed projection → `Vec<(V0, V1)>`.
79    pub async fn to_list_typed_2<V0, V1>(self) -> EFResult<Vec<(V0, V1)>>
80    where
81        V0: TryFrom<DbValue, Error = DbValueConvertError>,
82        V1: TryFrom<DbValue, Error = DbValueConvertError>,
83    {
84        let rows = self.fetch_rows().await?;
85        rows.into_iter()
86            .map(|row| {
87                let c0 = row.first().ok_or_else(|| {
88                    crate::error::EFError::query("projection row missing column 0")
89                })?;
90                let c1 = row.get(1).ok_or_else(|| {
91                    crate::error::EFError::query("projection row missing column 1")
92                })?;
93                Ok((
94                    V0::try_from(c0.clone()).map_err(crate::error::EFError::from)?,
95                    V1::try_from(c1.clone()).map_err(crate::error::EFError::from)?,
96                ))
97            })
98            .collect()
99    }
100
101    /// Three-column typed projection → `Vec<(V0, V1, V2)>`.
102    pub async fn to_list_typed_3<V0, V1, V2>(self) -> EFResult<Vec<(V0, V1, V2)>>
103    where
104        V0: TryFrom<DbValue, Error = DbValueConvertError>,
105        V1: TryFrom<DbValue, Error = DbValueConvertError>,
106        V2: TryFrom<DbValue, Error = DbValueConvertError>,
107    {
108        let rows = self.fetch_rows().await?;
109        rows.into_iter()
110            .map(|row| {
111                let c0 = row.first().ok_or_else(|| {
112                    crate::error::EFError::query("projection row missing column 0")
113                })?;
114                let c1 = row.get(1).ok_or_else(|| {
115                    crate::error::EFError::query("projection row missing column 1")
116                })?;
117                let c2 = row.get(2).ok_or_else(|| {
118                    crate::error::EFError::query("projection row missing column 2")
119                })?;
120                Ok((
121                    V0::try_from(c0.clone()).map_err(crate::error::EFError::from)?,
122                    V1::try_from(c1.clone()).map_err(crate::error::EFError::from)?,
123                    V2::try_from(c2.clone()).map_err(crate::error::EFError::from)?,
124                ))
125            })
126            .collect()
127    }
128
129    /// Four-column typed projection → `Vec<(V0, V1, V2, V3)>`.
130    pub async fn to_list_typed_4<V0, V1, V2, V3>(self) -> EFResult<Vec<(V0, V1, V2, V3)>>
131    where
132        V0: TryFrom<DbValue, Error = DbValueConvertError>,
133        V1: TryFrom<DbValue, Error = DbValueConvertError>,
134        V2: TryFrom<DbValue, Error = DbValueConvertError>,
135        V3: TryFrom<DbValue, Error = DbValueConvertError>,
136    {
137        let rows = self.fetch_rows().await?;
138        rows.into_iter()
139            .map(|row| {
140                let c0 = row.first().ok_or_else(|| {
141                    crate::error::EFError::query("projection row missing column 0")
142                })?;
143                let c1 = row.get(1).ok_or_else(|| {
144                    crate::error::EFError::query("projection row missing column 1")
145                })?;
146                let c2 = row.get(2).ok_or_else(|| {
147                    crate::error::EFError::query("projection row missing column 2")
148                })?;
149                let c3 = row.get(3).ok_or_else(|| {
150                    crate::error::EFError::query("projection row missing column 3")
151                })?;
152                Ok((
153                    V0::try_from(c0.clone()).map_err(crate::error::EFError::from)?,
154                    V1::try_from(c1.clone()).map_err(crate::error::EFError::from)?,
155                    V2::try_from(c2.clone()).map_err(crate::error::EFError::from)?,
156                    V3::try_from(c3.clone()).map_err(crate::error::EFError::from)?,
157                ))
158            })
159            .collect()
160    }
161}