1use super::ast::OrderDirection;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum WindowFuncKind {
17 RowNumber,
18 Rank,
19 DenseRank,
20 Lag,
21 Lead,
22 Sum,
23 Count,
24 Avg,
25 Min,
26 Max,
27}
28
29impl WindowFuncKind {
30 pub fn sql_name(&self) -> &'static str {
32 match self {
33 WindowFuncKind::RowNumber => "ROW_NUMBER",
34 WindowFuncKind::Rank => "RANK",
35 WindowFuncKind::DenseRank => "DENSE_RANK",
36 WindowFuncKind::Lag => "LAG",
37 WindowFuncKind::Lead => "LEAD",
38 WindowFuncKind::Sum => "SUM",
39 WindowFuncKind::Count => "COUNT",
40 WindowFuncKind::Avg => "AVG",
41 WindowFuncKind::Min => "MIN",
42 WindowFuncKind::Max => "MAX",
43 }
44 }
45
46 pub fn from_name(name: &str) -> Option<Self> {
48 match name.to_uppercase().as_str() {
49 "ROW_NUMBER" => Some(WindowFuncKind::RowNumber),
50 "RANK" => Some(WindowFuncKind::Rank),
51 "DENSE_RANK" => Some(WindowFuncKind::DenseRank),
52 "LAG" => Some(WindowFuncKind::Lag),
53 "LEAD" => Some(WindowFuncKind::Lead),
54 "SUM" => Some(WindowFuncKind::Sum),
55 "COUNT" => Some(WindowFuncKind::Count),
56 "AVG" => Some(WindowFuncKind::Avg),
57 "MIN" => Some(WindowFuncKind::Min),
58 "MAX" => Some(WindowFuncKind::Max),
59 _ => None,
60 }
61 }
62
63 pub fn takes_column(&self) -> bool {
65 !matches!(
66 self,
67 WindowFuncKind::RowNumber | WindowFuncKind::Rank | WindowFuncKind::DenseRank
68 )
69 }
70}
71
72#[derive(Debug, Clone)]
78pub struct WindowSpec {
79 pub func: WindowFuncKind,
81 pub column: Option<String>,
84 pub partition_by: Vec<String>,
86 pub order_by: Vec<(String, OrderDirection)>,
88 pub alias: String,
90}
91
92impl WindowSpec {
93 pub fn to_sql(&self, gen: &dyn crate::provider::ISqlGenerator) -> String {
99 let func_name = self.func.sql_name();
100 let arg = if self.func.takes_column() {
101 let col = self.column.as_deref().unwrap_or("*");
102 gen.quote_identifier(col)
103 } else {
104 String::new()
105 };
106 let call = if self.func.takes_column() {
107 format!("{}({})", func_name, arg)
108 } else {
109 format!("{}()", func_name)
110 };
111
112 let mut over = String::new();
113 if !self.partition_by.is_empty() {
114 let parts: Vec<String> = self
115 .partition_by
116 .iter()
117 .map(|c| gen.quote_identifier(c))
118 .collect();
119 over.push_str(&format!("PARTITION BY {}", parts.join(", ")));
120 }
121 if !self.order_by.is_empty() {
122 if !over.is_empty() {
123 over.push(' ');
124 }
125 let parts: Vec<String> = self
126 .order_by
127 .iter()
128 .map(|(c, d)| {
129 let quoted = gen.quote_identifier(c);
130 let dir = match d {
131 OrderDirection::Ascending => "ASC",
132 OrderDirection::Descending => "DESC",
133 };
134 format!("{} {}", quoted, dir)
135 })
136 .collect();
137 over.push_str(&format!("ORDER BY {}", parts.join(", ")));
138 }
139 let alias = gen.quote_identifier(&self.alias);
140 format!("{} OVER ({}) AS {}", call, over, alias)
141 }
142}