Skip to content

Commit 8106d25

Browse files
committed
Don't fail parsing a column definition with unexpected tokens
Since PR #93 `parse_column_def` parses a set of column options in a loop, e.g. given: ``` _______ column_def _______ CREATE TABLE foo (bar INT NOT NULL DEFAULT 1, ) -------- --------- option 1 option 2 ```` it parses column options until it encounters one of the delimiter tokens First when we only supported `CREATE TABLE`, the set of delimiters that stopped the parsing used to be `Token::Comma | Token::RParen`. Then we added support for `ALTER TABLE ADD COLUMN <column_def>`. Turns out the parser started to bail if the statement ended with a semicolon, while attempting to parse the semicolon as a column option, as we forgot to add it to the set of delimiter tokens. This was recently fixed in #246 by including Token::SemiColon to the list, but it felt wrong to have to update this list, and to have a common list of delimiters for two different contexts (CREATE TABLE with parens vs ALTER TABLE ADD COLUMN without parens). Also our current approach cannot handle multiple statements NOT separated by a semicolon, as is common in MS SQL DDL. We don't explicitly support it in `parse_statements`, but that's a use-case like to keep in mind nevertheless.
1 parent f57f314 commit 8106d25

File tree

2 files changed

+40
-27
lines changed

2 files changed

+40
-27
lines changed

src/parser.rs

+33-26
Original file line numberDiff line numberDiff line change
@@ -1225,10 +1225,21 @@ impl Parser {
12251225
};
12261226
let mut options = vec![];
12271227
loop {
1228-
match self.peek_token() {
1229-
Token::EOF | Token::Comma | Token::RParen | Token::SemiColon => break,
1230-
_ => options.push(self.parse_column_option_def()?),
1231-
}
1228+
if self.parse_keyword(Keyword::CONSTRAINT) {
1229+
let name = Some(self.parse_identifier()?);
1230+
if let Some(option) = self.parse_optional_column_option()? {
1231+
options.push(ColumnOptionDef { name, option });
1232+
} else {
1233+
return self.expected(
1234+
"constraint details after CONSTRAINT <name>",
1235+
self.peek_token(),
1236+
);
1237+
}
1238+
} else if let Some(option) = self.parse_optional_column_option()? {
1239+
options.push(ColumnOptionDef { name: None, option });
1240+
} else {
1241+
break;
1242+
};
12321243
}
12331244
Ok(ColumnDef {
12341245
name,
@@ -1238,23 +1249,17 @@ impl Parser {
12381249
})
12391250
}
12401251

1241-
pub fn parse_column_option_def(&mut self) -> Result<ColumnOptionDef, ParserError> {
1242-
let name = if self.parse_keyword(Keyword::CONSTRAINT) {
1243-
Some(self.parse_identifier()?)
1244-
} else {
1245-
None
1246-
};
1247-
1248-
let option = if self.parse_keywords(&[Keyword::NOT, Keyword::NULL]) {
1249-
ColumnOption::NotNull
1252+
pub fn parse_optional_column_option(&mut self) -> Result<Option<ColumnOption>, ParserError> {
1253+
if self.parse_keywords(&[Keyword::NOT, Keyword::NULL]) {
1254+
Ok(Some(ColumnOption::NotNull))
12501255
} else if self.parse_keyword(Keyword::NULL) {
1251-
ColumnOption::Null
1256+
Ok(Some(ColumnOption::Null))
12521257
} else if self.parse_keyword(Keyword::DEFAULT) {
1253-
ColumnOption::Default(self.parse_expr()?)
1258+
Ok(Some(ColumnOption::Default(self.parse_expr()?)))
12541259
} else if self.parse_keywords(&[Keyword::PRIMARY, Keyword::KEY]) {
1255-
ColumnOption::Unique { is_primary: true }
1260+
Ok(Some(ColumnOption::Unique { is_primary: true }))
12561261
} else if self.parse_keyword(Keyword::UNIQUE) {
1257-
ColumnOption::Unique { is_primary: false }
1262+
Ok(Some(ColumnOption::Unique { is_primary: false }))
12581263
} else if self.parse_keyword(Keyword::REFERENCES) {
12591264
let foreign_table = self.parse_object_name()?;
12601265
// PostgreSQL allows omitting the column list and
@@ -1273,28 +1278,30 @@ impl Parser {
12731278
break;
12741279
}
12751280
}
1276-
ColumnOption::ForeignKey {
1281+
Ok(Some(ColumnOption::ForeignKey {
12771282
foreign_table,
12781283
referred_columns,
12791284
on_delete,
12801285
on_update,
1281-
}
1286+
}))
12821287
} else if self.parse_keyword(Keyword::CHECK) {
12831288
self.expect_token(&Token::LParen)?;
12841289
let expr = self.parse_expr()?;
12851290
self.expect_token(&Token::RParen)?;
1286-
ColumnOption::Check(expr)
1291+
Ok(Some(ColumnOption::Check(expr)))
12871292
} else if self.parse_keyword(Keyword::AUTO_INCREMENT) {
12881293
// Support AUTO_INCREMENT for MySQL
1289-
ColumnOption::DialectSpecific(vec![Token::make_keyword("AUTO_INCREMENT")])
1294+
Ok(Some(ColumnOption::DialectSpecific(vec![
1295+
Token::make_keyword("AUTO_INCREMENT"),
1296+
])))
12901297
} else if self.parse_keyword(Keyword::AUTOINCREMENT) {
12911298
// Support AUTOINCREMENT for SQLite
1292-
ColumnOption::DialectSpecific(vec![Token::make_keyword("AUTOINCREMENT")])
1299+
Ok(Some(ColumnOption::DialectSpecific(vec![
1300+
Token::make_keyword("AUTOINCREMENT"),
1301+
])))
12931302
} else {
1294-
return self.expected("column option", self.peek_token());
1295-
};
1296-
1297-
Ok(ColumnOptionDef { name, option })
1303+
Ok(None)
1304+
}
12981305
}
12991306

13001307
pub fn parse_referential_action(&mut self) -> Result<ReferentialAction, ParserError> {

tests/sqlparser_common.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -1141,7 +1141,13 @@ fn parse_create_table() {
11411141
assert!(res
11421142
.unwrap_err()
11431143
.to_string()
1144-
.contains("Expected column option, found: GARBAGE"));
1144+
.contains("Expected \',\' or \')\' after column definition, found: GARBAGE"));
1145+
1146+
let res = parse_sql_statements("CREATE TABLE t (a int NOT NULL CONSTRAINT foo)");
1147+
assert!(res
1148+
.unwrap_err()
1149+
.to_string()
1150+
.contains("Expected constraint details after CONSTRAINT <name>"));
11451151
}
11461152

11471153
#[test]

0 commit comments

Comments
 (0)