use crate::ast::{MergeStmt, MergeWhen, RuleEvent};
use super::{
action_returning_scope, ColumnBindingContext, ColumnBindingMode, ColumnScope, RelationIdentity,
RuleColumnDependency, SQLError, Statement, StoredColumnBinder, StoredColumnBindingContext,
};
pub fn rewrite_stored_statement_column(
catalog: StoredColumnBindingContext<'_>,
statement: &mut Statement,
relation: &RelationIdentity,
from: &str,
to: &str,
) -> Result<bool, SQLError> {
let mode = ColumnBindingMode::Rename { relation, from, to };
let mut binder = StoredColumnBinder::new(catalog, mode);
binder.bind_statement(statement, &[], &ColumnBindingContext::default())?;
let shape_changed = binder.alias_shape_changed();
Ok(binder.finish().contains(&RuleColumnDependency {
relation: relation.clone(),
column: from.to_string(),
}) || shape_changed)
}
impl StoredColumnBinder<'_> {
pub(super) fn bind_merge(
&mut self,
merge: &mut MergeStmt,
outer: &[ColumnScope],
context: &ColumnBindingContext,
) -> Result<(), SQLError> {
let context = self.bind_ctes(&mut merge.with, outer, context)?;
let target = self.table_scope(
&merge.target,
&merge.target_qualifier,
merge.target_alias.as_deref(),
&[],
None,
&ColumnBindingContext::default(),
)?;
let (local, scopes) =
self.bind_dml_source(Some(&mut merge.source), &target, outer, &context)?;
let dropped_targets =
crate::routines::merge_columns::dropped_stored_merge_targets(self.catalog.merge, merge);
self.bind_expr(&mut merge.join_condition, &scopes, &context)?;
for action in &mut merge.when_clauses {
let condition = match action {
MergeWhen::UpdateMatched {
condition,
assignments,
}
| MergeWhen::UpdateNotMatchedBySource {
condition,
assignments,
} => {
for (column, expression) in assignments {
if self.mode.is_rename() && !dropped_targets.contains(column) {
self.bind_target_name(column, &target);
}
self.bind_expr(expression, &scopes, &context)?;
}
condition
}
MergeWhen::InsertNotMatched {
condition,
columns,
values,
} => {
if columns.is_empty() {
*columns = target
.output
.iter()
.take(values.len())
.map(|column| column.current_name.clone())
.collect();
}
if self.mode.is_rename() {
for column in columns {
if !dropped_targets.contains(column) {
self.bind_target_name(column, &target);
}
}
}
for expression in values {
self.bind_expr(expression, &scopes, &context)?;
}
condition
}
MergeWhen::DeleteMatched { condition }
| MergeWhen::DeleteNotMatchedBySource { condition }
| MergeWhen::NothingMatched { condition }
| MergeWhen::NothingNotMatched { condition }
| MergeWhen::NothingNotMatchedBySource { condition } => condition,
};
if let Some(condition) = condition {
self.bind_expr(condition, &scopes, &context)?;
}
}
if let ColumnBindingMode::Rename { relation, from, to } = self.mode {
if merge.target == relation.qualified_name() && !dropped_targets.contains(from) {
if let Some(binding) = merge.target_column_bindings.remove(from) {
merge.target_column_bindings.insert(to.to_string(), binding);
}
}
}
let returning =
action_returning_scope(&local, &target, RuleEvent::Update, &merge.returning_aliases);
let mut scopes = vec![returning.clone()];
scopes.extend_from_slice(outer);
self.bind_projections(&mut merge.returning, Some(&returning), &scopes, &context)
}
}