use std::collections::HashMap;
use vibesql_ast::{Expression, SelectItem, WindowDefinition, WindowFunctionSpec, WindowSpec};
use super::types::WindowFunctionInfo;
use crate::errors::ExecutorError;
fn build_window_map(
window_definitions: Option<&Vec<WindowDefinition>>,
) -> Result<HashMap<String, WindowSpec>, ExecutorError> {
let mut resolved_map: HashMap<String, WindowSpec> = HashMap::new();
if let Some(defs) = window_definitions {
let raw_map: HashMap<String, &WindowSpec> =
defs.iter().map(|def| (def.name.to_lowercase(), &def.spec)).collect();
for def in defs {
let resolved = resolve_window_spec_recursive(&def.spec, &raw_map, &resolved_map)?;
resolved_map.insert(def.name.to_lowercase(), resolved);
}
}
Ok(resolved_map)
}
fn resolve_window_spec_recursive(
spec: &WindowSpec,
raw_map: &HashMap<String, &WindowSpec>,
resolved_map: &HashMap<String, WindowSpec>,
) -> Result<WindowSpec, ExecutorError> {
match &spec.base_window_name {
Some(base_name) => {
let base_name_lower = base_name.to_lowercase();
let base_spec = if let Some(resolved) = resolved_map.get(&base_name_lower) {
resolved.clone()
} else if let Some(raw) = raw_map.get(&base_name_lower) {
resolve_window_spec_recursive(raw, raw_map, resolved_map)?
} else {
return Err(ExecutorError::Other(format!("no such window: {}", base_name)));
};
Ok(WindowSpec {
base_window_name: None, partition_by: spec.partition_by.clone().or(base_spec.partition_by),
order_by: spec.order_by.clone().or(base_spec.order_by),
frame: spec.frame.clone().or(base_spec.frame),
})
}
None => Ok(spec.clone()),
}
}
fn resolve_window_spec(
spec: &WindowSpec,
window_map: &HashMap<String, WindowSpec>,
) -> Result<WindowSpec, ExecutorError> {
match &spec.base_window_name {
Some(base_name) => {
let base_spec = window_map
.get(&base_name.to_lowercase())
.ok_or_else(|| ExecutorError::Other(format!("no such window: {}", base_name)))?;
Ok(WindowSpec {
base_window_name: None, partition_by: spec.partition_by.clone().or_else(|| base_spec.partition_by.clone()),
order_by: spec.order_by.clone().or_else(|| base_spec.order_by.clone()),
frame: spec.frame.clone().or_else(|| base_spec.frame.clone()),
})
}
None => Ok(spec.clone()),
}
}
pub(super) fn collect_window_functions(
select_list: &[SelectItem],
window_definitions: Option<&Vec<WindowDefinition>>,
) -> Result<Vec<WindowFunctionInfo>, ExecutorError> {
let window_map = build_window_map(window_definitions)?;
let mut window_functions = Vec::new();
for (idx, item) in select_list.iter().enumerate() {
if let SelectItem::Expression { expr, .. } = item {
collect_from_expression(expr, idx, &window_map, &mut window_functions)?;
}
}
Ok(window_functions)
}
fn collect_from_expression(
expr: &Expression,
select_index: usize,
window_map: &HashMap<String, WindowSpec>,
window_functions: &mut Vec<WindowFunctionInfo>,
) -> Result<(), ExecutorError> {
match expr {
Expression::WindowFunction { function, over } => {
match function {
WindowFunctionSpec::Aggregate { .. }
| WindowFunctionSpec::Ranking { .. }
| WindowFunctionSpec::Value { .. } => {
let resolved_spec = resolve_window_spec(over, window_map)?;
window_functions.push(WindowFunctionInfo {
_select_index: select_index,
function_spec: function.clone(),
window_spec: resolved_spec,
original_window_spec: over.clone(), });
}
}
}
Expression::BinaryOp { left, right, .. } => {
collect_from_expression(left, select_index, window_map, window_functions)?;
collect_from_expression(right, select_index, window_map, window_functions)?;
}
Expression::Conjunction(exprs) | Expression::Disjunction(exprs) => {
for e in exprs {
collect_from_expression(e, select_index, window_map, window_functions)?;
}
}
Expression::UnaryOp { expr, .. } => {
collect_from_expression(expr, select_index, window_map, window_functions)?;
}
Expression::Function { args, .. } => {
for arg in args {
collect_from_expression(arg, select_index, window_map, window_functions)?;
}
}
Expression::AggregateFunction { args, order_by, filter, .. } => {
for arg in args {
collect_from_expression(arg, select_index, window_map, window_functions)?;
}
if let Some(items) = order_by {
for item in items {
collect_from_expression(
&item.expr,
select_index,
window_map,
window_functions,
)?;
}
}
if let Some(f) = filter {
collect_from_expression(f, select_index, window_map, window_functions)?;
}
}
Expression::Case { operand, when_clauses, else_result } => {
if let Some(op) = operand {
collect_from_expression(op, select_index, window_map, window_functions)?;
}
for when_clause in when_clauses {
for cond in &when_clause.conditions {
collect_from_expression(cond, select_index, window_map, window_functions)?;
}
collect_from_expression(
&when_clause.result,
select_index,
window_map,
window_functions,
)?;
}
if let Some(else_expr) = else_result {
collect_from_expression(else_expr, select_index, window_map, window_functions)?;
}
}
Expression::IsNull { expr, .. } => {
collect_from_expression(expr, select_index, window_map, window_functions)?;
}
Expression::IsDistinctFrom { left, right, .. } => {
collect_from_expression(left, select_index, window_map, window_functions)?;
collect_from_expression(right, select_index, window_map, window_functions)?;
}
Expression::IsTruthValue { expr, .. } => {
collect_from_expression(expr, select_index, window_map, window_functions)?;
}
Expression::Cast { expr, .. } => {
collect_from_expression(expr, select_index, window_map, window_functions)?;
}
Expression::Like { expr, pattern, escape, .. }
| Expression::Glob { expr, pattern, escape, .. } => {
collect_from_expression(expr, select_index, window_map, window_functions)?;
collect_from_expression(pattern, select_index, window_map, window_functions)?;
if let Some(esc) = escape {
collect_from_expression(esc, select_index, window_map, window_functions)?;
}
}
Expression::InList { expr, values, .. } => {
collect_from_expression(expr, select_index, window_map, window_functions)?;
for v in values {
collect_from_expression(v, select_index, window_map, window_functions)?;
}
}
Expression::In { expr, .. } => {
collect_from_expression(expr, select_index, window_map, window_functions)?;
}
Expression::QuantifiedComparison { expr, .. } => {
collect_from_expression(expr, select_index, window_map, window_functions)?;
}
Expression::Between { expr, low, high, .. } => {
collect_from_expression(expr, select_index, window_map, window_functions)?;
collect_from_expression(low, select_index, window_map, window_functions)?;
collect_from_expression(high, select_index, window_map, window_functions)?;
}
Expression::Position { substring, string, .. } => {
collect_from_expression(substring, select_index, window_map, window_functions)?;
collect_from_expression(string, select_index, window_map, window_functions)?;
}
Expression::Trim { removal_char, string, .. } => {
if let Some(rc) = removal_char {
collect_from_expression(rc, select_index, window_map, window_functions)?;
}
collect_from_expression(string, select_index, window_map, window_functions)?;
}
Expression::Extract { expr, .. } => {
collect_from_expression(expr, select_index, window_map, window_functions)?;
}
Expression::Interval { value, .. } => {
collect_from_expression(value, select_index, window_map, window_functions)?;
}
_ => {}
}
Ok(())
}