Skip to main content

uqa_sql/binding/
stored_columns.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Stored statement column identities, durable source shapes, and rewrite-rule binding.
8
9mod expressions;
10mod helpers;
11mod lifecycle;
12mod sources;
13mod statements;
14
15pub use lifecycle::{
16    bind_rule_action_column_dependencies, bind_rule_condition_column_dependencies,
17    bind_stored_statement_source_columns, remove_rule_source_column_aliases,
18    remove_stored_statement_source_column_aliases, rewrite_rule_column_references,
19    stored_statement_column_dependencies,
20};
21pub use statements::rewrite_stored_statement_column;
22
23/// Current names of a bound row relation, resolved when a binding pass needs them.
24pub trait StoredColumnCatalog {
25    fn stored_relation_column_names(&self, name: &str) -> Result<Option<Vec<String>>, SQLError>;
26}
27
28/// Catalog and namespace snapshots captured before resolving stored source columns.
29pub struct StoredSourceColumns {
30    pub catalog: crate::catalog::analysis::CatalogReadView,
31    pub resolution: crate::catalog::resolution::RelationNameResolution,
32}
33pub trait StoredSourceCatalog {
34    fn stored_source_columns(&self) -> StoredSourceColumns;
35}
36impl StoredSourceColumns {
37    pub fn bind_statement(&self, statement: &mut Statement) -> Result<bool, SQLError> {
38        bind_stored_statement_source_columns(statement, |name| {
39            Ok(
40                if let Some(table) = self.catalog.table_resolved(&self.resolution, name)? {
41                    Some(
42                        table
43                            .columns
44                            .iter()
45                            .map(|column| column.name.clone())
46                            .collect(),
47                    )
48                } else {
49                    self.catalog
50                        .foreign_table_resolved(&self.resolution, name)?
51                        .map(|table| {
52                            table
53                                .columns
54                                .iter()
55                                .map(|column| column.name.clone())
56                                .collect()
57                        })
58                },
59            )
60        })
61    }
62}
63
64#[derive(Clone, Copy)]
65pub struct StoredColumnBindingContext<'a> {
66    pub sources: &'a dyn StoredColumnCatalog,
67    pub merge: &'a dyn crate::routines::merge_columns::StoredMergeColumnCatalog,
68}
69
70use std::collections::{BTreeMap, BTreeSet};
71
72use crate::ast::{
73    DeleteStmt, Expr, FromClause, InsertStmt, OnConflictAction, Projection, SelectStmt, Statement,
74    UpdateStmt, CTE,
75};
76use crate::SQLError;
77
78use uqa_core::RelationIdentity;
79
80use crate::catalog::events::RuleColumnDependency;
81use helpers::{
82    action_returning_scope, apply_positional_aliases, is_default_values_insert, is_output_alias,
83    opaque_scope, preserve_table_column_name, same_identifier, select_output_names,
84    table_alias_count_error, unique_current_name,
85};
86
87fn cte_output_names(body: &crate::ast::CteBody) -> Vec<String> {
88    if let Some(query) = body.query() {
89        return select_output_names(query);
90    }
91    helpers::projection_output_names(body.returning().unwrap_or_default())
92}
93
94#[derive(Clone, Copy)]
95enum ColumnBindingMode<'a> {
96    Bind,
97    Rename {
98        relation: &'a RelationIdentity,
99        from: &'a str,
100        to: &'a str,
101    },
102    Drop {
103        dependencies: &'a BTreeSet<RuleColumnDependency>,
104    },
105}
106
107impl<'a> ColumnBindingMode<'a> {
108    fn column_names(self, relation: &RelationIdentity, current: &str) -> (String, String) {
109        match self {
110            Self::Rename {
111                relation: target,
112                from,
113                to,
114            } if relation == target && current == to => (from.to_string(), to.to_string()),
115            Self::Bind | Self::Rename { .. } | Self::Drop { .. } => {
116                (current.to_string(), current.to_string())
117            }
118        }
119    }
120
121    const fn is_rename(self) -> bool {
122        matches!(self, Self::Rename { .. })
123    }
124
125    const fn dropped_dependencies(self) -> Option<&'a BTreeSet<RuleColumnDependency>> {
126        match self {
127            Self::Drop { dependencies } => Some(dependencies),
128            Self::Bind | Self::Rename { .. } => None,
129        }
130    }
131}
132
133#[derive(Clone)]
134struct ScopeColumn {
135    /// Name present in the stored definition before this binding pass.
136    name: String,
137    /// Name present in the live catalog after this binding pass.
138    current_name: String,
139    /// Reference that identifies this output in the enclosing query scope.
140    reference: Expr,
141    dependencies: BTreeSet<RuleColumnDependency>,
142}
143
144#[derive(Clone, Default)]
145struct ColumnScope {
146    output: Vec<ScopeColumn>,
147    qualifiers: BTreeMap<String, Vec<ScopeColumn>>,
148}
149
150impl ColumnScope {
151    fn insert_qualifier(&mut self, qualifier: &str, columns: &[ScopeColumn]) {
152        self.qualifiers
153            .insert(qualifier.to_ascii_lowercase(), columns.to_vec());
154    }
155
156    fn qualified(&self, qualifier: &str) -> Option<&[ScopeColumn]> {
157        self.qualifiers
158            .get(&qualifier.to_ascii_lowercase())
159            .map(Vec::as_slice)
160    }
161
162    fn unqualified(&self, name: &str) -> Vec<&ScopeColumn> {
163        self.output
164            .iter()
165            .filter(|column| same_identifier(&column.name, name))
166            .collect()
167    }
168
169    fn combined(left: &Self, right: &Self) -> Self {
170        let mut output = left.output.clone();
171        output.extend(right.output.iter().cloned());
172        let mut qualifiers = left.qualifiers.clone();
173        qualifiers.extend(right.qualifiers.clone());
174        Self { output, qualifiers }
175    }
176}
177
178#[derive(Clone, Default)]
179struct ColumnBindingContext {
180    ctes: BTreeMap<String, Vec<String>>,
181}
182
183struct StoredColumnBinder<'a> {
184    catalog: StoredColumnBindingContext<'a>,
185    mode: ColumnBindingMode<'a>,
186    dependencies: BTreeSet<RuleColumnDependency>,
187    alias_shape_changed: bool,
188}
189
190impl<'a> StoredColumnBinder<'a> {
191    fn new(catalog: StoredColumnBindingContext<'a>, mode: ColumnBindingMode<'a>) -> Self {
192        Self {
193            catalog,
194            mode,
195            dependencies: BTreeSet::new(),
196            alias_shape_changed: false,
197        }
198    }
199
200    fn finish(self) -> BTreeSet<RuleColumnDependency> {
201        self.dependencies
202    }
203
204    const fn alias_shape_changed(&self) -> bool {
205        self.alias_shape_changed
206    }
207
208    fn remove_dropped_column_aliases(
209        &mut self,
210        column_aliases: &mut Vec<String>,
211        scope: &ColumnScope,
212    ) {
213        let Some(dependencies) = self.mode.dropped_dependencies() else {
214            return;
215        };
216        let positions = scope
217            .output
218            .iter()
219            .enumerate()
220            .filter_map(|(position, column)| {
221                (!column.dependencies.is_disjoint(dependencies)).then_some(position)
222            })
223            .collect::<Vec<_>>();
224        for position in positions.into_iter().rev() {
225            if position < column_aliases.len() {
226                column_aliases.remove(position);
227                self.alias_shape_changed = true;
228            }
229        }
230    }
231
232    fn table_scope(
233        &self,
234        name: &str,
235        qualifier: &str,
236        alias: Option<&str>,
237        column_aliases: &[String],
238        bound_columns: Option<&[String]>,
239        context: &ColumnBindingContext,
240    ) -> Result<ColumnScope, SQLError> {
241        if let Some(columns) = context.ctes.get(&name.to_ascii_lowercase()) {
242            if column_aliases.len() > columns.len() {
243                return Err(table_alias_count_error(
244                    alias.unwrap_or(qualifier),
245                    columns.len(),
246                    column_aliases.len(),
247                ));
248            }
249            let mut columns = columns.clone();
250            apply_positional_aliases(&mut columns, column_aliases);
251            return Ok(opaque_scope(&columns, Some(alias.unwrap_or(qualifier))));
252        }
253        let columns = match bound_columns {
254            Some(columns) => columns.to_vec(),
255            None => self
256                .catalog
257                .sources
258                .stored_relation_column_names(name)?
259                .ok_or_else(|| SQLError::UnknownTable(name.to_string()))?,
260        };
261        if column_aliases.len() > columns.len() {
262            return Err(table_alias_count_error(
263                alias.unwrap_or(qualifier),
264                columns.len(),
265                column_aliases.len(),
266            ));
267        }
268        let relation = RelationIdentity::from_legacy_name(name).map_err(|error| {
269            SQLError::Internal(format!("decode bound rule relation `{name}`: {error}"))
270        })?;
271        let visible_qualifier = alias.unwrap_or(qualifier);
272        let output = columns
273            .into_iter()
274            .enumerate()
275            .map(|(position, column)| {
276                let (stored_physical, current_physical) =
277                    self.mode.column_names(&relation, &column);
278                let name = column_aliases
279                    .get(position)
280                    .cloned()
281                    .unwrap_or_else(|| stored_physical.clone());
282                let current_name = column_aliases
283                    .get(position)
284                    .cloned()
285                    .unwrap_or(current_physical);
286                ScopeColumn {
287                    reference: Expr::qualified_column(visible_qualifier, &current_name),
288                    dependencies: BTreeSet::from([RuleColumnDependency {
289                        relation: relation.clone(),
290                        column: stored_physical,
291                    }]),
292                    name,
293                    current_name,
294                }
295            })
296            .collect::<Vec<_>>();
297        let mut scope = ColumnScope {
298            output: output.clone(),
299            ..ColumnScope::default()
300        };
301        if let Some(alias) = alias {
302            scope.insert_qualifier(alias, &output);
303        } else {
304            scope.insert_qualifier(qualifier, &output);
305            scope.insert_qualifier(name, &output);
306            if let Some((_, local)) = name.rsplit_once('.') {
307                scope.insert_qualifier(local.trim_matches('"'), &output);
308            }
309        }
310        Ok(scope)
311    }
312
313    fn bind_statement(
314        &mut self,
315        statement: &mut Statement,
316        outer: &[ColumnScope],
317        context: &ColumnBindingContext,
318    ) -> Result<(), SQLError> {
319        match statement {
320            Statement::Select(select) => self.bind_select(select, outer, context),
321            Statement::Insert(insert) => self.bind_insert(insert, outer, context),
322            Statement::Update(update) => self.bind_update(update, outer, context),
323            Statement::Delete(delete) => self.bind_delete(delete, outer, context),
324            Statement::Merge(merge) => self.bind_merge(merge, outer, context),
325            Statement::Notify { .. } => Ok(()),
326            _ => Err(SQLError::Internal(
327                "validated rewrite-rule action has an unsupported statement kind".into(),
328            )),
329        }
330    }
331
332    fn bind_insert(
333        &mut self,
334        insert: &mut InsertStmt,
335        outer: &[ColumnScope],
336        context: &ColumnBindingContext,
337    ) -> Result<(), SQLError> {
338        let context = self.bind_ctes(&mut insert.with, outer, context)?;
339        for expression in insert.rows.iter_mut().flatten() {
340            self.bind_expr(expression, outer, &context)?;
341        }
342        if let Some(select) = insert.select_source.as_deref_mut() {
343            self.bind_select(select, outer, &context)?;
344        }
345        let target = self.table_scope(
346            &insert.table,
347            &insert.target_qualifier,
348            Some(&insert.target_qualifier),
349            &[],
350            None,
351            &ColumnBindingContext::default(),
352        )?;
353        if insert.columns.is_empty() && !is_default_values_insert(&insert.rows) {
354            let width = insert.rows.first().map(Vec::len).or_else(|| {
355                insert
356                    .select_source
357                    .as_deref()
358                    .map(select_output_names)
359                    .map(|columns| columns.len())
360            });
361            if let Some(width) = width {
362                insert.columns = target
363                    .output
364                    .iter()
365                    .take(width)
366                    .map(|column| column.current_name.clone().into())
367                    .collect();
368            }
369        }
370        for assignment in &mut insert.columns {
371            self.bind_target_name(&mut assignment.column, &target);
372            for expression in assignment.expressions_mut() {
373                self.bind_expr(expression, outer, &context)?;
374            }
375        }
376        let mut conflict_scope = target.clone();
377        conflict_scope.insert_qualifier("excluded", &target.output);
378        let mut target_scopes = vec![conflict_scope];
379        target_scopes.extend_from_slice(outer);
380        if let Some(conflict) = &mut insert.on_conflict {
381            self.bind_target_names(&mut conflict.conflict_columns, &target);
382            for expression in &mut conflict.expressions {
383                self.bind_expr(expression, &target_scopes, &context)?;
384            }
385            if let Some(predicate) = conflict.predicate.as_deref_mut() {
386                self.bind_expr(predicate, &target_scopes, &context)?;
387            }
388            if let OnConflictAction::Update {
389                assignments,
390                r#where,
391            } = &mut conflict.action
392            {
393                for (column, expression) in assignments {
394                    self.bind_target_name(&mut column.column, &target);
395                    for expression in column.expressions_mut() {
396                        self.bind_expr(expression, &target_scopes, &context)?;
397                    }
398                    self.bind_expr(expression, &target_scopes, &context)?;
399                }
400                if let Some(expression) = r#where {
401                    self.bind_expr(expression, &target_scopes, &context)?;
402                }
403            }
404        }
405        let returning = action_returning_scope(
406            &target,
407            &target,
408            crate::ast::RuleEvent::Insert,
409            &insert.returning_aliases,
410        );
411        let mut returning_scopes = vec![returning.clone()];
412        returning_scopes.extend_from_slice(outer);
413        self.bind_projections(
414            &mut insert.returning,
415            Some(&returning),
416            &returning_scopes,
417            &context,
418        )
419    }
420
421    fn bind_update(
422        &mut self,
423        update: &mut UpdateStmt,
424        outer: &[ColumnScope],
425        context: &ColumnBindingContext,
426    ) -> Result<(), SQLError> {
427        let context = self.bind_ctes(&mut update.with, outer, context)?;
428        let target = self.table_scope(
429            &update.table,
430            &update.target_qualifier,
431            Some(&update.target_qualifier),
432            &[],
433            None,
434            &ColumnBindingContext::default(),
435        )?;
436        let (local, scopes) =
437            self.bind_dml_source(update.from.as_mut(), &target, outer, &context)?;
438        for (column, expression) in &mut update.assignments {
439            self.bind_target_name(&mut column.column, &target);
440            for expression in column.expressions_mut() {
441                self.bind_expr(expression, &scopes, &context)?;
442            }
443            self.bind_expr(expression, &scopes, &context)?;
444        }
445        if let Some(expression) = &mut update.r#where {
446            self.bind_expr(expression, &scopes, &context)?;
447        }
448        let returning = action_returning_scope(
449            &local,
450            &target,
451            crate::ast::RuleEvent::Update,
452            &update.returning_aliases,
453        );
454        let mut returning_scopes = vec![returning.clone()];
455        returning_scopes.extend_from_slice(outer);
456        self.bind_projections(
457            &mut update.returning,
458            Some(&returning),
459            &returning_scopes,
460            &context,
461        )
462    }
463
464    fn bind_delete(
465        &mut self,
466        delete: &mut DeleteStmt,
467        outer: &[ColumnScope],
468        context: &ColumnBindingContext,
469    ) -> Result<(), SQLError> {
470        let context = self.bind_ctes(&mut delete.with, outer, context)?;
471        let target = self.table_scope(
472            &delete.table,
473            &delete.target_qualifier,
474            Some(&delete.target_qualifier),
475            &[],
476            None,
477            &ColumnBindingContext::default(),
478        )?;
479        let (local, scopes) =
480            self.bind_dml_source(delete.using.as_mut(), &target, outer, &context)?;
481        if let Some(expression) = &mut delete.r#where {
482            self.bind_expr(expression, &scopes, &context)?;
483        }
484        let returning = action_returning_scope(
485            &local,
486            &target,
487            crate::ast::RuleEvent::Delete,
488            &delete.returning_aliases,
489        );
490        let mut returning_scopes = vec![returning.clone()];
491        returning_scopes.extend_from_slice(outer);
492        self.bind_projections(
493            &mut delete.returning,
494            Some(&returning),
495            &returning_scopes,
496            &context,
497        )
498    }
499
500    fn bind_dml_source(
501        &mut self,
502        source: Option<&mut FromClause>,
503        target: &ColumnScope,
504        outer: &[ColumnScope],
505        context: &ColumnBindingContext,
506    ) -> Result<(ColumnScope, Vec<ColumnScope>), SQLError> {
507        let mut source_outer = vec![target.clone()];
508        source_outer.extend_from_slice(outer);
509        let source = source
510            .map(|source| self.bind_from(source, &source_outer, context))
511            .transpose()?;
512        let local = source.as_ref().map_or_else(
513            || target.clone(),
514            |source| ColumnScope::combined(target, source),
515        );
516        let mut scopes = vec![local.clone()];
517        scopes.extend_from_slice(outer);
518        Ok((local, scopes))
519    }
520
521    fn bind_target_names(&mut self, names: &mut [String], target: &ColumnScope) {
522        for name in names {
523            self.bind_target_name(name, target);
524        }
525    }
526
527    fn bind_target_name(&mut self, name: &mut String, target: &ColumnScope) {
528        let Some(column) = target
529            .output
530            .iter()
531            .find(|column| same_identifier(&column.name, name))
532        else {
533            return;
534        };
535        self.dependencies
536            .extend(column.dependencies.iter().cloned());
537        name.clone_from(&column.current_name);
538    }
539
540    fn bind_ctes(
541        &mut self,
542        ctes: &mut [CTE],
543        outer: &[ColumnScope],
544        context: &ColumnBindingContext,
545    ) -> Result<ColumnBindingContext, SQLError> {
546        let mut visible = context.clone();
547        let recursive_names = ctes
548            .iter()
549            .filter(|cte| cte.recursive)
550            .map(|cte| {
551                let columns = if cte.columns.is_empty() {
552                    cte_output_names(&cte.body)
553                } else {
554                    cte.columns.clone()
555                };
556                (cte.name.to_ascii_lowercase(), columns)
557            })
558            .collect::<Vec<_>>();
559        for (name, columns) in recursive_names {
560            visible.ctes.entry(name).or_insert(columns);
561        }
562        for cte in ctes {
563            let mut statement = cte.body.clone().into_statement();
564            self.bind_statement(&mut statement, outer, &visible)?;
565            cte.body = statement.try_into()?;
566            if let Some(cycle) = &mut cte.cycle {
567                self.bind_expr(&mut cycle.mark_value, outer, &visible)?;
568                self.bind_expr(&mut cycle.mark_default, outer, &visible)?;
569            }
570            let mut columns = cte_output_names(&cte.body);
571            apply_positional_aliases(&mut columns, &cte.columns);
572            if let Some(search) = &cte.search {
573                columns.push(search.sequence_column.clone());
574            }
575            if let Some(cycle) = &cte.cycle {
576                columns.push(cycle.mark_column.clone());
577                columns.push(cycle.path_column.clone());
578            }
579            visible.ctes.insert(cte.name.to_ascii_lowercase(), columns);
580        }
581        Ok(visible)
582    }
583
584    fn bind_select(
585        &mut self,
586        select: &mut SelectStmt,
587        outer: &[ColumnScope],
588        context: &ColumnBindingContext,
589    ) -> Result<(), SQLError> {
590        let context = self.bind_ctes(&mut select.with, outer, context)?;
591        let local = select
592            .from
593            .as_mut()
594            .map(|source| self.bind_from(source, outer, &context))
595            .transpose()?;
596        let mut scopes = local.iter().cloned().collect::<Vec<_>>();
597        scopes.extend_from_slice(outer);
598        self.bind_projections(&mut select.projections, local.as_ref(), &scopes, &context)?;
599        for expression in select.values.iter_mut().flatten() {
600            self.bind_expr(expression, &scopes, &context)?;
601        }
602        if let Some(expression) = &mut select.r#where {
603            self.bind_expr(expression, &scopes, &context)?;
604        }
605        for expression in &mut select.group_by {
606            self.bind_expr(expression, &scopes, &context)?;
607        }
608        for expression in select.grouping_sets.iter_mut().flatten() {
609            self.bind_expr(expression, &scopes, &context)?;
610        }
611        if let Some(expression) = &mut select.having {
612            self.bind_expr(expression, &scopes, &context)?;
613        }
614        let output_names = select_output_names(select);
615        for order in &mut select.order_by {
616            if !is_output_alias(&order.expr, &output_names) {
617                self.bind_expr(&mut order.expr, &scopes, &context)?;
618            }
619        }
620        if let Some(expression) = &mut select.limit {
621            self.bind_expr(expression, &scopes, &context)?;
622        }
623        if let Some(expression) = &mut select.offset {
624            self.bind_expr(expression, &scopes, &context)?;
625        }
626        for expression in &mut select.distinct_on {
627            self.bind_expr(expression, &scopes, &context)?;
628        }
629        if let Some(set) = &mut select.set_op {
630            if let Some(left) = &mut set.left {
631                self.bind_select(left, outer, &context)?;
632            }
633            self.bind_select(&mut set.right, outer, &context)?;
634            let set_output = set
635                .left
636                .as_deref()
637                .map_or_else(|| output_names.clone(), select_output_names);
638            for order in &mut set.combined_order_by {
639                if !is_output_alias(&order.expr, &set_output) {
640                    self.bind_expr(&mut order.expr, outer, &context)?;
641                }
642            }
643            if let Some(expression) = &mut set.combined_limit {
644                self.bind_expr(expression, outer, &context)?;
645            }
646            if let Some(expression) = &mut set.combined_offset {
647                self.bind_expr(expression, outer, &context)?;
648            }
649        }
650        Ok(())
651    }
652
653    fn bind_from(
654        &mut self,
655        source: &mut FromClause,
656        outer: &[ColumnScope],
657        context: &ColumnBindingContext,
658    ) -> Result<ColumnScope, SQLError> {
659        match source {
660            source @ FromClause::Table { .. } => self.bind_table_source(source, context),
661            source @ FromClause::Join { .. } => self.bind_join(source, outer, context),
662            FromClause::Values {
663                rows,
664                alias,
665                column_aliases,
666                ..
667            } => {
668                for expression in rows.iter_mut().flatten() {
669                    self.bind_expr(expression, outer, context)?;
670                }
671                let mut columns = if column_aliases.is_empty() {
672                    (1..=rows.first().map_or(0, Vec::len))
673                        .map(|position| format!("column{position}"))
674                        .collect::<Vec<_>>()
675                } else {
676                    column_aliases.clone()
677                };
678                apply_positional_aliases(&mut columns, column_aliases);
679                Ok(opaque_scope(&columns, alias.as_deref()))
680            }
681            FromClause::Function {
682                output_name,
683                args,
684                alias,
685                column_aliases,
686                ordinality,
687                ..
688            } => {
689                for expression in args {
690                    self.bind_expr(expression, outer, context)?;
691                }
692                let mut columns = vec![output_name.clone()];
693                apply_positional_aliases(&mut columns, column_aliases);
694                if *ordinality {
695                    columns.push("ordinality".into());
696                }
697                Ok(opaque_scope(
698                    &columns,
699                    Some(alias.as_deref().unwrap_or(output_name)),
700                ))
701            }
702            FromClause::FunctionGroup {
703                functions,
704                alias,
705                column_aliases,
706                ordinality,
707            } => {
708                for function in functions.iter_mut() {
709                    for expression in &mut function.args {
710                        self.bind_expr(expression, outer, context)?;
711                    }
712                }
713                let mut columns = functions
714                    .iter()
715                    .flat_map(|function| {
716                        if function.column_aliases.is_empty() {
717                            vec![function.output_name.clone()]
718                        } else {
719                            function.column_aliases.clone()
720                        }
721                    })
722                    .collect::<Vec<_>>();
723                apply_positional_aliases(&mut columns, column_aliases);
724                if *ordinality {
725                    columns.push("ordinality".into());
726                }
727                Ok(opaque_scope(&columns, alias.as_deref()))
728            }
729            FromClause::Subquery {
730                body,
731                alias,
732                column_aliases,
733            } => {
734                self.bind_select(body, outer, context)?;
735                let mut columns = select_output_names(body);
736                apply_positional_aliases(&mut columns, column_aliases);
737                Ok(opaque_scope(&columns, alias.as_deref()))
738            }
739        }
740    }
741
742    fn bind_join(
743        &mut self,
744        source: &mut FromClause,
745        outer: &[ColumnScope],
746        context: &ColumnBindingContext,
747    ) -> Result<ColumnScope, SQLError> {
748        let FromClause::Join {
749            left,
750            right,
751            on,
752            using,
753            natural,
754            alias,
755            column_aliases,
756            lateral,
757            ..
758        } = source
759        else {
760            unreachable!("join column binding requires a join source")
761        };
762        if let ColumnBindingMode::Rename { relation, from, to } = self.mode {
763            if *natural
764                || using.as_ref().is_some_and(|using| {
765                    using
766                        .columns
767                        .iter()
768                        .any(|column| same_identifier(column, from))
769                })
770            {
771                preserve_table_column_name(self.catalog, left, relation, from, to)?;
772                preserve_table_column_name(self.catalog, right, relation, from, to)?;
773            }
774        }
775        let left_scope = self.bind_from(left, outer, context)?;
776        let mut right_outer = Vec::new();
777        if *lateral {
778            right_outer.push(left_scope.clone());
779        }
780        right_outer.extend_from_slice(outer);
781        let right_scope = self.bind_from(right, &right_outer, context)?;
782        let input_scope = ColumnScope::combined(&left_scope, &right_scope);
783        let mut on_scopes = vec![input_scope];
784        on_scopes.extend_from_slice(outer);
785        if let Some(expression) = on {
786            self.bind_expr(expression, &on_scopes, context)?;
787        }
788        if *natural {
789            let columns = left_scope
790                .output
791                .iter()
792                .filter(|left| {
793                    right_scope
794                        .output
795                        .iter()
796                        .any(|right| same_identifier(&left.name, &right.name))
797                })
798                .map(|column| column.name.clone())
799                .collect();
800            *using = Some(crate::ast::JoinUsing {
801                columns,
802                alias: None,
803            });
804            *natural = false;
805        }
806        let scope = self.join_scope(
807            left_scope,
808            right_scope,
809            using.as_mut(),
810            alias.as_deref(),
811            column_aliases,
812        )?;
813        self.remove_dropped_column_aliases(column_aliases, &scope);
814        Ok(scope)
815    }
816
817    fn join_scope(
818        &mut self,
819        left: ColumnScope,
820        right: ColumnScope,
821        using: Option<&mut crate::ast::JoinUsing>,
822        alias: Option<&str>,
823        column_aliases: &[String],
824    ) -> Result<ColumnScope, SQLError> {
825        let using_alias = using.as_ref().and_then(|using| using.alias.clone());
826        let mut merged = Vec::new();
827        if let Some(using) = using {
828            for name in &mut using.columns {
829                let left_matches = left.unqualified(name);
830                let right_matches = right.unqualified(name);
831                for column in left_matches.iter().chain(&right_matches) {
832                    self.dependencies
833                        .extend(column.dependencies.iter().cloned());
834                }
835                let left_current = unique_current_name(&left_matches);
836                let right_current = unique_current_name(&right_matches);
837                if left_current.is_some()
838                    && right_current.is_some()
839                    && left_current != right_current
840                {
841                    return Err(SQLError::Internal(format!(
842                        "rule JOIN USING column \"{name}\" resolved to different visible names after column rebinding"
843                    )));
844                }
845                let current_name = left_current
846                    .or(right_current)
847                    .unwrap_or_else(|| name.clone());
848                let mut dependencies = BTreeSet::new();
849                for column in left_matches.iter().chain(&right_matches) {
850                    dependencies.extend(column.dependencies.iter().cloned());
851                }
852                let stored_name = name.clone();
853                name.clone_from(&current_name);
854                merged.push(ScopeColumn {
855                    name: stored_name,
856                    current_name: current_name.clone(),
857                    reference: Expr::Column(current_name),
858                    dependencies,
859                });
860            }
861        }
862        let merged_names = merged
863            .iter()
864            .map(|column| column.name.clone())
865            .collect::<Vec<_>>();
866        let mut output = merged;
867        output.extend(
868            left.output
869                .iter()
870                .chain(&right.output)
871                .filter(|column| {
872                    !merged_names
873                        .iter()
874                        .any(|name| same_identifier(name, &column.name))
875                })
876                .cloned(),
877        );
878        if let Some(alias) = alias {
879            for (position, column) in output.iter_mut().enumerate() {
880                if let Some(name) = column_aliases.get(position) {
881                    column.name.clone_from(name);
882                    column.current_name.clone_from(name);
883                }
884                column.reference = Expr::qualified_column(alias, &column.current_name);
885            }
886            let mut scope = ColumnScope {
887                output: output.clone(),
888                ..ColumnScope::default()
889            };
890            scope.insert_qualifier(alias, &output);
891            return Ok(scope);
892        }
893        let mut qualifiers = left.qualifiers;
894        qualifiers.extend(right.qualifiers);
895        let mut scope = ColumnScope { output, qualifiers };
896        if let Some(using_alias) = using_alias.as_deref() {
897            let merged = scope
898                .output
899                .iter()
900                .filter(|column| {
901                    merged_names
902                        .iter()
903                        .any(|name| same_identifier(name, &column.name))
904                })
905                .cloned()
906                .collect::<Vec<_>>();
907            scope.insert_qualifier(using_alias, &merged);
908        }
909        Ok(scope)
910    }
911
912    fn bind_projections(
913        &mut self,
914        projections: &mut Vec<Projection>,
915        local: Option<&ColumnScope>,
916        scopes: &[ColumnScope],
917        context: &ColumnBindingContext,
918    ) -> Result<(), SQLError> {
919        let mut bound = Vec::with_capacity(projections.len());
920        for mut projection in projections.drain(..) {
921            let expanded = match &projection.expr {
922                Expr::Star => local.map(|scope| scope.output.as_slice()),
923                Expr::QualifiedStar(qualifier) => {
924                    scopes.iter().find_map(|scope| scope.qualified(qualifier))
925                }
926                _ => None,
927            };
928            if let Some(columns) = expanded {
929                for column in columns {
930                    self.dependencies
931                        .extend(column.dependencies.iter().cloned());
932                    bound.push(Projection {
933                        expr: column.reference.clone(),
934                        alias: Some(column.name.clone()),
935                    });
936                }
937                continue;
938            }
939            let implicit_name = match &projection.expr {
940                Expr::Column(name) | Expr::QualifiedColumn { column: name, .. } => {
941                    Some(name.clone())
942                }
943                _ => None,
944            };
945            self.bind_expr(&mut projection.expr, scopes, context)?;
946            if projection.alias.is_none() && self.mode.is_rename() {
947                let current_name = match &projection.expr {
948                    Expr::Column(name) | Expr::QualifiedColumn { column: name, .. } => Some(name),
949                    _ => None,
950                };
951                if implicit_name
952                    .as_ref()
953                    .zip(current_name)
954                    .is_some_and(|(stored, current)| !same_identifier(stored, current))
955                {
956                    projection.alias = implicit_name;
957                }
958            }
959            bound.push(projection);
960        }
961        *projections = bound;
962        Ok(())
963    }
964}