feat: allow multiple actions in one ALTER TABLE statement (#960)

This commit is contained in:
Forbes Lindesay 2023-09-07 21:39:47 +01:00 committed by GitHub
parent e0afd4b179
commit 25e037c50f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 349 additions and 326 deletions

View file

@ -1407,7 +1407,9 @@ pub enum Statement {
/// Table name /// Table name
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))] #[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
name: ObjectName, name: ObjectName,
operation: AlterTableOperation, if_exists: bool,
only: bool,
operations: Vec<AlterTableOperation>,
}, },
AlterIndex { AlterIndex {
name: ObjectName, name: ObjectName,
@ -2618,8 +2620,24 @@ impl fmt::Display for Statement {
} }
Ok(()) Ok(())
} }
Statement::AlterTable { name, operation } => { Statement::AlterTable {
write!(f, "ALTER TABLE {name} {operation}") 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 } => { Statement::AlterIndex { name, operation } => {
write!(f, "ALTER INDEX {name} {operation}") write!(f, "ALTER INDEX {name} {operation}")

View file

@ -4081,6 +4081,174 @@ impl<'a> Parser<'a> {
Ok(SqlOption { name, value }) Ok(SqlOption { name, value })
} }
pub fn parse_alter_table_operation(&mut self) -> Result<AlterTableOperation, ParserError> {
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<Statement, ParserError> { pub fn parse_alter(&mut self) -> Result<Statement, ParserError> {
let object_type = self.expect_one_of_keywords(&[ let object_type = self.expect_one_of_keywords(&[
Keyword::VIEW, Keyword::VIEW,
@ -4091,177 +4259,15 @@ impl<'a> Parser<'a> {
match object_type { match object_type {
Keyword::VIEW => self.parse_alter_view(), Keyword::VIEW => self.parse_alter_view(),
Keyword::TABLE => { 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 table_name = self.parse_object_name()?;
let operation = if self.parse_keyword(Keyword::ADD) { let operations = self.parse_comma_separated(Parser::parse_alter_table_operation)?;
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(Statement::AlterTable { Ok(Statement::AlterTable {
name: table_name, name: table_name,
operation, if_exists,
only,
operations,
}) })
} }
Keyword::INDEX => { Keyword::INDEX => {

View file

@ -209,6 +209,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 /// Creates a `Value::Number`, panic'ing if n is not a number
pub fn number(n: &str) -> Value { pub fn number(n: &str) -> Value {
Value::Number(n.parse().unwrap(), false) Value::Number(n.parse().unwrap(), false)

View file

@ -30,8 +30,8 @@ use sqlparser::dialect::{
use sqlparser::keywords::ALL_KEYWORDS; use sqlparser::keywords::ALL_KEYWORDS;
use sqlparser::parser::{Parser, ParserError, ParserOptions}; use sqlparser::parser::{Parser, ParserError, ParserOptions};
use test_utils::{ use test_utils::{
all_dialects, assert_eq_vec, expr_from_projection, join, number, only, table, table_alias, all_dialects, alter_table_op, assert_eq_vec, expr_from_projection, join, number, only, table,
TestedDialects, table_alias, TestedDialects,
}; };
#[macro_use] #[macro_use]
@ -2920,19 +2920,17 @@ fn parse_create_external_table_lowercase() {
#[test] #[test]
fn parse_alter_table() { fn parse_alter_table() {
let add_column = "ALTER TABLE tab ADD COLUMN foo TEXT;"; let add_column = "ALTER TABLE tab ADD COLUMN foo TEXT;";
match one_statement_parses_to(add_column, "ALTER TABLE tab ADD COLUMN foo TEXT") { match alter_table_op(one_statement_parses_to(
Statement::AlterTable { add_column,
name, "ALTER TABLE tab ADD COLUMN foo TEXT",
operation: )) {
AlterTableOperation::AddColumn { AlterTableOperation::AddColumn {
column_keyword, column_keyword,
if_not_exists, if_not_exists,
column_def, column_def,
},
} => { } => {
assert!(column_keyword); assert!(column_keyword);
assert!(!if_not_exists); assert!(!if_not_exists);
assert_eq!("tab", name.to_string());
assert_eq!("foo", column_def.name.to_string()); assert_eq!("foo", column_def.name.to_string());
assert_eq!("TEXT", column_def.data_type.to_string()); assert_eq!("TEXT", column_def.data_type.to_string());
} }
@ -2940,28 +2938,19 @@ fn parse_alter_table() {
}; };
let rename_table = "ALTER TABLE tab RENAME TO new_tab"; let rename_table = "ALTER TABLE tab RENAME TO new_tab";
match verified_stmt(rename_table) { match alter_table_op(verified_stmt(rename_table)) {
Statement::AlterTable { AlterTableOperation::RenameTable { table_name } => {
name, assert_eq!("new_tab", table_name.to_string());
operation: AlterTableOperation::RenameTable { table_name },
} => {
assert_eq!("tab", name.to_string());
assert_eq!("new_tab", table_name.to_string())
} }
_ => unreachable!(), _ => unreachable!(),
}; };
let rename_column = "ALTER TABLE tab RENAME COLUMN foo TO new_foo"; let rename_column = "ALTER TABLE tab RENAME COLUMN foo TO new_foo";
match verified_stmt(rename_column) { match alter_table_op(verified_stmt(rename_column)) {
Statement::AlterTable { AlterTableOperation::RenameColumn {
name, old_column_name,
operation: new_column_name,
AlterTableOperation::RenameColumn {
old_column_name,
new_column_name,
},
} => { } => {
assert_eq!("tab", name.to_string());
assert_eq!(old_column_name.to_string(), "foo"); assert_eq!(old_column_name.to_string(), "foo");
assert_eq!(new_column_name.to_string(), "new_foo"); assert_eq!(new_column_name.to_string(), "new_foo");
} }
@ -3047,21 +3036,15 @@ fn parse_alter_view_with_columns() {
#[test] #[test]
fn parse_alter_table_add_column() { fn parse_alter_table_add_column() {
match verified_stmt("ALTER TABLE tab ADD foo TEXT") { match alter_table_op(verified_stmt("ALTER TABLE tab ADD foo TEXT")) {
Statement::AlterTable { AlterTableOperation::AddColumn { column_keyword, .. } => {
operation: AlterTableOperation::AddColumn { column_keyword, .. },
..
} => {
assert!(!column_keyword); assert!(!column_keyword);
} }
_ => unreachable!(), _ => unreachable!(),
}; };
match verified_stmt("ALTER TABLE tab ADD COLUMN foo TEXT") { match alter_table_op(verified_stmt("ALTER TABLE tab ADD COLUMN foo TEXT")) {
Statement::AlterTable { AlterTableOperation::AddColumn { column_keyword, .. } => {
operation: AlterTableOperation::AddColumn { column_keyword, .. },
..
} => {
assert!(column_keyword); assert!(column_keyword);
} }
_ => unreachable!(), _ => unreachable!(),
@ -3080,24 +3063,19 @@ fn parse_alter_table_add_column_if_not_exists() {
options: None, options: None,
}; };
match dialects.verified_stmt("ALTER TABLE tab ADD IF NOT EXISTS foo TEXT") { match alter_table_op(dialects.verified_stmt("ALTER TABLE tab ADD IF NOT EXISTS foo TEXT")) {
Statement::AlterTable { AlterTableOperation::AddColumn { if_not_exists, .. } => {
operation: AlterTableOperation::AddColumn { if_not_exists, .. },
..
} => {
assert!(if_not_exists); assert!(if_not_exists);
} }
_ => unreachable!(), _ => unreachable!(),
}; };
match dialects.verified_stmt("ALTER TABLE tab ADD COLUMN IF NOT EXISTS foo TEXT") { match alter_table_op(
Statement::AlterTable { dialects.verified_stmt("ALTER TABLE tab ADD COLUMN IF NOT EXISTS foo TEXT"),
operation: ) {
AlterTableOperation::AddColumn { AlterTableOperation::AddColumn {
column_keyword, column_keyword,
if_not_exists, if_not_exists,
..
},
.. ..
} => { } => {
assert!(column_keyword); assert!(column_keyword);
@ -3123,12 +3101,10 @@ fn parse_alter_table_constraints() {
check_one("CHECK (end_date > start_date OR end_date IS NULL)"); check_one("CHECK (end_date > start_date OR end_date IS NULL)");
fn check_one(constraint_text: &str) { fn check_one(constraint_text: &str) {
match verified_stmt(&format!("ALTER TABLE tab ADD {constraint_text}")) { match alter_table_op(verified_stmt(&format!(
Statement::AlterTable { "ALTER TABLE tab ADD {constraint_text}"
name, ))) {
operation: AlterTableOperation::AddConstraint(constraint), AlterTableOperation::AddConstraint(constraint) => {
} => {
assert_eq!("tab", name.to_string());
assert_eq!(constraint_text, constraint.to_string()); assert_eq!(constraint_text, constraint.to_string());
} }
_ => unreachable!(), _ => unreachable!(),
@ -3150,17 +3126,12 @@ fn parse_alter_table_drop_column() {
); );
fn check_one(constraint_text: &str) { fn check_one(constraint_text: &str) {
match verified_stmt(&format!("ALTER TABLE tab {constraint_text}")) { match alter_table_op(verified_stmt(&format!("ALTER TABLE tab {constraint_text}"))) {
Statement::AlterTable { AlterTableOperation::DropColumn {
name, column_name,
operation: if_exists,
AlterTableOperation::DropColumn { cascade,
column_name,
if_exists,
cascade,
},
} => { } => {
assert_eq!("tab", name.to_string());
assert_eq!("is_active", column_name.to_string()); assert_eq!("is_active", column_name.to_string());
assert!(if_exists); assert!(if_exists);
assert!(cascade); assert!(cascade);
@ -3173,12 +3144,10 @@ fn parse_alter_table_drop_column() {
#[test] #[test]
fn parse_alter_table_alter_column() { fn parse_alter_table_alter_column() {
let alter_stmt = "ALTER TABLE tab"; let alter_stmt = "ALTER TABLE tab";
match verified_stmt(&format!("{alter_stmt} ALTER COLUMN is_active SET NOT NULL")) { match alter_table_op(verified_stmt(&format!(
Statement::AlterTable { "{alter_stmt} ALTER COLUMN is_active SET NOT NULL"
name, ))) {
operation: AlterTableOperation::AlterColumn { column_name, op }, AlterTableOperation::AlterColumn { column_name, op } => {
} => {
assert_eq!("tab", name.to_string());
assert_eq!("is_active", column_name.to_string()); assert_eq!("is_active", column_name.to_string());
assert_eq!(op, AlterColumnOperation::SetNotNull {}); assert_eq!(op, AlterColumnOperation::SetNotNull {});
} }
@ -3190,14 +3159,10 @@ fn parse_alter_table_alter_column() {
"ALTER TABLE tab ALTER COLUMN is_active DROP NOT NULL", "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" "{alter_stmt} ALTER COLUMN is_active SET DEFAULT false"
)) { ))) {
Statement::AlterTable { AlterTableOperation::AlterColumn { column_name, op } => {
name,
operation: AlterTableOperation::AlterColumn { column_name, op },
} => {
assert_eq!("tab", name.to_string());
assert_eq!("is_active", column_name.to_string()); assert_eq!("is_active", column_name.to_string());
assert_eq!( assert_eq!(
op, op,
@ -3209,12 +3174,10 @@ fn parse_alter_table_alter_column() {
_ => unreachable!(), _ => unreachable!(),
} }
match verified_stmt(&format!("{alter_stmt} ALTER COLUMN is_active DROP DEFAULT")) { match alter_table_op(verified_stmt(&format!(
Statement::AlterTable { "{alter_stmt} ALTER COLUMN is_active DROP DEFAULT"
name, ))) {
operation: AlterTableOperation::AlterColumn { column_name, op }, AlterTableOperation::AlterColumn { column_name, op } => {
} => {
assert_eq!("tab", name.to_string());
assert_eq!("is_active", column_name.to_string()); assert_eq!("is_active", column_name.to_string());
assert_eq!(op, AlterColumnOperation::DropDefault {}); assert_eq!(op, AlterColumnOperation::DropDefault {});
} }
@ -3225,12 +3188,10 @@ fn parse_alter_table_alter_column() {
#[test] #[test]
fn parse_alter_table_alter_column_type() { fn parse_alter_table_alter_column_type() {
let alter_stmt = "ALTER TABLE tab"; let alter_stmt = "ALTER TABLE tab";
match verified_stmt("ALTER TABLE tab ALTER COLUMN is_active SET DATA TYPE TEXT") { match alter_table_op(verified_stmt(
Statement::AlterTable { "ALTER TABLE tab ALTER COLUMN is_active SET DATA TYPE TEXT",
name, )) {
operation: AlterTableOperation::AlterColumn { column_name, op }, AlterTableOperation::AlterColumn { column_name, op } => {
} => {
assert_eq!("tab", name.to_string());
assert_eq!("is_active", column_name.to_string()); assert_eq!("is_active", column_name.to_string());
assert_eq!( assert_eq!(
op, op,
@ -3267,34 +3228,28 @@ fn parse_alter_table_alter_column_type() {
#[test] #[test]
fn parse_alter_table_drop_constraint() { fn parse_alter_table_drop_constraint() {
let alter_stmt = "ALTER TABLE tab"; let alter_stmt = "ALTER TABLE tab";
match verified_stmt("ALTER TABLE tab DROP CONSTRAINT constraint_name CASCADE") { match alter_table_op(verified_stmt(
Statement::AlterTable { "ALTER TABLE tab DROP CONSTRAINT constraint_name CASCADE",
name, )) {
operation: AlterTableOperation::DropConstraint {
AlterTableOperation::DropConstraint { name: constr_name,
name: constr_name, if_exists,
if_exists, cascade,
cascade,
},
} => { } => {
assert_eq!("tab", name.to_string());
assert_eq!("constraint_name", constr_name.to_string()); assert_eq!("constraint_name", constr_name.to_string());
assert!(!if_exists); assert!(!if_exists);
assert!(cascade); assert!(cascade);
} }
_ => unreachable!(), _ => unreachable!(),
} }
match verified_stmt("ALTER TABLE tab DROP CONSTRAINT IF EXISTS constraint_name") { match alter_table_op(verified_stmt(
Statement::AlterTable { "ALTER TABLE tab DROP CONSTRAINT IF EXISTS constraint_name",
name, )) {
operation: AlterTableOperation::DropConstraint {
AlterTableOperation::DropConstraint { name: constr_name,
name: constr_name, if_exists,
if_exists, cascade,
cascade,
},
} => { } => {
assert_eq!("tab", name.to_string());
assert_eq!("constraint_name", constr_name.to_string()); assert_eq!("constraint_name", constr_name.to_string());
assert!(if_exists); assert!(if_exists);
assert!(!cascade); assert!(!cascade);

View file

@ -14,6 +14,7 @@
//! Test SQL syntax specific to MySQL. The parser based on the generic dialect //! Test SQL syntax specific to MySQL. The parser based on the generic dialect
//! is also tested (on the inputs it can handle). //! is also tested (on the inputs it can handle).
use matches::assert_matches;
use sqlparser::ast::Expr; use sqlparser::ast::Expr;
use sqlparser::ast::Value; use sqlparser::ast::Value;
use sqlparser::ast::*; use sqlparser::ast::*;
@ -1256,15 +1257,10 @@ fn parse_update_with_joins() {
#[test] #[test]
fn parse_alter_table_drop_primary_key() { fn parse_alter_table_drop_primary_key() {
match mysql_and_generic().verified_stmt("ALTER TABLE tab DROP PRIMARY KEY") { assert_matches!(
Statement::AlterTable { alter_table_op(mysql_and_generic().verified_stmt("ALTER TABLE tab DROP PRIMARY KEY")),
name, AlterTableOperation::DropPrimaryKey
operation: AlterTableOperation::DropPrimaryKey, );
} => {
assert_eq!("tab", name.to_string());
}
_ => unreachable!(),
}
} }
#[test] #[test]
@ -1278,22 +1274,16 @@ fn parse_alter_table_change_column() {
}; };
let sql1 = "ALTER TABLE orders CHANGE COLUMN description desc TEXT NOT NULL"; let sql1 = "ALTER TABLE orders CHANGE COLUMN description desc TEXT NOT NULL";
match mysql().verified_stmt(sql1) { let operation =
Statement::AlterTable { name, operation } => { alter_table_op_with_name(mysql().verified_stmt(sql1), &expected_name.to_string());
assert_eq!(expected_name, name); assert_eq!(expected_operation, operation);
assert_eq!(expected_operation, operation);
}
_ => unreachable!(),
}
let sql2 = "ALTER TABLE orders CHANGE description desc TEXT NOT NULL"; let sql2 = "ALTER TABLE orders CHANGE description desc TEXT NOT NULL";
match mysql().one_statement_parses_to(sql2, sql1) { let operation = alter_table_op_with_name(
Statement::AlterTable { name, operation } => { mysql().one_statement_parses_to(sql2, sql1),
assert_eq!(expected_name, name); &expected_name.to_string(),
assert_eq!(expected_operation, operation); );
} assert_eq!(expected_operation, operation);
_ => unreachable!(),
}
} }
#[test] #[test]

View file

@ -546,12 +546,10 @@ fn parse_create_table_constraints_only() {
#[test] #[test]
fn parse_alter_table_constraints_rename() { fn parse_alter_table_constraints_rename() {
match pg().verified_stmt("ALTER TABLE tab RENAME CONSTRAINT old_name TO new_name") { match alter_table_op(
Statement::AlterTable { pg().verified_stmt("ALTER TABLE tab RENAME CONSTRAINT old_name TO new_name"),
name, ) {
operation: AlterTableOperation::RenameConstraint { old_name, new_name }, AlterTableOperation::RenameConstraint { old_name, new_name } => {
} => {
assert_eq!("tab", name.to_string());
assert_eq!(old_name.to_string(), "old_name"); assert_eq!(old_name.to_string(), "old_name");
assert_eq!(new_name.to_string(), "new_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'", "ALTER TABLE tab ALTER COLUMN is_active SET DATA TYPE TEXT USING 'text'",
); );
match pg() match alter_table_op(
.verified_stmt("ALTER TABLE tab ALTER COLUMN is_active SET DATA TYPE TEXT USING 'text'") 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 }, AlterTableOperation::AlterColumn { column_name, op } => {
} => {
assert_eq!("tab", name.to_string());
assert_eq!("is_active", column_name.to_string()); assert_eq!("is_active", column_name.to_string());
let using_expr = Expr::Value(Value::SingleQuotedString("text".to_string())); let using_expr = Expr::Value(Value::SingleQuotedString("text".to_string()));
assert_eq!( 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] #[test]
fn parse_create_table_if_not_exists() { fn parse_create_table_if_not_exists() {
let sql = "CREATE TABLE IF NOT EXISTS uk_cities ()"; let sql = "CREATE TABLE IF NOT EXISTS uk_cities ()";

View file

@ -502,12 +502,8 @@ fn test_select_wildcard_with_exclude_and_rename() {
#[test] #[test]
fn test_alter_table_swap_with() { fn test_alter_table_swap_with() {
let sql = "ALTER TABLE tab1 SWAP WITH tab2"; let sql = "ALTER TABLE tab1 SWAP WITH tab2";
match snowflake_and_generic().verified_stmt(sql) { match alter_table_op_with_name(snowflake_and_generic().verified_stmt(sql), "tab1") {
Statement::AlterTable { AlterTableOperation::SwapWith { table_name } => {
name,
operation: AlterTableOperation::SwapWith { table_name },
} => {
assert_eq!("tab1", name.to_string());
assert_eq!("tab2", table_name.to_string()); assert_eq!("tab2", table_name.to_string());
} }
_ => unreachable!(), _ => unreachable!(),