Skip to main content

uqa_sql/plan/
subqueries.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Compact scalar-subquery arenas after expression rewrites discard inputs.
8
9use crate::ScalarExpr;
10use std::collections::BTreeMap;
11
12use super::{CommandPlan, QueryPlan, RelationalPlan, SourcePlan};
13
14mod transform;
15pub(crate) use transform::{
16    copy_occurrences, query_nodes as rewrite_query_with_arenas, rewrite_expression_with_arena,
17    rewrite_with_arenas,
18};
19
20/// Drop unreferenced query children and remap local command expressions without evaluating them.
21pub fn prune_command(command: &mut CommandPlan) {
22    let Some(arena) = command_arena(command) else {
23        return;
24    };
25    let arena = std::mem::take(arena);
26    let arena = compact(arena, |visitor| {
27        for expression in command.expressions_mut() {
28            super::rewrite_scalar_expression(expression, visitor);
29        }
30        if let Some(source) = command.source_input_mut() {
31            visit_source(source, visitor);
32        }
33    });
34    if let Some(target) = command_arena(command) {
35        *target = arena;
36    }
37}
38
39fn command_arena(command: &mut CommandPlan) -> Option<&mut Vec<QueryPlan>> {
40    match command {
41        CommandPlan::Insert(plan) => Some(&mut plan.subqueries),
42        CommandPlan::Update(plan) => Some(&mut plan.subqueries),
43        CommandPlan::Delete(plan) => Some(&mut plan.subqueries),
44        CommandPlan::Merge(plan) => Some(&mut plan.subqueries),
45        _ => None,
46    }
47}
48
49/// Drop unreferenced query children and remap local query expressions, including canonical window definitions.
50pub fn prune_query(query: &mut QueryPlan) {
51    let arena = std::mem::take(query_arena(&mut query.root));
52    let arena = compact(arena, |visitor| visit_root(&mut query.root, visitor));
53    *query_arena(&mut query.root) = arena;
54}
55
56/// Retain only query children actually referenced by one scalar expression.
57pub fn prune_expression(plan: &mut super::ExpressionPlan) {
58    let arena = std::mem::take(&mut plan.subqueries);
59    plan.subqueries = compact(arena, |visitor| {
60        super::rewrite_scalar_expression(&mut plan.scalar, visitor);
61    });
62}
63
64fn query_arena(root: &mut RelationalPlan) -> &mut Vec<QueryPlan> {
65    match root {
66        RelationalPlan::QueryBlock(block) => &mut block.subqueries,
67        RelationalPlan::Values { subqueries, .. } | RelationalPlan::SetOp { subqueries, .. } => {
68            subqueries
69        }
70    }
71}
72
73fn compact(
74    arena: Vec<QueryPlan>,
75    mut visit: impl FnMut(&mut dyn FnMut(&mut ScalarExpr)),
76) -> Vec<QueryPlan> {
77    if arena.is_empty() {
78        return arena;
79    }
80    let mut remap = BTreeMap::new();
81    visit(&mut |expression| {
82        if let Some(id) = subquery_id(expression) {
83            remap.insert(*id, 0);
84        }
85    });
86    // Invalid references retain their original diagnostic rather than aliasing
87    // a surviving entry after compaction.
88    if remap.keys().any(|id| *id >= arena.len()) || remap.len() == arena.len() {
89        return arena;
90    }
91    for (position, value) in remap.values_mut().enumerate() {
92        *value = position;
93    }
94    visit(&mut |expression| {
95        if let Some(id) = subquery_id(expression) {
96            *id = remap[id];
97        }
98    });
99    arena
100        .into_iter()
101        .enumerate()
102        .filter_map(|(id, query)| remap.contains_key(&id).then_some(query))
103        .collect()
104}
105
106fn subquery_id(expression: &mut ScalarExpr) -> Option<&mut usize> {
107    match expression {
108        ScalarExpr::ScalarSubquery(id)
109        | ScalarExpr::Exists { subquery: id, .. }
110        | ScalarExpr::InSubquery { subquery: id, .. } => Some(id),
111        _ => None,
112    }
113}
114
115fn visit_root(root: &mut RelationalPlan, visitor: &mut dyn FnMut(&mut ScalarExpr)) {
116    match root {
117        RelationalPlan::QueryBlock(block) => {
118            if let Some(source) = &mut block.from {
119                visit_source(source, visitor);
120            }
121            for expression in block
122                .projections
123                .iter_mut()
124                .map(|projection| &mut projection.expr)
125                .chain(block.r#where.iter_mut())
126                .chain(block.group_by.iter_mut())
127                .chain(block.grouping_sets.iter_mut().flatten())
128                .chain(block.having.iter_mut())
129                .chain(block.order_by.iter_mut().map(|order| &mut order.expr))
130                .chain(block.limit.iter_mut())
131                .chain(block.offset.iter_mut())
132                .chain(block.distinct_on.iter_mut())
133                .chain(
134                    block
135                        .windows
136                        .iter_mut()
137                        .flat_map(|window| window.spec.expressions_mut()),
138                )
139            {
140                super::rewrite_scalar_expression(expression, visitor);
141            }
142        }
143        RelationalPlan::Values { rows, .. } => {
144            for expression in rows.iter_mut().flatten() {
145                super::rewrite_scalar_expression(expression, visitor);
146            }
147        }
148        RelationalPlan::SetOp {
149            order_by,
150            limit,
151            offset,
152            ..
153        } => {
154            for expression in order_by
155                .iter_mut()
156                .map(|order| &mut order.expr)
157                .chain(limit.as_deref_mut())
158                .chain(offset.as_deref_mut())
159            {
160                super::rewrite_scalar_expression(expression, visitor);
161            }
162        }
163    }
164}
165
166fn visit_source(source: &mut SourcePlan, visitor: &mut dyn FnMut(&mut ScalarExpr)) {
167    match source {
168        SourcePlan::Join {
169            left, right, on, ..
170        } => {
171            visit_source(left, visitor);
172            visit_source(right, visitor);
173            if let Some(on) = on {
174                super::rewrite_scalar_expression(on, visitor);
175            }
176        }
177        SourcePlan::Values { rows, .. } => {
178            for expression in rows.iter_mut().flatten() {
179                super::rewrite_scalar_expression(expression, visitor);
180            }
181        }
182        SourcePlan::Function { args, .. } => {
183            for argument in args {
184                super::rewrite_scalar_expression(argument, visitor);
185            }
186        }
187        SourcePlan::FunctionGroup { functions, .. } => {
188            for argument in functions.iter_mut().flat_map(|function| &mut function.args) {
189                super::rewrite_scalar_expression(argument, visitor);
190            }
191        }
192        // A derived query owns its own scalar-subquery arena.
193        SourcePlan::Subquery { .. } | SourcePlan::Table { .. } => {}
194    }
195}