Skip to main content

uqa_sql/binding/stored_columns/
statements.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Column dependencies of stored MERGE actions, including target assignments.
8
9use crate::ast::{MergeStmt, MergeWhen, RuleEvent};
10
11use super::{
12    action_returning_scope, ColumnBindingContext, ColumnBindingMode, ColumnScope, RelationIdentity,
13    RuleColumnDependency, SQLError, Statement, StoredColumnBinder, StoredColumnBindingContext,
14};
15
16pub fn rewrite_stored_statement_column(
17    catalog: StoredColumnBindingContext<'_>,
18    statement: &mut Statement,
19    relation: &RelationIdentity,
20    from: &str,
21    to: &str,
22) -> Result<bool, SQLError> {
23    let mode = ColumnBindingMode::Rename { relation, from, to };
24    let mut binder = StoredColumnBinder::new(catalog, mode);
25    binder.bind_statement(statement, &[], &ColumnBindingContext::default())?;
26    let shape_changed = binder.alias_shape_changed();
27    Ok(binder.finish().contains(&RuleColumnDependency {
28        relation: relation.clone(),
29        column: from.to_string(),
30    }) || shape_changed)
31}
32
33impl StoredColumnBinder<'_> {
34    pub(super) fn bind_merge(
35        &mut self,
36        merge: &mut MergeStmt,
37        outer: &[ColumnScope],
38        context: &ColumnBindingContext,
39    ) -> Result<(), SQLError> {
40        let context = self.bind_ctes(&mut merge.with, outer, context)?;
41        let target = self.table_scope(
42            &merge.target,
43            &merge.target_qualifier,
44            merge.target_alias.as_deref(),
45            &[],
46            None,
47            &ColumnBindingContext::default(),
48        )?;
49        let (local, scopes) =
50            self.bind_dml_source(Some(&mut merge.source), &target, outer, &context)?;
51        let dropped_targets =
52            crate::routines::merge_columns::dropped_stored_merge_targets(self.catalog.merge, merge);
53        self.bind_expr(&mut merge.join_condition, &scopes, &context)?;
54        for action in &mut merge.when_clauses {
55            let condition = match action {
56                MergeWhen::UpdateMatched {
57                    condition,
58                    assignments,
59                }
60                | MergeWhen::UpdateNotMatchedBySource {
61                    condition,
62                    assignments,
63                } => {
64                    for (column, expression) in assignments {
65                        for target_column in column.targets_mut() {
66                            if self.mode.is_rename()
67                                && !dropped_targets.contains(&target_column.column)
68                            {
69                                self.bind_target_name(&mut target_column.column, &target);
70                            }
71                        }
72                        for expression in column.expressions_mut() {
73                            self.bind_expr(expression, &scopes, &context)?;
74                        }
75                        self.bind_expr(expression, &scopes, &context)?;
76                    }
77                    condition
78                }
79                MergeWhen::InsertNotMatched {
80                    condition,
81                    columns,
82                    values,
83                    ..
84                } => {
85                    if columns.is_empty() {
86                        *columns = target
87                            .output
88                            .iter()
89                            .take(values.len())
90                            .map(|column| column.current_name.clone().into())
91                            .collect();
92                    }
93                    for column in columns {
94                        if self.mode.is_rename() && !dropped_targets.contains(&column.column) {
95                            self.bind_target_name(&mut column.column, &target);
96                        }
97                        for expression in column.expressions_mut() {
98                            self.bind_expr(expression, &scopes, &context)?;
99                        }
100                    }
101                    for expression in values {
102                        self.bind_expr(expression, &scopes, &context)?;
103                    }
104                    condition
105                }
106                MergeWhen::DeleteMatched { condition }
107                | MergeWhen::DeleteNotMatchedBySource { condition }
108                | MergeWhen::NothingMatched { condition }
109                | MergeWhen::NothingNotMatched { condition }
110                | MergeWhen::NothingNotMatchedBySource { condition } => condition,
111            };
112            if let Some(condition) = condition {
113                self.bind_expr(condition, &scopes, &context)?;
114            }
115        }
116        if let ColumnBindingMode::Rename { relation, from, to } = self.mode {
117            if merge.target == relation.qualified_name() && !dropped_targets.contains(from) {
118                if let Some(binding) = merge.target_column_bindings.remove(from) {
119                    merge.target_column_bindings.insert(to.to_string(), binding);
120                }
121            }
122        }
123        let returning =
124            action_returning_scope(&local, &target, RuleEvent::Update, &merge.returning_aliases);
125        let mut scopes = vec![returning.clone()];
126        scopes.extend_from_slice(outer);
127        self.bind_projections(&mut merge.returning, Some(&returning), &scopes, &context)
128    }
129}