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