Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for MYSQL's RENAME TABLE #1616

Merged
merged 8 commits into from
Jan 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3401,6 +3401,13 @@ pub enum Statement {
partitioned: Option<Vec<Expr>>,
table_format: Option<HiveLoadDataFormat>,
},
/// ```sql
/// Rename TABLE tbl_name TO new_tbl_name[, tbl_name2 TO new_tbl_name2] ...
/// ```
/// Renames one or more tables
///
/// See Mysql <https://dev.mysql.com/doc/refman/9.1/en/rename-table.html>
RenameTable(Vec<RenameTable>),
}

impl fmt::Display for Statement {
Expand Down Expand Up @@ -4940,6 +4947,9 @@ impl fmt::Display for Statement {
}
Ok(())
}
Statement::RenameTable(rename_tables) => {
write!(f, "RENAME TABLE {}", display_comma_separated(rename_tables))
}
}
}
}
Expand Down Expand Up @@ -7642,6 +7652,22 @@ impl Display for JsonNullClause {
}
}

/// rename object definition
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct RenameTable {
pub old_name: ObjectName,
pub new_name: ObjectName,
}

impl fmt::Display for RenameTable {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} TO {}", self.old_name, self.new_name)?;
Ok(())
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
1 change: 1 addition & 0 deletions src/ast/spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,7 @@ impl Spanned for Statement {
Statement::NOTIFY { .. } => Span::empty(),
Statement::LoadData { .. } => Span::empty(),
Statement::UNLISTEN { .. } => Span::empty(),
Statement::RenameTable { .. } => Span::empty(),
}
}
}
Expand Down
18 changes: 18 additions & 0 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,7 @@ impl<'a> Parser<'a> {
// `PRAGMA` is sqlite specific https://www.sqlite.org/pragma.html
Keyword::PRAGMA => self.parse_pragma(),
Keyword::UNLOAD => self.parse_unload(),
Keyword::RENAME => self.parse_rename(),
// `INSTALL` is duckdb specific https://duckdb.org/docs/extensions/overview
Keyword::INSTALL if dialect_of!(self is DuckDbDialect | GenericDialect) => {
self.parse_install()
Expand Down Expand Up @@ -1044,6 +1045,23 @@ impl<'a> Parser<'a> {
Ok(Statement::NOTIFY { channel, payload })
}

/// Parses a `RENAME TABLE` statement. See [Statement::RenameTable]
pub fn parse_rename(&mut self) -> Result<Statement, ParserError> {
wugeer marked this conversation as resolved.
Show resolved Hide resolved
if self.peek_keyword(Keyword::TABLE) {
self.expect_keyword(Keyword::TABLE)?;
let rename_tables = self.parse_comma_separated(|parser| {
let old_name = parser.parse_object_name(false)?;
parser.expect_keyword(Keyword::TO)?;
let new_name = parser.parse_object_name(false)?;

Ok(RenameTable { old_name, new_name })
})?;
Ok(Statement::RenameTable(rename_tables))
} else {
self.expected("KEYWORD `TABLE` after RENAME", self.peek_token())
}
}

// Tries to parse an expression by matching the specified word to known keywords that have a special meaning in the dialect.
// Returns `None if no match is found.
fn parse_expr_prefix_by_reserved_word(
Expand Down
59 changes: 59 additions & 0 deletions tests/sqlparser_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4026,6 +4026,65 @@ fn parse_alter_table() {
}
}

#[test]
fn parse_rename_table() {
match verified_stmt("RENAME TABLE test.test1 TO test_db.test2") {
Statement::RenameTable(rename_tables) => {
assert_eq!(
vec![RenameTable {
old_name: ObjectName(vec![
Ident::new("test".to_string()),
Ident::new("test1".to_string()),
]),
new_name: ObjectName(vec![
Ident::new("test_db".to_string()),
Ident::new("test2".to_string()),
]),
}],
rename_tables
);
}
_ => unreachable!(),
};

match verified_stmt(
"RENAME TABLE old_table1 TO new_table1, old_table2 TO new_table2, old_table3 TO new_table3",
) {
Statement::RenameTable(rename_tables) => {
assert_eq!(
vec![
RenameTable {
old_name: ObjectName(vec![Ident::new("old_table1".to_string())]),
new_name: ObjectName(vec![Ident::new("new_table1".to_string())]),
},
RenameTable {
old_name: ObjectName(vec![Ident::new("old_table2".to_string())]),
new_name: ObjectName(vec![Ident::new("new_table2".to_string())]),
},
RenameTable {
old_name: ObjectName(vec![Ident::new("old_table3".to_string())]),
new_name: ObjectName(vec![Ident::new("new_table3".to_string())]),
}
],
rename_tables
);
}
_ => unreachable!(),
};

assert_eq!(
parse_sql_statements("RENAME TABLE old_table TO new_table a").unwrap_err(),
ParserError::ParserError("Expected: end of statement, found: a".to_string())
);

assert_eq!(
parse_sql_statements("RENAME TABLE1 old_table TO new_table a").unwrap_err(),
ParserError::ParserError(
"Expected: KEYWORD `TABLE` after RENAME, found: TABLE1".to_string()
)
);
}

#[test]
fn test_alter_table_with_on_cluster() {
match all_dialects()
Expand Down
Loading