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, Copy, PartialEq, Eq)]
82pub enum JoinType {
83 Inner,
85 Left,
87}
88
89#[derive(Debug, Clone, PartialEq)]
91pub struct JoinClause {
92 pub join_type: JoinType,
94 pub table: String,
96 pub on: Expr,
98 pub items: Vec<SelectItem>,
100}
101
102impl JoinClause {
103 pub fn new(
105 join_type: JoinType,
106 table: impl Into<String>,
107 on: Expr,
108 items: Vec<SelectItem>,
109 ) -> Self {
110 Self {
111 join_type,
112 table: table.into(),
113 on,
114 items,
115 }
116 }
117}
118
119#[derive(Debug, Clone, PartialEq)]
121pub struct Select {
122 pub table: String,
124 pub items: Vec<SelectItem>,
126 pub joins: Vec<JoinClause>,
128 pub filter: Option<Expr>,
130 pub order_by: Vec<OrderBy>,
132 pub take: Option<i32>,
138 pub skip: Option<u32>,
140 pub group_by: Vec<ColumnMarker>,
142 pub distinct: Vec<String>,
148 pub having: Option<Expr>,
150 pub order_by_exprs: Vec<(Expr, OrderDir)>,
152}
153
154impl Select {
155 pub fn from_table(table: impl Into<String>) -> SelectBuilder {
157 SelectBuilder {
158 table: table.into(),
159 items: Vec::new(),
160 joins: Vec::new(),
161 filter: None,
162 order_by: Vec::new(),
163 take: None,
164 skip: None,
165 group_by: Vec::new(),
166 distinct: Vec::new(),
167 having: None,
168 order_by_exprs: Vec::new(),
169 }
170 }
171}
172
173#[derive(Debug, Clone)]
175pub struct SelectBuilder {
176 table: String,
177 items: Vec<SelectItem>,
178 joins: Vec<JoinClause>,
179 filter: Option<Expr>,
180 order_by: Vec<OrderBy>,
181 take: Option<i32>,
182 skip: Option<u32>,
183 group_by: Vec<ColumnMarker>,
184 distinct: Vec<String>,
185 having: Option<Expr>,
186 order_by_exprs: Vec<(Expr, OrderDir)>,
187}
188
189impl SelectBuilder {
190 #[must_use]
192 pub fn items(mut self, items: Vec<SelectItem>) -> Self {
193 self.items = items;
194 self
195 }
196
197 #[must_use]
199 pub fn item(mut self, item: SelectItem) -> Self {
200 self.items.push(item);
201 self
202 }
203
204 #[must_use]
206 pub fn computed(mut self, expr: Expr, alias: impl Into<String>) -> Self {
207 self.items.push(SelectItem::computed(expr, alias));
208 self
209 }
210
211 #[must_use]
213 pub fn filter(mut self, expr: Expr) -> Self {
214 self.filter = Some(expr);
215 self
216 }
217
218 #[must_use]
220 pub fn order_by(mut self, column: impl Into<String>, direction: OrderDir) -> Self {
221 self.order_by.push(OrderBy::new(column, direction));
222 self
223 }
224
225 #[must_use]
227 pub fn order_by_asc(mut self, column: impl Into<String>) -> Self {
228 self.order_by.push(OrderBy::asc(column));
229 self
230 }
231
232 #[must_use]
234 pub fn order_by_desc(mut self, column: impl Into<String>) -> Self {
235 self.order_by.push(OrderBy::desc(column));
236 self
237 }
238
239 #[must_use]
244 pub fn take(mut self, n: i32) -> Self {
245 self.take = Some(n);
246 self
247 }
248
249 #[must_use]
251 pub fn skip(mut self, n: u32) -> Self {
252 self.skip = Some(n);
253 self
254 }
255
256 #[must_use]
258 pub fn join(mut self, clause: JoinClause) -> Self {
259 self.joins.push(clause);
260 self
261 }
262
263 #[must_use]
265 pub fn inner_join(self, table: impl Into<String>, on: Expr, items: Vec<SelectItem>) -> Self {
266 self.join(JoinClause::new(JoinType::Inner, table, on, items))
267 }
268
269 #[must_use]
271 pub fn left_join(self, table: impl Into<String>, on: Expr, items: Vec<SelectItem>) -> Self {
272 self.join(JoinClause::new(JoinType::Left, table, on, items))
273 }
274
275 #[must_use]
277 pub fn group_by_column(mut self, column: ColumnMarker) -> Self {
278 self.group_by.push(column);
279 self
280 }
281
282 #[must_use]
284 pub fn group_by(mut self, columns: Vec<ColumnMarker>) -> Self {
285 self.group_by.extend(columns);
286 self
287 }
288
289 #[must_use]
291 pub fn having(mut self, expr: Expr) -> Self {
292 self.having = Some(expr);
293 self
294 }
295
296 #[must_use]
298 pub fn order_by_expr(mut self, expr: Expr, direction: OrderDir) -> Self {
299 self.order_by_exprs.push((expr, direction));
300 self
301 }
302
303 #[must_use]
309 pub fn distinct(mut self, columns: Vec<String>) -> Self {
310 self.distinct = columns;
311 self
312 }
313
314 pub fn build(self) -> Result<Select> {
316 if self.table.is_empty() {
317 return Err(Error::MissingField("table".to_string()));
318 }
319
320 Ok(Select {
321 table: self.table,
322 items: self.items,
323 joins: self.joins,
324 filter: self.filter,
325 order_by: self.order_by,
326 take: self.take,
327 skip: self.skip,
328 group_by: self.group_by,
329 distinct: self.distinct,
330 having: self.having,
331 order_by_exprs: self.order_by_exprs,
332 })
333 }
334}
335
336#[cfg(test)]
337mod tests {
338 use super::*;
339 use crate::expr::Expr;
340
341 #[test]
342 fn test_order_by() {
343 let asc = OrderBy::asc("id");
344 assert_eq!(asc.column, "id");
345 assert_eq!(asc.direction, OrderDir::Asc);
346
347 let desc = OrderBy::desc("created_at");
348 assert_eq!(desc.column, "created_at");
349 assert_eq!(desc.direction, OrderDir::Desc);
350 }
351
352 #[test]
353 fn test_simple_select() {
354 let query = Select::from_table("users").build().unwrap();
355
356 assert_eq!(query.table, "users");
357 assert!(query.items.is_empty());
358 assert!(query.joins.is_empty());
359 assert!(query.filter.is_none());
360 assert!(query.order_by.is_empty());
361 assert!(query.take.is_none());
362 assert!(query.skip.is_none());
363 }
364
365 #[test]
366 fn test_select_with_columns() {
367 let query = Select::from_table("users")
368 .item(SelectItem::from(ColumnMarker::new("users", "id")))
369 .item(SelectItem::from(ColumnMarker::new("users", "email")))
370 .build()
371 .unwrap();
372
373 assert_eq!(query.items.len(), 2);
374 if let SelectItem::Column(col) = &query.items[0] {
375 assert_eq!(col.table, "users");
376 assert_eq!(col.name, "id");
377 }
378 if let SelectItem::Column(col) = &query.items[1] {
379 assert_eq!(col.table, "users");
380 assert_eq!(col.name, "email");
381 }
382 }
383
384 #[test]
385 fn test_select_with_filter() {
386 let filter = Expr::column("age").ge(Expr::param(18i64));
387 let query = Select::from_table("users")
388 .filter(filter.clone())
389 .build()
390 .unwrap();
391
392 assert_eq!(query.filter, Some(filter));
393 }
394
395 #[test]
396 fn test_select_with_order_by() {
397 let query = Select::from_table("users")
398 .order_by_desc("created_at")
399 .order_by_asc("email")
400 .build()
401 .unwrap();
402
403 assert_eq!(query.order_by.len(), 2);
404 assert_eq!(query.order_by[0].column, "created_at");
405 assert_eq!(query.order_by[0].direction, OrderDir::Desc);
406 assert_eq!(query.order_by[1].column, "email");
407 assert_eq!(query.order_by[1].direction, OrderDir::Asc);
408 }
409
410 #[test]
411 fn test_select_with_take_and_skip() {
412 let query = Select::from_table("users")
413 .take(10)
414 .skip(20)
415 .build()
416 .unwrap();
417
418 assert_eq!(query.take, Some(10));
419 assert_eq!(query.skip, Some(20));
420 }
421
422 #[test]
423 fn test_complex_select() {
424 let filter = Expr::column("age")
425 .ge(Expr::param(18i64))
426 .and(Expr::column("email").like(Expr::param("%@gmail.com")));
427
428 let query = Select::from_table("users")
429 .items(vec![
430 SelectItem::from(ColumnMarker::new("users", "id")),
431 SelectItem::from(ColumnMarker::new("users", "email")),
432 SelectItem::from(ColumnMarker::new("users", "age")),
433 ])
434 .filter(filter)
435 .order_by_desc("id")
436 .take(10)
437 .build()
438 .unwrap();
439
440 assert_eq!(query.table, "users");
441 assert_eq!(query.items.len(), 3);
442 assert!(query.filter.is_some());
443 assert_eq!(query.order_by.len(), 1);
444 assert_eq!(query.take, Some(10));
445 }
446
447 #[test]
448 fn test_select_with_inner_join() {
449 let on = Expr::column("users__id").eq(Expr::column("posts__user_id"));
450 let query = Select::from_table("users")
451 .item(SelectItem::from(ColumnMarker::new("users", "id")))
452 .inner_join(
453 "posts",
454 on.clone(),
455 vec![
456 SelectItem::from(ColumnMarker::new("posts", "id")),
457 SelectItem::from(ColumnMarker::new("posts", "title")),
458 ],
459 )
460 .build()
461 .unwrap();
462
463 assert_eq!(query.joins.len(), 1);
464 assert_eq!(query.joins[0].join_type, JoinType::Inner);
465 assert_eq!(query.joins[0].table, "posts");
466 assert_eq!(query.joins[0].on, on);
467 assert_eq!(query.joins[0].items.len(), 2);
468 }
469
470 #[test]
471 fn test_select_with_left_join() {
472 let on = Expr::column("users__id").eq(Expr::column("posts__user_id"));
473 let query = Select::from_table("users")
474 .item(SelectItem::from(ColumnMarker::new("users", "id")))
475 .left_join(
476 "posts",
477 on,
478 vec![SelectItem::from(ColumnMarker::new("posts", "title"))],
479 )
480 .build()
481 .unwrap();
482
483 assert_eq!(query.joins.len(), 1);
484 assert_eq!(query.joins[0].join_type, JoinType::Left);
485 assert_eq!(query.joins[0].table, "posts");
486 assert_eq!(query.joins[0].items.len(), 1);
487 }
488
489 #[test]
490 fn test_select_with_multiple_joins() {
491 let query = Select::from_table("users")
492 .inner_join(
493 "posts",
494 Expr::column("users__id").eq(Expr::column("posts__user_id")),
495 vec![SelectItem::from(ColumnMarker::new("posts", "title"))],
496 )
497 .left_join(
498 "comments",
499 Expr::column("posts__id").eq(Expr::column("comments__post_id")),
500 vec![SelectItem::from(ColumnMarker::new("comments", "body"))],
501 )
502 .build()
503 .unwrap();
504
505 assert_eq!(query.joins.len(), 2);
506 assert_eq!(query.joins[0].join_type, JoinType::Inner);
507 assert_eq!(query.joins[0].table, "posts");
508 assert_eq!(query.joins[1].join_type, JoinType::Left);
509 assert_eq!(query.joins[1].table, "comments");
510 }
511}