Plan a single SELECT statement (no UNION, no CTE wrapper).
(
select: &Select,
catalog: &dyn SqlCatalog,
functions: &FunctionRegistry,
temporal: TemporalScope,
)
| 21 | |
| 22 | /// Plan a single SELECT statement (no UNION, no CTE wrapper). |
| 23 | pub(super) fn plan_select( |
| 24 | select: &Select, |
| 25 | catalog: &dyn SqlCatalog, |
| 26 | functions: &FunctionRegistry, |
| 27 | temporal: TemporalScope, |
| 28 | ) -> Result<SqlPlan> { |
| 29 | // 0. Intercept array table-valued functions before catalog resolution |
| 30 | // so a name like `ARRAY_SLICE` is not looked up as a collection. |
| 31 | if let Some(plan) = |
| 32 | crate::planner::array_fn::try_plan_array_table_fn(&select.from, catalog, temporal)? |
| 33 | { |
| 34 | return Ok(plan); |
| 35 | } |
| 36 | |
| 37 | // 0.5. Derived FROM subquery: `FROM (SELECT ...) AS t`. |
| 38 | // |
| 39 | // Plan the inner subquery first, then desugar into a synthetic CTE |
| 40 | // so the outer SELECT — which may reference `t` like any other |
| 41 | // relation — plans against a catalog that resolves the alias to a |
| 42 | // schemaless source. Until this branch existed the resolver |
| 43 | // dropped non-LATERAL derived factors silently, the scope ended |
| 44 | // up empty, and the planner errored with "multi-table FROM |
| 45 | // without JOIN". |
| 46 | if let Some(plan) = try_plan_derived_from(select, catalog, functions, temporal)? { |
| 47 | return Ok(plan); |
| 48 | } |
| 49 | |
| 50 | // 1. Resolve FROM tables. |
| 51 | let scope = TableScope::resolve_from(catalog, &select.from)?; |
| 52 | |
| 53 | // 2. Handle constant queries (no FROM clause): SELECT 1, SELECT 'hello', etc. |
| 54 | if select.from.is_empty() { |
| 55 | // Intercept maintenance functions (ARRAY_FLUSH / ARRAY_COMPACT) |
| 56 | // before falling through to constant evaluation. |
| 57 | if let Some(plan) = |
| 58 | crate::planner::array_fn::try_plan_array_maint_fn(&select.projection, catalog)? |
| 59 | { |
| 60 | return Ok(plan); |
| 61 | } |
| 62 | let projection = convert_projection(&select.projection)?; |
| 63 | let mut columns = Vec::new(); |
| 64 | let mut values = Vec::new(); |
| 65 | for (i, proj) in projection.iter().enumerate() { |
| 66 | match proj { |
| 67 | Projection::Computed { expr, alias } => { |
| 68 | columns.push(alias.clone()); |
| 69 | values.push(eval_constant_expr(expr, functions)); |
| 70 | } |
| 71 | Projection::Column(name) => { |
| 72 | columns.push(name.clone()); |
| 73 | values.push(SqlValue::Null); |
| 74 | } |
| 75 | _ => { |
| 76 | columns.push(format!("col{i}")); |
| 77 | values.push(SqlValue::Null); |
| 78 | } |
| 79 | } |
| 80 | } |
no test coverage detected