Skip to content

Commit

Permalink
Add support for MYSQL's RENAME TABLE (#1616)
Browse files Browse the repository at this point in the history
Co-authored-by: Ifeanyi Ubah <[email protected]>
  • Loading branch information
wugeer and iffyio authored Jan 1, 2025
1 parent 3bad04e commit 94ea206
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 0 deletions.
26 changes: 26 additions & 0 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3413,6 +3413,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 @@ -4970,6 +4977,9 @@ impl fmt::Display for Statement {
}
Ok(())
}
Statement::RenameTable(rename_tables) => {
write!(f, "RENAME TABLE {}", display_comma_separated(rename_tables))
}
}
}
}
Expand Down Expand Up @@ -7672,6 +7682,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 @@ -492,6 +492,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 @@ -595,6 +595,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 @@ -1085,6 +1086,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> {
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 @@ -4133,6 +4133,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

0 comments on commit 94ea206

Please sign in to comment.