1use crate::common::PostgresSchemaType;
2use crate::helpers;
3use crate::traits::PostgresTable;
4use crate::values::PostgresValue;
5use drizzle_core::ToSQL;
6use drizzle_core::traits::SQLTable;
7use paste::paste;
8use std::fmt::Debug;
9use std::marker::PhantomData;
10
11use super::ExecutableState;
13
14#[derive(Debug, Clone, Copy, Default)]
20pub struct SelectInitial;
21
22impl SelectInitial {
23 #[inline]
25 pub const fn new() -> Self {
26 Self
27 }
28}
29
30#[derive(Debug, Clone, Copy, Default)]
32pub struct SelectFromSet;
33
34#[derive(Debug, Clone, Copy, Default)]
36pub struct SelectJoinSet;
37
38#[derive(Debug, Clone, Copy, Default)]
40pub struct SelectWhereSet;
41
42#[derive(Debug, Clone, Copy, Default)]
44pub struct SelectGroupSet;
45
46#[derive(Debug, Clone, Copy, Default)]
48pub struct SelectOrderSet;
49
50#[derive(Debug, Clone, Copy, Default)]
52pub struct SelectLimitSet;
53
54#[derive(Debug, Clone, Copy, Default)]
56pub struct SelectOffsetSet;
57
58#[derive(Debug, Clone, Copy, Default)]
60pub struct SelectSetOpSet;
61
62impl SelectFromSet {
64 #[inline]
65 pub const fn new() -> Self {
66 Self
67 }
68}
69impl SelectJoinSet {
70 #[inline]
71 pub const fn new() -> Self {
72 Self
73 }
74}
75impl SelectWhereSet {
76 #[inline]
77 pub const fn new() -> Self {
78 Self
79 }
80}
81impl SelectGroupSet {
82 #[inline]
83 pub const fn new() -> Self {
84 Self
85 }
86}
87impl SelectOrderSet {
88 #[inline]
89 pub const fn new() -> Self {
90 Self
91 }
92}
93impl SelectLimitSet {
94 #[inline]
95 pub const fn new() -> Self {
96 Self
97 }
98}
99impl SelectOffsetSet {
100 #[inline]
101 pub const fn new() -> Self {
102 Self
103 }
104}
105impl SelectSetOpSet {
106 #[inline]
107 pub const fn new() -> Self {
108 Self
109 }
110}
111
112#[doc(hidden)]
113macro_rules! join_impl {
114 () => {
115 join_impl!(natural);
116 join_impl!(natural_left);
117 join_impl!(left);
118 join_impl!(left_outer);
119 join_impl!(natural_left_outer);
120 join_impl!(natural_right);
121 join_impl!(right);
122 join_impl!(right_outer);
123 join_impl!(natural_right_outer);
124 join_impl!(natural_full);
125 join_impl!(full);
126 join_impl!(full_outer);
127 join_impl!(natural_full_outer);
128 join_impl!(inner);
129 join_impl!(cross);
130
131 join_using_impl!(left);
133 join_using_impl!(left_outer);
134 join_using_impl!(right);
135 join_using_impl!(right_outer);
136 join_using_impl!(full);
137 join_using_impl!(full_outer);
138 join_using_impl!(inner);
139 join_using_impl!(); };
141 ($type:ident) => {
142 paste! {
143 pub fn [<$type _join>]<U: PostgresTable<'a>>(
145 self,
146 table: U,
147 condition: impl ToSQL<'a, PostgresValue<'a>>,
148 ) -> SelectBuilder<'a, S, SelectJoinSet, T> {
149 SelectBuilder {
150 sql: self.sql.append(helpers::[<$type _join>](table, condition)),
151 schema: PhantomData,
152 state: PhantomData,
153 table: PhantomData,
154 }
155 }
156 }
157 };
158}
159
160macro_rules! join_using_impl {
161 () => {
162 pub fn join_using<U: PostgresTable<'a>>(
164 self,
165 table: U,
166 columns: impl ToSQL<'a, PostgresValue<'a>>,
167 ) -> SelectBuilder<'a, S, SelectJoinSet, T> {
168 SelectBuilder {
169 sql: self.sql.append(helpers::join_using(table, columns)),
170 schema: PhantomData,
171 state: PhantomData,
172 table: PhantomData,
173 }
174 }
175 };
176 ($type:ident) => {
177 paste! {
178 pub fn [<$type _join_using>]<U: PostgresTable<'a>>(
180 self,
181 table: U,
182 columns: impl ToSQL<'a, PostgresValue<'a>>,
183 ) -> SelectBuilder<'a, S, SelectJoinSet, T> {
184 SelectBuilder {
185 sql: self.sql.append(helpers::[<$type _join_using>](table, columns)),
186 schema: PhantomData,
187 state: PhantomData,
188 table: PhantomData,
189 }
190 }
191 }
192 };
193}
194
195impl ExecutableState for SelectFromSet {}
197impl ExecutableState for SelectWhereSet {}
198impl ExecutableState for SelectLimitSet {}
199impl ExecutableState for SelectOffsetSet {}
200impl ExecutableState for SelectOrderSet {}
201impl ExecutableState for SelectGroupSet {}
202impl ExecutableState for SelectJoinSet {}
203impl ExecutableState for SelectSetOpSet {}
204
205pub type SelectBuilder<'a, Schema, State, Table = ()> =
211 super::QueryBuilder<'a, Schema, State, Table>;
212
213impl<'a, S> SelectBuilder<'a, S, SelectInitial> {
218 #[inline]
220 pub fn from<T>(self, query: T) -> SelectBuilder<'a, S, SelectFromSet, T>
221 where
222 T: ToSQL<'a, PostgresValue<'a>>,
223 {
224 SelectBuilder {
225 sql: self.sql.append(helpers::from(query)),
226 schema: PhantomData,
227 state: PhantomData,
228 table: PhantomData,
229 }
230 }
231}
232
233impl<'a, S, T> SelectBuilder<'a, S, SelectFromSet, T> {
238 #[inline]
240 pub fn join<U: PostgresTable<'a>>(
241 self,
242 table: U,
243 condition: impl ToSQL<'a, PostgresValue<'a>>,
244 ) -> SelectBuilder<'a, S, SelectJoinSet, T> {
245 SelectBuilder {
246 sql: self.sql.append(helpers::join(table, condition)),
247 schema: PhantomData,
248 state: PhantomData,
249 table: PhantomData,
250 }
251 }
252
253 join_impl!();
254
255 #[inline]
256 pub fn r#where(
257 self,
258 condition: impl ToSQL<'a, PostgresValue<'a>>,
259 ) -> SelectBuilder<'a, S, SelectWhereSet, T> {
260 SelectBuilder {
261 sql: self.sql.append(helpers::r#where(condition)),
262 schema: PhantomData,
263 state: PhantomData,
264 table: PhantomData,
265 }
266 }
267
268 pub fn group_by(
270 self,
271 expressions: impl IntoIterator<Item = impl ToSQL<'a, PostgresValue<'a>>>,
272 ) -> SelectBuilder<'a, S, SelectGroupSet, T> {
273 SelectBuilder {
274 sql: self.sql.append(helpers::group_by(expressions)),
275 schema: PhantomData,
276 state: PhantomData,
277 table: PhantomData,
278 }
279 }
280
281 #[inline]
283 pub fn limit(self, limit: usize) -> SelectBuilder<'a, S, SelectLimitSet, T> {
284 SelectBuilder {
285 sql: self.sql.append(helpers::limit(limit)),
286 schema: PhantomData,
287 state: PhantomData,
288 table: PhantomData,
289 }
290 }
291
292 #[inline]
294 pub fn offset(self, offset: usize) -> SelectBuilder<'a, S, SelectOffsetSet, T> {
295 SelectBuilder {
296 sql: self.sql.append(helpers::offset(offset)),
297 schema: PhantomData,
298 state: PhantomData,
299 table: PhantomData,
300 }
301 }
302
303 #[inline]
305 pub fn order_by<TOrderBy>(
306 self,
307 expressions: TOrderBy,
308 ) -> SelectBuilder<'a, S, SelectOrderSet, T>
309 where
310 TOrderBy: ToSQL<'a, PostgresValue<'a>>,
311 {
312 SelectBuilder {
313 sql: self.sql.append(helpers::order_by(expressions)),
314 schema: PhantomData,
315 state: PhantomData,
316 table: PhantomData,
317 }
318 }
319
320 #[inline]
322 pub fn as_cte(
323 self,
324 name: &'static str,
325 ) -> super::CTEView<'a, <T as SQLTable<'a, PostgresSchemaType, PostgresValue<'a>>>::Aliased, Self>
326 where
327 T: SQLTable<'a, PostgresSchemaType, PostgresValue<'a>>,
328 {
329 super::CTEView::new(
330 <T as SQLTable<'a, PostgresSchemaType, PostgresValue<'a>>>::alias(name),
331 name,
332 self,
333 )
334 }
335}
336
337impl<'a, S, T> SelectBuilder<'a, S, SelectJoinSet, T> {
342 #[inline]
344 pub fn r#where(
345 self,
346 condition: impl ToSQL<'a, PostgresValue<'a>>,
347 ) -> SelectBuilder<'a, S, SelectWhereSet, T> {
348 SelectBuilder {
349 sql: self.sql.append(crate::helpers::r#where(condition)),
350 schema: PhantomData,
351 state: PhantomData,
352 table: PhantomData,
353 }
354 }
355 #[inline]
357 pub fn order_by<TOrderBy>(
358 self,
359 expressions: TOrderBy,
360 ) -> SelectBuilder<'a, S, SelectOrderSet, T>
361 where
362 TOrderBy: ToSQL<'a, PostgresValue<'a>>,
363 {
364 SelectBuilder {
365 sql: self.sql.append(helpers::order_by(expressions)),
366 schema: PhantomData,
367 state: PhantomData,
368 table: PhantomData,
369 }
370 }
371 #[inline]
373 pub fn join<U: PostgresTable<'a>>(
374 self,
375 table: U,
376 condition: impl ToSQL<'a, PostgresValue<'a>>,
377 ) -> SelectBuilder<'a, S, SelectJoinSet, T> {
378 SelectBuilder {
379 sql: self.sql.append(helpers::join(table, condition)),
380 schema: PhantomData,
381 state: PhantomData,
382 table: PhantomData,
383 }
384 }
385 join_impl!();
386}
387
388impl<'a, S, T> SelectBuilder<'a, S, SelectJoinSet, T>
389where
390 T: SQLTable<'a, PostgresSchemaType, PostgresValue<'a>>,
391{
392 #[inline]
394 pub fn as_cte(
395 self,
396 name: &'static str,
397 ) -> super::CTEView<'a, <T as SQLTable<'a, PostgresSchemaType, PostgresValue<'a>>>::Aliased, Self>
398 {
399 super::CTEView::new(
400 <T as SQLTable<'a, PostgresSchemaType, PostgresValue<'a>>>::alias(name),
401 name,
402 self,
403 )
404 }
405}
406
407impl<'a, S, T> SelectBuilder<'a, S, SelectWhereSet, T> {
412 pub fn group_by(
414 self,
415 expressions: impl IntoIterator<Item = impl ToSQL<'a, PostgresValue<'a>>>,
416 ) -> SelectBuilder<'a, S, SelectGroupSet, T> {
417 SelectBuilder {
418 sql: self.sql.append(helpers::group_by(expressions)),
419 schema: PhantomData,
420 state: PhantomData,
421 table: PhantomData,
422 }
423 }
424
425 pub fn order_by<TOrderBy>(
427 self,
428 expressions: TOrderBy,
429 ) -> SelectBuilder<'a, S, SelectOrderSet, T>
430 where
431 TOrderBy: ToSQL<'a, PostgresValue<'a>>,
432 {
433 SelectBuilder {
434 sql: self.sql.append(helpers::order_by(expressions)),
435 schema: PhantomData,
436 state: PhantomData,
437 table: PhantomData,
438 }
439 }
440
441 pub fn limit(self, limit: usize) -> SelectBuilder<'a, S, SelectLimitSet, T> {
443 SelectBuilder {
444 sql: self.sql.append(helpers::limit(limit)),
445 schema: PhantomData,
446 state: PhantomData,
447 table: PhantomData,
448 }
449 }
450}
451
452impl<'a, S, T> SelectBuilder<'a, S, SelectWhereSet, T>
453where
454 T: SQLTable<'a, PostgresSchemaType, PostgresValue<'a>>,
455{
456 #[inline]
458 pub fn as_cte(
459 self,
460 name: &'static str,
461 ) -> super::CTEView<'a, <T as SQLTable<'a, PostgresSchemaType, PostgresValue<'a>>>::Aliased, Self>
462 {
463 super::CTEView::new(
464 <T as SQLTable<'a, PostgresSchemaType, PostgresValue<'a>>>::alias(name),
465 name,
466 self,
467 )
468 }
469}
470
471impl<'a, S, T> SelectBuilder<'a, S, SelectGroupSet, T> {
476 pub fn having(
478 self,
479 condition: impl ToSQL<'a, PostgresValue<'a>>,
480 ) -> SelectBuilder<'a, S, SelectGroupSet, T> {
481 SelectBuilder {
482 sql: self.sql.append(helpers::having(condition)),
483 schema: PhantomData,
484 state: PhantomData,
485 table: PhantomData,
486 }
487 }
488
489 pub fn order_by<TOrderBy>(
491 self,
492 expressions: TOrderBy,
493 ) -> SelectBuilder<'a, S, SelectOrderSet, T>
494 where
495 TOrderBy: ToSQL<'a, PostgresValue<'a>>,
496 {
497 SelectBuilder {
498 sql: self.sql.append(helpers::order_by(expressions)),
499 schema: PhantomData,
500 state: PhantomData,
501 table: PhantomData,
502 }
503 }
504}
505
506impl<'a, S, T> SelectBuilder<'a, S, SelectGroupSet, T>
507where
508 T: SQLTable<'a, PostgresSchemaType, PostgresValue<'a>>,
509{
510 #[inline]
512 pub fn as_cte(
513 self,
514 name: &'static str,
515 ) -> super::CTEView<'a, <T as SQLTable<'a, PostgresSchemaType, PostgresValue<'a>>>::Aliased, Self>
516 {
517 super::CTEView::new(
518 <T as SQLTable<'a, PostgresSchemaType, PostgresValue<'a>>>::alias(name),
519 name,
520 self,
521 )
522 }
523}
524
525impl<'a, S, T> SelectBuilder<'a, S, SelectOrderSet, T> {
530 pub fn limit(self, limit: usize) -> SelectBuilder<'a, S, SelectLimitSet, T> {
532 SelectBuilder {
533 sql: self.sql.append(helpers::limit(limit)),
534 schema: PhantomData,
535 state: PhantomData,
536 table: PhantomData,
537 }
538 }
539}
540
541impl<'a, S, T> SelectBuilder<'a, S, SelectOrderSet, T>
542where
543 T: SQLTable<'a, PostgresSchemaType, PostgresValue<'a>>,
544{
545 #[inline]
547 pub fn as_cte(
548 self,
549 name: &'static str,
550 ) -> super::CTEView<'a, <T as SQLTable<'a, PostgresSchemaType, PostgresValue<'a>>>::Aliased, Self>
551 {
552 super::CTEView::new(
553 <T as SQLTable<'a, PostgresSchemaType, PostgresValue<'a>>>::alias(name),
554 name,
555 self,
556 )
557 }
558}
559
560impl<'a, S, T> SelectBuilder<'a, S, SelectLimitSet, T> {
565 pub fn offset(self, offset: usize) -> SelectBuilder<'a, S, SelectOffsetSet, T> {
567 SelectBuilder {
568 sql: self.sql.append(helpers::offset(offset)),
569 schema: PhantomData,
570 state: PhantomData,
571 table: PhantomData,
572 }
573 }
574}
575
576impl<'a, S, T> SelectBuilder<'a, S, SelectLimitSet, T>
577where
578 T: SQLTable<'a, PostgresSchemaType, PostgresValue<'a>>,
579{
580 #[inline]
582 pub fn as_cte(
583 self,
584 name: &'static str,
585 ) -> super::CTEView<'a, <T as SQLTable<'a, PostgresSchemaType, PostgresValue<'a>>>::Aliased, Self>
586 {
587 super::CTEView::new(
588 <T as SQLTable<'a, PostgresSchemaType, PostgresValue<'a>>>::alias(name),
589 name,
590 self,
591 )
592 }
593}
594
595impl<'a, S, T> SelectBuilder<'a, S, SelectOffsetSet, T>
596where
597 T: SQLTable<'a, PostgresSchemaType, PostgresValue<'a>>,
598{
599 #[inline]
601 pub fn as_cte(
602 self,
603 name: &'static str,
604 ) -> super::CTEView<'a, <T as SQLTable<'a, PostgresSchemaType, PostgresValue<'a>>>::Aliased, Self>
605 {
606 super::CTEView::new(
607 <T as SQLTable<'a, PostgresSchemaType, PostgresValue<'a>>>::alias(name),
608 name,
609 self,
610 )
611 }
612}
613
614impl<'a, S, State, T> SelectBuilder<'a, S, State, T>
619where
620 State: ExecutableState,
621{
622 pub fn union(
624 self,
625 other: impl ToSQL<'a, PostgresValue<'a>>,
626 ) -> SelectBuilder<'a, S, SelectSetOpSet, T> {
627 SelectBuilder {
628 sql: helpers::union(self.sql, other),
629 schema: PhantomData,
630 state: PhantomData,
631 table: PhantomData,
632 }
633 }
634
635 pub fn union_all(
637 self,
638 other: impl ToSQL<'a, PostgresValue<'a>>,
639 ) -> SelectBuilder<'a, S, SelectSetOpSet, T> {
640 SelectBuilder {
641 sql: helpers::union_all(self.sql, other),
642 schema: PhantomData,
643 state: PhantomData,
644 table: PhantomData,
645 }
646 }
647
648 pub fn intersect(
650 self,
651 other: impl ToSQL<'a, PostgresValue<'a>>,
652 ) -> SelectBuilder<'a, S, SelectSetOpSet, T> {
653 SelectBuilder {
654 sql: helpers::intersect(self.sql, other),
655 schema: PhantomData,
656 state: PhantomData,
657 table: PhantomData,
658 }
659 }
660
661 pub fn intersect_all(
663 self,
664 other: impl ToSQL<'a, PostgresValue<'a>>,
665 ) -> SelectBuilder<'a, S, SelectSetOpSet, T> {
666 SelectBuilder {
667 sql: helpers::intersect_all(self.sql, other),
668 schema: PhantomData,
669 state: PhantomData,
670 table: PhantomData,
671 }
672 }
673
674 pub fn except(
676 self,
677 other: impl ToSQL<'a, PostgresValue<'a>>,
678 ) -> SelectBuilder<'a, S, SelectSetOpSet, T> {
679 SelectBuilder {
680 sql: helpers::except(self.sql, other),
681 schema: PhantomData,
682 state: PhantomData,
683 table: PhantomData,
684 }
685 }
686
687 pub fn except_all(
689 self,
690 other: impl ToSQL<'a, PostgresValue<'a>>,
691 ) -> SelectBuilder<'a, S, SelectSetOpSet, T> {
692 SelectBuilder {
693 sql: helpers::except_all(self.sql, other),
694 schema: PhantomData,
695 state: PhantomData,
696 table: PhantomData,
697 }
698 }
699}
700
701impl<'a, S, T> SelectBuilder<'a, S, SelectSetOpSet, T> {
702 pub fn order_by<TOrderBy>(
704 self,
705 expressions: TOrderBy,
706 ) -> SelectBuilder<'a, S, SelectOrderSet, T>
707 where
708 TOrderBy: ToSQL<'a, PostgresValue<'a>>,
709 {
710 SelectBuilder {
711 sql: self.sql.append(helpers::order_by(expressions)),
712 schema: PhantomData,
713 state: PhantomData,
714 table: PhantomData,
715 }
716 }
717
718 pub fn limit(self, limit: usize) -> SelectBuilder<'a, S, SelectLimitSet, T> {
720 SelectBuilder {
721 sql: self.sql.append(helpers::limit(limit)),
722 schema: PhantomData,
723 state: PhantomData,
724 table: PhantomData,
725 }
726 }
727
728 pub fn offset(self, offset: usize) -> SelectBuilder<'a, S, SelectOffsetSet, T> {
730 SelectBuilder {
731 sql: self.sql.append(helpers::offset(offset)),
732 schema: PhantomData,
733 state: PhantomData,
734 table: PhantomData,
735 }
736 }
737}
738
739#[cfg(test)]
740mod tests {
741 use super::*;
742 use drizzle_core::{SQL, ToSQL};
743
744 #[test]
745 fn test_select_builder_creation() {
746 let builder = SelectBuilder::<(), SelectInitial> {
747 sql: SQL::raw("SELECT *"),
748 schema: PhantomData,
749 state: PhantomData,
750 table: PhantomData,
751 };
752
753 assert_eq!(builder.to_sql().sql(), "SELECT *");
754 }
755}