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::RenameColumn { .. }] => {
70 let relation = uqa_core::RelationIdentity::from_legacy_name(canonical)
71 .map_err(SQLError::Internal)?;
72 return Err(SQLError::Diagnostic {
73 sqlstate: "42809".into(),
74 message: format!("cannot rename columns of relation \"{}\"", relation.name),
75 detail: Some("This operation is not supported for sequences.".into()),
76 hint: None,
77 });
78 }
79 [AlterTableAction::SetPersistence { persistence }] => {
80 alter.persistence = Some(*persistence);
81 }
82 [AlterTableAction::RenameTable { to }] => {
83 alter.lifecycle = crate::ast::SequenceLifecycle::RenameTo { name: to.clone() };
84 }
85 [AlterTableAction::SetSchema { schema }] => {
86 alter.lifecycle = crate::ast::SequenceLifecycle::SetSchema {
87 schema: schema.clone(),
88 };
89 }
90 [AlterTableAction::ChangeOwner { owner }] => {
91 alter.role_owner = Some(owner.clone());
92 }
93 _ => {
94 return Err(SQLError::Routine {
95 sqlstate: "42809".into(),
96 message: format!("ALTER TABLE: relation `{canonical}` is a sequence, not a table"),
97 });
98 }
99 }
100 Ok(alter)
101}
102
103pub fn alter_view_from_table_syntax(
105 canonical: &str,
106 local_name: &str,
107 kind: &str,
108 stmt: &AlterTableStmt,
109) -> Result<Option<crate::ast::AlterViewStmt>, SQLError> {
110 views::validate_actions(local_name, kind, &stmt.actions)?;
111 let action = match stmt.actions.as_slice() {
112 [AlterTableAction::RenameColumn { from, to }] => {
113 Some(crate::ast::AlterViewAction::RenameColumn {
114 from: from.clone(),
115 to: to.clone(),
116 })
117 }
118 [AlterTableAction::RenameTable { to }] => {
119 Some(crate::ast::AlterViewAction::RenameTo(to.clone()))
120 }
121 [AlterTableAction::ChangeOwner { owner }] => {
122 Some(crate::ast::AlterViewAction::OwnerTo(owner.clone()))
123 }
124 [AlterTableAction::SetSchema { schema }] => {
125 Some(crate::ast::AlterViewAction::SetSchema(schema.clone()))
126 }
127 _ => None,
128 };
129 if let Some(action) = action {
130 return Ok(Some(crate::ast::AlterViewStmt {
131 name: canonical.to_string(),
132 kind: if kind == "view" {
133 crate::ast::AlterViewKind::View
134 } else {
135 crate::ast::AlterViewKind::MaterializedView
136 },
137 if_exists: stmt.if_exists,
138 action,
139 }));
140 }
141 if kind == "view"
142 && stmt.actions.iter().all(|action| {
143 matches!(
144 action,
145 AlterTableAction::RenameRule { .. } | AlterTableAction::RenameTrigger { .. }
146 )
147 })
148 {
149 return Ok(None);
150 }
151 Err(SQLError::Routine {
152 sqlstate: "42809".into(),
153 message: format!("ALTER TABLE: relation `{canonical}` is a {kind}, not a table"),
154 })
155}
156
157pub fn alter_foreign_table_from_table_syntax(
159 canonical: &str,
160 stmt: &AlterTableStmt,
161) -> Result<Option<crate::ast::AlterForeignTableStmt>, SQLError> {
162 if stmt.actions.iter().all(|action| {
163 matches!(
164 action,
165 AlterTableAction::RenameTrigger { .. } | AlterTableAction::SetTriggerEnableMode { .. }
166 )
167 }) {
168 return Ok(None);
169 }
170 let drops = stmt
171 .actions
172 .iter()
173 .map(|action| match action {
174 AlterTableAction::DropColumn {
175 name,
176 if_exists,
177 cascade,
178 } => Some(crate::ast::DropColumnAction {
179 name: name.clone(),
180 if_exists: *if_exists,
181 cascade: *cascade,
182 }),
183 _ => None,
184 })
185 .collect::<Option<Vec<_>>>();
186 if let Some(columns) = drops {
187 return Ok(Some(crate::ast::AlterForeignTableStmt {
188 name: canonical.to_string(),
189 if_exists: stmt.if_exists,
190 action: crate::ast::AlterForeignTableAction::DropColumns(columns),
191 }));
192 }
193 let action = match stmt.actions.as_slice() {
194 [AlterTableAction::ChangeOwner { owner }] => {
195 crate::ast::AlterForeignTableAction::OwnerTo(owner.clone())
196 }
197 [AlterTableAction::RenameTable { to }] => {
198 crate::ast::AlterForeignTableAction::RenameTo(to.clone())
199 }
200 [AlterTableAction::SetSchema { schema }] => {
201 crate::ast::AlterForeignTableAction::SetSchema(schema.clone())
202 }
203 _ => {
204 return Err(SQLError::Routine {
205 sqlstate: "42809".into(),
206 message: format!(
207 "ALTER TABLE: relation `{canonical}` is a foreign table, not a table"
208 ),
209 });
210 }
211 };
212 Ok(Some(crate::ast::AlterForeignTableStmt {
213 name: canonical.to_string(),
214 if_exists: stmt.if_exists,
215 action,
216 }))
217}
218
219#[cfg(test)]
220mod tests;