1use std::borrow::Cow;
24use std::marker::PhantomData;
25
26use keelson_core::clause::{
27 Combine, Cte, GroupByWith, HasCombines, HasGroupBy, HasHaving, HasJoins, HasLimit, HasLocks,
28 HasOffset, HasOrderBy, HasSelectList, HasSet, HasTableRef, HasValues, HasWhere, HasWindows,
29 HasWith, IndexHint, IndexHintKind, IndexHintScope, Join, JoinKind, Lock, LockStrength,
30 LockWait, NamedWindow, OrderBy, OrderDef, OrderDirection, Set, SetOp, TableRef, Values, Window,
31};
32use keelson_core::expr::{Expr, IntoExpr, IntoExprList, IntoIdent};
33use keelson_core::{Expression, Mod, SqlWriter, mod_fn};
34
35use crate::extras::{
36 HasDuplicateKeyUpdate, HasHints, HasModifiers, HasRowAlias, Modifier, RowAlias, row_value,
37 values_of,
38};
39use crate::statement::{HasDeleteTables, HasExtraTables, HasTargetTable};
40
41#[derive(Debug, Clone)]
55pub struct CteChain {
56 cte: Cte,
57}
58
59pub fn with(name: impl Into<Cow<'static, str>>, body: impl IntoExpr) -> CteChain {
66 CteChain {
67 cte: Cte::new(name, body),
68 }
69}
70
71impl CteChain {
72 #[must_use]
74 pub fn columns(
75 mut self,
76 columns: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
77 ) -> CteChain {
78 self.cte.columns = columns.into_iter().map(Into::into).collect();
79 self
80 }
81}
82
83impl<Q: HasWith> Mod<Q> for CteChain {
84 fn apply(self, q: &mut Q) {
85 q.with_mut().append_cte(self.cte);
86 }
87}
88
89pub fn recursive<Q: HasWith>(recursive: bool) -> impl Mod<Q> {
94 mod_fn(move |q: &mut Q| q.with_mut().set_recursive(recursive))
95}
96
97pub fn optimizer_hint<Q: HasHints>(hint: impl Into<Cow<'static, str>>) -> impl Mod<Q> {
109 let hint = hint.into();
110 mod_fn(move |q: &mut Q| q.hints_mut().append_hint(hint))
111}
112
113pub fn max_execution_time<Q: HasHints>(millis: u64) -> impl Mod<Q> {
116 optimizer_hint(format!("MAX_EXECUTION_TIME({millis})"))
117}
118
119pub fn set_var<Q: HasHints>(assignment: impl Into<Cow<'static, str>>) -> impl Mod<Q> {
121 optimizer_hint(format!("SET_VAR({})", assignment.into()))
122}
123
124pub fn qb_name<Q: HasHints>(name: impl Into<Cow<'static, str>>) -> impl Mod<Q> {
127 optimizer_hint(format!("QB_NAME({})", name.into()))
128}
129
130pub fn resource_group<Q: HasHints>(name: impl Into<Cow<'static, str>>) -> impl Mod<Q> {
132 optimizer_hint(format!("RESOURCE_GROUP({})", name.into()))
133}
134
135fn modifier<Q: HasModifiers>(modifier: Modifier) -> impl Mod<Q> {
140 mod_fn(move |q: &mut Q| q.modifiers_mut().append_modifier(modifier))
141}
142
143pub fn distinct<Q: HasModifiers>() -> impl Mod<Q> {
146 modifier(Modifier::Distinct)
147}
148
149pub fn distinct_row<Q: HasModifiers>() -> impl Mod<Q> {
151 modifier(Modifier::DistinctRow)
152}
153
154pub fn low_priority<Q: HasModifiers>() -> impl Mod<Q> {
156 modifier(Modifier::LowPriority)
157}
158
159pub fn high_priority<Q: HasModifiers>() -> impl Mod<Q> {
161 modifier(Modifier::HighPriority)
162}
163
164pub fn delayed<Q: HasModifiers>() -> impl Mod<Q> {
167 modifier(Modifier::Delayed)
168}
169
170pub fn quick<Q: HasModifiers>() -> impl Mod<Q> {
172 modifier(Modifier::Quick)
173}
174
175pub fn ignore<Q: HasModifiers>() -> impl Mod<Q> {
177 modifier(Modifier::Ignore)
178}
179
180pub fn straight<Q: HasModifiers>() -> impl Mod<Q> {
185 modifier(Modifier::StraightJoin)
186}
187
188pub fn sql_small_result<Q: HasModifiers>() -> impl Mod<Q> {
190 modifier(Modifier::SmallResult)
191}
192
193pub fn sql_big_result<Q: HasModifiers>() -> impl Mod<Q> {
195 modifier(Modifier::BigResult)
196}
197
198pub fn sql_buffer_result<Q: HasModifiers>() -> impl Mod<Q> {
201 modifier(Modifier::BufferResult)
202}
203
204pub fn sql_no_cache<Q: HasModifiers>() -> impl Mod<Q> {
206 modifier(Modifier::NoCache)
207}
208
209pub fn sql_calc_found_rows<Q: HasModifiers>() -> impl Mod<Q> {
211 modifier(Modifier::CalcFoundRows)
212}
213
214pub fn columns<Q: HasSelectList>(columns: impl IntoExprList) -> impl Mod<Q> {
220 let columns = columns.into_expr_list();
221 mod_fn(move |q: &mut Q| q.select_list_mut().append_select(columns))
222}
223
224pub fn preload_columns<Q: HasSelectList>(columns: impl IntoExprList) -> impl Mod<Q> {
228 let columns = columns.into_expr_list();
229 mod_fn(move |q: &mut Q| q.select_list_mut().append_preload_select(columns))
230}
231
232pub trait TableSlot<Q> {
242 fn place(q: &mut Q, table: TableRef);
244}
245
246#[derive(Debug, Clone, Copy, Default)]
249pub struct FromSlot;
250
251#[derive(Debug, Clone, Copy, Default)]
253pub struct TargetSlot;
254
255#[derive(Debug, Clone, Copy, Default)]
258pub struct ExtraSlot;
259
260#[derive(Debug, Clone, Copy, Default)]
262pub struct DeleteSlot;
263
264impl<Q: HasTableRef> TableSlot<Q> for FromSlot {
265 fn place(q: &mut Q, mut table: TableRef) {
266 table.joins.append(&mut q.table_ref_mut().joins);
269 *q.table_ref_mut() = table;
270 }
271}
272
273impl<Q: HasTargetTable> TableSlot<Q> for TargetSlot {
274 fn place(q: &mut Q, mut table: TableRef) {
275 table.joins.append(&mut q.target_table_mut().joins);
276 *q.target_table_mut() = table;
277 }
278}
279
280impl<Q: HasExtraTables> TableSlot<Q> for ExtraSlot {
281 fn place(q: &mut Q, table: TableRef) {
282 q.extra_tables_mut().push(table);
283 }
284}
285
286impl<Q: HasDeleteTables> TableSlot<Q> for DeleteSlot {
287 fn place(q: &mut Q, mut table: TableRef) {
288 let partitions = std::mem::take(&mut table.partitions);
292 q.delete_partitions_mut().extend(partitions);
293 q.delete_tables_mut().push(table);
294 }
295}
296
297#[derive(Debug, Clone)]
305pub struct TableChain<S> {
306 table: TableRef,
307 slot: PhantomData<S>,
308}
309
310fn table_chain<S>(table: impl IntoExpr) -> TableChain<S> {
311 TableChain {
312 table: TableRef::new(table),
313 slot: PhantomData,
314 }
315}
316
317pub fn from_item(table: impl IntoExpr) -> TableChain<FromSlot> {
319 table_chain(table)
320}
321
322pub fn extra_from_item(table: impl IntoExpr) -> TableChain<ExtraSlot> {
324 table_chain(table)
325}
326
327pub fn target_table(table: impl IntoExpr) -> TableChain<TargetSlot> {
329 table_chain(table)
330}
331
332pub fn delete_table(table: impl IntoExpr) -> TableChain<DeleteSlot> {
335 table_chain(table)
336}
337
338impl<S> TableChain<S> {
339 #[must_use]
341 pub fn as_(mut self, alias: impl Into<Cow<'static, str>>) -> TableChain<S> {
342 self.table.set_alias(alias);
343 self
344 }
345
346 #[must_use]
349 pub fn columns(
350 mut self,
351 columns: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
352 ) -> TableChain<S> {
353 self.table.set_columns(columns);
354 self
355 }
356
357 #[must_use]
364 pub fn lateral(mut self) -> TableChain<S> {
365 self.table = lateral_table(self.table);
366 self
367 }
368
369 #[must_use]
371 pub fn partition(
372 mut self,
373 partitions: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
374 ) -> TableChain<S> {
375 self.table.append_partition(partitions);
376 self
377 }
378
379 #[must_use]
382 pub fn use_index(
383 self,
384 indexes: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
385 ) -> TableChain<S> {
386 self.index_hint(IndexHintKind::Use, indexes)
387 }
388
389 #[must_use]
391 pub fn ignore_index(
392 self,
393 indexes: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
394 ) -> TableChain<S> {
395 self.index_hint(IndexHintKind::Ignore, indexes)
396 }
397
398 #[must_use]
400 pub fn force_index(
401 self,
402 indexes: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
403 ) -> TableChain<S> {
404 self.index_hint(IndexHintKind::Force, indexes)
405 }
406
407 #[must_use]
412 pub fn for_join(self) -> TableChain<S> {
413 self.hint_scope(IndexHintScope::Join)
414 }
415
416 #[must_use]
418 pub fn for_order_by(self) -> TableChain<S> {
419 self.hint_scope(IndexHintScope::OrderBy)
420 }
421
422 #[must_use]
424 pub fn for_group_by(self) -> TableChain<S> {
425 self.hint_scope(IndexHintScope::GroupBy)
426 }
427
428 fn index_hint(
429 mut self,
430 kind: IndexHintKind,
431 indexes: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
432 ) -> TableChain<S> {
433 self.table.append_index_hint(IndexHint::new(kind, indexes));
434 self
435 }
436
437 fn hint_scope(mut self, scope: IndexHintScope) -> TableChain<S> {
438 if let Some(hint) = self.table.index_hints.last_mut() {
439 hint.for_ = Some(scope);
440 }
441 self
442 }
443}
444
445impl<Q, S: TableSlot<Q>> Mod<Q> for TableChain<S> {
446 fn apply(self, q: &mut Q) {
447 S::place(q, self.table);
448 }
449}
450
451fn lateral_table(mut table: TableRef) -> TableRef {
467 table.lateral = true;
468 if matches!(table.expression, Some(Expr::Ident(_))) {
469 let name = table.expression.take().expect("just matched Some");
470 table.expression = Some(Expr::custom(LateralBareName(name)));
471 }
472 table
473}
474
475#[derive(Debug)]
482struct LateralBareName(Expr);
483
484impl Expression for LateralBareName {
485 fn write_sql(&self, w: &mut SqlWriter<'_>) {
486 w.record_error(keelson_core::Error::other(
487 "LATERAL is set on a bare table or CTE name, but LATERAL can precede only a derived table",
488 ));
489 w.write_expr(&self.0);
490 }
491}
492
493#[derive(Debug, Clone)]
502pub struct JoinChain {
503 join: Join,
504}
505
506fn join_chain(kind: JoinKind, to: impl IntoExpr) -> JoinChain {
507 JoinChain {
508 join: Join::new(kind, TableRef::new(to)),
509 }
510}
511
512pub fn inner_join(table: impl IntoExpr) -> JoinChain {
514 join_chain(JoinKind::Inner, table)
515}
516
517pub fn left_join(table: impl IntoExpr) -> JoinChain {
519 join_chain(JoinKind::Left, table)
520}
521
522pub fn right_join(table: impl IntoExpr) -> JoinChain {
524 join_chain(JoinKind::Right, table)
525}
526
527pub fn cross_join(table: impl IntoExpr) -> PlainJoinChain {
534 PlainJoinChain(join_chain(JoinKind::Cross, table))
535}
536
537pub fn straight_join(table: impl IntoExpr) -> PlainJoinChain {
542 PlainJoinChain(join_chain(JoinKind::Custom("STRAIGHT_JOIN".into()), table))
543}
544
545impl JoinChain {
546 #[must_use]
548 pub fn as_(mut self, alias: impl Into<Cow<'static, str>>) -> JoinChain {
549 self.join.to.set_alias(alias);
550 self
551 }
552
553 #[must_use]
555 pub fn columns(
556 mut self,
557 columns: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
558 ) -> JoinChain {
559 self.join.to.set_columns(columns);
560 self
561 }
562
563 #[must_use]
570 pub fn lateral(mut self) -> JoinChain {
571 self.join.to = lateral_table(self.join.to);
572 self
573 }
574
575 #[must_use]
577 pub fn partition(
578 mut self,
579 partitions: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
580 ) -> JoinChain {
581 self.join.to.append_partition(partitions);
582 self
583 }
584
585 #[must_use]
587 pub fn use_index(
588 self,
589 indexes: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
590 ) -> JoinChain {
591 self.index_hint(IndexHintKind::Use, indexes)
592 }
593
594 #[must_use]
596 pub fn ignore_index(
597 self,
598 indexes: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
599 ) -> JoinChain {
600 self.index_hint(IndexHintKind::Ignore, indexes)
601 }
602
603 #[must_use]
605 pub fn force_index(
606 self,
607 indexes: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
608 ) -> JoinChain {
609 self.index_hint(IndexHintKind::Force, indexes)
610 }
611
612 #[must_use]
614 pub fn for_join(self) -> JoinChain {
615 self.hint_scope(IndexHintScope::Join)
616 }
617
618 #[must_use]
620 pub fn for_order_by(self) -> JoinChain {
621 self.hint_scope(IndexHintScope::OrderBy)
622 }
623
624 #[must_use]
626 pub fn for_group_by(self) -> JoinChain {
627 self.hint_scope(IndexHintScope::GroupBy)
628 }
629
630 #[must_use]
632 pub fn natural(mut self) -> JoinChain {
633 self.join.natural = true;
634 self
635 }
636
637 #[must_use]
639 pub fn on(mut self, condition: impl IntoExpr) -> JoinChain {
640 self.join.append_on(condition);
641 self
642 }
643
644 #[must_use]
646 pub fn on_eq(self, a: impl IntoExpr, b: impl IntoExpr) -> JoinChain {
647 self.on(Expr::binary(a, "=", b).grouped())
648 }
649
650 #[must_use]
652 pub fn using(
653 mut self,
654 columns: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
655 ) -> JoinChain {
656 self.join.append_using(columns);
657 self
658 }
659
660 fn index_hint(
661 mut self,
662 kind: IndexHintKind,
663 indexes: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
664 ) -> JoinChain {
665 self.join
666 .to
667 .append_index_hint(IndexHint::new(kind, indexes));
668 self
669 }
670
671 fn hint_scope(mut self, scope: IndexHintScope) -> JoinChain {
672 if let Some(hint) = self.join.to.index_hints.last_mut() {
673 hint.for_ = Some(scope);
674 }
675 self
676 }
677}
678
679impl From<JoinChain> for Join {
680 fn from(chain: JoinChain) -> Join {
681 chain.join
682 }
683}
684
685impl<Q: HasJoins> Mod<Q> for JoinChain {
686 fn apply(self, q: &mut Q) {
687 q.joins_mut().push(self.into());
688 }
689}
690
691#[derive(Debug, Clone)]
695pub struct PlainJoinChain(JoinChain);
696
697impl PlainJoinChain {
698 #[must_use]
700 pub fn as_(self, alias: impl Into<Cow<'static, str>>) -> PlainJoinChain {
701 PlainJoinChain(self.0.as_(alias))
702 }
703
704 #[must_use]
706 pub fn columns(
707 self,
708 columns: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
709 ) -> PlainJoinChain {
710 PlainJoinChain(self.0.columns(columns))
711 }
712
713 #[must_use]
715 pub fn lateral(self) -> PlainJoinChain {
716 PlainJoinChain(self.0.lateral())
717 }
718
719 #[must_use]
721 pub fn partition(
722 self,
723 partitions: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
724 ) -> PlainJoinChain {
725 PlainJoinChain(self.0.partition(partitions))
726 }
727
728 #[must_use]
730 pub fn use_index(
731 self,
732 indexes: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
733 ) -> PlainJoinChain {
734 PlainJoinChain(self.0.use_index(indexes))
735 }
736
737 #[must_use]
739 pub fn ignore_index(
740 self,
741 indexes: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
742 ) -> PlainJoinChain {
743 PlainJoinChain(self.0.ignore_index(indexes))
744 }
745
746 #[must_use]
748 pub fn force_index(
749 self,
750 indexes: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
751 ) -> PlainJoinChain {
752 PlainJoinChain(self.0.force_index(indexes))
753 }
754
755 #[must_use]
757 pub fn on(self, condition: impl IntoExpr) -> PlainJoinChain {
758 PlainJoinChain(self.0.on(condition))
759 }
760
761 #[must_use]
763 pub fn on_eq(self, a: impl IntoExpr, b: impl IntoExpr) -> PlainJoinChain {
764 PlainJoinChain(self.0.on_eq(a, b))
765 }
766
767 #[must_use]
769 pub fn using(
770 self,
771 columns: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
772 ) -> PlainJoinChain {
773 PlainJoinChain(self.0.using(columns))
774 }
775}
776
777impl From<PlainJoinChain> for Join {
778 fn from(chain: PlainJoinChain) -> Join {
779 chain.0.join
780 }
781}
782
783impl<Q: HasJoins> Mod<Q> for PlainJoinChain {
784 fn apply(self, q: &mut Q) {
785 self.0.apply(q);
786 }
787}
788
789impl TableChain<ExtraSlot> {
790 #[must_use]
804 pub fn join(mut self, join: impl Into<Join>) -> TableChain<ExtraSlot> {
805 self.table.joins.push(join.into());
806 self
807 }
808}
809
810pub fn where_<Q: HasWhere>(condition: impl IntoExpr) -> impl Mod<Q> {
817 let condition = condition.into_expr();
818 mod_fn(move |q: &mut Q| q.where_mut().append_where(condition))
819}
820
821pub fn having<Q: HasHaving>(condition: impl IntoExpr) -> impl Mod<Q> {
823 let condition = condition.into_expr();
824 mod_fn(move |q: &mut Q| q.having_mut().append_having(condition))
825}
826
827pub fn group_by<Q: HasGroupBy>(group: impl IntoExpr) -> impl Mod<Q> {
833 let group = group.into_expr();
834 mod_fn(move |q: &mut Q| q.group_by_mut().append_group(group))
835}
836
837pub fn with_rollup<Q: HasGroupBy>() -> impl Mod<Q> {
839 mod_fn(move |q: &mut Q| q.group_by_mut().with = Some(GroupByWith::Rollup))
840}
841
842pub fn window<Q: HasWindows>(
852 name: impl Into<Cow<'static, str>>,
853 definition: impl Mod<Window>,
854) -> impl Mod<Q> {
855 let mut w = Window::default();
856 definition.apply(&mut w);
857 let named = NamedWindow::new(name, w);
858 mod_fn(move |q: &mut Q| q.windows_mut().append_window(named))
859}
860
861pub trait OrderSlot<Q> {
867 fn slot(q: &mut Q) -> &mut OrderBy;
869}
870
871#[derive(Debug, Clone, Copy, Default)]
873pub struct DirectOrder;
874
875#[derive(Debug, Clone, Copy, Default)]
877pub struct CombinedOrder;
878
879impl<Q: HasOrderBy> OrderSlot<Q> for DirectOrder {
880 fn slot(q: &mut Q) -> &mut OrderBy {
881 q.order_by_mut()
882 }
883}
884
885impl<Q: HasCombines> OrderSlot<Q> for CombinedOrder {
886 fn slot(q: &mut Q) -> &mut OrderBy {
887 &mut q.combines_mut().order_by
888 }
889}
890
891#[derive(Debug, Clone)]
897pub struct OrderChain<S> {
898 def: OrderDef,
899 slot: PhantomData<S>,
900}
901
902pub fn order_by(expression: impl IntoExpr) -> OrderChain<DirectOrder> {
904 OrderChain {
905 def: OrderDef::new(expression),
906 slot: PhantomData,
907 }
908}
909
910pub fn order_by_combined(expression: impl IntoExpr) -> OrderChain<CombinedOrder> {
913 OrderChain {
914 def: OrderDef::new(expression),
915 slot: PhantomData,
916 }
917}
918
919impl<S> OrderChain<S> {
920 #[must_use]
922 pub fn asc(mut self) -> OrderChain<S> {
923 self.def.direction = Some(OrderDirection::Asc);
924 self
925 }
926
927 #[must_use]
929 pub fn desc(mut self) -> OrderChain<S> {
930 self.def.direction = Some(OrderDirection::Desc);
931 self
932 }
933
934 #[must_use]
939 pub fn collate(mut self, name: impl Into<Cow<'static, str>>) -> OrderChain<S> {
940 self.def.collation = Some(name.into());
941 self
942 }
943}
944
945impl<Q, S: OrderSlot<Q>> Mod<Q> for OrderChain<S> {
946 fn apply(self, q: &mut Q) {
947 S::slot(q).append_order(Expr::custom(self.def));
951 }
952}
953
954pub fn limit<Q: HasLimit>(count: impl IntoExpr) -> impl Mod<Q> {
964 let count = count.into_expr();
965 mod_fn(move |q: &mut Q| q.limit_mut().set_limit(count))
966}
967
968pub fn offset<Q: HasOffset>(start: impl IntoExpr) -> impl Mod<Q> {
973 let start = start.into_expr();
974 mod_fn(move |q: &mut Q| q.offset_mut().set_offset(start))
975}
976
977pub fn limit_combined<Q: HasCombines>(count: impl IntoExpr) -> impl Mod<Q> {
979 let count = count.into_expr();
980 mod_fn(move |q: &mut Q| q.combines_mut().limit.set_limit(count))
981}
982
983pub fn offset_combined<Q: HasCombines>(start: impl IntoExpr) -> impl Mod<Q> {
985 let start = start.into_expr();
986 mod_fn(move |q: &mut Q| q.combines_mut().offset.set_offset(start))
987}
988
989#[derive(Debug, Clone)]
995pub struct LockChain {
996 lock: Lock,
997}
998
999pub fn for_update() -> LockChain {
1004 LockChain {
1005 lock: Lock::new(LockStrength::Update),
1006 }
1007}
1008
1009pub fn for_share() -> LockChain {
1013 LockChain {
1014 lock: Lock::new(LockStrength::Share),
1015 }
1016}
1017
1018impl LockChain {
1019 #[must_use]
1021 pub fn of(
1022 mut self,
1023 tables: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
1024 ) -> LockChain {
1025 self.lock.append_table(tables);
1026 self
1027 }
1028
1029 #[must_use]
1031 pub fn no_wait(mut self) -> LockChain {
1032 self.lock.wait = Some(LockWait::NoWait);
1033 self
1034 }
1035
1036 #[must_use]
1038 pub fn skip_locked(mut self) -> LockChain {
1039 self.lock.wait = Some(LockWait::SkipLocked);
1040 self
1041 }
1042}
1043
1044impl<Q: HasLocks> Mod<Q> for LockChain {
1045 fn apply(self, q: &mut Q) {
1046 q.locks_mut().append_lock(self.lock);
1047 }
1048}
1049
1050fn combine<Q: HasCombines>(op: SetOp, all: bool, query: impl IntoExpr) -> impl Mod<Q> {
1055 let mut c = Combine::new(op, query);
1056 c.all = all;
1057 mod_fn(move |q: &mut Q| q.combines_mut().append_combine(c))
1058}
1059
1060pub fn union<Q: HasCombines>(query: impl IntoExpr) -> impl Mod<Q> {
1063 combine(SetOp::Union, false, query)
1064}
1065
1066pub fn union_all<Q: HasCombines>(query: impl IntoExpr) -> impl Mod<Q> {
1068 combine(SetOp::Union, true, query)
1069}
1070
1071pub fn intersect<Q: HasCombines>(query: impl IntoExpr) -> impl Mod<Q> {
1073 combine(SetOp::Intersect, false, query)
1074}
1075
1076pub fn intersect_all<Q: HasCombines>(query: impl IntoExpr) -> impl Mod<Q> {
1078 combine(SetOp::Intersect, true, query)
1079}
1080
1081pub fn except<Q: HasCombines>(query: impl IntoExpr) -> impl Mod<Q> {
1083 combine(SetOp::Except, false, query)
1084}
1085
1086pub fn except_all<Q: HasCombines>(query: impl IntoExpr) -> impl Mod<Q> {
1088 combine(SetOp::Except, true, query)
1089}
1090
1091pub fn set<Q: HasSet>(assignment: impl IntoExpr) -> impl Mod<Q> {
1100 let assignment = assignment.into_expr();
1101 mod_fn(move |q: &mut Q| q.set_mut().append_set(assignment))
1102}
1103
1104#[derive(Debug, Clone)]
1109pub struct SetChain {
1110 column: Expr,
1111}
1112
1113pub fn set_col(column: impl IntoIdent) -> SetChain {
1116 SetChain {
1117 column: Expr::ident(column),
1118 }
1119}
1120
1121impl SetChain {
1122 pub fn to<Q: HasSet>(self, value: impl IntoExpr) -> impl Mod<Q> {
1124 set(Expr::binary(self.column, "=", value))
1125 }
1126
1127 pub fn to_arg<Q: HasSet>(self, value: impl keelson_core::ToValue) -> impl Mod<Q> {
1129 set(Expr::binary(self.column, "=", Expr::arg(value)))
1130 }
1131}
1132
1133pub fn set_values<Q: HasSet>(
1138 columns: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
1139) -> impl Mod<Q> {
1140 let assignments: Vec<Expr> = columns
1141 .into_iter()
1142 .map(Into::into)
1143 .filter(|c: &Cow<'static, str>| !c.is_empty())
1144 .map(|c| Expr::binary(Expr::ident(c.clone()), "=", values_of(c)))
1145 .collect();
1146 mod_fn(move |q: &mut Q| q.set_mut().append_sets(assignments))
1147}
1148
1149pub fn set_row<Q: HasSet>(
1153 alias: impl Into<Cow<'static, str>>,
1154 columns: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
1155) -> impl Mod<Q> {
1156 let alias = alias.into();
1157 let assignments: Vec<Expr> = columns
1158 .into_iter()
1159 .map(Into::into)
1160 .filter(|c: &Cow<'static, str>| !c.is_empty())
1161 .map(|c| Expr::binary(Expr::ident(c.clone()), "=", row_value(alias.clone(), c)))
1162 .collect();
1163 mod_fn(move |q: &mut Q| q.set_mut().append_sets(assignments))
1164}
1165
1166pub fn values<Q: HasValues>(row: impl IntoExprList) -> impl Mod<Q> {
1174 let row = row.into_expr_list();
1175 mod_fn(move |q: &mut Q| q.values_mut().append_values(row))
1176}
1177
1178pub fn rows<Q: HasValues, R: IntoExprList>(rows: impl IntoIterator<Item = R>) -> impl Mod<Q> {
1180 let rows: Vec<Vec<Expr>> = rows.into_iter().map(IntoExprList::into_expr_list).collect();
1181 mod_fn(move |q: &mut Q| {
1182 let values = q.values_mut();
1183 for row in rows {
1184 values.append_values(row);
1185 }
1186 })
1187}
1188
1189pub fn values_from_query<Q: HasValues>(query: impl IntoExpr) -> impl Mod<Q> {
1195 let query = query.into_expr();
1196 mod_fn(move |q: &mut Q| *q.values_mut() = Values::from_query(query))
1197}
1198
1199#[derive(Debug, Clone)]
1206pub struct RowAliasChain {
1207 alias: RowAlias,
1208}
1209
1210pub fn as_(alias: impl Into<Cow<'static, str>>) -> RowAliasChain {
1212 RowAliasChain {
1213 alias: RowAlias::new(alias),
1214 }
1215}
1216
1217impl RowAliasChain {
1218 #[must_use]
1220 pub fn columns(
1221 mut self,
1222 columns: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
1223 ) -> RowAliasChain {
1224 self.alias.columns = columns.into_iter().map(Into::into).collect();
1225 self
1226 }
1227}
1228
1229impl<Q: HasRowAlias> Mod<Q> for RowAliasChain {
1230 fn apply(self, q: &mut Q) {
1231 *q.row_alias_mut() = self.alias;
1232 }
1233}
1234
1235pub fn on_duplicate_key_update<Q: HasDuplicateKeyUpdate>(body: impl Mod<Set>) -> impl Mod<Q> {
1243 let mut set = Set::default();
1244 body.apply(&mut set);
1245 mod_fn(move |q: &mut Q| q.duplicate_key_update_mut().append_sets(set.exprs))
1246}