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
62#[derive(Debug, Clone, Copy, Default)]
64pub struct SelectForSet;
65
66impl SelectFromSet {
68 #[inline]
69 pub const fn new() -> Self {
70 Self
71 }
72}
73impl SelectJoinSet {
74 #[inline]
75 pub const fn new() -> Self {
76 Self
77 }
78}
79impl SelectWhereSet {
80 #[inline]
81 pub const fn new() -> Self {
82 Self
83 }
84}
85impl SelectGroupSet {
86 #[inline]
87 pub const fn new() -> Self {
88 Self
89 }
90}
91impl SelectOrderSet {
92 #[inline]
93 pub const fn new() -> Self {
94 Self
95 }
96}
97impl SelectLimitSet {
98 #[inline]
99 pub const fn new() -> Self {
100 Self
101 }
102}
103impl SelectOffsetSet {
104 #[inline]
105 pub const fn new() -> Self {
106 Self
107 }
108}
109impl SelectSetOpSet {
110 #[inline]
111 pub const fn new() -> Self {
112 Self
113 }
114}
115impl SelectForSet {
116 #[inline]
117 pub const fn new() -> Self {
118 Self
119 }
120}
121
122#[doc(hidden)]
123macro_rules! join_impl {
124 () => {
125 join_impl!(natural);
126 join_impl!(natural_left);
127 join_impl!(left);
128 join_impl!(left_outer);
129 join_impl!(natural_left_outer);
130 join_impl!(natural_right);
131 join_impl!(right);
132 join_impl!(right_outer);
133 join_impl!(natural_right_outer);
134 join_impl!(natural_full);
135 join_impl!(full);
136 join_impl!(full_outer);
137 join_impl!(natural_full_outer);
138 join_impl!(inner);
139 join_impl!(cross);
140
141 join_using_impl!(left);
143 join_using_impl!(left_outer);
144 join_using_impl!(right);
145 join_using_impl!(right_outer);
146 join_using_impl!(full);
147 join_using_impl!(full_outer);
148 join_using_impl!(inner);
149 join_using_impl!(); };
151 ($type:ident) => {
152 paste! {
153 pub fn [<$type _join>]<U: PostgresTable<'a>>(
155 self,
156 table: U,
157 condition: impl ToSQL<'a, PostgresValue<'a>>,
158 ) -> SelectBuilder<'a, S, SelectJoinSet, T> {
159 SelectBuilder {
160 sql: self.sql.append(helpers::[<$type _join>](table, condition)),
161 schema: PhantomData,
162 state: PhantomData,
163 table: PhantomData,
164 }
165 }
166 }
167 };
168}
169
170macro_rules! join_using_impl {
171 () => {
172 pub fn join_using<U: PostgresTable<'a>>(
174 self,
175 table: U,
176 columns: impl ToSQL<'a, PostgresValue<'a>>,
177 ) -> SelectBuilder<'a, S, SelectJoinSet, T> {
178 SelectBuilder {
179 sql: self.sql.append(helpers::join_using(table, columns)),
180 schema: PhantomData,
181 state: PhantomData,
182 table: PhantomData,
183 }
184 }
185 };
186 ($type:ident) => {
187 paste! {
188 pub fn [<$type _join_using>]<U: PostgresTable<'a>>(
190 self,
191 table: U,
192 columns: impl ToSQL<'a, PostgresValue<'a>>,
193 ) -> SelectBuilder<'a, S, SelectJoinSet, T> {
194 SelectBuilder {
195 sql: self.sql.append(helpers::[<$type _join_using>](table, columns)),
196 schema: PhantomData,
197 state: PhantomData,
198 table: PhantomData,
199 }
200 }
201 }
202 };
203}
204
205impl ExecutableState for SelectFromSet {}
207impl ExecutableState for SelectWhereSet {}
208impl ExecutableState for SelectLimitSet {}
209impl ExecutableState for SelectOffsetSet {}
210impl ExecutableState for SelectOrderSet {}
211impl ExecutableState for SelectGroupSet {}
212impl ExecutableState for SelectJoinSet {}
213impl ExecutableState for SelectSetOpSet {}
214impl ExecutableState for SelectForSet {}
215
216#[doc(hidden)]
217pub trait AsCteState {}
218
219impl AsCteState for SelectFromSet {}
220impl AsCteState for SelectJoinSet {}
221impl AsCteState for SelectWhereSet {}
222impl AsCteState for SelectGroupSet {}
223impl AsCteState for SelectOrderSet {}
224impl AsCteState for SelectLimitSet {}
225impl AsCteState for SelectOffsetSet {}
226
227pub type SelectBuilder<'a, Schema, State, Table = ()> =
233 super::QueryBuilder<'a, Schema, State, Table>;
234
235impl<'a, S> SelectBuilder<'a, S, SelectInitial> {
240 #[inline]
242 pub fn from<T>(self, query: T) -> SelectBuilder<'a, S, SelectFromSet, T>
243 where
244 T: ToSQL<'a, PostgresValue<'a>>,
245 {
246 SelectBuilder {
247 sql: self.sql.append(helpers::from(query)),
248 schema: PhantomData,
249 state: PhantomData,
250 table: PhantomData,
251 }
252 }
253}
254
255impl<'a, S, T> SelectBuilder<'a, S, SelectFromSet, T> {
260 #[inline]
262 pub fn join<U: PostgresTable<'a>>(
263 self,
264 table: U,
265 condition: impl ToSQL<'a, PostgresValue<'a>>,
266 ) -> SelectBuilder<'a, S, SelectJoinSet, T> {
267 SelectBuilder {
268 sql: self.sql.append(helpers::join(table, condition)),
269 schema: PhantomData,
270 state: PhantomData,
271 table: PhantomData,
272 }
273 }
274
275 join_impl!();
276
277 #[inline]
278 pub fn r#where(
279 self,
280 condition: impl ToSQL<'a, PostgresValue<'a>>,
281 ) -> SelectBuilder<'a, S, SelectWhereSet, T> {
282 SelectBuilder {
283 sql: self.sql.append(helpers::r#where(condition)),
284 schema: PhantomData,
285 state: PhantomData,
286 table: PhantomData,
287 }
288 }
289
290 pub fn group_by(
292 self,
293 expressions: impl IntoIterator<Item = impl ToSQL<'a, PostgresValue<'a>>>,
294 ) -> SelectBuilder<'a, S, SelectGroupSet, T> {
295 SelectBuilder {
296 sql: self.sql.append(helpers::group_by(expressions)),
297 schema: PhantomData,
298 state: PhantomData,
299 table: PhantomData,
300 }
301 }
302
303 #[inline]
305 pub fn limit(self, limit: usize) -> SelectBuilder<'a, S, SelectLimitSet, T> {
306 SelectBuilder {
307 sql: self.sql.append(helpers::limit(limit)),
308 schema: PhantomData,
309 state: PhantomData,
310 table: PhantomData,
311 }
312 }
313
314 #[inline]
316 pub fn offset(self, offset: usize) -> SelectBuilder<'a, S, SelectOffsetSet, T> {
317 SelectBuilder {
318 sql: self.sql.append(helpers::offset(offset)),
319 schema: PhantomData,
320 state: PhantomData,
321 table: PhantomData,
322 }
323 }
324
325 #[inline]
327 pub fn order_by<TOrderBy>(
328 self,
329 expressions: TOrderBy,
330 ) -> SelectBuilder<'a, S, SelectOrderSet, T>
331 where
332 TOrderBy: ToSQL<'a, PostgresValue<'a>>,
333 {
334 SelectBuilder {
335 sql: self.sql.append(helpers::order_by(expressions)),
336 schema: PhantomData,
337 state: PhantomData,
338 table: PhantomData,
339 }
340 }
341}
342
343impl<'a, S, T> SelectBuilder<'a, S, SelectJoinSet, T> {
348 #[inline]
350 pub fn r#where(
351 self,
352 condition: impl ToSQL<'a, PostgresValue<'a>>,
353 ) -> SelectBuilder<'a, S, SelectWhereSet, T> {
354 SelectBuilder {
355 sql: self.sql.append(crate::helpers::r#where(condition)),
356 schema: PhantomData,
357 state: PhantomData,
358 table: PhantomData,
359 }
360 }
361 #[inline]
363 pub fn order_by<TOrderBy>(
364 self,
365 expressions: TOrderBy,
366 ) -> SelectBuilder<'a, S, SelectOrderSet, T>
367 where
368 TOrderBy: ToSQL<'a, PostgresValue<'a>>,
369 {
370 SelectBuilder {
371 sql: self.sql.append(helpers::order_by(expressions)),
372 schema: PhantomData,
373 state: PhantomData,
374 table: PhantomData,
375 }
376 }
377 #[inline]
379 pub fn join<U: PostgresTable<'a>>(
380 self,
381 table: U,
382 condition: impl ToSQL<'a, PostgresValue<'a>>,
383 ) -> SelectBuilder<'a, S, SelectJoinSet, T> {
384 SelectBuilder {
385 sql: self.sql.append(helpers::join(table, condition)),
386 schema: PhantomData,
387 state: PhantomData,
388 table: PhantomData,
389 }
390 }
391 join_impl!();
392}
393
394impl<'a, S, T> SelectBuilder<'a, S, SelectWhereSet, T> {
399 pub fn group_by(
401 self,
402 expressions: impl IntoIterator<Item = impl ToSQL<'a, PostgresValue<'a>>>,
403 ) -> SelectBuilder<'a, S, SelectGroupSet, T> {
404 SelectBuilder {
405 sql: self.sql.append(helpers::group_by(expressions)),
406 schema: PhantomData,
407 state: PhantomData,
408 table: PhantomData,
409 }
410 }
411
412 pub fn order_by<TOrderBy>(
414 self,
415 expressions: TOrderBy,
416 ) -> SelectBuilder<'a, S, SelectOrderSet, T>
417 where
418 TOrderBy: ToSQL<'a, PostgresValue<'a>>,
419 {
420 SelectBuilder {
421 sql: self.sql.append(helpers::order_by(expressions)),
422 schema: PhantomData,
423 state: PhantomData,
424 table: PhantomData,
425 }
426 }
427
428 pub fn limit(self, limit: usize) -> SelectBuilder<'a, S, SelectLimitSet, T> {
430 SelectBuilder {
431 sql: self.sql.append(helpers::limit(limit)),
432 schema: PhantomData,
433 state: PhantomData,
434 table: PhantomData,
435 }
436 }
437}
438
439impl<'a, S, T> SelectBuilder<'a, S, SelectGroupSet, T> {
444 pub fn having(
446 self,
447 condition: impl ToSQL<'a, PostgresValue<'a>>,
448 ) -> SelectBuilder<'a, S, SelectGroupSet, T> {
449 SelectBuilder {
450 sql: self.sql.append(helpers::having(condition)),
451 schema: PhantomData,
452 state: PhantomData,
453 table: PhantomData,
454 }
455 }
456
457 pub fn order_by<TOrderBy>(
459 self,
460 expressions: TOrderBy,
461 ) -> SelectBuilder<'a, S, SelectOrderSet, T>
462 where
463 TOrderBy: ToSQL<'a, PostgresValue<'a>>,
464 {
465 SelectBuilder {
466 sql: self.sql.append(helpers::order_by(expressions)),
467 schema: PhantomData,
468 state: PhantomData,
469 table: PhantomData,
470 }
471 }
472}
473
474impl<'a, S, T> SelectBuilder<'a, S, SelectOrderSet, T> {
479 pub fn limit(self, limit: usize) -> SelectBuilder<'a, S, SelectLimitSet, T> {
481 SelectBuilder {
482 sql: self.sql.append(helpers::limit(limit)),
483 schema: PhantomData,
484 state: PhantomData,
485 table: PhantomData,
486 }
487 }
488}
489
490impl<'a, S, T> SelectBuilder<'a, S, SelectLimitSet, T> {
495 pub fn offset(self, offset: usize) -> SelectBuilder<'a, S, SelectOffsetSet, T> {
497 SelectBuilder {
498 sql: self.sql.append(helpers::offset(offset)),
499 schema: PhantomData,
500 state: PhantomData,
501 table: PhantomData,
502 }
503 }
504}
505
506impl<'a, S, State, T> SelectBuilder<'a, S, State, T>
507where
508 State: AsCteState,
509 T: SQLTable<'a, PostgresSchemaType, PostgresValue<'a>>,
510{
511 #[inline]
513 pub fn into_cte(
514 self,
515 name: &'static str,
516 ) -> super::CTEView<'a, <T as SQLTable<'a, PostgresSchemaType, PostgresValue<'a>>>::Aliased, Self>
517 {
518 super::CTEView::new(
519 <T as SQLTable<'a, PostgresSchemaType, PostgresValue<'a>>>::alias(name),
520 name,
521 self,
522 )
523 }
524}
525
526impl<'a, S, State, T> SelectBuilder<'a, S, State, T>
531where
532 State: ExecutableState,
533{
534 pub fn union(
536 self,
537 other: impl ToSQL<'a, PostgresValue<'a>>,
538 ) -> SelectBuilder<'a, S, SelectSetOpSet, T> {
539 SelectBuilder {
540 sql: helpers::union(self.sql, other),
541 schema: PhantomData,
542 state: PhantomData,
543 table: PhantomData,
544 }
545 }
546
547 pub fn union_all(
549 self,
550 other: impl ToSQL<'a, PostgresValue<'a>>,
551 ) -> SelectBuilder<'a, S, SelectSetOpSet, T> {
552 SelectBuilder {
553 sql: helpers::union_all(self.sql, other),
554 schema: PhantomData,
555 state: PhantomData,
556 table: PhantomData,
557 }
558 }
559
560 pub fn intersect(
562 self,
563 other: impl ToSQL<'a, PostgresValue<'a>>,
564 ) -> SelectBuilder<'a, S, SelectSetOpSet, T> {
565 SelectBuilder {
566 sql: helpers::intersect(self.sql, other),
567 schema: PhantomData,
568 state: PhantomData,
569 table: PhantomData,
570 }
571 }
572
573 pub fn intersect_all(
575 self,
576 other: impl ToSQL<'a, PostgresValue<'a>>,
577 ) -> SelectBuilder<'a, S, SelectSetOpSet, T> {
578 SelectBuilder {
579 sql: helpers::intersect_all(self.sql, other),
580 schema: PhantomData,
581 state: PhantomData,
582 table: PhantomData,
583 }
584 }
585
586 pub fn except(
588 self,
589 other: impl ToSQL<'a, PostgresValue<'a>>,
590 ) -> SelectBuilder<'a, S, SelectSetOpSet, T> {
591 SelectBuilder {
592 sql: helpers::except(self.sql, other),
593 schema: PhantomData,
594 state: PhantomData,
595 table: PhantomData,
596 }
597 }
598
599 pub fn except_all(
601 self,
602 other: impl ToSQL<'a, PostgresValue<'a>>,
603 ) -> SelectBuilder<'a, S, SelectSetOpSet, T> {
604 SelectBuilder {
605 sql: helpers::except_all(self.sql, other),
606 schema: PhantomData,
607 state: PhantomData,
608 table: PhantomData,
609 }
610 }
611}
612
613impl<'a, S, T> SelectBuilder<'a, S, SelectSetOpSet, T> {
614 pub fn order_by<TOrderBy>(
616 self,
617 expressions: TOrderBy,
618 ) -> SelectBuilder<'a, S, SelectOrderSet, T>
619 where
620 TOrderBy: ToSQL<'a, PostgresValue<'a>>,
621 {
622 SelectBuilder {
623 sql: self.sql.append(helpers::order_by(expressions)),
624 schema: PhantomData,
625 state: PhantomData,
626 table: PhantomData,
627 }
628 }
629
630 pub fn limit(self, limit: usize) -> SelectBuilder<'a, S, SelectLimitSet, T> {
632 SelectBuilder {
633 sql: self.sql.append(helpers::limit(limit)),
634 schema: PhantomData,
635 state: PhantomData,
636 table: PhantomData,
637 }
638 }
639
640 pub fn offset(self, offset: usize) -> SelectBuilder<'a, S, SelectOffsetSet, T> {
642 SelectBuilder {
643 sql: self.sql.append(helpers::offset(offset)),
644 schema: PhantomData,
645 state: PhantomData,
646 table: PhantomData,
647 }
648 }
649}
650
651pub trait ForLockableState {}
658
659impl ForLockableState for SelectFromSet {}
660impl ForLockableState for SelectWhereSet {}
661impl ForLockableState for SelectOrderSet {}
662impl ForLockableState for SelectLimitSet {}
663impl ForLockableState for SelectOffsetSet {}
664impl ForLockableState for SelectJoinSet {}
665impl ForLockableState for SelectGroupSet {}
666
667impl<'a, S, State, T> SelectBuilder<'a, S, State, T>
668where
669 State: ForLockableState,
670{
671 pub fn for_update(self) -> SelectBuilder<'a, S, SelectForSet, T> {
683 SelectBuilder {
684 sql: self.sql.append(helpers::for_update()),
685 schema: PhantomData,
686 state: PhantomData,
687 table: PhantomData,
688 }
689 }
690
691 pub fn for_share(self) -> SelectBuilder<'a, S, SelectForSet, T> {
703 SelectBuilder {
704 sql: self.sql.append(helpers::for_share()),
705 schema: PhantomData,
706 state: PhantomData,
707 table: PhantomData,
708 }
709 }
710
711 pub fn for_no_key_update(self) -> SelectBuilder<'a, S, SelectForSet, T> {
722 SelectBuilder {
723 sql: self.sql.append(helpers::for_no_key_update()),
724 schema: PhantomData,
725 state: PhantomData,
726 table: PhantomData,
727 }
728 }
729
730 pub fn for_key_share(self) -> SelectBuilder<'a, S, SelectForSet, T> {
741 SelectBuilder {
742 sql: self.sql.append(helpers::for_key_share()),
743 schema: PhantomData,
744 state: PhantomData,
745 table: PhantomData,
746 }
747 }
748
749 pub fn for_update_of<U: PostgresTable<'a>>(
764 self,
765 table: U,
766 ) -> SelectBuilder<'a, S, SelectForSet, T> {
767 SelectBuilder {
768 sql: self.sql.append(helpers::for_update_of(table.name())),
769 schema: PhantomData,
770 state: PhantomData,
771 table: PhantomData,
772 }
773 }
774
775 pub fn for_share_of<U: PostgresTable<'a>>(
790 self,
791 table: U,
792 ) -> SelectBuilder<'a, S, SelectForSet, T> {
793 SelectBuilder {
794 sql: self.sql.append(helpers::for_share_of(table.name())),
795 schema: PhantomData,
796 state: PhantomData,
797 table: PhantomData,
798 }
799 }
800}
801
802impl<'a, S, T> SelectBuilder<'a, S, SelectForSet, T> {
807 pub fn nowait(self) -> SelectBuilder<'a, S, SelectForSet, T> {
819 SelectBuilder {
820 sql: self.sql.append(helpers::nowait()),
821 schema: PhantomData,
822 state: PhantomData,
823 table: PhantomData,
824 }
825 }
826
827 pub fn skip_locked(self) -> SelectBuilder<'a, S, SelectForSet, T> {
839 SelectBuilder {
840 sql: self.sql.append(helpers::skip_locked()),
841 schema: PhantomData,
842 state: PhantomData,
843 table: PhantomData,
844 }
845 }
846}
847
848#[cfg(test)]
849mod tests {
850 use super::*;
851 use drizzle_core::{SQL, ToSQL};
852
853 #[test]
854 fn test_select_builder_creation() {
855 let builder = SelectBuilder::<(), SelectInitial> {
856 sql: SQL::raw("SELECT *"),
857 schema: PhantomData,
858 state: PhantomData,
859 table: PhantomData,
860 };
861
862 assert_eq!(builder.to_sql().sql(), "SELECT *");
863 }
864}