uqa_planner/statement_planning/
rule_inputs.rs1use crate::{
9 CommandPlan, CtePlan, CtePlanBody, QueryPlan, RelationalPlan, SourcePlan, UnifiedPlan,
10};
11use std::collections::BTreeSet;
12use uqa_core::Value;
13use uqa_sql::{
14 ast::RuleEvent,
15 semantics::{rules::RuleCatalog, view_rewrite::context::ViewRewriteContext},
16 SQLError, ScalarExpr,
17};
18
19pub trait RuleInputColumns {
20 fn source_column_names(
21 &self,
22 table: &str,
23 relations_bound: bool,
24 ) -> Result<Option<Vec<String>>, SQLError>;
25}
26pub struct RuleInputPlanningContext<'a> {
27 pub rules: &'a dyn RuleCatalog,
28 pub views: ViewRewriteContext<'a>,
29 pub columns: &'a dyn RuleInputColumns,
30}
31pub fn rewrite_plan(
32 context: &RuleInputPlanningContext<'_>,
33 plan: &mut UnifiedPlan,
34) -> Result<(), SQLError> {
35 match plan {
36 UnifiedPlan::Query(query) => rewrite_query(context, query),
37 UnifiedPlan::Command(command) => rewrite_command(context, command),
38 }
39}
40
41fn rewrite_ctes(
42 context: &RuleInputPlanningContext<'_>,
43 ctes: &mut [CtePlan],
44) -> Result<(), SQLError> {
45 for cte in ctes {
46 match &mut cte.body {
47 CtePlanBody::Query(query) => rewrite_query(context, query)?,
48 CtePlanBody::Command(command) => rewrite_command(context, command)?,
49 }
50 }
51 Ok(())
52}
53
54fn rewrite_query(
55 context: &RuleInputPlanningContext<'_>,
56 query: &mut QueryPlan,
57) -> Result<(), SQLError> {
58 rewrite_ctes(context, &mut query.ctes)?;
59 let subqueries = match &mut query.root {
60 RelationalPlan::QueryBlock(block) => {
61 if let Some(source) = &mut block.from {
62 rewrite_source(context, source)?;
63 }
64 &mut block.subqueries
65 }
66 RelationalPlan::Values { subqueries, .. } => subqueries,
67 RelationalPlan::SetOp {
68 left,
69 right,
70 subqueries,
71 ..
72 } => {
73 rewrite_query(context, left)?;
74 rewrite_query(context, right)?;
75 subqueries
76 }
77 };
78 for query in subqueries {
79 rewrite_query(context, query)?;
80 }
81 Ok(())
82}
83
84fn rewrite_source(
85 context: &RuleInputPlanningContext<'_>,
86 source: &mut SourcePlan,
87) -> Result<(), SQLError> {
88 match source {
89 SourcePlan::Subquery { body, .. } => rewrite_query(context, body)?,
90 SourcePlan::Join { left, right, .. } => {
91 rewrite_source(context, left)?;
92 rewrite_source(context, right)?;
93 }
94 _ => {}
95 }
96 Ok(())
97}
98
99fn rewrite_command(
100 context: &RuleInputPlanningContext<'_>,
101 command: &mut CommandPlan,
102) -> Result<(), SQLError> {
103 match command {
104 CommandPlan::Explain { body, .. } => return rewrite_plan(context, body),
105 CommandPlan::DeclareCursor { query, .. } => return rewrite_query(context, query),
106 _ => {}
107 }
108 prune_rule_inputs(context, command)?;
109 if let Some(ctes) = command.ctes_mut() {
110 rewrite_ctes(context, ctes)?;
111 }
112 if let Some(source) = command.source_input_mut() {
113 rewrite_source(context, source)?;
114 }
115 for query in command.query_inputs_mut() {
116 rewrite_query(context, query)?;
117 }
118 Ok(())
119}
120
121fn prune_rule_inputs(
122 context: &RuleInputPlanningContext<'_>,
123 command: &mut CommandPlan,
124) -> Result<(), SQLError> {
125 let (table, bound, event) = match command {
126 CommandPlan::Insert(plan) => (&plan.table, plan.target_relation_bound, RuleEvent::Insert),
127 CommandPlan::Update(plan) => (&plan.table, plan.target_relation_bound, RuleEvent::Update),
128 CommandPlan::Delete(plan) => (&plan.table, plan.target_relation_bound, RuleEvent::Delete),
129 _ => return Ok(()),
130 };
131 let table = context.rules.resolve_mutation_target(table, bound)?;
132 let Some(requirements) =
133 uqa_sql::semantics::view_rewrite::rule_input_requirements(context.views, &table, event)?
134 else {
135 return Ok(());
136 };
137 uqa_sql::semantics::rules::validate_rule_returning_contract(
138 context.rules,
139 &table,
140 event,
141 command
142 .returning()
143 .is_some_and(|returning| !returning.is_empty()),
144 )?;
145 let requires_rows = requirements.requires_rows;
146 let required = requirements.columns;
147 match command {
148 CommandPlan::Insert(plan) => {
149 let columns = if plan.columns.is_empty() {
150 context
151 .columns
152 .source_column_names(&table, true)?
153 .unwrap_or_default()
154 } else {
155 plan.columns
156 .iter()
157 .map(|target| target.column.clone())
158 .collect()
159 };
160 let positions = columns
161 .iter()
162 .enumerate()
163 .filter_map(|(position, column)| required.contains(column).then_some(position))
164 .collect::<BTreeSet<_>>();
165 for (position, target) in plan.columns.iter_mut().enumerate() {
166 if !positions.contains(&position) {
167 for expression in target.expressions_mut() {
168 *expression = ScalarExpr::Literal(Value::Null);
169 }
170 }
171 }
172 for row in &mut plan.rows {
173 for (position, expression) in row.iter_mut().enumerate() {
174 if !positions.contains(&position) {
175 *expression = ScalarExpr::Literal(Value::Null);
176 }
177 }
178 }
179 if !requires_rows {
180 plan.source = None;
181 plan.ctes.clear();
182 plan.subqueries.clear();
183 } else if let Some(source) = &mut plan.source {
184 crate::mutation_outputs::prune_unused_query_outputs(
185 source,
186 &positions,
187 columns.len(),
188 );
189 }
190 }
191 CommandPlan::Update(plan) => {
192 for assignment in &mut plan.assignments {
193 if !required.contains(&assignment.target.column) {
194 for expression in assignment.expressions_mut() {
195 *expression = ScalarExpr::Literal(Value::Null);
196 }
197 }
198 }
199 if !requires_rows {
200 plan.source = None;
201 plan.predicate = None;
202 plan.ctes.clear();
203 plan.subqueries.clear();
204 }
205 }
206 CommandPlan::Delete(plan) if !requires_rows => {
207 plan.source = None;
208 plan.predicate = None;
209 plan.ctes.clear();
210 plan.subqueries.clear();
211 }
212 _ => {}
213 }
214 Ok(())
215}