1use crate::column::ColumnMarker;
4use crate::error::{Error, Result};
5use crate::expr::Expr;
6
7#[derive(Debug, Clone, PartialEq)]
9pub enum SelectItem {
10 Column(ColumnMarker),
12 Computed {
14 expr: Expr,
16 alias: String,
18 },
19}
20
21impl SelectItem {
22 pub fn column(marker: ColumnMarker) -> Self {
24 SelectItem::Column(marker)
25 }
26
27 pub fn computed(expr: Expr, alias: impl Into<String>) -> Self {
29 SelectItem::Computed {
30 expr,
31 alias: alias.into(),
32 }
33 }
34}
35
36impl From<ColumnMarker> for SelectItem {
37 fn from(marker: ColumnMarker) -> Self {
38 SelectItem::Column(marker)
39 }
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44pub enum OrderDir {
45 Asc,
47 Desc,
49}
50
51#[derive(Debug, Clone, PartialEq, Eq)]
53pub struct OrderBy {
54 pub column: String,
56 pub direction: OrderDir,
58}
59
60impl OrderBy {
61 pub fn new(column: impl Into<String>, direction: OrderDir) -> Self {
63 OrderBy {
64 column: column.into(),
65 direction,
66 }
67 }
68
69 pub fn asc(column: impl Into<String>) -> Self {
71 OrderBy::new(column, OrderDir::Asc)
72 }
73
74 pub fn desc(column: impl Into<String>) -> Self {
76 OrderBy::new(column, OrderDir::Desc)
77 }
78}
79
80#[derive(Debug, Clone, PartialEq)]
82pub enum OrderByItem {
83 Column(OrderBy),
85 Expr(Expr, OrderDir),
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq)]
91pub enum JoinType {
92 Inner,
94 Left,
96}
97
98#[derive(Debug, Clone, PartialEq)]
100pub struct JoinClause {
101 pub join_type: JoinType,
103 pub table: String,
105 pub on: Expr,
107 pub items: Vec<SelectItem>,
109}
110
111impl JoinClause {
112 pub fn new(
114 join_type: JoinType,
115 table: impl Into<String>,
116 on: Expr,
117 items: Vec<SelectItem>,
118 ) -> Self {
119 Self {
120 join_type,
121 table: table.into(),
122 on,
123 items,
124 }
125 }
126}
127
128#[derive(Debug, Clone, PartialEq)]
130pub struct Select {
131 pub table: String,
133 pub items: Vec<SelectItem>,
135 pub joins: Vec<JoinClause>,
137 pub filter: Option<Expr>,
139 pub order_by: Vec<OrderBy>,
141 pub take: Option<i32>,
147 pub skip: Option<u32>,
149 pub group_by: Vec<ColumnMarker>,
151 pub distinct: Vec<String>,
157 pub having: Option<Expr>,
159 pub order_by_items: Vec<OrderByItem>,
161 pub order_by_exprs: Vec<(Expr, OrderDir)>,
163}
164
165impl Select {
166 pub fn from_table(table: impl Into<String>) -> SelectBuilder {
168 SelectBuilder {
169 table: table.into(),
170 items: Vec::new(),
171 joins: Vec::new(),
172 filter: None,
173 order_by: Vec::new(),
174 take: None,
175 skip: None,
176 group_by: Vec::new(),
177 distinct: Vec::new(),
178 having: None,
179 order_by_items: Vec::new(),
180 order_by_exprs: Vec::new(),
181 }
182 }
183}
184
185#[derive(Debug, Clone)]
187pub struct SelectBuilder {
188 table: String,
189 items: Vec<SelectItem>,
190 joins: Vec<JoinClause>,
191 filter: Option<Expr>,
192 order_by: Vec<OrderBy>,
193 take: Option<i32>,
194 skip: Option<u32>,
195 group_by: Vec<ColumnMarker>,
196 distinct: Vec<String>,
197 having: Option<Expr>,
198 order_by_items: Vec<OrderByItem>,
199 order_by_exprs: Vec<(Expr, OrderDir)>,
200}
201
202impl SelectBuilder {
203 #[must_use]
205 pub fn items(mut self, items: Vec<SelectItem>) -> Self {
206 self.items = items;
207 self
208 }
209
210 #[must_use]
212 pub fn item(mut self, item: SelectItem) -> Self {
213 self.items.push(item);
214 self
215 }
216
217 #[must_use]
219 pub fn computed(mut self, expr: Expr, alias: impl Into<String>) -> Self {
220 self.items.push(SelectItem::computed(expr, alias));
221 self
222 }
223
224 #[must_use]
226 pub fn filter(mut self, expr: Expr) -> Self {
227 self.filter = Some(expr);
228 self
229 }
230
231 #[must_use]
233 pub fn order_by(mut self, column: impl Into<String>, direction: OrderDir) -> Self {
234 let order = OrderBy::new(column, direction);
235 self.order_by.push(order.clone());
236 self.order_by_items.push(OrderByItem::Column(order));
237 self
238 }
239
240 #[must_use]
242 pub fn order_by_asc(mut self, column: impl Into<String>) -> Self {
243 let order = OrderBy::asc(column);
244 self.order_by.push(order.clone());
245 self.order_by_items.push(OrderByItem::Column(order));
246 self
247 }
248
249 #[must_use]
251 pub fn order_by_desc(mut self, column: impl Into<String>) -> Self {
252 let order = OrderBy::desc(column);
253 self.order_by.push(order.clone());
254 self.order_by_items.push(OrderByItem::Column(order));
255 self
256 }
257
258 #[must_use]
263 pub fn take(mut self, n: i32) -> Self {
264 self.take = Some(n);
265 self
266 }
267
268 #[must_use]
270 pub fn skip(mut self, n: u32) -> Self {
271 self.skip = Some(n);
272 self
273 }
274
275 #[must_use]
277 pub fn join(mut self, clause: JoinClause) -> Self {
278 self.joins.push(clause);
279 self
280 }
281
282 #[must_use]
284 pub fn inner_join(self, table: impl Into<String>, on: Expr, items: Vec<SelectItem>) -> Self {
285 self.join(JoinClause::new(JoinType::Inner, table, on, items))
286 }
287
288 #[must_use]
290 pub fn left_join(self, table: impl Into<String>, on: Expr, items: Vec<SelectItem>) -> Self {
291 self.join(JoinClause::new(JoinType::Left, table, on, items))
292 }
293
294 #[must_use]
296 pub fn group_by_column(mut self, column: ColumnMarker) -> Self {
297 self.group_by.push(column);
298 self
299 }
300
301 #[must_use]
303 pub fn group_by(mut self, columns: Vec<ColumnMarker>) -> Self {
304 self.group_by.extend(columns);
305 self
306 }
307
308 #[must_use]
310 pub fn having(mut self, expr: Expr) -> Self {
311 self.having = Some(expr);
312 self
313 }
314
315 #[must_use]
317 pub fn order_by_expr(mut self, expr: Expr, direction: OrderDir) -> Self {
318 self.order_by_exprs.push((expr.clone(), direction));
319 self.order_by_items.push(OrderByItem::Expr(expr, direction));
320 self
321 }
322
323 #[must_use]
329 pub fn distinct(mut self, columns: Vec<String>) -> Self {
330 self.distinct = columns;
331 self
332 }
333
334 pub fn build(self) -> Result<Select> {
336 if self.table.is_empty() {
337 return Err(Error::MissingField("table".to_string()));
338 }
339
340 Ok(Select {
341 table: self.table,
342 items: self.items,
343 joins: self.joins,
344 filter: self.filter,
345 order_by: self.order_by,
346 take: self.take,
347 skip: self.skip,
348 group_by: self.group_by,
349 distinct: self.distinct,
350 having: self.having,
351 order_by_items: self.order_by_items,
352 order_by_exprs: self.order_by_exprs,
353 })
354 }
355}
356
357#[cfg(test)]
358mod tests {
359 use super::*;
360 use crate::expr::Expr;
361
362 #[test]
363 fn test_order_by() {
364 let asc = OrderBy::asc("id");
365 assert_eq!(asc.column, "id");
366 assert_eq!(asc.direction, OrderDir::Asc);
367
368 let desc = OrderBy::desc("created_at");
369 assert_eq!(desc.column, "created_at");
370 assert_eq!(desc.direction, OrderDir::Desc);
371 }
372
373 #[test]
374 fn test_simple_select() {
375 let query = Select::from_table("users").build().unwrap();
376
377 assert_eq!(query.table, "users");
378 assert!(query.items.is_empty());
379 assert!(query.joins.is_empty());
380 assert!(query.filter.is_none());
381 assert!(query.order_by.is_empty());
382 assert!(query.take.is_none());
383 assert!(query.skip.is_none());
384 }
385
386 #[test]
387 fn test_select_with_columns() {
388 let query = Select::from_table("users")
389 .item(SelectItem::from(ColumnMarker::new("users", "id")))
390 .item(SelectItem::from(ColumnMarker::new("users", "email")))
391 .build()
392 .unwrap();
393
394 assert_eq!(query.items.len(), 2);
395 if let SelectItem::Column(col) = &query.items[0] {
396 assert_eq!(col.table, "users");
397 assert_eq!(col.name, "id");
398 }
399 if let SelectItem::Column(col) = &query.items[1] {
400 assert_eq!(col.table, "users");
401 assert_eq!(col.name, "email");
402 }
403 }
404
405 #[test]
406 fn test_select_with_filter() {
407 let filter = Expr::column("age").ge(Expr::param(18i64));
408 let query = Select::from_table("users")
409 .filter(filter.clone())
410 .build()
411 .unwrap();
412
413 assert_eq!(query.filter, Some(filter));
414 }
415
416 #[test]
417 fn test_select_with_order_by() {
418 let query = Select::from_table("users")
419 .order_by_desc("created_at")
420 .order_by_asc("email")
421 .build()
422 .unwrap();
423
424 assert_eq!(query.order_by.len(), 2);
425 assert_eq!(query.order_by[0].column, "created_at");
426 assert_eq!(query.order_by[0].direction, OrderDir::Desc);
427 assert_eq!(query.order_by[1].column, "email");
428 assert_eq!(query.order_by[1].direction, OrderDir::Asc);
429 }
430
431 #[test]
432 fn test_select_with_take_and_skip() {
433 let query = Select::from_table("users")
434 .take(10)
435 .skip(20)
436 .build()
437 .unwrap();
438
439 assert_eq!(query.take, Some(10));
440 assert_eq!(query.skip, Some(20));
441 }
442
443 #[test]
444 fn test_complex_select() {
445 let filter = Expr::column("age")
446 .ge(Expr::param(18i64))
447 .and(Expr::column("email").like(Expr::param("%@gmail.com")));
448
449 let query = Select::from_table("users")
450 .items(vec![
451 SelectItem::from(ColumnMarker::new("users", "id")),
452 SelectItem::from(ColumnMarker::new("users", "email")),
453 SelectItem::from(ColumnMarker::new("users", "age")),
454 ])
455 .filter(filter)
456 .order_by_desc("id")
457 .take(10)
458 .build()
459 .unwrap();
460
461 assert_eq!(query.table, "users");
462 assert_eq!(query.items.len(), 3);
463 assert!(query.filter.is_some());
464 assert_eq!(query.order_by.len(), 1);
465 assert_eq!(query.take, Some(10));
466 }
467
468 #[test]
469 fn test_select_with_inner_join() {
470 let on = Expr::column("users__id").eq(Expr::column("posts__user_id"));
471 let query = Select::from_table("users")
472 .item(SelectItem::from(ColumnMarker::new("users", "id")))
473 .inner_join(
474 "posts",
475 on.clone(),
476 vec![
477 SelectItem::from(ColumnMarker::new("posts", "id")),
478 SelectItem::from(ColumnMarker::new("posts", "title")),
479 ],
480 )
481 .build()
482 .unwrap();
483
484 assert_eq!(query.joins.len(), 1);
485 assert_eq!(query.joins[0].join_type, JoinType::Inner);
486 assert_eq!(query.joins[0].table, "posts");
487 assert_eq!(query.joins[0].on, on);
488 assert_eq!(query.joins[0].items.len(), 2);
489 }
490
491 #[test]
492 fn test_select_with_left_join() {
493 let on = Expr::column("users__id").eq(Expr::column("posts__user_id"));
494 let query = Select::from_table("users")
495 .item(SelectItem::from(ColumnMarker::new("users", "id")))
496 .left_join(
497 "posts",
498 on,
499 vec![SelectItem::from(ColumnMarker::new("posts", "title"))],
500 )
501 .build()
502 .unwrap();
503
504 assert_eq!(query.joins.len(), 1);
505 assert_eq!(query.joins[0].join_type, JoinType::Left);
506 assert_eq!(query.joins[0].table, "posts");
507 assert_eq!(query.joins[0].items.len(), 1);
508 }
509
510 #[test]
511 fn test_select_with_multiple_joins() {
512 let query = Select::from_table("users")
513 .inner_join(
514 "posts",
515 Expr::column("users__id").eq(Expr::column("posts__user_id")),
516 vec![SelectItem::from(ColumnMarker::new("posts", "title"))],
517 )
518 .left_join(
519 "comments",
520 Expr::column("posts__id").eq(Expr::column("comments__post_id")),
521 vec![SelectItem::from(ColumnMarker::new("comments", "body"))],
522 )
523 .build()
524 .unwrap();
525
526 assert_eq!(query.joins.len(), 2);
527 assert_eq!(query.joins[0].join_type, JoinType::Inner);
528 assert_eq!(query.joins[0].table, "posts");
529 assert_eq!(query.joins[1].join_type, JoinType::Left);
530 assert_eq!(query.joins[1].table, "comments");
531 }
532}