Skip to content

Commit 3d99926

Browse files
committed
Fix column definition COLLATE parsing
Since `COLLATE` has somewhat special parsing (not handled as an infix operator, since the right-hand side is not really an expression), it also needs special handling to not allow parsing it in column definitions, where it should instead be interpreted as a column option.
1 parent 3583514 commit 3d99926

File tree

2 files changed

+65
-25
lines changed

2 files changed

+65
-25
lines changed

src/parser/mod.rs

Lines changed: 8 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1248,6 +1248,12 @@ impl<'a> Parser<'a> {
12481248
debug!("parsing expr");
12491249
let mut expr = self.parse_prefix()?;
12501250

1251+
// We would have returned early in `parse_prefix` before checking for `COLLATE`, and there's
1252+
// no infix operator handling for `COLLATE`, so we must return now.
1253+
if self.in_column_definition_state() && self.peek_keyword(Keyword::COLLATE) {
1254+
return Ok(expr);
1255+
}
1256+
12511257
expr = self.parse_compound_expr(expr, vec![])?;
12521258

12531259
debug!("prefix: {expr:?}");
@@ -1722,7 +1728,7 @@ impl<'a> Parser<'a> {
17221728
_ => self.expected_at("an expression", next_token_index),
17231729
}?;
17241730

1725-
if self.parse_keyword(Keyword::COLLATE) {
1731+
if !self.in_column_definition_state() && self.parse_keyword(Keyword::COLLATE) {
17261732
Ok(Expr::Collate {
17271733
expr: Box::new(expr),
17281734
collation: self.parse_object_name(false)?,
@@ -3372,6 +3378,7 @@ impl<'a> Parser<'a> {
33723378

33733379
self.advance_token();
33743380
let tok = self.get_current_token();
3381+
debug!("infix: {tok:?}");
33753382
let tok_index = self.get_current_index();
33763383
let span = tok.span;
33773384
let regular_binary_operator = match &tok.token {
@@ -17698,30 +17705,6 @@ mod tests {
1769817705
assert!(Parser::parse_sql(&MySqlDialect {}, sql).is_err());
1769917706
}
1770017707

17701-
#[test]
17702-
fn test_parse_not_null_in_column_options() {
17703-
let canonical = concat!(
17704-
"CREATE TABLE foo (",
17705-
"abc INT DEFAULT (42 IS NOT NULL) NOT NULL,",
17706-
" def INT,",
17707-
" def_null BOOL GENERATED ALWAYS AS (def IS NOT NULL) STORED,",
17708-
" CHECK (abc IS NOT NULL)",
17709-
")"
17710-
);
17711-
all_dialects().verified_stmt(canonical);
17712-
all_dialects().one_statement_parses_to(
17713-
concat!(
17714-
"CREATE TABLE foo (",
17715-
"abc INT DEFAULT (42 NOT NULL) NOT NULL,",
17716-
" def INT,",
17717-
" def_null BOOL GENERATED ALWAYS AS (def NOT NULL) STORED,",
17718-
" CHECK (abc NOT NULL)",
17719-
")"
17720-
),
17721-
canonical,
17722-
);
17723-
}
17724-
1772517708
#[test]
1772617709
fn test_placeholder_invalid_whitespace() {
1772717710
for w in [" ", "/*invalid*/"] {

tests/sqlparser_common.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16574,3 +16574,60 @@ fn parse_create_view_if_not_exists() {
1657416574
res.unwrap_err()
1657516575
);
1657616576
}
16577+
16578+
#[test]
16579+
fn test_parse_not_null_in_column_options() {
16580+
let canonical = concat!(
16581+
"CREATE TABLE foo (",
16582+
"abc INT DEFAULT (42 IS NOT NULL) NOT NULL,",
16583+
" def INT,",
16584+
" def_null BOOL GENERATED ALWAYS AS (def IS NOT NULL) STORED,",
16585+
" CHECK (abc IS NOT NULL)",
16586+
")"
16587+
);
16588+
all_dialects().verified_stmt(canonical);
16589+
all_dialects().one_statement_parses_to(
16590+
concat!(
16591+
"CREATE TABLE foo (",
16592+
"abc INT DEFAULT (42 NOT NULL) NOT NULL,",
16593+
" def INT,",
16594+
" def_null BOOL GENERATED ALWAYS AS (def NOT NULL) STORED,",
16595+
" CHECK (abc NOT NULL)",
16596+
")"
16597+
),
16598+
canonical,
16599+
);
16600+
}
16601+
16602+
#[test]
16603+
fn test_parse_default_with_collate_column_option() {
16604+
let sql = "CREATE TABLE foo (abc TEXT DEFAULT 'foo' COLLATE 'en_US')";
16605+
let stmt = all_dialects().verified_stmt(sql);
16606+
if let Statement::CreateTable(CreateTable { mut columns, .. }) = stmt {
16607+
let mut column = columns.pop().unwrap();
16608+
assert_eq!(&column.name.value, "abc");
16609+
assert_eq!(column.data_type, DataType::Text);
16610+
let collate_option = column.options.pop().unwrap();
16611+
if let ColumnOptionDef {
16612+
name: None,
16613+
option: ColumnOption::Collation(collate),
16614+
} = collate_option
16615+
{
16616+
assert_eq!(collate.to_string(), "'en_US'");
16617+
} else {
16618+
panic!("Expected collate column option, got {collate_option}");
16619+
}
16620+
let default_option = column.options.pop().unwrap();
16621+
if let ColumnOptionDef {
16622+
name: None,
16623+
option: ColumnOption::Default(Expr::Value(value)),
16624+
} = default_option
16625+
{
16626+
assert_eq!(value.to_string(), "'foo'");
16627+
} else {
16628+
panic!("Expected default column option, got {default_option}");
16629+
}
16630+
} else {
16631+
panic!("Expected create table statement");
16632+
}
16633+
}

0 commit comments

Comments
 (0)