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//! Bind ALTER TABLE targets to native relation actions before execution enters a transaction.
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::{resolve_relation_rename_source, RelationResolution},
15    SQLError,
16};
17
18pub enum BoundTableAlteration {
19    Table(AlterTableStmt),
20    Sequence(AlterSequence),
21    View(AlterViewStmt),
22    ForeignTable(AlterForeignTableStmt),
23    ViewEvents {
24        name: String,
25        actions: Vec<AlterTableAction>,
26    },
27    ForeignTableEvents {
28        name: String,
29        actions: Vec<AlterTableAction>,
30    },
31}
32
33pub fn bind_table_alteration(
34    resolution: RelationResolution,
35    mut statement: AlterTableStmt,
36    notice: &mut dyn FnMut(&str),
37) -> Result<Option<BoundTableAlteration>, SQLError> {
38    let resolution = if matches!(
39        statement.actions.as_slice(),
40        [AlterTableAction::RenameTable { .. }]
41    ) {
42        let Some(resolution) = resolve_relation_rename_source(
43            resolution,
44            &statement.table,
45            statement.if_exists,
46            notice,
47        )?
48        else {
49            return Ok(None);
50        };
51        Some(resolution)
52    } else {
53        resolution.into_found()
54    };
55    let bound = match resolution {
56        Some((canonical, "table")) => {
57            statement.table = canonical;
58            BoundTableAlteration::Table(statement)
59        }
60        Some((canonical, "sequence")) => BoundTableAlteration::Sequence(
61            alter_sequence_from_table_syntax(&canonical, &statement)?,
62        ),
63        Some((canonical, "foreign table")) => {
64            match alter_foreign_table_from_table_syntax(&canonical, &statement)? {
65                Some(change) => BoundTableAlteration::ForeignTable(change),
66                None => BoundTableAlteration::ForeignTableEvents {
67                    name: canonical,
68                    actions: statement.actions,
69                },
70            }
71        }
72        Some((canonical, kind @ ("view" | "materialized view"))) => {
73            match alter_view_from_table_syntax(&canonical, kind, &statement)? {
74                Some(change) => BoundTableAlteration::View(change),
75                None => BoundTableAlteration::ViewEvents {
76                    name: canonical,
77                    actions: statement.actions,
78                },
79            }
80        }
81        Some((canonical, kind)) => {
82            return Err(SQLError::Routine {
83                sqlstate: "42809".into(),
84                message: format!("ALTER TABLE: relation `{canonical}` is a {kind}, not a table"),
85            });
86        }
87        None if statement.if_exists => {
88            notice(&format!(
89                "relation \"{}\" does not exist, skipping",
90                statement.table
91            ));
92            return Ok(None);
93        }
94        None => {
95            return Err(SQLError::Unsupported(format!(
96                "ALTER TABLE: relation `{}` does not exist",
97                statement.table
98            )));
99        }
100    };
101    Ok(Some(bound))
102}