Implement ALTER TABLE ADD COLUMN and RENAME (#203)

Based on sqlite grammar
https://www.sqlite.org/lang_altertable.html
This commit is contained in:
mz 2020-06-17 03:52:37 +08:00 committed by GitHub
parent fab6e28271
commit faeb7d440a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 117 additions and 27 deletions

View file

@ -16,6 +16,7 @@ Check https://github.com/andygrove/sqlparser-rs/commits/master for undocumented
- Use Token::EOF instead of Option<Token> (#195)
- Make the units keyword following `INTERVAL '...'` optional (#184) - thanks @maxcountryman!
- Generalize `DATE`/`TIME`/`TIMESTAMP` literals representation in the AST (`TypedString { data_type, value }`) and allow `DATE` and other keywords to be used as identifiers when not followed by a string (#187) - thanks @maxcountryman!
- Output DataType capitalized (`fmt::Display`) (#202) - thanks @Dandandan!
### Added
- Support MSSQL `TOP (<N>) [ PERCENT ] [ WITH TIES ]` (#150) - thanks @alexkyllo!
@ -29,9 +30,11 @@ Check https://github.com/andygrove/sqlparser-rs/commits/master for undocumented
- Support the string concatentation operator `||` (#178) - thanks @Dandandan!
- Support bitwise AND (`&`), OR (`|`), XOR (`^`) (#181) - thanks @Dandandan!
- Add serde support to AST structs and enums (#196) - thanks @panarch!
- Support `ALTER TABLE ADD COLUMN`, `RENAME COLUMN`, and `RENAME TO` (#203) - thanks @mashuai!
### Fixed
- Report an error for unterminated string literals (#165)
- Make file format (`STORED AS`) case insensitive (#200) and don't allow quoting it (#201) - thanks @Dandandan!
## [0.5.0] - 2019-10-10

View file

@ -23,15 +23,38 @@ use std::fmt;
pub enum AlterTableOperation {
/// `ADD <table_constraint>`
AddConstraint(TableConstraint),
/// `ADD [ COLUMN ] <column_def>`
AddColumn { column_def: ColumnDef },
/// TODO: implement `DROP CONSTRAINT <name>`
DropConstraint { name: Ident },
/// `RENAME [ COLUMN ] <old_column_name> TO <new_column_name>`
RenameColumn {
old_column_name: Ident,
new_column_name: Ident,
},
/// `RENAME TO <table_name>`
RenameTable { table_name: Ident },
}
impl fmt::Display for AlterTableOperation {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
AlterTableOperation::AddConstraint(c) => write!(f, "ADD {}", c),
AlterTableOperation::AddColumn { column_def } => {
write!(f, "ADD COLUMN {}", column_def.to_string())
}
AlterTableOperation::DropConstraint { name } => write!(f, "DROP CONSTRAINT {}", name),
AlterTableOperation::RenameColumn {
old_column_name,
new_column_name,
} => write!(
f,
"RENAME COLUMN {} TO {}",
old_column_name, new_column_name
),
AlterTableOperation::RenameTable { table_name } => {
write!(f, "RENAME TO {}", table_name)
}
}
}
}

View file

@ -340,6 +340,7 @@ define_keywords!(
REGR_SXY,
REGR_SYY,
RELEASE,
RENAME,
REPEATABLE,
RESTRICT,
RESULT,

View file

@ -1122,6 +1122,29 @@ impl Parser {
})
}
fn parse_column_def(&mut self) -> Result<ColumnDef, ParserError> {
let name = self.parse_identifier()?;
let data_type = self.parse_data_type()?;
let collation = if self.parse_keyword(Keyword::COLLATE) {
Some(self.parse_object_name()?)
} else {
None
};
let mut options = vec![];
loop {
match self.peek_token() {
Token::EOF | Token::Comma | Token::RParen => break,
_ => options.push(self.parse_column_option_def()?),
}
}
Ok(ColumnDef {
name,
data_type,
collation,
options,
})
}
fn parse_columns(&mut self) -> Result<(Vec<ColumnDef>, Vec<TableConstraint>), ParserError> {
let mut columns = vec![];
let mut constraints = vec![];
@ -1132,28 +1155,9 @@ impl Parser {
loop {
if let Some(constraint) = self.parse_optional_table_constraint()? {
constraints.push(constraint);
} else if let Token::Word(column_name) = self.peek_token() {
self.next_token();
let data_type = self.parse_data_type()?;
let collation = if self.parse_keyword(Keyword::COLLATE) {
Some(self.parse_object_name()?)
} else {
None
};
let mut options = vec![];
loop {
match self.peek_token() {
Token::EOF | Token::Comma | Token::RParen => break,
_ => options.push(self.parse_column_option_def()?),
}
}
columns.push(ColumnDef {
name: column_name.to_ident(),
data_type,
collation,
options,
});
} else if let Token::Word(_) = self.peek_token() {
let column_def = self.parse_column_def()?;
columns.push(column_def);
} else {
return self.expected("column name or constraint definition", self.peek_token());
}
@ -1318,10 +1322,26 @@ impl Parser {
if let Some(constraint) = self.parse_optional_table_constraint()? {
AlterTableOperation::AddConstraint(constraint)
} else {
return self.expected("a constraint in ALTER TABLE .. ADD", self.peek_token());
let _ = self.parse_keyword(Keyword::COLUMN);
let column_def = self.parse_column_def()?;
AlterTableOperation::AddColumn { column_def }
}
} else if self.parse_keyword(Keyword::RENAME) {
if self.parse_keyword(Keyword::TO) {
let table_name = self.parse_identifier()?;
AlterTableOperation::RenameTable { table_name }
} else {
let _ = self.parse_keyword(Keyword::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 {
return self.expected("ADD after ALTER TABLE", self.peek_token());
return self.expected("ADD or RENAME after ALTER TABLE", self.peek_token());
};
Ok(Statement::AlterTable {
name: table_name,

View file

@ -1313,6 +1313,51 @@ fn parse_create_table_empty() {
let _ = verified_stmt("CREATE TABLE t ()");
}
#[test]
fn parse_alter_table() {
let add_column = "ALTER TABLE tab ADD COLUMN foo TEXT";
match verified_stmt(add_column) {
Statement::AlterTable {
name,
operation: AlterTableOperation::AddColumn { column_def },
} => {
assert_eq!("tab", name.to_string());
assert_eq!("foo", column_def.name.to_string());
assert_eq!("TEXT", column_def.data_type.to_string());
}
_ => unreachable!(),
};
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())
}
_ => 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,
},
} => {
assert_eq!("tab", name.to_string());
assert_eq!(old_column_name.to_string(), "foo");
assert_eq!(new_column_name.to_string(), "new_foo");
}
_ => unreachable!(),
}
}
#[test]
fn parse_alter_table_constraints() {
check_one("CONSTRAINT address_pkey PRIMARY KEY (address_id)");
@ -1347,9 +1392,7 @@ fn parse_alter_table_constraints() {
fn parse_bad_constraint() {
let res = parse_sql_statements("ALTER TABLE tab ADD");
assert_eq!(
ParserError::ParserError(
"Expected a constraint in ALTER TABLE .. ADD, found: EOF".to_string()
),
ParserError::ParserError("Expected identifier, found: EOF".to_string()),
res.unwrap_err()
);