Skip to main content

nodedb_sql/planner/
window.rs

1//! Window function extraction from SELECT projection.
2
3use sqlparser::ast;
4
5use crate::error::Result;
6use crate::functions::registry::FunctionRegistry;
7use crate::parser::normalize::normalize_ident;
8use crate::resolver::expr::convert_expr;
9use crate::types::{SortKey, WindowSpec};
10
11/// Extract window function specifications from SELECT items.
12pub fn extract_window_functions(
13    items: &[ast::SelectItem],
14    _functions: &FunctionRegistry,
15) -> Result<Vec<WindowSpec>> {
16    let mut specs = Vec::new();
17    for item in items {
18        let (expr, alias) = match item {
19            ast::SelectItem::UnnamedExpr(e) => (e, format!("{e}")),
20            ast::SelectItem::ExprWithAlias { expr, alias } => (expr, normalize_ident(alias)),
21            _ => continue,
22        };
23        if let ast::Expr::Function(func) = expr
24            && func.over.is_some()
25        {
26            specs.push(convert_window_spec(func, &alias)?);
27        }
28    }
29    Ok(specs)
30}
31
32fn convert_window_spec(func: &ast::Function, alias: &str) -> Result<WindowSpec> {
33    let name = func
34        .name
35        .0
36        .iter()
37        .map(|p| match p {
38            ast::ObjectNamePart::Identifier(ident) => normalize_ident(ident),
39            _ => String::new(),
40        })
41        .collect::<Vec<_>>()
42        .join(".");
43
44    let args = match &func.args {
45        ast::FunctionArguments::List(args) => args
46            .args
47            .iter()
48            .filter_map(|a| match a {
49                ast::FunctionArg::Unnamed(ast::FunctionArgExpr::Expr(e)) => convert_expr(e).ok(),
50                _ => None,
51            })
52            .collect(),
53        _ => Vec::new(),
54    };
55
56    let (partition_by, order_by) = match &func.over {
57        Some(ast::WindowType::WindowSpec(spec)) => {
58            let pb = spec
59                .partition_by
60                .iter()
61                .map(convert_expr)
62                .collect::<Result<Vec<_>>>()?;
63            let ob = spec
64                .order_by
65                .iter()
66                .map(|o| {
67                    Ok(SortKey {
68                        expr: convert_expr(&o.expr)?,
69                        ascending: o.options.asc.unwrap_or(true),
70                        nulls_first: o.options.nulls_first.unwrap_or(false),
71                    })
72                })
73                .collect::<Result<Vec<_>>>()?;
74            (pb, ob)
75        }
76        _ => (Vec::new(), Vec::new()),
77    };
78
79    Ok(WindowSpec {
80        function: name,
81        args,
82        partition_by,
83        order_by,
84        alias: alias.into(),
85    })
86}