Skip to main content

uqa_sql/semantics/view_rewrite/
rewrite_insert.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7use super::{
8    active_unconditional_instead_rule, add_check_option, automatic_view_layer,
9    duplicate_assignment, insert_conflict_subquery_ids, insert_input_width,
10    instead_of_trigger_definition, not_automatically_updatable, preserve_view_rule_returning,
11    record_view_rule_relation, returning_subquery_ids, rewrite_correlated_dml_context,
12    rewrite_existing_view_checks, rewrite_returning, rewrite_target_expression,
13    validate_direct_view_rule_path, validate_insert_expressions, validate_insert_targets,
14    validate_mapped_columns, validate_public_insert_contract, validate_public_view_targets,
15    view_updatability, writable_column, BTreeSet, ConflictActionPlan, CorrelatedDmlContext,
16    ExpressionScope, InsertPlan, SQLError, TriggerEvent, ViewRewriteContext, ViewRuleInsertPlan,
17};
18
19#[expect(
20    clippy::too_many_lines,
21    reason = "preserves view qualifier and row identity"
22)]
23pub fn rewrite_insert_to_base(
24    services: ViewRewriteContext<'_>,
25    statement: &InsertPlan,
26    params: &[crate::SQLParam],
27    inherited_ctes: Option<&super::CteScope>,
28) -> Result<InsertPlan, SQLError> {
29    validate_public_view_targets(
30        services,
31        &statement.table,
32        statement
33            .columns
34            .iter()
35            .map(|target| target.column.as_str()),
36    )?;
37    validate_public_insert_contract(services, statement)?;
38    let Some(initial_layer) = automatic_view_layer(services, &statement.table)? else {
39        return Err(not_automatically_updatable(&statement.table, "INSERT"));
40    };
41    validate_insert_targets(&initial_layer, statement)?;
42    validate_direct_view_rule_path(
43        services,
44        &initial_layer.canonical_name,
45        crate::ast::RuleEvent::Insert,
46        "INSERT",
47    )?;
48    if !view_updatability(services, &statement.table)?
49        .automatic
50        .insertable
51    {
52        return Err(not_automatically_updatable(&statement.table, "INSERT"));
53    }
54    let mut plan = statement.clone();
55    let next_privilege_subject =
56        crate::semantics::view_privileges::ensure_insert(services.authorization, &plan)?;
57    plan.target_privilege_subject = Some(next_privilege_subject);
58    let mut implicit_width = if statement.columns.is_empty() {
59        Some(insert_input_width(
60            services,
61            statement,
62            params,
63            inherited_ctes,
64        )?)
65    } else {
66        None
67    };
68    let mut cascaded = false;
69    let mut visited = BTreeSet::new();
70    let mut rewrite_suppressed = false;
71    loop {
72        let Some(layer) = automatic_view_layer(services, &plan.table)? else {
73            if active_unconditional_instead_rule(
74                services,
75                &plan.table,
76                crate::ast::RuleEvent::Insert,
77            )? {
78                break;
79            }
80            return Err(not_automatically_updatable(&plan.table, "INSERT"));
81        };
82        if !visited.insert(layer.canonical_name.clone()) {
83            return Err(SQLError::Internal(format!(
84                "cycle while rewriting automatically updatable view `{}`",
85                layer.canonical_name
86            )));
87        }
88        if !rewrite_suppressed {
89            validate_direct_view_rule_path(
90                services,
91                &layer.canonical_name,
92                crate::ast::RuleEvent::Insert,
93                "INSERT",
94            )?;
95        }
96        if !rewrite_suppressed
97            && visited.len() > 1
98            && instead_of_trigger_definition(services, &layer.canonical_name, TriggerEvent::Insert)?
99        {
100            return Err(not_automatically_updatable(&layer.canonical_name, "INSERT"));
101        }
102        let has_view_rules = if rewrite_suppressed {
103            false
104        } else {
105            record_view_rule_relation(
106                services,
107                &mut plan.view_rule_relations,
108                &layer,
109                crate::ast::RuleEvent::Insert,
110            )?
111        };
112        let layer_suppresses = has_view_rules
113            && super::context::relation_suppresses_original_query(
114                services,
115                &layer.canonical_name,
116                crate::ast::RuleEvent::Insert,
117            )?;
118        if visited.len() > 1 && !rewrite_suppressed && !layer_suppresses {
119            let next_privilege_subject =
120                crate::semantics::view_privileges::ensure_insert(services.authorization, &plan)?;
121            plan.target_privilege_subject = Some(next_privilege_subject);
122        }
123        if has_view_rules
124            && super::context::relation_has_returning_provider(
125                services,
126                &layer.canonical_name,
127                crate::ast::RuleEvent::Insert,
128            )?
129        {
130            preserve_view_rule_returning(
131                &mut plan.view_rule_returning,
132                &layer.canonical_name,
133                &plan.target_qualifier,
134                &plan.returning,
135                &plan.returning_aliases,
136                &plan.subqueries,
137            );
138        }
139        let target_qualifier = plan.target_qualifier.clone();
140        if visited.len() == 1 {
141            validate_insert_expressions(services, &plan, &layer, params, inherited_ctes)?;
142        }
143        let conflict_subquery_ids = insert_conflict_subquery_ids(&plan);
144        rewrite_correlated_dml_context(
145            CorrelatedDmlContext {
146                inherited_ctes,
147                services,
148                layer: &layer,
149                target_qualifier: &plan.target_qualifier,
150                source: None,
151                returning_aliases: None,
152                include_excluded: true,
153                ctes: &plan.ctes,
154                ids: &conflict_subquery_ids,
155                params,
156            },
157            &mut plan.subqueries,
158        )?;
159        let returning_subquery_ids = returning_subquery_ids(&plan.returning);
160        rewrite_correlated_dml_context(
161            CorrelatedDmlContext {
162                inherited_ctes,
163                services,
164                layer: &layer,
165                target_qualifier: &plan.target_qualifier,
166                source: None,
167                returning_aliases: Some(&plan.returning_aliases),
168                include_excluded: false,
169                ctes: &plan.ctes,
170                ids: &returning_subquery_ids,
171                params,
172            },
173            &mut plan.subqueries,
174        )?;
175        let supplied_columns = if let Some(width) = implicit_width.take() {
176            layer
177                .columns
178                .iter()
179                .take(width)
180                .map(|column| column.name.clone().into())
181                .collect::<Vec<_>>()
182        } else {
183            plan.columns.clone()
184        };
185        let columns = if rewrite_suppressed || layer_suppresses {
186            supplied_columns.clone()
187        } else {
188            supplied_columns
189                .clone()
190                .into_iter()
191                .map(|mut target| {
192                    target.column = writable_column(&layer, &target.column, "INSERT")?;
193                    Ok::<_, SQLError>(target)
194                })
195                .collect::<Result<Vec<_>, _>>()?
196        };
197        if has_view_rules {
198            plan.view_rule_insert_plans.push(ViewRuleInsertPlan {
199                relation: layer.canonical_name.clone(),
200                supplied_columns: supplied_columns
201                    .into_iter()
202                    .map(|target| target.column)
203                    .collect(),
204                input_columns: Vec::new(),
205            });
206        }
207        validate_mapped_columns(&columns, duplicate_assignment)?;
208        if let Some(conflict) = &mut plan.on_conflict {
209            for predicate in conflict
210                .expressions
211                .iter_mut()
212                .chain(conflict.predicate.iter_mut().map(Box::as_mut))
213            {
214                rewrite_target_expression(
215                    services,
216                    predicate,
217                    &layer,
218                    ExpressionScope {
219                        target_qualifier: &target_qualifier,
220                        returning_aliases: None,
221                        source: None,
222                        include_excluded: false,
223                    },
224                    &mut plan.subqueries,
225                )?;
226            }
227            conflict.conflict_columns = conflict
228                .conflict_columns
229                .iter()
230                .map(|column| writable_column(&layer, column, "INSERT"))
231                .collect::<Result<Vec<_>, _>>()?;
232            if let ConflictActionPlan::Update {
233                assignments,
234                predicate,
235            } = &mut conflict.action
236            {
237                let scope = ExpressionScope {
238                    target_qualifier: &target_qualifier,
239                    returning_aliases: None,
240                    source: None,
241                    include_excluded: true,
242                };
243                for assignment in assignments.iter_mut() {
244                    assignment.target.column =
245                        writable_column(&layer, &assignment.target.column, "UPDATE")?;
246                    for expression in assignment.expressions_mut() {
247                        rewrite_target_expression(
248                            services,
249                            expression,
250                            &layer,
251                            scope,
252                            &mut plan.subqueries,
253                        )?;
254                    }
255                }
256                let mapped = assignments
257                    .iter()
258                    .map(|assignment| assignment.target.clone())
259                    .collect::<Vec<_>>();
260                validate_mapped_columns(&mapped, duplicate_assignment)?;
261                if let Some(predicate) = predicate {
262                    rewrite_target_expression(
263                        services,
264                        predicate,
265                        &layer,
266                        scope,
267                        &mut plan.subqueries,
268                    )?;
269                }
270            }
271        }
272        rewrite_existing_view_checks(
273            services,
274            &mut plan.view_checks,
275            &layer,
276            &target_qualifier,
277            &mut plan.subqueries,
278        )?;
279        let (returning, _) = rewrite_returning(
280            services,
281            plan.returning,
282            &layer,
283            &target_qualifier,
284            &plan.returning_aliases,
285            None,
286            &mut plan.subqueries,
287        )?;
288        plan.returning = returning;
289        add_check_option(
290            services,
291            &mut plan.view_checks,
292            &layer,
293            &target_qualifier,
294            &mut cascaded,
295            &mut plan.subqueries,
296        )?;
297        plan.columns = columns;
298        plan.table = layer.source_name;
299        plan.include_descendants = true;
300        rewrite_suppressed |= layer_suppresses;
301        if !super::context::target_is_view(services, &plan.table)? {
302            break;
303        }
304    }
305    for insert_plan in &mut plan.view_rule_insert_plans {
306        insert_plan.input_columns = plan
307            .columns
308            .iter()
309            .map(|target| target.column.clone())
310            .collect();
311    }
312    Ok(plan)
313}