1use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
6pub enum FrameType {
7 Range,
9 Rows,
11 Groups,
13}
14
15impl FrameType {
16 pub fn to_sql(&self) -> &'static str {
28 match self {
29 FrameType::Range => "RANGE",
30 FrameType::Rows => "ROWS",
31 FrameType::Groups => "GROUPS",
32 }
33 }
34}
35
36#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
38pub enum FrameBoundary {
39 UnboundedPreceding,
41 Preceding(i64),
43 CurrentRow,
45 Following(i64),
47 UnboundedFollowing,
49}
50
51impl FrameBoundary {
52 pub fn to_sql(&self) -> String {
66 match self {
67 FrameBoundary::UnboundedPreceding => "UNBOUNDED PRECEDING".to_string(),
68 FrameBoundary::Preceding(n) => format!("{} PRECEDING", n),
69 FrameBoundary::CurrentRow => "CURRENT ROW".to_string(),
70 FrameBoundary::Following(n) => format!("{} FOLLOWING", n),
71 FrameBoundary::UnboundedFollowing => "UNBOUNDED FOLLOWING".to_string(),
72 }
73 }
74}
75
76#[derive(Debug, Clone, Serialize, Deserialize)]
78pub struct Frame {
79 pub frame_type: FrameType,
81 pub start: FrameBoundary,
83 pub end: Option<FrameBoundary>,
85}
86
87impl Frame {
88 pub fn rows(start: FrameBoundary, end: Option<FrameBoundary>) -> Self {
102 Self {
103 frame_type: FrameType::Rows,
104 start,
105 end,
106 }
107 }
108 pub fn range(start: FrameBoundary, end: Option<FrameBoundary>) -> Self {
122 Self {
123 frame_type: FrameType::Range,
124 start,
125 end,
126 }
127 }
128 pub fn groups(start: FrameBoundary, end: Option<FrameBoundary>) -> Self {
142 Self {
143 frame_type: FrameType::Groups,
144 start,
145 end,
146 }
147 }
148 pub fn to_sql(&self) -> String {
159 let mut sql = format!(
160 "{} BETWEEN {}",
161 self.frame_type.to_sql(),
162 self.start.to_sql()
163 );
164
165 if let Some(ref end) = self.end {
166 sql.push_str(&format!(" AND {}", end.to_sql()));
167 } else {
168 sql.push_str(" AND CURRENT ROW");
169 }
170
171 sql
172 }
173}
174
175#[derive(Debug, Clone, Serialize, Deserialize)]
177pub struct Window {
178 pub partition_by: Vec<String>,
180 pub order_by: Vec<String>,
182 pub frame: Option<Frame>,
184}
185
186impl Window {
187 pub fn new() -> Self {
198 Self {
199 partition_by: Vec::new(),
200 order_by: Vec::new(),
201 frame: None,
202 }
203 }
204 pub fn partition_by(mut self, field: impl Into<String>) -> Self {
215 self.partition_by.push(field.into());
216 self
217 }
218 pub fn order_by(mut self, field: impl Into<String>) -> Self {
229 self.order_by.push(field.into());
230 self
231 }
232 pub fn frame(mut self, frame: Frame) -> Self {
247 self.frame = Some(frame);
248 self
249 }
250 pub fn to_sql(&self) -> String {
263 let mut parts = Vec::new();
264
265 if !self.partition_by.is_empty() {
266 parts.push(format!("PARTITION BY {}", self.partition_by.join(", ")));
267 }
268
269 if !self.order_by.is_empty() {
270 parts.push(format!("ORDER BY {}", self.order_by.join(", ")));
271 }
272
273 if let Some(ref frame) = self.frame {
274 parts.push(frame.to_sql());
275 }
276
277 parts.join(" ")
278 }
279}
280
281impl Default for Window {
282 fn default() -> Self {
283 Self::new()
284 }
285}
286
287pub trait WindowFunction {
289 fn to_sql(&self, window: &Window) -> String;
291}
292
293#[derive(Debug, Clone, Serialize, Deserialize)]
295pub struct RowNumber;
296
297impl RowNumber {
298 pub fn new() -> Self {
315 Self
316 }
317}
318
319impl Default for RowNumber {
320 fn default() -> Self {
321 Self::new()
322 }
323}
324
325impl WindowFunction for RowNumber {
326 fn to_sql(&self, window: &Window) -> String {
327 format!("ROW_NUMBER() OVER ({})", window.to_sql())
328 }
329}
330
331#[derive(Debug, Clone, Serialize, Deserialize)]
333pub struct Rank;
334
335impl Rank {
336 pub fn new() -> Self {
353 Self
354 }
355}
356
357impl Default for Rank {
358 fn default() -> Self {
359 Self::new()
360 }
361}
362
363impl WindowFunction for Rank {
364 fn to_sql(&self, window: &Window) -> String {
365 format!("RANK() OVER ({})", window.to_sql())
366 }
367}
368
369#[derive(Debug, Clone, Serialize, Deserialize)]
371pub struct DenseRank;
372
373impl DenseRank {
374 pub fn new() -> Self {
389 Self
390 }
391}
392
393impl Default for DenseRank {
394 fn default() -> Self {
395 Self::new()
396 }
397}
398
399impl WindowFunction for DenseRank {
400 fn to_sql(&self, window: &Window) -> String {
401 format!("DENSE_RANK() OVER ({})", window.to_sql())
402 }
403}
404
405#[derive(Debug, Clone, Serialize, Deserialize)]
407pub struct NTile {
408 pub num_buckets: i64,
410}
411
412impl NTile {
413 pub fn new(num_buckets: i64) -> Self {
425 Self { num_buckets }
426 }
427}
428
429impl WindowFunction for NTile {
430 fn to_sql(&self, window: &Window) -> String {
431 format!("NTILE({}) OVER ({})", self.num_buckets, window.to_sql())
432 }
433}
434
435#[derive(Debug, Clone, Serialize, Deserialize)]
437pub struct Lead {
438 pub expression: String,
440 pub offset: i64,
442 pub default: Option<String>,
444}
445
446impl Lead {
447 pub fn new(expression: impl Into<String>) -> Self {
459 Self {
460 expression: expression.into(),
461 offset: 1,
462 default: None,
463 }
464 }
465 pub fn offset(mut self, offset: i64) -> Self {
477 self.offset = offset;
478 self
479 }
480 pub fn default(mut self, default: impl Into<String>) -> Self {
492 self.default = Some(default.into());
493 self
494 }
495}
496
497impl WindowFunction for Lead {
498 fn to_sql(&self, window: &Window) -> String {
499 let mut args = vec![self.expression.clone(), self.offset.to_string()];
500 if let Some(ref default) = self.default {
501 args.push(default.clone());
502 }
503 format!("LEAD({}) OVER ({})", args.join(", "), window.to_sql())
504 }
505}
506
507#[derive(Debug, Clone, Serialize, Deserialize)]
509pub struct Lag {
510 pub expression: String,
512 pub offset: i64,
514 pub default: Option<String>,
516}
517
518impl Lag {
519 pub fn new(expression: impl Into<String>) -> Self {
531 Self {
532 expression: expression.into(),
533 offset: 1,
534 default: None,
535 }
536 }
537 pub fn offset(mut self, offset: i64) -> Self {
549 self.offset = offset;
550 self
551 }
552 pub fn default(mut self, default: impl Into<String>) -> Self {
564 self.default = Some(default.into());
565 self
566 }
567}
568
569impl WindowFunction for Lag {
570 fn to_sql(&self, window: &Window) -> String {
571 let mut args = vec![self.expression.clone(), self.offset.to_string()];
572 if let Some(ref default) = self.default {
573 args.push(default.clone());
574 }
575 format!("LAG({}) OVER ({})", args.join(", "), window.to_sql())
576 }
577}
578
579#[derive(Debug, Clone, Serialize, Deserialize)]
581pub struct FirstValue {
582 pub expression: String,
584}
585
586impl FirstValue {
587 pub fn new(expression: impl Into<String>) -> Self {
604 Self {
605 expression: expression.into(),
606 }
607 }
608}
609
610impl WindowFunction for FirstValue {
611 fn to_sql(&self, window: &Window) -> String {
612 format!(
613 "FIRST_VALUE({}) OVER ({})",
614 self.expression,
615 window.to_sql()
616 )
617 }
618}
619
620#[derive(Debug, Clone, Serialize, Deserialize)]
622pub struct LastValue {
623 pub expression: String,
625}
626
627impl LastValue {
628 pub fn new(expression: impl Into<String>) -> Self {
645 Self {
646 expression: expression.into(),
647 }
648 }
649}
650
651impl WindowFunction for LastValue {
652 fn to_sql(&self, window: &Window) -> String {
653 format!("LAST_VALUE({}) OVER ({})", self.expression, window.to_sql())
654 }
655}
656
657#[derive(Debug, Clone, Serialize, Deserialize)]
659pub struct NthValue {
660 pub expression: String,
662 pub n: i64,
664}
665
666impl NthValue {
667 pub fn new(expression: impl Into<String>, n: i64) -> Self {
682 Self {
683 expression: expression.into(),
684 n,
685 }
686 }
687}
688
689impl WindowFunction for NthValue {
690 fn to_sql(&self, window: &Window) -> String {
691 format!(
692 "NTH_VALUE({}, {}) OVER ({})",
693 self.expression,
694 self.n,
695 window.to_sql()
696 )
697 }
698}
699
700#[cfg(test)]
701mod tests {
702 use super::*;
703
704 #[test]
705 fn test_window_partition_by() {
706 let window = Window::new().partition_by("department");
707 assert_eq!(window.to_sql(), "PARTITION BY department");
708 }
709
710 #[test]
711 fn test_window_order_by() {
712 let window = Window::new().order_by("salary DESC");
713 assert_eq!(window.to_sql(), "ORDER BY salary DESC");
714 }
715
716 #[test]
717 fn test_window_partition_and_order() {
718 let window = Window::new()
719 .partition_by("department")
720 .order_by("salary DESC");
721 assert_eq!(
722 window.to_sql(),
723 "PARTITION BY department ORDER BY salary DESC"
724 );
725 }
726
727 #[test]
728 fn test_frame_rows() {
729 let frame = Frame::rows(
730 FrameBoundary::UnboundedPreceding,
731 Some(FrameBoundary::CurrentRow),
732 );
733 assert_eq!(
734 frame.to_sql(),
735 "ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW"
736 );
737 }
738
739 #[test]
740 fn test_frame_range() {
741 let frame = Frame::range(
742 FrameBoundary::Preceding(3),
743 Some(FrameBoundary::Following(3)),
744 );
745 assert_eq!(frame.to_sql(), "RANGE BETWEEN 3 PRECEDING AND 3 FOLLOWING");
746 }
747
748 #[test]
749 fn test_row_number() {
750 let window = Window::new()
751 .partition_by("department")
752 .order_by("hire_date");
753 let row_num = RowNumber::new();
754 assert_eq!(
755 row_num.to_sql(&window),
756 "ROW_NUMBER() OVER (PARTITION BY department ORDER BY hire_date)"
757 );
758 }
759
760 #[test]
761 fn test_rank() {
762 let window = Window::new()
763 .partition_by("department")
764 .order_by("salary DESC");
765 let rank = Rank::new();
766 assert_eq!(
767 rank.to_sql(&window),
768 "RANK() OVER (PARTITION BY department ORDER BY salary DESC)"
769 );
770 }
771
772 #[test]
773 fn test_dense_rank() {
774 let window = Window::new().order_by("score DESC");
775 let dense_rank = DenseRank::new();
776 assert_eq!(
777 dense_rank.to_sql(&window),
778 "DENSE_RANK() OVER (ORDER BY score DESC)"
779 );
780 }
781
782 #[test]
783 fn test_ntile() {
784 let window = Window::new().order_by("salary");
785 let ntile = NTile::new(4);
786 assert_eq!(ntile.to_sql(&window), "NTILE(4) OVER (ORDER BY salary)");
787 }
788
789 #[test]
790 fn test_lead() {
791 let window = Window::new().order_by("date");
792 let lead = Lead::new("value");
793 assert_eq!(lead.to_sql(&window), "LEAD(value, 1) OVER (ORDER BY date)");
794 }
795
796 #[test]
797 fn test_lead_with_offset_and_default() {
798 let window = Window::new().order_by("date");
799 let lead = Lead::new("value").offset(2).default("0");
800 assert_eq!(
801 lead.to_sql(&window),
802 "LEAD(value, 2, 0) OVER (ORDER BY date)"
803 );
804 }
805
806 #[test]
807 fn test_lag() {
808 let window = Window::new().order_by("date");
809 let lag = Lag::new("value");
810 assert_eq!(lag.to_sql(&window), "LAG(value, 1) OVER (ORDER BY date)");
811 }
812
813 #[test]
814 fn test_lag_with_offset() {
815 let window = Window::new().order_by("date");
816 let lag = Lag::new("value").offset(3);
817 assert_eq!(lag.to_sql(&window), "LAG(value, 3) OVER (ORDER BY date)");
818 }
819
820 #[test]
821 fn test_first_value() {
822 let window = Window::new()
823 .partition_by("department")
824 .order_by("salary DESC");
825 let first_val = FirstValue::new("salary");
826 assert_eq!(
827 first_val.to_sql(&window),
828 "FIRST_VALUE(salary) OVER (PARTITION BY department ORDER BY salary DESC)"
829 );
830 }
831
832 #[test]
833 fn test_last_value() {
834 let window = Window::new()
835 .partition_by("department")
836 .order_by("salary DESC");
837 let last_val = LastValue::new("salary");
838 assert_eq!(
839 last_val.to_sql(&window),
840 "LAST_VALUE(salary) OVER (PARTITION BY department ORDER BY salary DESC)"
841 );
842 }
843
844 #[test]
845 fn test_nth_value() {
846 let window = Window::new().order_by("salary DESC");
847 let nth_val = NthValue::new("salary", 2);
848 assert_eq!(
849 nth_val.to_sql(&window),
850 "NTH_VALUE(salary, 2) OVER (ORDER BY salary DESC)"
851 );
852 }
853
854 #[test]
855 fn test_window_with_frame() {
856 let frame = Frame::rows(
857 FrameBoundary::Preceding(1),
858 Some(FrameBoundary::Following(1)),
859 );
860 let window = Window::new()
861 .partition_by("department")
862 .order_by("date")
863 .frame(frame);
864 assert_eq!(
865 window.to_sql(),
866 "PARTITION BY department ORDER BY date ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING"
867 );
868 }
869}