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 }
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")]
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 }
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 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 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}