use uqa_sql::SQLError;
use super::{
optimize_assignments, optimize_projections, optimize_query, optimize_scalar_slot,
optimize_source, optimize_unified_plan, prioritize_access_predicates, AggregateClassifier,
CommandPlan, ConflictActionPlan, ExpressionPlan, MergeWhenPlan, OptimizerConfig,
};
#[expect(
clippy::too_many_lines,
reason = "optimizer rewrite preserves exhaustive variants and fixed-point order"
)]
pub(super) fn optimize_command(
command: &mut CommandPlan,
config: &OptimizerConfig,
aggregates: &dyn AggregateClassifier,
) -> Result<(), SQLError> {
super::subqueries::prune_command(command);
match command {
CommandPlan::Insert(plan) => {
for cte in &mut plan.ctes {
super::traversal::optimize_cte(&mut cte.body, config, aggregates)?;
}
if let Some(source) = &mut plan.source {
optimize_query(source, config, aggregates)?;
}
for row in &mut plan.rows {
for expression in row {
optimize_scalar_slot(expression, config)?;
}
}
if let Some(conflict) = &mut plan.on_conflict {
if let ConflictActionPlan::Update {
assignments,
predicate,
} = &mut conflict.action
{
optimize_assignments(assignments, config)?;
if let Some(predicate) = predicate {
optimize_scalar_slot(predicate, config)?;
}
}
}
optimize_projections(&mut plan.returning, config)?;
let expressions = plan.expressions();
let live = super::subqueries::live_slots(expressions)?;
super::subqueries::optimize_live(&mut plan.subqueries, &live, config, aggregates)?;
}
CommandPlan::Update(plan) => {
for cte in &mut plan.ctes {
super::traversal::optimize_cte(&mut cte.body, config, aggregates)?;
}
if let Some(source) = &mut plan.source {
optimize_source(source, config, aggregates)?;
}
optimize_assignments(&mut plan.assignments, config)?;
if let Some(predicate) = &mut plan.predicate {
optimize_scalar_slot(predicate, config)?;
if config.enable_filter_pushdown {
prioritize_access_predicates(predicate);
}
}
optimize_projections(&mut plan.returning, config)?;
let mut expressions = plan.expressions();
if let Some(source) = &plan.source {
source.push_expressions(&mut expressions);
}
let live = super::subqueries::live_slots(expressions)?;
super::subqueries::optimize_live(&mut plan.subqueries, &live, config, aggregates)?;
}
CommandPlan::Delete(plan) => {
for cte in &mut plan.ctes {
super::traversal::optimize_cte(&mut cte.body, config, aggregates)?;
}
if let Some(source) = &mut plan.source {
optimize_source(source, config, aggregates)?;
}
if let Some(predicate) = &mut plan.predicate {
optimize_scalar_slot(predicate, config)?;
if config.enable_filter_pushdown {
prioritize_access_predicates(predicate);
}
}
optimize_projections(&mut plan.returning, config)?;
let mut expressions = plan.expressions();
if let Some(source) = &plan.source {
source.push_expressions(&mut expressions);
}
let live = super::subqueries::live_slots(expressions)?;
super::subqueries::optimize_live(&mut plan.subqueries, &live, config, aggregates)?;
}
CommandPlan::Merge(plan) => {
for cte in &mut plan.ctes {
super::traversal::optimize_cte(&mut cte.body, config, aggregates)?;
}
optimize_source(&mut plan.source, config, aggregates)?;
optimize_scalar_slot(&mut plan.join_condition, config)?;
for clause in &mut plan.when_clauses {
match clause {
MergeWhenPlan::UpdateMatched {
condition,
assignments,
}
| MergeWhenPlan::UpdateNotMatchedBySource {
condition,
assignments,
} => {
if let Some(condition) = condition {
optimize_scalar_slot(condition, config)?;
}
optimize_assignments(assignments, config)?;
}
MergeWhenPlan::DeleteMatched { condition }
| MergeWhenPlan::DeleteNotMatchedBySource { condition }
| MergeWhenPlan::NothingMatched { condition }
| MergeWhenPlan::NothingNotMatched { condition }
| MergeWhenPlan::NothingNotMatchedBySource { condition } => {
if let Some(condition) = condition {
optimize_scalar_slot(condition, config)?;
}
}
MergeWhenPlan::InsertNotMatched {
condition, values, ..
} => {
if let Some(condition) = condition {
optimize_scalar_slot(condition, config)?;
}
for value in values {
optimize_scalar_slot(value, config)?;
}
}
}
}
optimize_projections(&mut plan.returning, config)?;
let mut expressions = plan.expressions();
plan.source.push_expressions(&mut expressions);
let live = super::subqueries::live_slots(expressions)?;
super::subqueries::optimize_live(&mut plan.subqueries, &live, config, aggregates)?;
}
CommandPlan::DeclareCursor { query, .. } => {
optimize_query(query, config, aggregates)?;
}
CommandPlan::Explain { body, .. } => {
optimize_unified_plan(body, config, aggregates)?;
}
CommandPlan::Call { args: params, .. } => {
for expression in params {
optimize_expression_plan(expression, config, aggregates)?;
}
}
CommandPlan::CreateTableAs { .. }
| CommandPlan::CreateMaterializedView { .. }
| CommandPlan::CreateView { .. }
| CommandPlan::Execute { .. }
| CommandPlan::Prepare { .. }
| CommandPlan::CreateTable(_)
| CommandPlan::CreateTableIfNotExists(_)
| CommandPlan::CreateIndex(_)
| CommandPlan::RenameIndex(_)
| CommandPlan::Drop(_)
| CommandPlan::AlterTable(_)
| CommandPlan::AlterForeignTable(_)
| CommandPlan::AlterView(_)
| CommandPlan::RefreshMaterializedView { .. }
| CommandPlan::CreateSchema { .. }
| CommandPlan::AlterSchemaOwner { .. }
| CommandPlan::RenameSchema { .. }
| CommandPlan::Notify { .. }
| CommandPlan::Listen { .. }
| CommandPlan::Unlisten { .. }
| CommandPlan::SetVariable { .. }
| CommandPlan::ResetVariable { .. }
| CommandPlan::ResetAllVariables
| CommandPlan::SetConstraints { .. }
| CommandPlan::ShowVariable { .. }
| CommandPlan::Discard { .. }
| CommandPlan::Load { .. }
| CommandPlan::Analyze { .. }
| CommandPlan::LockTable(_)
| CommandPlan::Vacuum(_)
| CommandPlan::Truncate { .. }
| CommandPlan::Transaction(_)
| CommandPlan::FetchCursor(_)
| CommandPlan::CloseCursor { .. }
| CommandPlan::CreateSequence(_)
| CommandPlan::CreateDomain(_)
| CommandPlan::AlterDomain(_)
| CommandPlan::CreateEnum(_)
| CommandPlan::CreateCompositeType(_)
| CommandPlan::AlterEnum(_)
| CommandPlan::AlterTypeObject(_)
| CommandPlan::GrantType(_)
| CommandPlan::AlterSequence(_)
| CommandPlan::Deallocate { .. }
| CommandPlan::CreateForeignWrapper(_)
| CommandPlan::CreateForeignServer(_)
| CommandPlan::CreateForeignTable(_)
| CommandPlan::CreateForeignTableDefinition(_)
| CommandPlan::CreateFunction(_)
| CommandPlan::DropFunction(_)
| CommandPlan::AlterRoutine(_)
| CommandPlan::AlterRoutineOwner(_)
| CommandPlan::RenameRoutine(_)
| CommandPlan::GrantRoutine(_)
| CommandPlan::GrantTable(_)
| CommandPlan::GrantSequence(_)
| CommandPlan::GrantDatabase(_)
| CommandPlan::GrantSchema(_)
| CommandPlan::GrantRole(_)
| CommandPlan::CreateRole(_)
| CommandPlan::AlterRole(_)
| CommandPlan::RenameRole(_)
| CommandPlan::DropRole(_)
| CommandPlan::CreateTrigger(_)
| CommandPlan::DropTrigger(_)
| CommandPlan::CreateRule(_)
| CommandPlan::DropRule(_)
| CommandPlan::DoBlock { .. } => {}
}
Ok(())
}
fn optimize_expression_plan(
expression: &mut ExpressionPlan,
config: &OptimizerConfig,
aggregates: &dyn AggregateClassifier,
) -> Result<(), SQLError> {
optimize_scalar_slot(&mut expression.scalar, config)?;
let live = super::subqueries::live_slots([&expression.scalar])?;
super::subqueries::optimize_live(&mut expression.subqueries, &live, config, aggregates)?;
Ok(())
}