easy_sqlx_core/sql/builder/
select_builder.rs

1use std::hash;
2
3use crate::sql::{
4    dialects::{
5        condition::{Condition, Where, WhereAppend},
6        page::{Order, PageRequest, PageResult},
7        schema::{self, schema::Schema},
8    },
9    schema::{column::Column, table::TableSchema},
10};
11
12use super::builder::QueryBuilder;
13use sqlx::{Database, FromRow};
14
15#[derive(Debug)]
16pub struct SelectBuilder<'a> {
17    table: TableSchema,
18    default_schema: &'a str,
19    columns: Vec<Column>,
20    wh: Option<Where>,
21    orders: Vec<Order>,
22}
23
24impl<'a> SelectBuilder<'a> {
25    pub fn new(table: TableSchema) -> Self {
26        Self {
27            table,
28            default_schema: "",
29            columns: vec![],
30            wh: None,
31            orders: vec![],
32        }
33    }
34
35    pub fn with_default_schema(mut self, schema: &'a str) -> Self {
36        self.default_schema = schema;
37        self
38    }
39
40    pub fn order_by(mut self, item: Order) -> Self {
41        self.orders.push(item);
42        self
43    }
44
45    fn generate_query_as(&self) -> String {
46        let schema = schema::new(self.default_schema.to_string());
47        if self.columns.is_empty() {
48            schema.sql_select(&self.table, self.wh.clone(), &self.orders, None)
49        } else {
50            schema.sql_select_columns(
51                &self.table,
52                &self.columns,
53                self.wh.clone(),
54                &self.orders,
55                None,
56            )
57        }
58    }
59
60    fn generate_page_query_as(&self, pg: &PageRequest) -> String {
61        let schema = schema::new(self.default_schema.to_string());
62        if self.columns.is_empty() {
63            schema.sql_select(&self.table, self.wh.clone(), &self.orders, Some(pg))
64        } else {
65            schema.sql_select_columns(
66                &self.table,
67                &self.columns,
68                self.wh.clone(),
69                &self.orders,
70                Some(pg),
71            )
72        }
73
74        // let sql = schema.sql_select(&self.table, self.wh.clone(), &self.orders, Some(pg));
75        // sql
76    }
77
78    fn generate_query_scalar(&self, field: &Column) -> String {
79        let schema = schema::new(self.default_schema.to_string());
80        let sql = schema.sql_select_columns(
81            &self.table,
82            &vec![field.clone()],
83            self.wh.clone(),
84            &self.orders,
85            None,
86        );
87        sql
88    }
89
90    fn generate_query_page_scalar(&self, field: &Column, pg: &PageRequest) -> String {
91        let schema = schema::new(self.default_schema.to_string());
92        let sql = schema.sql_select_columns(
93            &self.table,
94            &vec![field.clone()],
95            self.wh.clone(),
96            &self.orders,
97            Some(pg),
98        );
99        sql
100    }
101}
102impl<'a> WhereAppend<Condition> for SelectBuilder<'a> {
103    fn and(mut self, cond: Condition) -> Self {
104        if let Some(w) = self.wh {
105            self.wh = Some(w.and(cond));
106        } else {
107            self.wh = Some(Where::new(cond));
108        }
109        self
110    }
111
112    fn or(mut self, cond: Condition) -> Self {
113        if let Some(w) = self.wh {
114            self.wh = Some(w.or(cond));
115        } else {
116            self.wh = Some(Where::new(cond));
117        }
118        self
119    }
120}
121
122impl<'a> WhereAppend<Where> for SelectBuilder<'a> {
123    fn and(mut self, wh: Where) -> Self {
124        if let Some(w) = self.wh {
125            self.wh = Some(w.and(wh));
126        } else {
127            self.wh = Some(wh);
128        }
129        self
130    }
131
132    fn or(mut self, wh: Where) -> Self {
133        if let Some(w) = self.wh {
134            self.wh = Some(w.or(wh));
135        } else {
136            self.wh = Some(wh);
137        }
138        self
139    }
140}
141
142#[cfg(feature = "postgres")]
143use sqlx::Postgres;
144
145impl<'a> QueryBuilder<'a> for SelectBuilder<'a> {
146    #[cfg(feature = "postgres")]
147    type DB = Postgres;
148
149    async fn one<'e, 'c: 'e, E, O>(&self, executor: E) -> Result<O, sqlx::Error>
150    where
151        E: 'e + sqlx::Executor<'c, Database = Self::DB>,
152        O: 'e,
153        for<'r> O: FromRow<'r, <Self::DB as Database>::Row>,
154        O: std::marker::Send,
155        O: Unpin,
156    {
157        let sql = self.generate_query_as();
158        // #[cfg(feature = "logsql")]
159        // tracing::info!("easy-sqlx: {sql} [{}]", w.list_params());
160
161        #[cfg(feature = "logsql")]
162        {
163            let wh = if let Some(w) = &self.wh {
164                w.list_params()
165            } else {
166                "".to_string()
167            };
168            tracing::info!("easy-sqlx: {sql} [{wh}]");
169        }
170
171        let mut query: sqlx::query::QueryAs<'_, Postgres, O, sqlx::postgres::PgArguments> = sqlx::query_as::<Self::DB, O>(&sql);
172        if let Some(w) = &self.wh {
173            query = w.bind_to_query_as(query);
174        }
175
176        query.fetch_one(executor).await.map_err(|err| {
177            let wh = if let Some(w) = &self.wh {
178                w.list_params()
179            } else {
180                "".to_string()
181            };
182            tracing::info!("easy-sqlx: {} [{}]\n{:?}", sql, wh, err);
183            err
184        })
185
186        // todo!()
187    }
188
189    async fn optional<'e, 'c: 'e, E, O>(&self, executor: E) -> Result<Option<O>, sqlx::Error>
190    where
191        E: 'e + sqlx::Executor<'c, Database = Self::DB>,
192        O: 'e,
193        O: std::marker::Send,
194        O: Unpin,
195        for<'r> O: FromRow<'r, <Self::DB as Database>::Row>,
196    {
197        let sql = self.generate_query_as();
198
199        #[cfg(feature = "logsql")]
200        {
201            let wh = if let Some(w) = &self.wh {
202                w.list_params()
203            } else {
204                "".to_string()
205            };
206            tracing::info!("easy-sqlx: {sql} [{wh}]");
207        }
208
209        let mut query: sqlx::query::QueryAs<'_, Postgres, O, sqlx::postgres::PgArguments> = sqlx::query_as(&sql);
210        if let Some(w) = &self.wh {
211            query = w.bind_to_query_as(query);
212        }
213        query.fetch_optional(executor).await.map_err(|err| {
214            let wh = if let Some(w) = &self.wh {
215                w.list_params()
216            } else {
217                "".to_string()
218            };
219            tracing::info!("easy-sqlx: {} [{}]\n{:?}", sql, wh, err);
220            err
221        })
222    }
223
224    async fn all<'e, 'c: 'e, E, O>(&self, executor: E) -> Result<Vec<O>, sqlx::Error>
225    where
226        E: 'e + sqlx::Executor<'c, Database = Self::DB>,
227        for<'r> O: FromRow<'r, <Self::DB as Database>::Row>,
228        O: 'e,
229        O: std::marker::Send,
230        O: Unpin,
231    {
232        let sql = self.generate_query_as();
233        #[cfg(feature = "logsql")]
234        {
235            let wh = if let Some(w) = &self.wh {
236                w.list_params()
237            } else {
238                "".to_string()
239            };
240            tracing::info!("easy-sqlx: {sql} [{wh}]");
241        }
242
243        let mut query: sqlx::query::QueryAs<'_, Postgres, O, sqlx::postgres::PgArguments> = sqlx::query_as(&sql);
244        if let Some(w) = &self.wh {
245            query = w.bind_to_query_as(query);
246        }
247
248        let result = query.fetch_all(executor).await.map_err(|err| {
249            let wh = if let Some(w) = &self.wh {
250                w.list_params()
251            } else {
252                "".to_string()
253            };
254            tracing::info!("easy-sqlx: {} [{}]\n{:?}", sql, wh, err);
255            err
256        })?;
257
258        Ok(result)
259    }
260
261    async fn page<'e, 'c: 'e, E, O>(
262        &self,
263        executor: E,
264        page: &PageRequest,
265    ) -> Result<PageResult<O>, sqlx::Error>
266    where
267        E: 'e + sqlx::Executor<'c, Database = Self::DB>,
268        for<'r> O: FromRow<'r, <Self::DB as Database>::Row>,
269        O: 'e,
270        O: std::marker::Send,
271        O: Unpin,
272    {
273        let mut result: PageResult<O> = PageResult {
274            records: vec![],
275            page_count: 0,
276            total: 0,
277            page_no: page.get_page_no(),
278            page_size: page.get_page_size(),
279        };
280
281        let sql = self.generate_page_query_as(page);
282        #[cfg(feature = "logsql")]
283        {
284            let wh = if let Some(w) = &self.wh {
285                w.list_params()
286            } else {
287                "".to_string()
288            };
289            tracing::info!("easy-sqlx: {sql} [{wh}]");
290        }
291        let mut query: sqlx::query::QueryAs<'_, Postgres, O, sqlx::postgres::PgArguments> = sqlx::query_as(&sql);
292        if let Some(w) = &self.wh {
293            query = w.bind_to_query_as(query);
294        }
295
296        result.records = query.fetch_all(executor).await.map_err(|err| {
297            let wh = if let Some(w) = &self.wh {
298                w.list_params()
299            } else {
300                "".to_string()
301            };
302            tracing::info!("easy-sqlx: {} [{}]\n{:?}", sql, wh, err);
303            err
304        })?;
305
306        Ok(result)
307    }
308
309    // async fn fetch_one_scalar<'q>(&self, field: &'q str) -> Result<O, Error>
310    // where
311    //     (O,): for<'r> FromRow<'r, <Self::DB as Database>::Row>,
312    // {
313    //     let schema = schema::new(self.default_schema.to_string());
314    //     let sql = schema.sql_select_columns(
315    //         &self.table,
316    //         &vec![field.to_string()],
317    //         self.wh.clone(),
318    //         &self.orders,
319    //         None,
320    //     );
321    //     //
322    //     let mut query = sqlx::query_scalar(&sql);
323    //     // query
324    //     if let Some(w) = &self.wh {
325    //         query = w.bind_to_query_scalar(query);
326    //     }
327    //     query.fetch_all(executor)
328    //     // query.fe
329    // }
330
331    async fn one_scalar<'c, E, O>(&self, executor: E, field: &Column) -> Result<O, sqlx::Error>
332    where
333        (O,): for<'r> FromRow<'r, <Self::DB as Database>::Row>,
334        E: 'c + sqlx::Executor<'c, Database = Self::DB>,
335        O: Send + Unpin,
336    {
337        let sql = self.generate_query_scalar(field);
338        #[cfg(feature = "logsql")]
339        {
340            let wh = if let Some(w) = &self.wh {
341                w.list_params()
342            } else {
343                "".to_string()
344            };
345            tracing::info!("easy-sqlx: {sql} [{wh}]");
346        }
347        let mut query: sqlx::query::QueryScalar<'_, Postgres, O, sqlx::postgres::PgArguments> = sqlx::query_scalar(&sql);
348        if let Some(w) = &self.wh {
349            query = w.bind_to_query_scalar(query);
350        }
351        query.fetch_one(executor).await.map_err(|err| {
352            let wh = if let Some(w) = &self.wh {
353                w.list_params()
354            } else {
355                "".to_string()
356            };
357            tracing::info!("easy-sqlx: {} [{}]\n{:?}", sql, wh, err);
358            err
359        })
360    }
361
362    async fn optional_scalar<'q, 'c, E, O>(
363        &self,
364        executor: E,
365        field: &'q Column,
366    ) -> Result<Option<O>, sqlx::Error>
367    where
368        (O,): for<'r> FromRow<'r, <Self::DB as Database>::Row>,
369        E: 'c + sqlx::Executor<'c, Database = Self::DB>,
370        O: Send + Unpin,
371    {
372        let sql = self.generate_query_scalar(field);
373        #[cfg(feature = "logsql")]
374        {
375            let wh = if let Some(w) = &self.wh {
376                w.list_params()
377            } else {
378                "".to_string()
379            };
380            tracing::info!("easy-sqlx: {sql} [{wh}]");
381        }
382        let mut query: sqlx::query::QueryScalar<'_, Postgres, O, sqlx::postgres::PgArguments> =
383            sqlx::query_scalar(&sql);
384        if let Some(w) = &self.wh {
385            query = w.bind_to_query_scalar(query);
386        }
387        query.fetch_optional(executor).await.map_err(|err| {
388            let wh = if let Some(w) = &self.wh {
389                w.list_params()
390            } else {
391                "".to_string()
392            };
393            tracing::info!("easy-sqlx: {} [{}]\n{:?}", sql, wh, err);
394            err
395        })
396    }
397
398    async fn all_scalars<'q, 'c, E, O>(
399        &self,
400        executor: E,
401        field: &'q Column,
402    ) -> Result<Vec<O>, sqlx::Error>
403    where
404        (O,): for<'r> FromRow<'r, <Self::DB as Database>::Row>,
405        E: 'c + sqlx::Executor<'c, Database = Self::DB>,
406        O: Send + Unpin,
407    {
408        let sql = self.generate_query_scalar(field);
409        #[cfg(feature = "logsql")]
410        {
411            let wh = if let Some(w) = &self.wh {
412                w.list_params()
413            } else {
414                "".to_string()
415            };
416            tracing::info!("easy-sqlx: {sql} [{wh}]");
417        }
418        let mut query: sqlx::query::QueryScalar<'_, Postgres, O, sqlx::postgres::PgArguments> = sqlx::query_scalar(&sql);
419        if let Some(w) = &self.wh {
420            query = w.bind_to_query_scalar(query);
421        }
422        query.fetch_all(executor).await.map_err(|err| {
423            let wh = if let Some(w) = &self.wh {
424                w.list_params()
425            } else {
426                "".to_string()
427            };
428            tracing::info!("easy-sqlx: {} [{}]\n{:?}", sql, wh, err);
429            err
430        })
431    }
432
433    async fn count<'c, E>(&self, executor: E) -> Result<usize, sqlx::Error>
434    where
435        E: 'c + sqlx::Executor<'c, Database = Self::DB>,
436    {
437        let schema = schema::new(self.default_schema.to_string());
438        let sql = schema.sql_count(&self.table, self.wh.clone());
439        #[cfg(feature = "logsql")]
440        {
441            let wh = if let Some(w) = &self.wh {
442                w.list_params()
443            } else {
444                "".to_string()
445            };
446            tracing::info!("easy-sqlx: {sql} [{wh}]");
447        }
448        let mut query: sqlx::query::QueryScalar<'_, Postgres, i64, sqlx::postgres::PgArguments> = sqlx::query_scalar(&sql);
449        if let Some(w) = &self.wh {
450            query = w.bind_to_query_scalar(query);
451        }
452
453        let c: i64 = query.fetch_one(executor).await.map_err(|err| {
454            let wh = if let Some(w) = &self.wh {
455                w.list_params()
456            } else {
457                "".to_string()
458            };
459            tracing::info!("easy-sqlx: {} [{}]\n{:?}", sql, wh, err);
460            err
461        })?;
462        Ok(c as usize)
463    }
464
465    async fn page_scalars<'e, 'c: 'e, E, O>(
466        &self,
467        executor: E,
468        field: &'c Column,
469        page: &PageRequest,
470    ) -> Result<PageResult<O>, sqlx::Error>
471    where
472        (O,): for<'r> FromRow<'r, <Self::DB as Database>::Row>,
473        E: 'e + sqlx::Executor<'c, Database = Self::DB>,
474        O: Send + Unpin,
475    {
476        let mut result: PageResult<O> = PageResult {
477            records: vec![],
478            page_count: 0,
479            total: 0,
480            page_no: page.get_page_no(),
481            page_size: page.get_page_size(),
482        };
483
484        let sql = self.generate_query_page_scalar(field, page);
485        #[cfg(feature = "logsql")]
486        {
487            let wh = if let Some(w) = &self.wh {
488                w.list_params()
489            } else {
490                "".to_string()
491            };
492            tracing::info!("easy-sqlx: {sql} [{wh}]");
493        }
494        let mut query: sqlx::query::QueryScalar<'_, Postgres, O, sqlx::postgres::PgArguments> = sqlx::query_scalar(&sql);
495        if let Some(w) = &self.wh {
496            query = w.bind_to_query_scalar(query);
497        }
498        result.records = query.fetch_all(executor).await.map_err(|err| {
499            let wh = if let Some(w) = &self.wh {
500                w.list_params()
501            } else {
502                "".to_string()
503            };
504            tracing::info!("easy-sqlx: {} [{}]\n{:?}", sql, wh, err);
505            err
506        })?;
507
508        Ok(result)
509    }
510
511    async fn map_all<'e, 'c: 'e, E, O, F, K>(
512        &self,
513        executor: E,
514        key_fn: F,
515    ) -> Result<std::collections::HashMap<K, O>, sqlx::Error>
516    where
517        // 'q: 'e,
518        E: 'e + sqlx::Executor<'c, Database = Self::DB>,
519        for<'r> O: FromRow<'r, <Self::DB as Database>::Row>,
520        O: 'e,
521        O: std::marker::Send,
522        O: Unpin,
523        F: Fn(&O) -> K,
524        K: Eq + hash::Hash,
525    {
526        let sql = self.generate_query_as();
527        #[cfg(feature = "logsql")]
528        {
529            let wh = if let Some(w) = &self.wh {
530                w.list_params()
531            } else {
532                "".to_string()
533            };
534            tracing::info!("easy-sqlx: {sql} [{wh}]");
535        }
536
537        let mut query: sqlx::query::QueryAs<'_, Postgres, O, sqlx::postgres::PgArguments> = sqlx::query_as(&sql);
538        if let Some(w) = &self.wh {
539            query = w.bind_to_query_as(query);
540        }
541
542        let result: Vec<O> = query.fetch_all(executor).await.map_err(|err| {
543            let wh = if let Some(w) = &self.wh {
544                w.list_params()
545            } else {
546                "".to_string()
547            };
548            tracing::info!("easy-sqlx: {} [{}]\n{:?}", sql, wh, err);
549            err
550        })?;
551
552        let mut map = std::collections::HashMap::<K, O>::new();
553
554        for o in result {
555            let key = key_fn(&o);
556            map.insert(key, o);
557        }
558
559        Ok(map)
560    }
561}