use sqlparser::ast::{
Expr, FunctionArg, FunctionArgExpr, FunctionArguments, OrderByExpr, SelectItem, WindowType,
};
use crate::{SortExpr, WindowFunctionDef};
pub(crate) fn select_item_is_windowed(item: &SelectItem) -> bool {
match item {
SelectItem::UnnamedExpr(e)
| SelectItem::ExprWithAlias { expr: e, .. }
| SelectItem::ExprWithAliases { expr: e, .. } => expr_is_windowed(e),
_ => false,
}
}
fn expr_is_windowed(expr: &Expr) -> bool {
match expr {
Expr::Function(f) => f.over.is_some(),
Expr::BinaryOp { left, right, .. } => expr_is_windowed(left) || expr_is_windowed(right),
Expr::Nested(inner) => expr_is_windowed(inner),
_ => false,
}
}
pub(crate) fn extract_window_def(item: &SelectItem) -> Option<WindowFunctionDef> {
let (expr, alias_override) = match item {
SelectItem::UnnamedExpr(e) => (e, None),
SelectItem::ExprWithAlias { expr: e, alias: a } => (e, Some(a.value.clone())),
SelectItem::ExprWithAliases {
expr: e,
aliases: a,
} => (e, a.first().map(|id| id.value.clone())),
_ => return None,
};
let func = match expr {
Expr::Function(f) if f.over.is_some() => f,
_ => return None,
};
let name = func.name.to_string();
let args: Vec<String> = match &func.args {
FunctionArguments::List(arg_list) => arg_list
.args
.iter()
.filter_map(|arg| match arg {
FunctionArg::Unnamed(FunctionArgExpr::Expr(e)) => Some(e.to_string()),
FunctionArg::Unnamed(FunctionArgExpr::Wildcard) => Some("*".to_string()),
FunctionArg::Named {
arg: FunctionArgExpr::Expr(e),
..
} => Some(e.to_string()),
_ => None,
})
.collect(),
_ => vec![],
};
let (partition_by, order_by) = match func.over.as_ref() {
Some(WindowType::WindowSpec(spec)) => {
let pb: Vec<String> = spec.partition_by.iter().map(|e| e.to_string()).collect();
let ob: Vec<SortExpr> = spec
.order_by
.iter()
.map(order_by_expr_to_sort_expr)
.collect();
(pb, ob)
}
Some(WindowType::NamedWindow(ident)) => {
(vec![ident.value.clone()], vec![])
}
None => (vec![], vec![]),
};
let alias = alias_override.unwrap_or_else(|| name.clone());
Some(WindowFunctionDef {
name,
args,
partition_by,
order_by,
alias,
})
}
fn order_by_expr_to_sort_expr(e: &OrderByExpr) -> SortExpr {
SortExpr {
column: e.expr.to_string(),
ascending: e.options.asc.unwrap_or(true),
}
}