Skip to content

Commit faeb7d4

Browse files
authored
Implement ALTER TABLE ADD COLUMN and RENAME (#203)
Based on sqlite grammar https://www.sqlite.org/lang_altertable.html
1 parent fab6e28 commit faeb7d4

File tree

5 files changed

+117
-27
lines changed

5 files changed

+117
-27
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Check https://github.com/andygrove/sqlparser-rs/commits/master for undocumented
1616
- Use Token::EOF instead of Option<Token> (#195)
1717
- Make the units keyword following `INTERVAL '...'` optional (#184) - thanks @maxcountryman!
1818
- 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!
19+
- Output DataType capitalized (`fmt::Display`) (#202) - thanks @Dandandan!
1920

2021
### Added
2122
- 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
2930
- Support the string concatentation operator `||` (#178) - thanks @Dandandan!
3031
- Support bitwise AND (`&`), OR (`|`), XOR (`^`) (#181) - thanks @Dandandan!
3132
- Add serde support to AST structs and enums (#196) - thanks @panarch!
33+
- Support `ALTER TABLE ADD COLUMN`, `RENAME COLUMN`, and `RENAME TO` (#203) - thanks @mashuai!
3234

3335
### Fixed
3436
- Report an error for unterminated string literals (#165)
37+
- Make file format (`STORED AS`) case insensitive (#200) and don't allow quoting it (#201) - thanks @Dandandan!
3538

3639
## [0.5.0] - 2019-10-10
3740

src/ast/ddl.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,38 @@ use std::fmt;
2323
pub enum AlterTableOperation {
2424
/// `ADD <table_constraint>`
2525
AddConstraint(TableConstraint),
26+
/// `ADD [ COLUMN ] <column_def>`
27+
AddColumn { column_def: ColumnDef },
2628
/// TODO: implement `DROP CONSTRAINT <name>`
2729
DropConstraint { name: Ident },
30+
/// `RENAME [ COLUMN ] <old_column_name> TO <new_column_name>`
31+
RenameColumn {
32+
old_column_name: Ident,
33+
new_column_name: Ident,
34+
},
35+
/// `RENAME TO <table_name>`
36+
RenameTable { table_name: Ident },
2837
}
2938

3039
impl fmt::Display for AlterTableOperation {
3140
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3241
match self {
3342
AlterTableOperation::AddConstraint(c) => write!(f, "ADD {}", c),
43+
AlterTableOperation::AddColumn { column_def } => {
44+
write!(f, "ADD COLUMN {}", column_def.to_string())
45+
}
3446
AlterTableOperation::DropConstraint { name } => write!(f, "DROP CONSTRAINT {}", name),
47+
AlterTableOperation::RenameColumn {
48+
old_column_name,
49+
new_column_name,
50+
} => write!(
51+
f,
52+
"RENAME COLUMN {} TO {}",
53+
old_column_name, new_column_name
54+
),
55+
AlterTableOperation::RenameTable { table_name } => {
56+
write!(f, "RENAME TO {}", table_name)
57+
}
3558
}
3659
}
3760
}

src/dialect/keywords.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@ define_keywords!(
340340
REGR_SXY,
341341
REGR_SYY,
342342
RELEASE,
343+
RENAME,
343344
REPEATABLE,
344345
RESTRICT,
345346
RESULT,

src/parser.rs

Lines changed: 44 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,6 +1122,29 @@ impl Parser {
11221122
})
11231123
}
11241124

1125+
fn parse_column_def(&mut self) -> Result<ColumnDef, ParserError> {
1126+
let name = self.parse_identifier()?;
1127+
let data_type = self.parse_data_type()?;
1128+
let collation = if self.parse_keyword(Keyword::COLLATE) {
1129+
Some(self.parse_object_name()?)
1130+
} else {
1131+
None
1132+
};
1133+
let mut options = vec![];
1134+
loop {
1135+
match self.peek_token() {
1136+
Token::EOF | Token::Comma | Token::RParen => break,
1137+
_ => options.push(self.parse_column_option_def()?),
1138+
}
1139+
}
1140+
Ok(ColumnDef {
1141+
name,
1142+
data_type,
1143+
collation,
1144+
options,
1145+
})
1146+
}
1147+
11251148
fn parse_columns(&mut self) -> Result<(Vec<ColumnDef>, Vec<TableConstraint>), ParserError> {
11261149
let mut columns = vec![];
11271150
let mut constraints = vec![];
@@ -1132,28 +1155,9 @@ impl Parser {
11321155
loop {
11331156
if let Some(constraint) = self.parse_optional_table_constraint()? {
11341157
constraints.push(constraint);
1135-
} else if let Token::Word(column_name) = self.peek_token() {
1136-
self.next_token();
1137-
let data_type = self.parse_data_type()?;
1138-
let collation = if self.parse_keyword(Keyword::COLLATE) {
1139-
Some(self.parse_object_name()?)
1140-
} else {
1141-
None
1142-
};
1143-
let mut options = vec![];
1144-
loop {
1145-
match self.peek_token() {
1146-
Token::EOF | Token::Comma | Token::RParen => break,
1147-
_ => options.push(self.parse_column_option_def()?),
1148-
}
1149-
}
1150-
1151-
columns.push(ColumnDef {
1152-
name: column_name.to_ident(),
1153-
data_type,
1154-
collation,
1155-
options,
1156-
});
1158+
} else if let Token::Word(_) = self.peek_token() {
1159+
let column_def = self.parse_column_def()?;
1160+
columns.push(column_def);
11571161
} else {
11581162
return self.expected("column name or constraint definition", self.peek_token());
11591163
}
@@ -1318,10 +1322,26 @@ impl Parser {
13181322
if let Some(constraint) = self.parse_optional_table_constraint()? {
13191323
AlterTableOperation::AddConstraint(constraint)
13201324
} else {
1321-
return self.expected("a constraint in ALTER TABLE .. ADD", self.peek_token());
1325+
let _ = self.parse_keyword(Keyword::COLUMN);
1326+
let column_def = self.parse_column_def()?;
1327+
AlterTableOperation::AddColumn { column_def }
1328+
}
1329+
} else if self.parse_keyword(Keyword::RENAME) {
1330+
if self.parse_keyword(Keyword::TO) {
1331+
let table_name = self.parse_identifier()?;
1332+
AlterTableOperation::RenameTable { table_name }
1333+
} else {
1334+
let _ = self.parse_keyword(Keyword::COLUMN);
1335+
let old_column_name = self.parse_identifier()?;
1336+
self.expect_keyword(Keyword::TO)?;
1337+
let new_column_name = self.parse_identifier()?;
1338+
AlterTableOperation::RenameColumn {
1339+
old_column_name,
1340+
new_column_name,
1341+
}
13221342
}
13231343
} else {
1324-
return self.expected("ADD after ALTER TABLE", self.peek_token());
1344+
return self.expected("ADD or RENAME after ALTER TABLE", self.peek_token());
13251345
};
13261346
Ok(Statement::AlterTable {
13271347
name: table_name,

tests/sqlparser_common.rs

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,6 +1313,51 @@ fn parse_create_table_empty() {
13131313
let _ = verified_stmt("CREATE TABLE t ()");
13141314
}
13151315

1316+
#[test]
1317+
fn parse_alter_table() {
1318+
let add_column = "ALTER TABLE tab ADD COLUMN foo TEXT";
1319+
match verified_stmt(add_column) {
1320+
Statement::AlterTable {
1321+
name,
1322+
operation: AlterTableOperation::AddColumn { column_def },
1323+
} => {
1324+
assert_eq!("tab", name.to_string());
1325+
assert_eq!("foo", column_def.name.to_string());
1326+
assert_eq!("TEXT", column_def.data_type.to_string());
1327+
}
1328+
_ => unreachable!(),
1329+
};
1330+
1331+
let rename_table = "ALTER TABLE tab RENAME TO new_tab";
1332+
match verified_stmt(rename_table) {
1333+
Statement::AlterTable {
1334+
name,
1335+
operation: AlterTableOperation::RenameTable { table_name },
1336+
} => {
1337+
assert_eq!("tab", name.to_string());
1338+
assert_eq!("new_tab", table_name.to_string())
1339+
}
1340+
_ => unreachable!(),
1341+
};
1342+
1343+
let rename_column = "ALTER TABLE tab RENAME COLUMN foo TO new_foo";
1344+
match verified_stmt(rename_column) {
1345+
Statement::AlterTable {
1346+
name,
1347+
operation:
1348+
AlterTableOperation::RenameColumn {
1349+
old_column_name,
1350+
new_column_name,
1351+
},
1352+
} => {
1353+
assert_eq!("tab", name.to_string());
1354+
assert_eq!(old_column_name.to_string(), "foo");
1355+
assert_eq!(new_column_name.to_string(), "new_foo");
1356+
}
1357+
_ => unreachable!(),
1358+
}
1359+
}
1360+
13161361
#[test]
13171362
fn parse_alter_table_constraints() {
13181363
check_one("CONSTRAINT address_pkey PRIMARY KEY (address_id)");
@@ -1347,9 +1392,7 @@ fn parse_alter_table_constraints() {
13471392
fn parse_bad_constraint() {
13481393
let res = parse_sql_statements("ALTER TABLE tab ADD");
13491394
assert_eq!(
1350-
ParserError::ParserError(
1351-
"Expected a constraint in ALTER TABLE .. ADD, found: EOF".to_string()
1352-
),
1395+
ParserError::ParserError("Expected identifier, found: EOF".to_string()),
13531396
res.unwrap_err()
13541397
);
13551398

0 commit comments

Comments
 (0)