Skip to main content

uqa_sql/schema/table_alteration/
targets.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Resolve ALTER TABLE names before locking and lower the revalidated relation kind afterward.
8use super::syntax::{
9    alter_foreign_table_from_table_syntax, alter_sequence_from_table_syntax,
10    alter_view_from_table_syntax,
11};
12use crate::{
13    ast::{AlterForeignTableStmt, AlterSequence, AlterTableAction, AlterTableStmt, AlterViewStmt},
14    catalog::resolution::RelationResolution,
15    schema::relation_alteration::RelationAlterTarget,
16    SQLError,
17};
18
19pub enum BoundTableAlteration {
20    Table(AlterTableStmt),
21    Sequence(AlterSequence),
22    View(AlterViewStmt),
23    ForeignTable(AlterForeignTableStmt),
24    IndexRename {
25        name: String,
26        new_name: String,
27    },
28    ViewEvents {
29        name: String,
30        actions: Vec<AlterTableAction>,
31    },
32    ForeignTableEvents {
33        name: String,
34        actions: Vec<AlterTableAction>,
35    },
36}
37
38pub fn table_alter_target(
39    resolution: RelationResolution,
40    statement: &AlterTableStmt,
41    notice: &mut dyn FnMut(&str),
42) -> Result<Option<RelationAlterTarget>, SQLError> {
43    RelationAlterTarget::resolve(resolution, &statement.table, statement.if_exists, notice)
44}
45
46pub fn bind_table_alteration(
47    target: RelationAlterTarget,
48    mut statement: AlterTableStmt,
49) -> Result<BoundTableAlteration, SQLError> {
50    let RelationAlterTarget {
51        canonical, kind, ..
52    } = target;
53    let bound = match kind {
54        "index"
55            if matches!(
56                statement.actions.as_slice(),
57                [AlterTableAction::RenameTable { .. }]
58            ) =>
59        {
60            let AlterTableAction::RenameTable { to } = &statement.actions[0] else {
61                unreachable!();
62            };
63            BoundTableAlteration::IndexRename {
64                name: canonical,
65                new_name: to.clone(),
66            }
67        }
68        "table" => {
69            statement.table = canonical;
70            BoundTableAlteration::Table(statement)
71        }
72        "sequence" => BoundTableAlteration::Sequence(alter_sequence_from_table_syntax(
73            &canonical, &statement,
74        )?),
75        "foreign table" => match alter_foreign_table_from_table_syntax(&canonical, &statement)? {
76            Some(change) => BoundTableAlteration::ForeignTable(change),
77            None => BoundTableAlteration::ForeignTableEvents {
78                name: canonical,
79                actions: statement.actions,
80            },
81        },
82        "view" | "materialized view" => {
83            match alter_view_from_table_syntax(&canonical, kind, &statement)? {
84                Some(change) => BoundTableAlteration::View(change),
85                None => BoundTableAlteration::ViewEvents {
86                    name: canonical,
87                    actions: statement.actions,
88                },
89            }
90        }
91        _ => {
92            return Err(SQLError::Routine {
93                sqlstate: "42809".into(),
94                message: format!("ALTER TABLE: relation `{canonical}` is a {kind}, not a table"),
95            });
96        }
97    };
98    Ok(bound)
99}
100
101#[cfg(test)]
102mod tests;