nodedb_sql/planner/
aggregate_order.rs1use sqlparser::ast;
8
9use crate::error::Result;
10use crate::functions::registry::FunctionRegistry;
11use crate::parser::normalize::normalize_ident;
12use crate::planner::aggregate::{
13 extract_aggregates_from_projection, function_args_exprs, normalize_function_name,
14};
15use crate::planner::group_by::{expr_column_name, key_column_name};
16use crate::resolver::ColumnScope;
17use crate::resolver::columns::TableScope;
18use crate::resolver::expr::convert_expr;
19use crate::types::query::AggOutputSlot;
20use crate::types_expr::SqlExpr;
21
22pub fn compute_output_order(
33 projection: &[ast::SelectItem],
34 group_by: &[SqlExpr],
35 functions: &FunctionRegistry,
36 scope: &TableScope,
37) -> Result<Vec<AggOutputSlot>> {
38 Ok(
39 compute_output_order_by_item(projection, group_by, functions, scope)?
40 .into_iter()
41 .flatten()
42 .collect(),
43 )
44}
45
46pub fn compute_output_order_by_item(
54 projection: &[ast::SelectItem],
55 group_by: &[SqlExpr],
56 functions: &FunctionRegistry,
57 scope: &TableScope,
58) -> Result<Vec<Vec<AggOutputSlot>>> {
59 let real_agg_count = extract_aggregates_from_projection(projection, functions, scope)?.len();
60 let mut by_item = Vec::with_capacity(projection.len());
61 let mut agg_cursor = 0usize;
62 let mut grouping_cursor = 0usize;
63 for item in projection {
64 let mut order = Vec::new();
65 let expr = match item {
66 ast::SelectItem::UnnamedExpr(expr) => expr,
67 ast::SelectItem::ExprWithAlias { expr, .. } => expr,
68 _ => {
69 by_item.push(order);
70 continue;
71 }
72 };
73 if let Some(name) = expr_column_name(expr)
75 && let Some(index) = group_by
76 .iter()
77 .position(|key| key_column_name(key) == Some(name.as_str()))
78 {
79 order.push(AggOutputSlot::GroupKey(index));
80 by_item.push(order);
81 continue;
82 }
83 if let Ok(converted) = convert_expr(expr, &ColumnScope::Relations(scope)) {
92 let rendered = format!("{converted:?}");
93 if let Some(index) = group_by
94 .iter()
95 .position(|key| format!("{key:?}") == rendered)
96 {
97 order.push(AggOutputSlot::GroupKey(index));
98 by_item.push(order);
99 continue;
100 }
101 }
102 let grouping_count = count_grouping_calls(expr);
106 if grouping_count > 0 {
107 for _ in 0..grouping_count {
108 order.push(AggOutputSlot::Aggregate(real_agg_count + grouping_cursor));
109 grouping_cursor += 1;
110 }
111 by_item.push(order);
112 continue;
113 }
114 if crate::aggregate_walk::contains_aggregate(expr, functions) {
116 let alias = match item {
117 ast::SelectItem::ExprWithAlias { alias, .. } => normalize_ident(alias),
118 _ => format!("{expr}").to_lowercase(),
119 };
120 let produced =
121 crate::aggregate_walk::extract_aggregates(expr, &alias, functions, scope)?.len();
122 for _ in 0..produced {
123 order.push(AggOutputSlot::Aggregate(agg_cursor));
124 agg_cursor += 1;
125 }
126 }
127 by_item.push(order);
128 }
129 Ok(by_item)
130}
131
132fn count_grouping_calls(expr: &ast::Expr) -> usize {
136 match expr {
137 ast::Expr::Function(f) if normalize_function_name(f).eq_ignore_ascii_case("grouping") => {
138 function_args_exprs(f).len()
139 }
140 ast::Expr::BinaryOp { left, right, .. } => {
141 count_grouping_calls(left) + count_grouping_calls(right)
142 }
143 _ => 0,
144 }
145}