1use super::{
8 add_check_option, bind_unqualified_source_positions, canonical_view_name,
9 combine_view_predicate, delete_ordinary_subquery_ids, dml_source_schema, dml_target_width,
10 duplicate_assignment, finalize_source_returning, instead_of_trigger_definition,
11 next_rewritten_layer, preserve_view_rule_returning, record_view_rule_relation,
12 returning_subquery_ids, rewritable_layer, rewrite_correlated_dml_context,
13 rewrite_existing_view_checks, rewrite_returning, rewrite_target_expression,
14 update_ordinary_subquery_ids, validate_delete_expressions, validate_direct_view_rule_path,
15 validate_mapped_columns, validate_public_delete_contract, validate_public_update_contract,
16 validate_public_view_targets, validate_update_expressions, validate_update_targets,
17 validate_writable_columns, view_not_updatable, writable_column, AssignmentPlan, BTreeSet,
18 ColumnWrite, CorrelatedDmlContext, DeletePlan, ExpressionScope, LayerPrivileges,
19 NotUpdatableReason, SQLError, TriggerEvent, UpdatePlan, ViewCommand, ViewRewriteContext,
20 ViewRuleUpdatePlan,
21};
22
23#[expect(
24 clippy::too_many_lines,
25 reason = "preserves view qualifier and row identity"
26)]
27pub fn rewrite_update_to_base(
28 services: ViewRewriteContext<'_>,
29 statement: &UpdatePlan,
30 params: &[crate::SQLParam],
31 inherited_ctes: Option<&super::CteScope>,
32) -> Result<UpdatePlan, SQLError> {
33 validate_public_view_targets(
34 services,
35 &statement.table,
36 statement
37 .assignments
38 .iter()
39 .flat_map(|assignment| assignment.target.column_names()),
40 )?;
41 let source_schema = dml_source_schema(
42 services,
43 statement.source.as_deref(),
44 &statement.ctes,
45 &statement.subqueries,
46 params,
47 inherited_ctes,
48 )?;
49 validate_public_update_contract(services, statement, source_schema.as_ref())?;
50 let view = canonical_view_name(services, &statement.table)?;
51 validate_direct_view_rule_path(
52 services,
53 &view,
54 crate::ast::RuleEvent::Update,
55 ViewCommand::Update,
56 )?;
57 let initial_layer = rewritable_layer(services, &view, ViewCommand::Update)?;
58 if !initial_layer.has_writable_column() {
59 return Err(view_not_updatable(
60 &view,
61 ViewCommand::Update,
62 NotUpdatableReason::NoUpdatableColumns,
63 ));
64 }
65 validate_update_targets(&initial_layer, statement)?;
66 let mut initial_layer = Some(initial_layer);
67 let mut plan = statement.clone();
68 let mut privileges = LayerPrivileges::new();
69 plan.target_privilege_subject = Some(privileges.check(
70 services.authorization,
71 &plan.table,
72 plan.target_privilege_subject.as_ref(),
73 || crate::semantics::view_privileges::ensure_update(services.authorization, &plan),
74 )?);
75 let mut cascaded = false;
76 let mut visited = BTreeSet::new();
77 let mut source_star_boundaries = Vec::new();
78 let mut rewrite_suppressed = false;
79 loop {
80 if !visited.is_empty()
82 && !rewrite_suppressed
83 && instead_of_trigger_definition(services, &plan.table, TriggerEvent::Update)?
84 {
85 break;
86 }
87 let Some(layer) = next_rewritten_layer(
88 services,
89 &plan.table,
90 &mut initial_layer,
91 rewrite_suppressed,
92 crate::ast::RuleEvent::Update,
93 ViewCommand::Update,
94 )?
95 else {
96 break;
97 };
98 if !visited.insert(layer.canonical_name.clone()) {
99 return Err(SQLError::Internal(format!(
100 "cycle while rewriting automatically updatable view `{}`",
101 layer.canonical_name
102 )));
103 }
104 let has_view_rules = if rewrite_suppressed {
105 false
106 } else {
107 record_view_rule_relation(
108 services,
109 &mut plan.view_rule_relations,
110 &layer,
111 crate::ast::RuleEvent::Update,
112 )?
113 };
114 let layer_suppresses = has_view_rules
115 && super::context::relation_suppresses_original_query(
116 services,
117 &layer.canonical_name,
118 crate::ast::RuleEvent::Update,
119 )?;
120 if visited.len() > 1 && !rewrite_suppressed && !layer_suppresses {
121 if !layer.has_writable_column() {
122 return Err(view_not_updatable(
123 &layer.canonical_name,
124 ViewCommand::Update,
125 NotUpdatableReason::NoUpdatableColumns,
126 ));
127 }
128 plan.target_privilege_subject = Some(privileges.check(
129 services.authorization,
130 &plan.table,
131 plan.target_privilege_subject.as_ref(),
132 || crate::semantics::view_privileges::ensure_update(services.authorization, &plan),
133 )?);
134 }
135 if has_view_rules
136 && super::context::relation_has_returning_provider(
137 services,
138 &layer.canonical_name,
139 crate::ast::RuleEvent::Update,
140 )?
141 {
142 preserve_view_rule_returning(
143 &mut plan.view_rule_returning,
144 &layer.canonical_name,
145 &plan.target_qualifier,
146 &plan.returning,
147 &plan.returning_aliases,
148 &plan.subqueries,
149 );
150 }
151 if has_view_rules {
152 plan.view_rule_update_plans.push(ViewRuleUpdatePlan {
153 relation: layer.canonical_name.clone(),
154 assigned_columns: plan
155 .assignments
156 .iter()
157 .flat_map(|assignment| assignment.target.column_names())
158 .map(str::to_owned)
159 .collect(),
160 input_columns: Vec::new(),
161 });
162 }
163 let target_qualifier = plan.target_qualifier.clone();
164 if visited.len() == 1 {
165 validate_update_expressions(
166 services,
167 &plan,
168 &layer,
169 source_schema.as_ref(),
170 params,
171 inherited_ctes,
172 )?;
173 }
174 let ordinary_subquery_ids = update_ordinary_subquery_ids(&plan);
175 rewrite_correlated_dml_context(
176 CorrelatedDmlContext {
177 inherited_ctes,
178 services,
179 layer: &layer,
180 target_qualifier: &target_qualifier,
181 source: source_schema.as_ref(),
182 returning_aliases: None,
183 include_excluded: false,
184 ctes: &plan.ctes,
185 ids: &ordinary_subquery_ids,
186 params,
187 },
188 &mut plan.subqueries,
189 )?;
190 let returning_subquery_ids = returning_subquery_ids(&plan.returning);
191 rewrite_correlated_dml_context(
192 CorrelatedDmlContext {
193 inherited_ctes,
194 services,
195 layer: &layer,
196 target_qualifier: &target_qualifier,
197 source: source_schema.as_ref(),
198 returning_aliases: Some(&plan.returning_aliases),
199 include_excluded: false,
200 ctes: &plan.ctes,
201 ids: &returning_subquery_ids,
202 params,
203 },
204 &mut plan.subqueries,
205 )?;
206 let ordinary_scope = ExpressionScope {
207 target_qualifier: &target_qualifier,
208 returning_aliases: None,
209 source: source_schema.as_ref(),
210 include_excluded: false,
211 };
212 if !layer_suppresses && !rewrite_suppressed {
213 validate_writable_columns(
214 &layer,
215 plan.assignments
216 .iter()
217 .flat_map(|assignment| assignment.target.column_names()),
218 ColumnWrite::Update,
219 )?;
220 }
221 for AssignmentPlan { target, value } in &mut plan.assignments {
222 for expression in target.expressions_mut() {
223 rewrite_target_expression(
224 services,
225 expression,
226 &layer,
227 ordinary_scope,
228 &mut plan.subqueries,
229 )?;
230 }
231 rewrite_target_expression(
232 services,
233 value,
234 &layer,
235 ordinary_scope,
236 &mut plan.subqueries,
237 )?;
238 if !layer_suppresses && !rewrite_suppressed {
239 for target in target.targets_mut() {
240 target.column = writable_column(&layer, &target.column, ColumnWrite::Update)?;
241 }
242 }
243 }
244 let mapped = plan
245 .assignments
246 .iter()
247 .flat_map(|assignment| assignment.target.targets().iter().cloned())
248 .collect::<Vec<_>>();
249 validate_mapped_columns(&mapped, duplicate_assignment)?;
250 if let Some(predicate) = &mut plan.predicate {
251 rewrite_target_expression(
252 services,
253 predicate,
254 &layer,
255 ordinary_scope,
256 &mut plan.subqueries,
257 )?;
258 }
259 rewrite_existing_view_checks(
260 services,
261 &mut plan.view_checks,
262 &layer,
263 &target_qualifier,
264 &mut plan.subqueries,
265 )?;
266 let (returning, boundaries) = rewrite_returning(
267 services,
268 plan.returning,
269 &layer,
270 &target_qualifier,
271 &plan.returning_aliases,
272 source_schema.as_ref(),
273 &mut plan.subqueries,
274 )?;
275 plan.returning = returning;
276 if visited.len() == 1 {
277 source_star_boundaries = boundaries;
278 }
279 plan.predicate = combine_view_predicate(
280 services,
281 plan.predicate,
282 &layer,
283 &target_qualifier,
284 &mut plan.subqueries,
285 )?;
286 add_check_option(
287 services,
288 &mut plan.view_checks,
289 &layer,
290 &target_qualifier,
291 &mut cascaded,
292 &mut plan.subqueries,
293 )?;
294 plan.table = layer.source_name;
295 plan.include_descendants = layer.source_include_descendants;
296 rewrite_suppressed |= layer_suppresses;
297 if !super::context::target_is_view(services, &plan.table)? {
298 break;
299 }
300 }
301 let input_columns = plan
302 .assignments
303 .iter()
304 .flat_map(|assignment| assignment.target.column_names())
305 .map(str::to_owned)
306 .collect::<Vec<_>>();
307 for update_plan in &mut plan.view_rule_update_plans {
308 update_plan.input_columns.clone_from(&input_columns);
309 }
310 if let Some(source) = source_schema.as_ref() {
311 let target_width = dml_target_width(services, &plan.table)?;
312 for assignment in &mut plan.assignments {
313 for expression in assignment.expressions_mut() {
314 bind_unqualified_source_positions(expression, source, target_width);
315 }
316 }
317 if let Some(predicate) = &mut plan.predicate {
318 bind_unqualified_source_positions(predicate, source, target_width);
319 }
320 for projection in &mut plan.returning {
321 bind_unqualified_source_positions(&mut projection.expr, source, target_width);
322 }
323 }
324 plan.returning = finalize_source_returning(
325 services,
326 &plan.table,
327 plan.returning,
328 source_schema.as_ref(),
329 &source_star_boundaries,
330 )?;
331 privileges.finish()?;
332 Ok(plan)
333}
334
335#[expect(
336 clippy::too_many_lines,
337 reason = "preserves view qualifier and row identity"
338)]
339pub fn rewrite_delete_to_base(
340 services: ViewRewriteContext<'_>,
341 statement: &DeletePlan,
342 params: &[crate::SQLParam],
343 inherited_ctes: Option<&super::CteScope>,
344) -> Result<DeletePlan, SQLError> {
345 let source_schema = dml_source_schema(
346 services,
347 statement.source.as_deref(),
348 &statement.ctes,
349 &statement.subqueries,
350 params,
351 inherited_ctes,
352 )?;
353 validate_public_delete_contract(services, statement, source_schema.as_ref())?;
354 let mut plan = statement.clone();
355 let mut privileges = LayerPrivileges::new();
356 plan.target_privilege_subject = Some(privileges.check(
357 services.authorization,
358 &plan.table,
359 plan.target_privilege_subject.as_ref(),
360 || crate::semantics::view_privileges::ensure_delete(services.authorization, &plan),
361 )?);
362 let mut initial_layer = None;
363 let mut visited = BTreeSet::new();
364 let mut source_star_boundaries = Vec::new();
365 let mut rewrite_suppressed = false;
366 loop {
367 if !visited.is_empty()
369 && !rewrite_suppressed
370 && instead_of_trigger_definition(services, &plan.table, TriggerEvent::Delete)?
371 {
372 break;
373 }
374 let Some(layer) = next_rewritten_layer(
375 services,
376 &plan.table,
377 &mut initial_layer,
378 rewrite_suppressed,
379 crate::ast::RuleEvent::Delete,
380 ViewCommand::Delete,
381 )?
382 else {
383 break;
384 };
385 if !visited.insert(layer.canonical_name.clone()) {
386 return Err(SQLError::Internal(format!(
387 "cycle while rewriting automatically updatable view `{}`",
388 layer.canonical_name
389 )));
390 }
391 let has_view_rules = if rewrite_suppressed {
392 false
393 } else {
394 record_view_rule_relation(
395 services,
396 &mut plan.view_rule_relations,
397 &layer,
398 crate::ast::RuleEvent::Delete,
399 )?
400 };
401 let layer_suppresses = has_view_rules
402 && super::context::relation_suppresses_original_query(
403 services,
404 &layer.canonical_name,
405 crate::ast::RuleEvent::Delete,
406 )?;
407 if visited.len() > 1 && !rewrite_suppressed && !layer_suppresses {
408 plan.target_privilege_subject = Some(privileges.check(
409 services.authorization,
410 &plan.table,
411 plan.target_privilege_subject.as_ref(),
412 || crate::semantics::view_privileges::ensure_delete(services.authorization, &plan),
413 )?);
414 }
415 if has_view_rules
416 && super::context::relation_has_returning_provider(
417 services,
418 &layer.canonical_name,
419 crate::ast::RuleEvent::Delete,
420 )?
421 {
422 preserve_view_rule_returning(
423 &mut plan.view_rule_returning,
424 &layer.canonical_name,
425 &plan.target_qualifier,
426 &plan.returning,
427 &plan.returning_aliases,
428 &plan.subqueries,
429 );
430 }
431 let target_qualifier = plan.target_qualifier.clone();
432 if visited.len() == 1 {
433 validate_delete_expressions(
434 services,
435 &plan,
436 &layer,
437 source_schema.as_ref(),
438 params,
439 inherited_ctes,
440 )?;
441 }
442 let ordinary_subquery_ids = delete_ordinary_subquery_ids(&plan);
443 rewrite_correlated_dml_context(
444 CorrelatedDmlContext {
445 inherited_ctes,
446 services,
447 layer: &layer,
448 target_qualifier: &target_qualifier,
449 source: source_schema.as_ref(),
450 returning_aliases: None,
451 include_excluded: false,
452 ctes: &plan.ctes,
453 ids: &ordinary_subquery_ids,
454 params,
455 },
456 &mut plan.subqueries,
457 )?;
458 let returning_subquery_ids = returning_subquery_ids(&plan.returning);
459 rewrite_correlated_dml_context(
460 CorrelatedDmlContext {
461 inherited_ctes,
462 services,
463 layer: &layer,
464 target_qualifier: &target_qualifier,
465 source: source_schema.as_ref(),
466 returning_aliases: Some(&plan.returning_aliases),
467 include_excluded: false,
468 ctes: &plan.ctes,
469 ids: &returning_subquery_ids,
470 params,
471 },
472 &mut plan.subqueries,
473 )?;
474 let ordinary_scope = ExpressionScope {
475 target_qualifier: &target_qualifier,
476 returning_aliases: None,
477 source: source_schema.as_ref(),
478 include_excluded: false,
479 };
480 if let Some(predicate) = &mut plan.predicate {
481 rewrite_target_expression(
482 services,
483 predicate,
484 &layer,
485 ordinary_scope,
486 &mut plan.subqueries,
487 )?;
488 }
489 let (returning, boundaries) = rewrite_returning(
490 services,
491 plan.returning,
492 &layer,
493 &target_qualifier,
494 &plan.returning_aliases,
495 source_schema.as_ref(),
496 &mut plan.subqueries,
497 )?;
498 plan.returning = returning;
499 if visited.len() == 1 {
500 source_star_boundaries = boundaries;
501 }
502 plan.predicate = combine_view_predicate(
503 services,
504 plan.predicate,
505 &layer,
506 &target_qualifier,
507 &mut plan.subqueries,
508 )?;
509 plan.table = layer.source_name;
510 plan.include_descendants = layer.source_include_descendants;
511 rewrite_suppressed |= layer_suppresses;
512 if !super::context::target_is_view(services, &plan.table)? {
513 break;
514 }
515 }
516 if let Some(source) = source_schema.as_ref() {
517 let target_width = dml_target_width(services, &plan.table)?;
518 if let Some(predicate) = &mut plan.predicate {
519 bind_unqualified_source_positions(predicate, source, target_width);
520 }
521 for projection in &mut plan.returning {
522 bind_unqualified_source_positions(&mut projection.expr, source, target_width);
523 }
524 }
525 plan.returning = finalize_source_returning(
526 services,
527 &plan.table,
528 plan.returning,
529 source_schema.as_ref(),
530 &source_star_boundaries,
531 )?;
532 privileges.finish()?;
533 Ok(plan)
534}