Extract the projected column name from a scalar subquery. Handles aliased aggregates like `SELECT AVG(amount) AS avg_amount`. For unaliased aggregates, returns the canonical aggregate key emitted by the aggregate executor (e.g. `avg(amount)`, `count(*)`).
(query: &ast::Query)
| 395 | /// For unaliased aggregates, returns the canonical aggregate key emitted by |
| 396 | /// the aggregate executor (e.g. `avg(amount)`, `count(*)`). |
| 397 | fn extract_scalar_column(query: &ast::Query) -> Option<String> { |
| 398 | let select = match &*query.body { |
| 399 | SetExpr::Select(s) => s, |
| 400 | _ => return None, |
| 401 | }; |
| 402 | if select.projection.len() != 1 { |
| 403 | return None; |
| 404 | } |
| 405 | match &select.projection[0] { |
| 406 | ast::SelectItem::ExprWithAlias { alias, .. } => Some(normalize_ident(alias)), |
| 407 | ast::SelectItem::UnnamedExpr(expr) => match expr { |
| 408 | Expr::Identifier(ident) => Some(normalize_ident(ident)), |
| 409 | Expr::CompoundIdentifier(parts) if parts.len() >= 3 => { |
| 410 | // Schema-qualified: return None to propagate "unsupported" through convert_expr. |
| 411 | None |
| 412 | } |
| 413 | Expr::CompoundIdentifier(parts) if parts.len() == 2 => Some(normalize_ident(&parts[1])), |
| 414 | Expr::Function(func) => { |
| 415 | let func_name = func |
| 416 | .name |
| 417 | .0 |
| 418 | .iter() |
| 419 | .map(|p| match p { |
| 420 | ast::ObjectNamePart::Identifier(ident) => normalize_ident(ident), |
| 421 | _ => String::new(), |
| 422 | }) |
| 423 | .collect::<Vec<_>>() |
| 424 | .join(".") |
| 425 | .to_lowercase(); |
| 426 | let arg = match &func.args { |
| 427 | ast::FunctionArguments::List(arg_list) => arg_list |
| 428 | .args |
| 429 | .first() |
| 430 | .and_then(|a| match a { |
| 431 | ast::FunctionArg::Unnamed(ast::FunctionArgExpr::Expr( |
| 432 | Expr::Identifier(ident), |
| 433 | )) => Some(normalize_ident(ident)), |
| 434 | ast::FunctionArg::Unnamed(ast::FunctionArgExpr::Expr( |
| 435 | Expr::CompoundIdentifier(parts), |
| 436 | )) if parts.len() >= 3 => None, |
| 437 | ast::FunctionArg::Unnamed(ast::FunctionArgExpr::Expr( |
| 438 | Expr::CompoundIdentifier(parts), |
| 439 | )) if parts.len() == 2 => Some(normalize_ident(&parts[1])), |
| 440 | ast::FunctionArg::Unnamed( |
| 441 | ast::FunctionArgExpr::Wildcard |
| 442 | | ast::FunctionArgExpr::QualifiedWildcard(_), |
| 443 | ) => Some("all".to_string()), |
| 444 | _ => None, |
| 445 | }) |
| 446 | .unwrap_or_else(|| "*".to_string()), |
| 447 | _ => "*".to_string(), |
| 448 | }; |
| 449 | Some(canonical_aggregate_key(&func_name, &arg)) |
| 450 | } |
| 451 | _ => None, |
| 452 | }, |
| 453 | _ => None, |
| 454 | } |
no test coverage detected