diff --git a/src/ast/mod.rs b/src/ast/mod.rs index a241f9509..e51699986 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -1391,7 +1391,9 @@ pub enum Statement { /// Table name #[cfg_attr(feature = "visitor", visit(with = "visit_relation"))] name: ObjectName, - operation: AlterTableOperation, + if_exists: bool, + only: bool, + operations: Vec, }, AlterIndex { name: ObjectName, @@ -2602,8 +2604,24 @@ impl fmt::Display for Statement { } Ok(()) } - Statement::AlterTable { name, operation } => { - write!(f, "ALTER TABLE {name} {operation}") + Statement::AlterTable { + name, + if_exists, + only, + operations, + } => { + write!(f, "ALTER TABLE ")?; + if *if_exists { + write!(f, "IF EXISTS ")?; + } + if *only { + write!(f, "ONLY ")?; + } + write!( + f, + "{name} {operations}", + operations = display_comma_separated(operations) + ) } Statement::AlterIndex { name, operation } => { write!(f, "ALTER INDEX {name} {operation}") diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 846215249..52dda8296 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -4037,6 +4037,174 @@ impl<'a> Parser<'a> { Ok(SqlOption { name, value }) } + pub fn parse_alter_table_operation(&mut self) -> Result { + let operation = if self.parse_keyword(Keyword::ADD) { + if let Some(constraint) = self.parse_optional_table_constraint()? { + AlterTableOperation::AddConstraint(constraint) + } else { + let if_not_exists = + self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); + if self.parse_keyword(Keyword::PARTITION) { + self.expect_token(&Token::LParen)?; + let partitions = self.parse_comma_separated(Parser::parse_expr)?; + self.expect_token(&Token::RParen)?; + AlterTableOperation::AddPartitions { + if_not_exists, + new_partitions: partitions, + } + } else { + let column_keyword = self.parse_keyword(Keyword::COLUMN); + + let if_not_exists = if dialect_of!(self is PostgreSqlDialect | BigQueryDialect | DuckDbDialect | GenericDialect) + { + self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]) + || if_not_exists + } else { + false + }; + + let column_def = self.parse_column_def()?; + AlterTableOperation::AddColumn { + column_keyword, + if_not_exists, + column_def, + } + } + } + } else if self.parse_keyword(Keyword::RENAME) { + if dialect_of!(self is PostgreSqlDialect) && self.parse_keyword(Keyword::CONSTRAINT) { + let old_name = self.parse_identifier()?; + self.expect_keyword(Keyword::TO)?; + let new_name = self.parse_identifier()?; + AlterTableOperation::RenameConstraint { old_name, new_name } + } else if self.parse_keyword(Keyword::TO) { + let table_name = self.parse_object_name()?; + AlterTableOperation::RenameTable { table_name } + } else { + let _ = self.parse_keyword(Keyword::COLUMN); // [ COLUMN ] + let old_column_name = self.parse_identifier()?; + self.expect_keyword(Keyword::TO)?; + let new_column_name = self.parse_identifier()?; + AlterTableOperation::RenameColumn { + old_column_name, + new_column_name, + } + } + } else if self.parse_keyword(Keyword::DROP) { + if self.parse_keywords(&[Keyword::IF, Keyword::EXISTS, Keyword::PARTITION]) { + self.expect_token(&Token::LParen)?; + let partitions = self.parse_comma_separated(Parser::parse_expr)?; + self.expect_token(&Token::RParen)?; + AlterTableOperation::DropPartitions { + partitions, + if_exists: true, + } + } else if self.parse_keyword(Keyword::PARTITION) { + self.expect_token(&Token::LParen)?; + let partitions = self.parse_comma_separated(Parser::parse_expr)?; + self.expect_token(&Token::RParen)?; + AlterTableOperation::DropPartitions { + partitions, + if_exists: false, + } + } else if self.parse_keyword(Keyword::CONSTRAINT) { + let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); + let name = self.parse_identifier()?; + let cascade = self.parse_keyword(Keyword::CASCADE); + AlterTableOperation::DropConstraint { + if_exists, + name, + cascade, + } + } else if self.parse_keywords(&[Keyword::PRIMARY, Keyword::KEY]) + && dialect_of!(self is MySqlDialect | GenericDialect) + { + AlterTableOperation::DropPrimaryKey + } else { + let _ = self.parse_keyword(Keyword::COLUMN); // [ COLUMN ] + let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); + let column_name = self.parse_identifier()?; + let cascade = self.parse_keyword(Keyword::CASCADE); + AlterTableOperation::DropColumn { + column_name, + if_exists, + cascade, + } + } + } else if self.parse_keyword(Keyword::PARTITION) { + self.expect_token(&Token::LParen)?; + let before = self.parse_comma_separated(Parser::parse_expr)?; + self.expect_token(&Token::RParen)?; + self.expect_keyword(Keyword::RENAME)?; + self.expect_keywords(&[Keyword::TO, Keyword::PARTITION])?; + self.expect_token(&Token::LParen)?; + let renames = self.parse_comma_separated(Parser::parse_expr)?; + self.expect_token(&Token::RParen)?; + AlterTableOperation::RenamePartitions { + old_partitions: before, + new_partitions: renames, + } + } else if self.parse_keyword(Keyword::CHANGE) { + let _ = self.parse_keyword(Keyword::COLUMN); // [ COLUMN ] + let old_name = self.parse_identifier()?; + let new_name = self.parse_identifier()?; + let data_type = self.parse_data_type()?; + let mut options = vec![]; + while let Some(option) = self.parse_optional_column_option()? { + options.push(option); + } + + AlterTableOperation::ChangeColumn { + old_name, + new_name, + data_type, + options, + } + } else if self.parse_keyword(Keyword::ALTER) { + let _ = self.parse_keyword(Keyword::COLUMN); // [ COLUMN ] + let column_name = self.parse_identifier()?; + let is_postgresql = dialect_of!(self is PostgreSqlDialect); + + let op = if self.parse_keywords(&[Keyword::SET, Keyword::NOT, Keyword::NULL]) { + AlterColumnOperation::SetNotNull {} + } else if self.parse_keywords(&[Keyword::DROP, Keyword::NOT, Keyword::NULL]) { + AlterColumnOperation::DropNotNull {} + } else if self.parse_keywords(&[Keyword::SET, Keyword::DEFAULT]) { + AlterColumnOperation::SetDefault { + value: self.parse_expr()?, + } + } else if self.parse_keywords(&[Keyword::DROP, Keyword::DEFAULT]) { + AlterColumnOperation::DropDefault {} + } else if self.parse_keywords(&[Keyword::SET, Keyword::DATA, Keyword::TYPE]) + || (is_postgresql && self.parse_keyword(Keyword::TYPE)) + { + let data_type = self.parse_data_type()?; + let using = if is_postgresql && self.parse_keyword(Keyword::USING) { + Some(self.parse_expr()?) + } else { + None + }; + AlterColumnOperation::SetDataType { data_type, using } + } else { + return self.expected( + "SET/DROP NOT NULL, SET DEFAULT, SET DATA TYPE after ALTER COLUMN", + self.peek_token(), + ); + }; + AlterTableOperation::AlterColumn { column_name, op } + } else if self.parse_keyword(Keyword::SWAP) { + self.expect_keyword(Keyword::WITH)?; + let table_name = self.parse_object_name()?; + AlterTableOperation::SwapWith { table_name } + } else { + return self.expected( + "ADD, RENAME, PARTITION, SWAP or DROP after ALTER TABLE", + self.peek_token(), + ); + }; + Ok(operation) + } + pub fn parse_alter(&mut self) -> Result { let object_type = self.expect_one_of_keywords(&[ Keyword::VIEW, @@ -4047,177 +4215,15 @@ impl<'a> Parser<'a> { match object_type { Keyword::VIEW => self.parse_alter_view(), Keyword::TABLE => { - let _ = self.parse_keyword(Keyword::ONLY); // [ ONLY ] + let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); + let only = self.parse_keyword(Keyword::ONLY); // [ ONLY ] let table_name = self.parse_object_name()?; - let operation = if self.parse_keyword(Keyword::ADD) { - if let Some(constraint) = self.parse_optional_table_constraint()? { - AlterTableOperation::AddConstraint(constraint) - } else { - let if_not_exists = - self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); - if self.parse_keyword(Keyword::PARTITION) { - self.expect_token(&Token::LParen)?; - let partitions = self.parse_comma_separated(Parser::parse_expr)?; - self.expect_token(&Token::RParen)?; - AlterTableOperation::AddPartitions { - if_not_exists, - new_partitions: partitions, - } - } else { - let column_keyword = self.parse_keyword(Keyword::COLUMN); - - let if_not_exists = if dialect_of!(self is PostgreSqlDialect | BigQueryDialect | DuckDbDialect | GenericDialect) - { - self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]) - || if_not_exists - } else { - false - }; - - let column_def = self.parse_column_def()?; - AlterTableOperation::AddColumn { - column_keyword, - if_not_exists, - column_def, - } - } - } - } else if self.parse_keyword(Keyword::RENAME) { - if dialect_of!(self is PostgreSqlDialect) - && self.parse_keyword(Keyword::CONSTRAINT) - { - let old_name = self.parse_identifier()?; - self.expect_keyword(Keyword::TO)?; - let new_name = self.parse_identifier()?; - AlterTableOperation::RenameConstraint { old_name, new_name } - } else if self.parse_keyword(Keyword::TO) { - let table_name = self.parse_object_name()?; - AlterTableOperation::RenameTable { table_name } - } else { - let _ = self.parse_keyword(Keyword::COLUMN); // [ COLUMN ] - let old_column_name = self.parse_identifier()?; - self.expect_keyword(Keyword::TO)?; - let new_column_name = self.parse_identifier()?; - AlterTableOperation::RenameColumn { - old_column_name, - new_column_name, - } - } - } else if self.parse_keyword(Keyword::DROP) { - if self.parse_keywords(&[Keyword::IF, Keyword::EXISTS, Keyword::PARTITION]) { - self.expect_token(&Token::LParen)?; - let partitions = self.parse_comma_separated(Parser::parse_expr)?; - self.expect_token(&Token::RParen)?; - AlterTableOperation::DropPartitions { - partitions, - if_exists: true, - } - } else if self.parse_keyword(Keyword::PARTITION) { - self.expect_token(&Token::LParen)?; - let partitions = self.parse_comma_separated(Parser::parse_expr)?; - self.expect_token(&Token::RParen)?; - AlterTableOperation::DropPartitions { - partitions, - if_exists: false, - } - } else if self.parse_keyword(Keyword::CONSTRAINT) { - let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); - let name = self.parse_identifier()?; - let cascade = self.parse_keyword(Keyword::CASCADE); - AlterTableOperation::DropConstraint { - if_exists, - name, - cascade, - } - } else if self.parse_keywords(&[Keyword::PRIMARY, Keyword::KEY]) - && dialect_of!(self is MySqlDialect | GenericDialect) - { - AlterTableOperation::DropPrimaryKey - } else { - let _ = self.parse_keyword(Keyword::COLUMN); // [ COLUMN ] - let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); - let column_name = self.parse_identifier()?; - let cascade = self.parse_keyword(Keyword::CASCADE); - AlterTableOperation::DropColumn { - column_name, - if_exists, - cascade, - } - } - } else if self.parse_keyword(Keyword::PARTITION) { - self.expect_token(&Token::LParen)?; - let before = self.parse_comma_separated(Parser::parse_expr)?; - self.expect_token(&Token::RParen)?; - self.expect_keyword(Keyword::RENAME)?; - self.expect_keywords(&[Keyword::TO, Keyword::PARTITION])?; - self.expect_token(&Token::LParen)?; - let renames = self.parse_comma_separated(Parser::parse_expr)?; - self.expect_token(&Token::RParen)?; - AlterTableOperation::RenamePartitions { - old_partitions: before, - new_partitions: renames, - } - } else if self.parse_keyword(Keyword::CHANGE) { - let _ = self.parse_keyword(Keyword::COLUMN); // [ COLUMN ] - let old_name = self.parse_identifier()?; - let new_name = self.parse_identifier()?; - let data_type = self.parse_data_type()?; - let mut options = vec![]; - while let Some(option) = self.parse_optional_column_option()? { - options.push(option); - } - - AlterTableOperation::ChangeColumn { - old_name, - new_name, - data_type, - options, - } - } else if self.parse_keyword(Keyword::ALTER) { - let _ = self.parse_keyword(Keyword::COLUMN); // [ COLUMN ] - let column_name = self.parse_identifier()?; - let is_postgresql = dialect_of!(self is PostgreSqlDialect); - - let op = if self.parse_keywords(&[Keyword::SET, Keyword::NOT, Keyword::NULL]) { - AlterColumnOperation::SetNotNull {} - } else if self.parse_keywords(&[Keyword::DROP, Keyword::NOT, Keyword::NULL]) { - AlterColumnOperation::DropNotNull {} - } else if self.parse_keywords(&[Keyword::SET, Keyword::DEFAULT]) { - AlterColumnOperation::SetDefault { - value: self.parse_expr()?, - } - } else if self.parse_keywords(&[Keyword::DROP, Keyword::DEFAULT]) { - AlterColumnOperation::DropDefault {} - } else if self.parse_keywords(&[Keyword::SET, Keyword::DATA, Keyword::TYPE]) - || (is_postgresql && self.parse_keyword(Keyword::TYPE)) - { - let data_type = self.parse_data_type()?; - let using = if is_postgresql && self.parse_keyword(Keyword::USING) { - Some(self.parse_expr()?) - } else { - None - }; - AlterColumnOperation::SetDataType { data_type, using } - } else { - return self.expected( - "SET/DROP NOT NULL, SET DEFAULT, SET DATA TYPE after ALTER COLUMN", - self.peek_token(), - ); - }; - AlterTableOperation::AlterColumn { column_name, op } - } else if self.parse_keyword(Keyword::SWAP) { - self.expect_keyword(Keyword::WITH)?; - let table_name = self.parse_object_name()?; - AlterTableOperation::SwapWith { table_name } - } else { - return self.expected( - "ADD, RENAME, PARTITION, SWAP or DROP after ALTER TABLE", - self.peek_token(), - ); - }; + let operations = self.parse_comma_separated(Parser::parse_alter_table_operation)?; Ok(Statement::AlterTable { name: table_name, - operation, + if_exists, + only, + operations, }) } Keyword::INDEX => { diff --git a/src/test_utils.rs b/src/test_utils.rs index 91130fb51..3dbac94d2 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -203,6 +203,26 @@ pub fn expr_from_projection(item: &SelectItem) -> &Expr { } } +pub fn alter_table_op_with_name(stmt: Statement, expected_name: &str) -> AlterTableOperation { + match stmt { + Statement::AlterTable { + name, + if_exists, + only: is_only, + operations, + } => { + assert_eq!(name.to_string(), expected_name); + assert!(!if_exists); + assert!(!is_only); + only(operations) + } + _ => panic!("Expected ALTER TABLE statement"), + } +} +pub fn alter_table_op(stmt: Statement) -> AlterTableOperation { + alter_table_op_with_name(stmt, "tab") +} + /// Creates a `Value::Number`, panic'ing if n is not a number pub fn number(n: &str) -> Value { Value::Number(n.parse().unwrap(), false) diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 3fdf3d211..c9f504c25 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -30,8 +30,8 @@ use sqlparser::dialect::{ use sqlparser::keywords::ALL_KEYWORDS; use sqlparser::parser::{Parser, ParserError, ParserOptions}; use test_utils::{ - all_dialects, assert_eq_vec, expr_from_projection, join, number, only, table, table_alias, - TestedDialects, + all_dialects, alter_table_op, assert_eq_vec, expr_from_projection, join, number, only, table, + table_alias, TestedDialects, }; #[macro_use] @@ -2915,19 +2915,17 @@ fn parse_create_external_table_lowercase() { #[test] fn parse_alter_table() { let add_column = "ALTER TABLE tab ADD COLUMN foo TEXT;"; - match one_statement_parses_to(add_column, "ALTER TABLE tab ADD COLUMN foo TEXT") { - Statement::AlterTable { - name, - operation: - AlterTableOperation::AddColumn { - column_keyword, - if_not_exists, - column_def, - }, + match alter_table_op(one_statement_parses_to( + add_column, + "ALTER TABLE tab ADD COLUMN foo TEXT", + )) { + AlterTableOperation::AddColumn { + column_keyword, + if_not_exists, + column_def, } => { assert!(column_keyword); assert!(!if_not_exists); - assert_eq!("tab", name.to_string()); assert_eq!("foo", column_def.name.to_string()); assert_eq!("TEXT", column_def.data_type.to_string()); } @@ -2935,28 +2933,19 @@ fn parse_alter_table() { }; let rename_table = "ALTER TABLE tab RENAME TO new_tab"; - match verified_stmt(rename_table) { - Statement::AlterTable { - name, - operation: AlterTableOperation::RenameTable { table_name }, - } => { - assert_eq!("tab", name.to_string()); - assert_eq!("new_tab", table_name.to_string()) + match alter_table_op(verified_stmt(rename_table)) { + AlterTableOperation::RenameTable { table_name } => { + assert_eq!("new_tab", table_name.to_string()); } _ => unreachable!(), }; let rename_column = "ALTER TABLE tab RENAME COLUMN foo TO new_foo"; - match verified_stmt(rename_column) { - Statement::AlterTable { - name, - operation: - AlterTableOperation::RenameColumn { - old_column_name, - new_column_name, - }, + match alter_table_op(verified_stmt(rename_column)) { + AlterTableOperation::RenameColumn { + old_column_name, + new_column_name, } => { - assert_eq!("tab", name.to_string()); assert_eq!(old_column_name.to_string(), "foo"); assert_eq!(new_column_name.to_string(), "new_foo"); } @@ -3042,21 +3031,15 @@ fn parse_alter_view_with_columns() { #[test] fn parse_alter_table_add_column() { - match verified_stmt("ALTER TABLE tab ADD foo TEXT") { - Statement::AlterTable { - operation: AlterTableOperation::AddColumn { column_keyword, .. }, - .. - } => { + match alter_table_op(verified_stmt("ALTER TABLE tab ADD foo TEXT")) { + AlterTableOperation::AddColumn { column_keyword, .. } => { assert!(!column_keyword); } _ => unreachable!(), }; - match verified_stmt("ALTER TABLE tab ADD COLUMN foo TEXT") { - Statement::AlterTable { - operation: AlterTableOperation::AddColumn { column_keyword, .. }, - .. - } => { + match alter_table_op(verified_stmt("ALTER TABLE tab ADD COLUMN foo TEXT")) { + AlterTableOperation::AddColumn { column_keyword, .. } => { assert!(column_keyword); } _ => unreachable!(), @@ -3075,24 +3058,19 @@ fn parse_alter_table_add_column_if_not_exists() { options: None, }; - match dialects.verified_stmt("ALTER TABLE tab ADD IF NOT EXISTS foo TEXT") { - Statement::AlterTable { - operation: AlterTableOperation::AddColumn { if_not_exists, .. }, - .. - } => { + match alter_table_op(dialects.verified_stmt("ALTER TABLE tab ADD IF NOT EXISTS foo TEXT")) { + AlterTableOperation::AddColumn { if_not_exists, .. } => { assert!(if_not_exists); } _ => unreachable!(), }; - match dialects.verified_stmt("ALTER TABLE tab ADD COLUMN IF NOT EXISTS foo TEXT") { - Statement::AlterTable { - operation: - AlterTableOperation::AddColumn { - column_keyword, - if_not_exists, - .. - }, + match alter_table_op( + dialects.verified_stmt("ALTER TABLE tab ADD COLUMN IF NOT EXISTS foo TEXT"), + ) { + AlterTableOperation::AddColumn { + column_keyword, + if_not_exists, .. } => { assert!(column_keyword); @@ -3118,12 +3096,10 @@ fn parse_alter_table_constraints() { check_one("CHECK (end_date > start_date OR end_date IS NULL)"); fn check_one(constraint_text: &str) { - match verified_stmt(&format!("ALTER TABLE tab ADD {constraint_text}")) { - Statement::AlterTable { - name, - operation: AlterTableOperation::AddConstraint(constraint), - } => { - assert_eq!("tab", name.to_string()); + match alter_table_op(verified_stmt(&format!( + "ALTER TABLE tab ADD {constraint_text}" + ))) { + AlterTableOperation::AddConstraint(constraint) => { assert_eq!(constraint_text, constraint.to_string()); } _ => unreachable!(), @@ -3145,17 +3121,12 @@ fn parse_alter_table_drop_column() { ); fn check_one(constraint_text: &str) { - match verified_stmt(&format!("ALTER TABLE tab {constraint_text}")) { - Statement::AlterTable { - name, - operation: - AlterTableOperation::DropColumn { - column_name, - if_exists, - cascade, - }, + match alter_table_op(verified_stmt(&format!("ALTER TABLE tab {constraint_text}"))) { + AlterTableOperation::DropColumn { + column_name, + if_exists, + cascade, } => { - assert_eq!("tab", name.to_string()); assert_eq!("is_active", column_name.to_string()); assert!(if_exists); assert!(cascade); @@ -3168,12 +3139,10 @@ fn parse_alter_table_drop_column() { #[test] fn parse_alter_table_alter_column() { let alter_stmt = "ALTER TABLE tab"; - match verified_stmt(&format!("{alter_stmt} ALTER COLUMN is_active SET NOT NULL")) { - Statement::AlterTable { - name, - operation: AlterTableOperation::AlterColumn { column_name, op }, - } => { - assert_eq!("tab", name.to_string()); + match alter_table_op(verified_stmt(&format!( + "{alter_stmt} ALTER COLUMN is_active SET NOT NULL" + ))) { + AlterTableOperation::AlterColumn { column_name, op } => { assert_eq!("is_active", column_name.to_string()); assert_eq!(op, AlterColumnOperation::SetNotNull {}); } @@ -3185,14 +3154,10 @@ fn parse_alter_table_alter_column() { "ALTER TABLE tab ALTER COLUMN is_active DROP NOT NULL", ); - match verified_stmt(&format!( + match alter_table_op(verified_stmt(&format!( "{alter_stmt} ALTER COLUMN is_active SET DEFAULT false" - )) { - Statement::AlterTable { - name, - operation: AlterTableOperation::AlterColumn { column_name, op }, - } => { - assert_eq!("tab", name.to_string()); + ))) { + AlterTableOperation::AlterColumn { column_name, op } => { assert_eq!("is_active", column_name.to_string()); assert_eq!( op, @@ -3204,12 +3169,10 @@ fn parse_alter_table_alter_column() { _ => unreachable!(), } - match verified_stmt(&format!("{alter_stmt} ALTER COLUMN is_active DROP DEFAULT")) { - Statement::AlterTable { - name, - operation: AlterTableOperation::AlterColumn { column_name, op }, - } => { - assert_eq!("tab", name.to_string()); + match alter_table_op(verified_stmt(&format!( + "{alter_stmt} ALTER COLUMN is_active DROP DEFAULT" + ))) { + AlterTableOperation::AlterColumn { column_name, op } => { assert_eq!("is_active", column_name.to_string()); assert_eq!(op, AlterColumnOperation::DropDefault {}); } @@ -3220,12 +3183,10 @@ fn parse_alter_table_alter_column() { #[test] fn parse_alter_table_alter_column_type() { let alter_stmt = "ALTER TABLE tab"; - match verified_stmt("ALTER TABLE tab ALTER COLUMN is_active SET DATA TYPE TEXT") { - Statement::AlterTable { - name, - operation: AlterTableOperation::AlterColumn { column_name, op }, - } => { - assert_eq!("tab", name.to_string()); + match alter_table_op(verified_stmt( + "ALTER TABLE tab ALTER COLUMN is_active SET DATA TYPE TEXT", + )) { + AlterTableOperation::AlterColumn { column_name, op } => { assert_eq!("is_active", column_name.to_string()); assert_eq!( op, @@ -3260,34 +3221,28 @@ fn parse_alter_table_alter_column_type() { #[test] fn parse_alter_table_drop_constraint() { let alter_stmt = "ALTER TABLE tab"; - match verified_stmt("ALTER TABLE tab DROP CONSTRAINT constraint_name CASCADE") { - Statement::AlterTable { - name, - operation: - AlterTableOperation::DropConstraint { - name: constr_name, - if_exists, - cascade, - }, + match alter_table_op(verified_stmt( + "ALTER TABLE tab DROP CONSTRAINT constraint_name CASCADE", + )) { + AlterTableOperation::DropConstraint { + name: constr_name, + if_exists, + cascade, } => { - assert_eq!("tab", name.to_string()); assert_eq!("constraint_name", constr_name.to_string()); assert!(!if_exists); assert!(cascade); } _ => unreachable!(), } - match verified_stmt("ALTER TABLE tab DROP CONSTRAINT IF EXISTS constraint_name") { - Statement::AlterTable { - name, - operation: - AlterTableOperation::DropConstraint { - name: constr_name, - if_exists, - cascade, - }, + match alter_table_op(verified_stmt( + "ALTER TABLE tab DROP CONSTRAINT IF EXISTS constraint_name", + )) { + AlterTableOperation::DropConstraint { + name: constr_name, + if_exists, + cascade, } => { - assert_eq!("tab", name.to_string()); assert_eq!("constraint_name", constr_name.to_string()); assert!(if_exists); assert!(!cascade); diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index ab7997cdf..7346b9c0d 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -14,6 +14,7 @@ //! Test SQL syntax specific to MySQL. The parser based on the generic dialect //! is also tested (on the inputs it can handle). +use matches::assert_matches; use sqlparser::ast::Expr; use sqlparser::ast::Value; use sqlparser::ast::*; @@ -1256,15 +1257,10 @@ fn parse_update_with_joins() { #[test] fn parse_alter_table_drop_primary_key() { - match mysql_and_generic().verified_stmt("ALTER TABLE tab DROP PRIMARY KEY") { - Statement::AlterTable { - name, - operation: AlterTableOperation::DropPrimaryKey, - } => { - assert_eq!("tab", name.to_string()); - } - _ => unreachable!(), - } + assert_matches!( + alter_table_op(mysql_and_generic().verified_stmt("ALTER TABLE tab DROP PRIMARY KEY")), + AlterTableOperation::DropPrimaryKey + ); } #[test] @@ -1278,22 +1274,16 @@ fn parse_alter_table_change_column() { }; let sql1 = "ALTER TABLE orders CHANGE COLUMN description desc TEXT NOT NULL"; - match mysql().verified_stmt(sql1) { - Statement::AlterTable { name, operation } => { - assert_eq!(expected_name, name); - assert_eq!(expected_operation, operation); - } - _ => unreachable!(), - } + let operation = + alter_table_op_with_name(mysql().verified_stmt(sql1), &expected_name.to_string()); + assert_eq!(expected_operation, operation); let sql2 = "ALTER TABLE orders CHANGE description desc TEXT NOT NULL"; - match mysql().one_statement_parses_to(sql2, sql1) { - Statement::AlterTable { name, operation } => { - assert_eq!(expected_name, name); - assert_eq!(expected_operation, operation); - } - _ => unreachable!(), - } + let operation = alter_table_op_with_name( + mysql().one_statement_parses_to(sql2, sql1), + &expected_name.to_string(), + ); + assert_eq!(expected_operation, operation); } #[test] diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index c34ba75a8..093fac8c1 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -546,12 +546,10 @@ fn parse_create_table_constraints_only() { #[test] fn parse_alter_table_constraints_rename() { - match pg().verified_stmt("ALTER TABLE tab RENAME CONSTRAINT old_name TO new_name") { - Statement::AlterTable { - name, - operation: AlterTableOperation::RenameConstraint { old_name, new_name }, - } => { - assert_eq!("tab", name.to_string()); + match alter_table_op( + pg().verified_stmt("ALTER TABLE tab RENAME CONSTRAINT old_name TO new_name"), + ) { + AlterTableOperation::RenameConstraint { old_name, new_name } => { assert_eq!(old_name.to_string(), "old_name"); assert_eq!(new_name.to_string(), "new_name"); } @@ -566,14 +564,12 @@ fn parse_alter_table_alter_column() { "ALTER TABLE tab ALTER COLUMN is_active SET DATA TYPE TEXT USING 'text'", ); - match pg() - .verified_stmt("ALTER TABLE tab ALTER COLUMN is_active SET DATA TYPE TEXT USING 'text'") - { - Statement::AlterTable { - name, - operation: AlterTableOperation::AlterColumn { column_name, op }, - } => { - assert_eq!("tab", name.to_string()); + match alter_table_op( + pg().verified_stmt( + "ALTER TABLE tab ALTER COLUMN is_active SET DATA TYPE TEXT USING 'text'", + ), + ) { + AlterTableOperation::AlterColumn { column_name, op } => { assert_eq!("is_active", column_name.to_string()); let using_expr = Expr::Value(Value::SingleQuotedString("text".to_string())); assert_eq!( @@ -588,6 +584,48 @@ fn parse_alter_table_alter_column() { } } +#[test] +fn parse_alter_table_add_columns() { + match pg().verified_stmt("ALTER TABLE IF EXISTS ONLY tab ADD COLUMN a TEXT, ADD COLUMN b INT") { + Statement::AlterTable { + name, + if_exists, + only, + operations, + } => { + assert_eq!(name.to_string(), "tab"); + assert!(if_exists); + assert!(only); + assert_eq!( + operations, + vec![ + AlterTableOperation::AddColumn { + column_keyword: true, + if_not_exists: false, + column_def: ColumnDef { + name: "a".into(), + data_type: DataType::Text, + collation: None, + options: vec![], + }, + }, + AlterTableOperation::AddColumn { + column_keyword: true, + if_not_exists: false, + column_def: ColumnDef { + name: "b".into(), + data_type: DataType::Int(None), + collation: None, + options: vec![], + }, + }, + ] + ); + } + _ => unreachable!(), + } +} + #[test] fn parse_create_table_if_not_exists() { let sql = "CREATE TABLE IF NOT EXISTS uk_cities ()"; diff --git a/tests/sqlparser_snowflake.rs b/tests/sqlparser_snowflake.rs index ecd608527..dabe66e1d 100644 --- a/tests/sqlparser_snowflake.rs +++ b/tests/sqlparser_snowflake.rs @@ -502,12 +502,8 @@ fn test_select_wildcard_with_exclude_and_rename() { #[test] fn test_alter_table_swap_with() { let sql = "ALTER TABLE tab1 SWAP WITH tab2"; - match snowflake_and_generic().verified_stmt(sql) { - Statement::AlterTable { - name, - operation: AlterTableOperation::SwapWith { table_name }, - } => { - assert_eq!("tab1", name.to_string()); + match alter_table_op_with_name(snowflake_and_generic().verified_stmt(sql), "tab1") { + AlterTableOperation::SwapWith { table_name } => { assert_eq!("tab2", table_name.to_string()); } _ => unreachable!(),