uqa_sql/schema/table_alteration/
syntax.rs1use crate::{
9 ast::{AlterTableAction, AlterTableStmt, TableLockMode},
10 SQLError,
11};
12
13mod views;
14
15pub fn table_alter_lock_mode(statement: &AlterTableStmt) -> TableLockMode {
16 let mut mode = TableLockMode::ShareUpdateExclusive;
17 for action in &statement.actions {
18 match action {
19 AlterTableAction::SetTriggerEnableMode { .. }
20 | AlterTableAction::AddForeignKeyConstraint { .. } => {
21 mode = TableLockMode::ShareRowExclusive;
22 }
23 AlterTableAction::ValidateConstraint { .. }
24 | AlterTableAction::AttachPartition { .. }
25 | AlterTableAction::DetachPartition {
26 concurrently: true, ..
27 }
28 | AlterTableAction::DetachPartition { finalize: true, .. } => {}
29 _ => return TableLockMode::AccessExclusive,
30 }
31 }
32 mode
33}
34
35pub fn validate_alter_table_transaction(
36 stmt: &AlterTableStmt,
37 in_transaction_block: bool,
38) -> Result<(), SQLError> {
39 if in_transaction_block
40 && stmt.actions.iter().any(|action| {
41 matches!(
42 action,
43 AlterTableAction::DetachPartition {
44 concurrently: true,
45 ..
46 }
47 )
48 })
49 {
50 return Err(SQLError::Routine {
51 sqlstate: "25001".into(),
52 message: "ALTER TABLE ... DETACH CONCURRENTLY cannot run inside a transaction block"
53 .into(),
54 });
55 }
56 Ok(())
57}
58
59pub fn alter_sequence_from_table_syntax(
60 canonical: &str,
61 stmt: &AlterTableStmt,
62) -> Result<crate::ast::AlterSequence, SQLError> {
63 let mut alter = crate::ast::AlterSequence {
64 name: canonical.to_string(),
65 if_exists: stmt.if_exists,
66 ..crate::ast::AlterSequence::default()
67 };
68 match stmt.actions.as_slice() {
69 [AlterTableAction::SetPersistence { persistence }] => {
70 alter.persistence = Some(*persistence);
71 }
72 [AlterTableAction::RenameTable { to }] => {
73 alter.lifecycle = crate::ast::SequenceLifecycle::RenameTo { name: to.clone() };
74 }
75 [AlterTableAction::SetSchema { schema }] => {
76 alter.lifecycle = crate::ast::SequenceLifecycle::SetSchema {
77 schema: schema.clone(),
78 };
79 }
80 [AlterTableAction::ChangeOwner { owner }] => {
81 alter.role_owner = Some(owner.clone());
82 }
83 _ => {
84 return Err(SQLError::Routine {
85 sqlstate: "42809".into(),
86 message: format!("ALTER TABLE: relation `{canonical}` is a sequence, not a table"),
87 });
88 }
89 }
90 Ok(alter)
91}
92
93pub fn alter_view_from_table_syntax(
95 canonical: &str,
96 local_name: &str,
97 kind: &str,
98 stmt: &AlterTableStmt,
99) -> Result<Option<crate::ast::AlterViewStmt>, SQLError> {
100 views::validate_actions(local_name, kind, &stmt.actions)?;
101 let action = match stmt.actions.as_slice() {
102 [AlterTableAction::RenameTable { to }] => {
103 Some(crate::ast::AlterViewAction::RenameTo(to.clone()))
104 }
105 [AlterTableAction::ChangeOwner { owner }] => {
106 Some(crate::ast::AlterViewAction::OwnerTo(owner.clone()))
107 }
108 [AlterTableAction::SetSchema { schema }] => {
109 Some(crate::ast::AlterViewAction::SetSchema(schema.clone()))
110 }
111 _ => None,
112 };
113 if let Some(action) = action {
114 return Ok(Some(crate::ast::AlterViewStmt {
115 name: canonical.to_string(),
116 kind: if kind == "view" {
117 crate::ast::AlterViewKind::View
118 } else {
119 crate::ast::AlterViewKind::MaterializedView
120 },
121 if_exists: stmt.if_exists,
122 action,
123 }));
124 }
125 if kind == "view"
126 && stmt.actions.iter().all(|action| {
127 matches!(
128 action,
129 AlterTableAction::RenameRule { .. } | AlterTableAction::RenameTrigger { .. }
130 )
131 })
132 {
133 return Ok(None);
134 }
135 Err(SQLError::Routine {
136 sqlstate: "42809".into(),
137 message: format!("ALTER TABLE: relation `{canonical}` is a {kind}, not a table"),
138 })
139}
140
141pub fn alter_foreign_table_from_table_syntax(
143 canonical: &str,
144 stmt: &AlterTableStmt,
145) -> Result<Option<crate::ast::AlterForeignTableStmt>, SQLError> {
146 if stmt.actions.iter().all(|action| {
147 matches!(
148 action,
149 AlterTableAction::RenameTrigger { .. } | AlterTableAction::SetTriggerEnableMode { .. }
150 )
151 }) {
152 return Ok(None);
153 }
154 let drops = stmt
155 .actions
156 .iter()
157 .map(|action| match action {
158 AlterTableAction::DropColumn {
159 name,
160 if_exists,
161 cascade,
162 } => Some(crate::ast::DropColumnAction {
163 name: name.clone(),
164 if_exists: *if_exists,
165 cascade: *cascade,
166 }),
167 _ => None,
168 })
169 .collect::<Option<Vec<_>>>();
170 if let Some(columns) = drops {
171 return Ok(Some(crate::ast::AlterForeignTableStmt {
172 name: canonical.to_string(),
173 if_exists: stmt.if_exists,
174 action: crate::ast::AlterForeignTableAction::DropColumns(columns),
175 }));
176 }
177 let action = match stmt.actions.as_slice() {
178 [AlterTableAction::ChangeOwner { owner }] => {
179 crate::ast::AlterForeignTableAction::OwnerTo(owner.clone())
180 }
181 [AlterTableAction::RenameTable { to }] => {
182 crate::ast::AlterForeignTableAction::RenameTo(to.clone())
183 }
184 [AlterTableAction::SetSchema { schema }] => {
185 crate::ast::AlterForeignTableAction::SetSchema(schema.clone())
186 }
187 _ => {
188 return Err(SQLError::Routine {
189 sqlstate: "42809".into(),
190 message: format!(
191 "ALTER TABLE: relation `{canonical}` is a foreign table, not a table"
192 ),
193 });
194 }
195 };
196 Ok(Some(crate::ast::AlterForeignTableStmt {
197 name: canonical.to_string(),
198 if_exists: stmt.if_exists,
199 action,
200 }))
201}
202
203#[cfg(test)]
204mod tests;