mod collection;
mod detection;
mod evaluation;
mod order_by;
mod types;
use std::collections::HashMap;
pub(crate) use detection::expression_has_window_function;
pub(super) use detection::has_window_functions;
pub(super) use order_by::{collect_order_by_window_functions, evaluate_order_by_window_functions};
pub use types::WindowFunctionKey;
use vibesql_ast::{SelectItem, WindowDefinition};
use vibesql_storage::Row;
use crate::{errors::ExecutorError, evaluator::CombinedExpressionEvaluator};
pub(crate) fn collect_resolved_window_specs(
select_list: &[SelectItem],
window_definitions: Option<&Vec<WindowDefinition>>,
) -> Result<Vec<vibesql_ast::WindowSpec>, ExecutorError> {
Ok(collection::collect_window_functions(select_list, window_definitions)?
.into_iter()
.map(|info| info.window_spec)
.collect())
}
pub(super) fn evaluate_window_functions(
mut rows: Vec<Row>,
select_list: &[SelectItem],
window_definitions: Option<&Vec<WindowDefinition>>,
evaluator: &CombinedExpressionEvaluator,
) -> Result<(Vec<Row>, HashMap<WindowFunctionKey, usize>), ExecutorError> {
let window_functions = collection::collect_window_functions(select_list, window_definitions)?;
if window_functions.is_empty() {
return Ok((rows, HashMap::new()));
}
let mut window_results: Vec<Vec<::vibesql_types::SqlValue>> = Vec::new();
let mut window_mapping = HashMap::new();
let base_column_count = if rows.is_empty() { 0 } else { rows[0].values.len() };
let mut row_reordering: Option<Vec<usize>> = None;
for (idx, win_func) in window_functions.iter().enumerate() {
let result = evaluation::evaluate_single_window_function(&rows, win_func, evaluator)?;
window_results.push(result.values);
if result.partition_order.is_some() {
row_reordering = result.partition_order;
}
let key =
WindowFunctionKey::from_expression(&win_func.function_spec, &win_func.original_window_spec);
let col_idx = base_column_count + idx;
window_mapping.insert(key, col_idx);
}
if let Some(ref order) = row_reordering {
let reordered_rows: Vec<Row> =
order.iter().map(|&orig_idx| rows[orig_idx].clone()).collect();
rows = reordered_rows;
for results in &mut window_results {
let reordered: Vec<_> =
order.iter().map(|&orig_idx| results[orig_idx].clone()).collect();
*results = reordered;
}
}
for (row_idx, row) in rows.iter_mut().enumerate() {
for results in &window_results {
row.values.push(results[row_idx].clone());
}
}
Ok((rows, window_mapping))
}