use super::ast::OrderDirection;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WindowFuncKind {
RowNumber,
Rank,
DenseRank,
Lag,
Lead,
Sum,
Count,
Avg,
Min,
Max,
}
impl WindowFuncKind {
pub fn sql_name(&self) -> &'static str {
match self {
WindowFuncKind::RowNumber => "ROW_NUMBER",
WindowFuncKind::Rank => "RANK",
WindowFuncKind::DenseRank => "DENSE_RANK",
WindowFuncKind::Lag => "LAG",
WindowFuncKind::Lead => "LEAD",
WindowFuncKind::Sum => "SUM",
WindowFuncKind::Count => "COUNT",
WindowFuncKind::Avg => "AVG",
WindowFuncKind::Min => "MIN",
WindowFuncKind::Max => "MAX",
}
}
pub fn from_name(name: &str) -> Option<Self> {
match name.to_uppercase().as_str() {
"ROW_NUMBER" => Some(WindowFuncKind::RowNumber),
"RANK" => Some(WindowFuncKind::Rank),
"DENSE_RANK" => Some(WindowFuncKind::DenseRank),
"LAG" => Some(WindowFuncKind::Lag),
"LEAD" => Some(WindowFuncKind::Lead),
"SUM" => Some(WindowFuncKind::Sum),
"COUNT" => Some(WindowFuncKind::Count),
"AVG" => Some(WindowFuncKind::Avg),
"MIN" => Some(WindowFuncKind::Min),
"MAX" => Some(WindowFuncKind::Max),
_ => None,
}
}
pub fn takes_column(&self) -> bool {
!matches!(
self,
WindowFuncKind::RowNumber | WindowFuncKind::Rank | WindowFuncKind::DenseRank
)
}
}
#[derive(Debug, Clone)]
pub struct WindowSpec {
pub func: WindowFuncKind,
pub column: Option<String>,
pub partition_by: Vec<String>,
pub order_by: Vec<(String, OrderDirection)>,
pub alias: String,
}
impl WindowSpec {
pub fn to_sql(&self, gen: &dyn crate::provider::ISqlGenerator) -> String {
let func_name = self.func.sql_name();
let arg = if self.func.takes_column() {
let col = self.column.as_deref().unwrap_or("*");
gen.quote_identifier(col)
} else {
String::new()
};
let call = if self.func.takes_column() {
format!("{}({})", func_name, arg)
} else {
format!("{}()", func_name)
};
let mut over = String::new();
if !self.partition_by.is_empty() {
let parts: Vec<String> = self
.partition_by
.iter()
.map(|c| gen.quote_identifier(c))
.collect();
over.push_str(&format!("PARTITION BY {}", parts.join(", ")));
}
if !self.order_by.is_empty() {
if !over.is_empty() {
over.push(' ');
}
let parts: Vec<String> = self
.order_by
.iter()
.map(|(c, d)| {
let quoted = gen.quote_identifier(c);
let dir = match d {
OrderDirection::Ascending => "ASC",
OrderDirection::Descending => "DESC",
};
format!("{} {}", quoted, dir)
})
.collect();
over.push_str(&format!("ORDER BY {}", parts.join(", ")));
}
let alias = gen.quote_identifier(&self.alias);
format!("{} OVER ({}) AS {}", call, over, alias)
}
}