nodedb_sql/planner/
window.rs1use 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
11pub 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}