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 if let CommandPlan::Update(plan) = command {
133 uqa_sql::semantics::view_rewrite::validate_grouped_rule_inputs(
134 context.views,
135 &table,
136 &plan.assignments,
137 )?;
138 }
139 let Some(requirements) =
140 uqa_sql::semantics::view_rewrite::rule_input_requirements(context.views, &table, event)?
141 else {
142 return Ok(());
143 };
144 uqa_sql::semantics::rules::validate_rule_returning_contract(
145 context.rules,
146 &table,
147 event,
148 command
149 .returning()
150 .is_some_and(|returning| !returning.is_empty()),
151 )?;
152 let requires_rows = requirements.requires_rows;
153 let required = requirements.columns;
154 match command {
155 CommandPlan::Insert(plan) => {
156 let columns = if plan.columns.is_empty() {
157 context
158 .columns
159 .source_column_names(&table, true)?
160 .unwrap_or_default()
161 } else {
162 plan.columns
163 .iter()
164 .map(|target| target.column.clone())
165 .collect()
166 };
167 let positions = columns
168 .iter()
169 .enumerate()
170 .filter_map(|(position, column)| required.contains(column).then_some(position))
171 .collect::<BTreeSet<_>>();
172 for (position, target) in plan.columns.iter_mut().enumerate() {
173 if !positions.contains(&position) {
174 for expression in target.expressions_mut() {
175 *expression = ScalarExpr::Literal(Value::Null);
176 }
177 }
178 }
179 for row in &mut plan.rows {
180 for (position, expression) in row.iter_mut().enumerate() {
181 if !positions.contains(&position) {
182 *expression = ScalarExpr::Literal(Value::Null);
183 }
184 }
185 }
186 if !requires_rows {
187 plan.source = None;
188 plan.ctes.clear();
189 plan.subqueries.clear();
190 } else if let Some(source) = &mut plan.source {
191 crate::mutation_outputs::prune_unused_query_outputs(
192 source,
193 &positions,
194 columns.len(),
195 );
196 }
197 }
198 CommandPlan::Update(plan) => {
199 prune_update_assignments(&mut plan.assignments, &required);
200 if !requires_rows {
201 plan.source = None;
202 plan.predicate = None;
203 plan.ctes.clear();
204 plan.subqueries.clear();
205 }
206 }
207 CommandPlan::Delete(plan) if !requires_rows => {
208 plan.source = None;
209 plan.predicate = None;
210 plan.ctes.clear();
211 plan.subqueries.clear();
212 }
213 _ => {}
214 }
215 Ok(())
216}
217
218fn prune_update_assignments(
219 assignments: &mut Vec<uqa_sql::plan::AssignmentPlan>,
220 required: &BTreeSet<String>,
221) {
222 *assignments = std::mem::take(assignments)
223 .into_iter()
224 .flat_map(|assignment| {
225 if assignment
226 .target
227 .column_names()
228 .all(|column| !required.contains(column))
229 {
230 assignment
231 .target
232 .targets()
233 .iter()
234 .cloned()
235 .map(|target| uqa_sql::plan::AssignmentPlan {
236 target: target.into(),
237 value: ScalarExpr::Literal(Value::Null),
238 })
239 .collect::<Vec<_>>()
240 } else {
241 vec![assignment]
242 }
243 })
244 .collect();
245 for assignment in assignments {
246 if assignment
247 .target
248 .column_names()
249 .all(|column| !required.contains(column))
250 {
251 for expression in assignment.expressions_mut() {
252 *expression = ScalarExpr::Literal(Value::Null);
253 }
254 }
255 }
256}