Skip to main content

uqa_sql/plan/
rewrite.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Recursive scalar-expression traversal and in-place plan rewriting.
8
9use super::{
10    AssignmentPlan, CommandPlan, ConflictActionPlan, MergeWhenPlan, OrderPlan, ProjectionPlan,
11    QueryPlan, RelationalPlan, ScalarExpr, ScalarFrameBound, SourcePlan,
12};
13
14pub(super) fn rewrite_query_scalars(
15    query: &mut QueryPlan,
16    rewrite: &mut dyn FnMut(&mut ScalarExpr),
17) {
18    for cte in &mut query.ctes {
19        match &mut cte.body {
20            super::CtePlanBody::Query(query) => rewrite_query_scalars(query, rewrite),
21            super::CtePlanBody::Command(command) => rewrite_command_scalars(command, rewrite),
22        }
23    }
24    match &mut query.root {
25        RelationalPlan::QueryBlock(block) => {
26            if let Some(source) = &mut block.from {
27                rewrite_source_scalars(source, rewrite);
28            }
29            rewrite_optional_scalar(&mut block.r#where, rewrite);
30            for projection in &mut block.projections {
31                rewrite_scalar(&mut projection.expr, rewrite);
32            }
33            for expression in &mut block.group_by {
34                rewrite_scalar(expression, rewrite);
35            }
36            for set in &mut block.grouping_sets {
37                for expression in set {
38                    rewrite_scalar(expression, rewrite);
39                }
40            }
41            rewrite_optional_scalar(&mut block.having, rewrite);
42            rewrite_orders(&mut block.order_by, rewrite);
43            rewrite_optional_scalar(&mut block.limit, rewrite);
44            rewrite_optional_scalar(&mut block.offset, rewrite);
45            for expression in &mut block.distinct_on {
46                rewrite_scalar(expression, rewrite);
47            }
48            for subquery in &mut block.subqueries {
49                rewrite_query_scalars(subquery, rewrite);
50            }
51        }
52        RelationalPlan::SetOp {
53            left,
54            right,
55            order_by,
56            limit,
57            offset,
58            subqueries,
59            ..
60        } => {
61            rewrite_query_scalars(left, rewrite);
62            rewrite_query_scalars(right, rewrite);
63            rewrite_orders(order_by, rewrite);
64            if let Some(limit) = limit {
65                rewrite_scalar(limit, rewrite);
66            }
67            if let Some(offset) = offset {
68                rewrite_scalar(offset, rewrite);
69            }
70            for subquery in subqueries {
71                rewrite_query_scalars(subquery, rewrite);
72            }
73        }
74        RelationalPlan::Values { rows, subqueries } => {
75            for row in rows {
76                for expression in row {
77                    rewrite_scalar(expression, rewrite);
78                }
79            }
80            for subquery in subqueries {
81                rewrite_query_scalars(subquery, rewrite);
82            }
83        }
84    }
85}
86
87pub(super) fn rewrite_source_scalars(
88    source: &mut SourcePlan,
89    rewrite: &mut dyn FnMut(&mut ScalarExpr),
90) {
91    match source {
92        SourcePlan::Table { .. } => {}
93        SourcePlan::Join {
94            left, right, on, ..
95        } => {
96            rewrite_source_scalars(left, rewrite);
97            rewrite_source_scalars(right, rewrite);
98            rewrite_optional_scalar(on, rewrite);
99        }
100        SourcePlan::Values { rows, .. } => {
101            for row in rows {
102                for expression in row {
103                    rewrite_scalar(expression, rewrite);
104                }
105            }
106        }
107        SourcePlan::Function { args, .. } => {
108            for expression in args {
109                rewrite_scalar(expression, rewrite);
110            }
111        }
112        SourcePlan::FunctionGroup { functions, .. } => {
113            for function in functions {
114                for expression in &mut function.args {
115                    rewrite_scalar(expression, rewrite);
116                }
117            }
118        }
119        SourcePlan::Subquery { body, .. } => rewrite_query_scalars(body, rewrite),
120    }
121}
122
123#[expect(
124    clippy::too_many_lines,
125    reason = "optimizer rewrite preserves exhaustive variants and fixed-point order"
126)]
127pub(super) fn rewrite_command_scalars(
128    command: &mut CommandPlan,
129    rewrite: &mut dyn FnMut(&mut ScalarExpr),
130) {
131    match command {
132        CommandPlan::Insert(plan) => {
133            for expression in plan
134                .columns
135                .iter_mut()
136                .flat_map(crate::ast::AssignmentTarget::expressions_mut)
137            {
138                rewrite_scalar(expression, rewrite);
139            }
140            for cte in &mut plan.ctes {
141                match &mut cte.body {
142                    super::CtePlanBody::Query(query) => rewrite_query_scalars(query, rewrite),
143                    super::CtePlanBody::Command(command) => {
144                        rewrite_command_scalars(command, rewrite);
145                    }
146                }
147            }
148            for row in &mut plan.rows {
149                for expression in row {
150                    rewrite_scalar(expression, rewrite);
151                }
152            }
153            if let Some(source) = &mut plan.source {
154                rewrite_query_scalars(source, rewrite);
155            }
156            if let Some(conflict) = &mut plan.on_conflict {
157                for expression in &mut conflict.expressions {
158                    rewrite_scalar(expression, rewrite);
159                }
160                if let Some(predicate) = &mut conflict.predicate {
161                    rewrite_scalar(predicate, rewrite);
162                }
163                if let ConflictActionPlan::Update {
164                    assignments,
165                    predicate,
166                } = &mut conflict.action
167                {
168                    rewrite_assignments(assignments, rewrite);
169                    if let Some(predicate) = predicate {
170                        rewrite_scalar(predicate, rewrite);
171                    }
172                }
173            }
174            rewrite_projections(&mut plan.returning, rewrite);
175            for check in &mut plan.view_checks {
176                rewrite_scalar(&mut check.predicate, rewrite);
177            }
178            rewrite_subqueries(&mut plan.subqueries, rewrite);
179        }
180        CommandPlan::Update(plan) => {
181            for cte in &mut plan.ctes {
182                match &mut cte.body {
183                    super::CtePlanBody::Query(query) => rewrite_query_scalars(query, rewrite),
184                    super::CtePlanBody::Command(command) => {
185                        rewrite_command_scalars(command, rewrite);
186                    }
187                }
188            }
189            if let Some(source) = &mut plan.source {
190                rewrite_source_scalars(source, rewrite);
191            }
192            rewrite_assignments(&mut plan.assignments, rewrite);
193            rewrite_optional_scalar(&mut plan.predicate, rewrite);
194            rewrite_projections(&mut plan.returning, rewrite);
195            for check in &mut plan.view_checks {
196                rewrite_scalar(&mut check.predicate, rewrite);
197            }
198            rewrite_subqueries(&mut plan.subqueries, rewrite);
199        }
200        CommandPlan::Delete(plan) => {
201            for cte in &mut plan.ctes {
202                match &mut cte.body {
203                    super::CtePlanBody::Query(query) => rewrite_query_scalars(query, rewrite),
204                    super::CtePlanBody::Command(command) => {
205                        rewrite_command_scalars(command, rewrite);
206                    }
207                }
208            }
209            if let Some(source) = &mut plan.source {
210                rewrite_source_scalars(source, rewrite);
211            }
212            rewrite_optional_scalar(&mut plan.predicate, rewrite);
213            rewrite_projections(&mut plan.returning, rewrite);
214            rewrite_subqueries(&mut plan.subqueries, rewrite);
215        }
216        CommandPlan::Merge(plan) => {
217            for cte in &mut plan.ctes {
218                match &mut cte.body {
219                    super::CtePlanBody::Query(query) => rewrite_query_scalars(query, rewrite),
220                    super::CtePlanBody::Command(command) => {
221                        rewrite_command_scalars(command, rewrite);
222                    }
223                }
224            }
225            rewrite_source_scalars(&mut plan.source, rewrite);
226            rewrite_optional_scalar(&mut plan.target_predicate, rewrite);
227            rewrite_scalar(&mut plan.join_condition, rewrite);
228            for clause in &mut plan.when_clauses {
229                match clause {
230                    MergeWhenPlan::UpdateMatched {
231                        condition,
232                        assignments,
233                    }
234                    | MergeWhenPlan::UpdateNotMatchedBySource {
235                        condition,
236                        assignments,
237                    } => {
238                        rewrite_optional_scalar(condition, rewrite);
239                        rewrite_assignments(assignments, rewrite);
240                    }
241                    MergeWhenPlan::DeleteMatched { condition }
242                    | MergeWhenPlan::DeleteNotMatchedBySource { condition }
243                    | MergeWhenPlan::NothingMatched { condition }
244                    | MergeWhenPlan::NothingNotMatched { condition }
245                    | MergeWhenPlan::NothingNotMatchedBySource { condition } => {
246                        rewrite_optional_scalar(condition, rewrite);
247                    }
248                    MergeWhenPlan::InsertNotMatched {
249                        condition,
250                        columns,
251                        values,
252                    } => {
253                        rewrite_optional_scalar(condition, rewrite);
254                        for expression in columns
255                            .iter_mut()
256                            .flat_map(crate::ast::AssignmentTarget::expressions_mut)
257                        {
258                            rewrite_scalar(expression, rewrite);
259                        }
260                        for value in values {
261                            rewrite_scalar(value, rewrite);
262                        }
263                    }
264                }
265            }
266            rewrite_projections(&mut plan.returning, rewrite);
267            for check in &mut plan.view_checks {
268                rewrite_scalar(&mut check.predicate, rewrite);
269            }
270            rewrite_subqueries(&mut plan.subqueries, rewrite);
271        }
272        CommandPlan::CreateView { query, .. }
273        | CommandPlan::CreateMaterializedView { query, .. }
274        | CommandPlan::CreateTableAs { query, .. }
275        | CommandPlan::DeclareCursor { query, .. } => {
276            rewrite_query_scalars(query, rewrite);
277        }
278        CommandPlan::Explain { body, .. } | CommandPlan::Prepare { body, .. } => {
279            body.rewrite_scalar_expressions(rewrite);
280        }
281        CommandPlan::Execute { params, .. } | CommandPlan::Call { args: params, .. } => {
282            for expression in params {
283                rewrite_scalar(&mut expression.scalar, rewrite);
284                rewrite_subqueries(&mut expression.subqueries, rewrite);
285            }
286        }
287        CommandPlan::CreateTable(_)
288        | CommandPlan::CreateTableIfNotExists(_)
289        | CommandPlan::CreateIndex(_)
290        | CommandPlan::RenameIndex(_)
291        | CommandPlan::Drop(_)
292        | CommandPlan::AlterTable(_)
293        | CommandPlan::AlterForeignTable(_)
294        | CommandPlan::AlterView(_)
295        | CommandPlan::RefreshMaterializedView { .. }
296        | CommandPlan::CreateSchema { .. }
297        | CommandPlan::AlterSchemaOwner { .. }
298        | CommandPlan::Notify { .. }
299        | CommandPlan::Listen { .. }
300        | CommandPlan::Unlisten { .. }
301        | CommandPlan::SetVariable { .. }
302        | CommandPlan::ResetVariable { .. }
303        | CommandPlan::ResetAllVariables
304        | CommandPlan::SetConstraints { .. }
305        | CommandPlan::ShowVariable { .. }
306        | CommandPlan::Discard { .. }
307        | CommandPlan::Load { .. }
308        | CommandPlan::Analyze { .. }
309        | CommandPlan::Vacuum(_)
310        | CommandPlan::LockTable(_)
311        | CommandPlan::Truncate { .. }
312        | CommandPlan::Transaction(_)
313        | CommandPlan::FetchCursor(_)
314        | CommandPlan::CloseCursor { .. }
315        | CommandPlan::CreateSequence(_)
316        | CommandPlan::CreateDomain(_)
317        | CommandPlan::AlterSequence(_)
318        | CommandPlan::Deallocate { .. }
319        | CommandPlan::CreateForeignServer(_)
320        | CommandPlan::CreateForeignTable(_)
321        | CommandPlan::CreateForeignTableIfNotExists(_)
322        | CommandPlan::CreateFunction(_)
323        | CommandPlan::DropFunction(_)
324        | CommandPlan::AlterRoutine(_)
325        | CommandPlan::AlterRoutineOwner(_)
326        | CommandPlan::RenameRoutine(_)
327        | CommandPlan::GrantRoutine(_)
328        | CommandPlan::GrantTable(_)
329        | CommandPlan::GrantSequence(_)
330        | CommandPlan::GrantDatabase(_)
331        | CommandPlan::GrantSchema(_)
332        | CommandPlan::GrantRole(_)
333        | CommandPlan::CreateRole(_)
334        | CommandPlan::AlterRole(_)
335        | CommandPlan::RenameRole(_)
336        | CommandPlan::DropRole(_)
337        | CommandPlan::CreateTrigger(_)
338        | CommandPlan::DropTrigger(_)
339        | CommandPlan::CreateRule(_)
340        | CommandPlan::DropRule(_)
341        | CommandPlan::DoBlock { .. } => {}
342    }
343}
344
345pub(super) fn rewrite_assignments(
346    assignments: &mut [AssignmentPlan],
347    rewrite: &mut dyn FnMut(&mut ScalarExpr),
348) {
349    for assignment in assignments {
350        for expression in assignment.expressions_mut() {
351            rewrite_scalar(expression, rewrite);
352        }
353    }
354}
355
356pub(super) fn rewrite_projections(
357    projections: &mut [ProjectionPlan],
358    rewrite: &mut dyn FnMut(&mut ScalarExpr),
359) {
360    for projection in projections {
361        rewrite_scalar(&mut projection.expr, rewrite);
362    }
363}
364
365pub(super) fn rewrite_orders(orders: &mut [OrderPlan], rewrite: &mut dyn FnMut(&mut ScalarExpr)) {
366    for order in orders {
367        rewrite_scalar(&mut order.expr, rewrite);
368    }
369}
370
371pub(super) fn rewrite_subqueries(
372    subqueries: &mut [QueryPlan],
373    rewrite: &mut dyn FnMut(&mut ScalarExpr),
374) {
375    for subquery in subqueries {
376        rewrite_query_scalars(subquery, rewrite);
377    }
378}
379
380pub(super) fn rewrite_optional_scalar(
381    expression: &mut Option<ScalarExpr>,
382    rewrite: &mut dyn FnMut(&mut ScalarExpr),
383) {
384    if let Some(expression) = expression {
385        rewrite_scalar(expression, rewrite);
386    }
387}
388
389pub(super) fn rewrite_scalar(
390    expression: &mut ScalarExpr,
391    rewrite: &mut dyn FnMut(&mut ScalarExpr),
392) {
393    match expression {
394        ScalarExpr::Func {
395            args,
396            order_by,
397            filter,
398            ..
399        } => {
400            for argument in args {
401                rewrite_scalar(argument, rewrite);
402            }
403            for order in order_by {
404                rewrite_scalar(&mut order.expr, rewrite);
405            }
406            if let Some(filter) = filter {
407                rewrite_scalar(filter, rewrite);
408            }
409        }
410        ScalarExpr::Array(items)
411        | ScalarExpr::Row(items)
412        | ScalarExpr::And(items)
413        | ScalarExpr::Or(items) => {
414            for item in items {
415                rewrite_scalar(item, rewrite);
416            }
417        }
418        ScalarExpr::Binary { lhs, rhs, .. } => {
419            rewrite_scalar(lhs, rewrite);
420            rewrite_scalar(rhs, rewrite);
421        }
422        ScalarExpr::UnaryMinus(inner)
423        | ScalarExpr::Not(inner)
424        | ScalarExpr::IsNull { expr: inner, .. }
425        | ScalarExpr::Cast { expr: inner, .. } => rewrite_scalar(inner, rewrite),
426        ScalarExpr::Between { expr, low, high } => {
427            rewrite_scalar(expr, rewrite);
428            rewrite_scalar(low, rewrite);
429            rewrite_scalar(high, rewrite);
430        }
431        ScalarExpr::InList { expr, list, .. } => {
432            rewrite_scalar(expr, rewrite);
433            for item in list {
434                rewrite_scalar(item, rewrite);
435            }
436        }
437        ScalarExpr::WindowCall { args, spec, .. } => {
438            for argument in args {
439                rewrite_scalar(argument, rewrite);
440            }
441            for expression in &mut spec.partition_by {
442                rewrite_scalar(expression, rewrite);
443            }
444            for order in &mut spec.order_by {
445                rewrite_scalar(&mut order.expr, rewrite);
446            }
447            if let Some(frame) = &mut spec.frame {
448                rewrite_frame_bound(&mut frame.start, rewrite);
449                rewrite_frame_bound(&mut frame.end, rewrite);
450            }
451        }
452        ScalarExpr::Case {
453            base,
454            when,
455            else_branch,
456        } => {
457            if let Some(base) = base {
458                rewrite_scalar(base, rewrite);
459            }
460            for (condition, result) in when {
461                rewrite_scalar(condition, rewrite);
462                rewrite_scalar(result, rewrite);
463            }
464            if let Some(branch) = else_branch {
465                rewrite_scalar(branch, rewrite);
466            }
467        }
468        ScalarExpr::InSubquery { expr, .. } => rewrite_scalar(expr, rewrite),
469        ScalarExpr::Default
470        | ScalarExpr::Star
471        | ScalarExpr::QualifiedStar(_)
472        | ScalarExpr::Column(_)
473        | ScalarExpr::Position(_)
474        | ScalarExpr::InternalColumn(_)
475        | ScalarExpr::QualifiedColumn { .. }
476        | ScalarExpr::Literal(_)
477        | ScalarExpr::TypedLiteral { .. }
478        | ScalarExpr::Param(_)
479        | ScalarExpr::ScalarSubquery(_)
480        | ScalarExpr::Exists { .. } => {}
481    }
482    rewrite(expression);
483}
484
485/// Visit one scalar-expression tree in post-order and rewrite each node once.
486///
487/// Query-owned callers that need scope-sensitive rewriting can use this entry
488/// point without duplicating the exhaustive [`ScalarExpr`] traversal.
489pub fn rewrite_scalar_expression(
490    expression: &mut ScalarExpr,
491    rewrite: &mut dyn FnMut(&mut ScalarExpr),
492) {
493    rewrite_scalar(expression, rewrite);
494}
495
496pub(super) fn rewrite_frame_bound(
497    bound: &mut ScalarFrameBound,
498    rewrite: &mut dyn FnMut(&mut ScalarExpr),
499) {
500    match bound {
501        ScalarFrameBound::Preceding(expression) | ScalarFrameBound::Following(expression) => {
502            rewrite_scalar(expression, rewrite);
503        }
504        ScalarFrameBound::UnboundedPreceding
505        | ScalarFrameBound::UnboundedFollowing
506        | ScalarFrameBound::CurrentRow => {}
507    }
508}