From 698154d0e03ddce63e9a459795f3523db7aaef42 Mon Sep 17 00:00:00 2001 From: Marcelo Altmann Date: Wed, 6 Aug 2025 08:18:58 -0300 Subject: [PATCH] MySQL: Support `ALTER TABLE RENAME AS` (#1965) --- src/ast/ddl.rs | 41 ++++++++++++++++++++++++++++++++++----- src/ast/mod.rs | 6 +++--- src/parser/mod.rs | 9 ++++++++- tests/sqlparser_common.rs | 16 ++++++++++++++- 4 files changed, 62 insertions(+), 10 deletions(-) diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs index 231ab49c..4d1fc462 100644 --- a/src/ast/ddl.rs +++ b/src/ast/ddl.rs @@ -33,11 +33,11 @@ use crate::ast::{ display_comma_separated, display_separated, ArgMode, CommentDef, CreateFunctionBody, CreateFunctionUsing, DataType, Expr, FunctionBehavior, FunctionCalledOnNull, FunctionDeterminismSpecifier, FunctionParallel, Ident, IndexColumn, MySQLColumnPosition, - ObjectName, OperateFunctionArg, OrderByExpr, ProjectionSelect, SequenceOptions, SqlOption, Tag, - Value, ValueWithSpan, + ObjectName, OperateFunctionArg, OrderByExpr, ProjectionSelect, SequenceOptions, Spanned, + SqlOption, Tag, Value, ValueWithSpan, }; use crate::keywords::Keyword; -use crate::tokenizer::Token; +use crate::tokenizer::{Span, Token}; /// ALTER TABLE operation REPLICA IDENTITY values /// See [Postgres ALTER TABLE docs](https://www.postgresql.org/docs/current/sql-altertable.html) @@ -264,7 +264,7 @@ pub enum AlterTableOperation { }, /// `RENAME TO ` RenameTable { - table_name: ObjectName, + table_name: RenameTableNameKind, }, // CHANGE [ COLUMN ] [ ] ChangeColumn { @@ -697,7 +697,7 @@ impl fmt::Display for AlterTableOperation { new_column_name, } => write!(f, "RENAME COLUMN {old_column_name} TO {new_column_name}"), AlterTableOperation::RenameTable { table_name } => { - write!(f, "RENAME TO {table_name}") + write!(f, "RENAME {table_name}") } AlterTableOperation::ChangeColumn { old_name, @@ -2537,3 +2537,34 @@ impl fmt::Display for CreateConnector { Ok(()) } } + +/// `RenameTableNameKind` is the kind used in an `ALTER TABLE _ RENAME` statement. +/// +/// Note: [MySQL] is the only database that supports the AS keyword for this operation. +/// +/// [MySQL]: https://dev.mysql.com/doc/refman/8.4/en/alter-table.html +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] +pub enum RenameTableNameKind { + As(ObjectName), + To(ObjectName), +} + +impl fmt::Display for RenameTableNameKind { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + RenameTableNameKind::As(name) => write!(f, "AS {name}"), + RenameTableNameKind::To(name) => write!(f, "TO {name}"), + } + } +} + +impl Spanned for RenameTableNameKind { + fn span(&self) -> Span { + match self { + RenameTableNameKind::As(name) => name.span(), + RenameTableNameKind::To(name) => name.span(), + } + } +} diff --git a/src/ast/mod.rs b/src/ast/mod.rs index c6212f1e..4a864cfa 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -66,9 +66,9 @@ pub use self::ddl::{ Deduplicate, DeferrableInitial, DropBehavior, GeneratedAs, GeneratedExpressionMode, IdentityParameters, IdentityProperty, IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder, IndexOption, IndexType, KeyOrIndexDisplay, NullsDistinctOption, Owner, - Partition, ProcedureParam, ReferentialAction, ReplicaIdentity, TableConstraint, - TagsColumnOption, UserDefinedTypeCompositeAttributeDef, UserDefinedTypeRepresentation, - ViewColumnDef, + Partition, ProcedureParam, ReferentialAction, RenameTableNameKind, ReplicaIdentity, + TableConstraint, TagsColumnOption, UserDefinedTypeCompositeAttributeDef, + UserDefinedTypeRepresentation, ViewColumnDef, }; pub use self::dml::{CreateIndex, CreateTable, Delete, IndexColumn, Insert}; pub use self::operator::{BinaryOperator, UnaryOperator}; diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 39571ba1..d9fe7999 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -8752,7 +8752,14 @@ impl<'a> Parser<'a> { AlterTableOperation::RenameConstraint { old_name, new_name } } else if self.parse_keyword(Keyword::TO) { let table_name = self.parse_object_name(false)?; - AlterTableOperation::RenameTable { table_name } + AlterTableOperation::RenameTable { + table_name: RenameTableNameKind::To(table_name), + } + } else if self.parse_keyword(Keyword::AS) { + let table_name = self.parse_object_name(false)?; + AlterTableOperation::RenameTable { + table_name: RenameTableNameKind::As(table_name), + } } else { let _ = self.parse_keyword(Keyword::COLUMN); // [ COLUMN ] let old_column_name = self.parse_identifier()?; diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index a64733d6..7eff5503 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -4692,7 +4692,21 @@ fn parse_alter_table() { let rename_table = "ALTER TABLE tab RENAME TO new_tab"; match alter_table_op(verified_stmt(rename_table)) { AlterTableOperation::RenameTable { table_name } => { - assert_eq!("new_tab", table_name.to_string()); + assert_eq!( + RenameTableNameKind::To(ObjectName::from(vec![Ident::new("new_tab")])), + table_name + ); + } + _ => unreachable!(), + }; + + let rename_table_as = "ALTER TABLE tab RENAME AS new_tab"; + match alter_table_op(verified_stmt(rename_table_as)) { + AlterTableOperation::RenameTable { table_name } => { + assert_eq!( + RenameTableNameKind::As(ObjectName::from(vec![Ident::new("new_tab")])), + table_name + ); } _ => unreachable!(), };