Skip to content

Commit d387576

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 9127370 commit d387576

File tree

2 files changed

+64
-25
lines changed

2 files changed

+64
-25
lines changed

src/parser/mod.rs

Lines changed: 7 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 exited 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)?,
@@ -17562,28 +17568,4 @@ mod tests {
1756217568

1756317569
assert!(Parser::parse_sql(&MySqlDialect {}, sql).is_err());
1756417570
}
17565-
17566-
#[test]
17567-
fn test_parse_not_null_in_column_options() {
17568-
let canonical = concat!(
17569-
"CREATE TABLE foo (",
17570-
"abc INT DEFAULT (42 IS NOT NULL) NOT NULL,",
17571-
" def INT,",
17572-
" def_null BOOL GENERATED ALWAYS AS (def IS NOT NULL) STORED,",
17573-
" CHECK (abc IS NOT NULL)",
17574-
")"
17575-
);
17576-
all_dialects().verified_stmt(canonical);
17577-
all_dialects().one_statement_parses_to(
17578-
concat!(
17579-
"CREATE TABLE foo (",
17580-
"abc INT DEFAULT (42 NOT NULL) NOT NULL,",
17581-
" def INT,",
17582-
" def_null BOOL GENERATED ALWAYS AS (def NOT NULL) STORED,",
17583-
" CHECK (abc NOT NULL)",
17584-
")"
17585-
),
17586-
canonical,
17587-
);
17588-
}
1758917571
}

tests/sqlparser_common.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16451,3 +16451,60 @@ fn parse_create_view_if_not_exists() {
1645116451
res.unwrap_err()
1645216452
);
1645316453
}
16454+
16455+
#[test]
16456+
fn test_parse_not_null_in_column_options() {
16457+
let canonical = concat!(
16458+
"CREATE TABLE foo (",
16459+
"abc INT DEFAULT (42 IS NOT NULL) NOT NULL,",
16460+
" def INT,",
16461+
" def_null BOOL GENERATED ALWAYS AS (def IS NOT NULL) STORED,",
16462+
" CHECK (abc IS NOT NULL)",
16463+
")"
16464+
);
16465+
all_dialects().verified_stmt(canonical);
16466+
all_dialects().one_statement_parses_to(
16467+
concat!(
16468+
"CREATE TABLE foo (",
16469+
"abc INT DEFAULT (42 NOT NULL) NOT NULL,",
16470+
" def INT,",
16471+
" def_null BOOL GENERATED ALWAYS AS (def NOT NULL) STORED,",
16472+
" CHECK (abc NOT NULL)",
16473+
")"
16474+
),
16475+
canonical,
16476+
);
16477+
}
16478+
16479+
#[test]
16480+
fn test_parse_default_with_collate_column_option() {
16481+
let sql = "CREATE TABLE foo (abc TEXT DEFAULT 'foo' COLLATE 'en_US')";
16482+
let stmt = all_dialects().verified_stmt(sql);
16483+
if let Statement::CreateTable(CreateTable { mut columns, .. }) = stmt {
16484+
let mut column = columns.pop().unwrap();
16485+
assert_eq!(&column.name.value, "abc");
16486+
assert_eq!(column.data_type, DataType::Text);
16487+
let collate_option = column.options.pop().unwrap();
16488+
if let ColumnOptionDef {
16489+
name: None,
16490+
option: ColumnOption::Collation(collate),
16491+
} = collate_option
16492+
{
16493+
assert_eq!(collate.to_string(), "'en_US'");
16494+
} else {
16495+
panic!("Expected collate column option, got {collate_option}");
16496+
}
16497+
let default_option = column.options.pop().unwrap();
16498+
if let ColumnOptionDef {
16499+
name: None,
16500+
option: ColumnOption::Default(Expr::Value(value)),
16501+
} = default_option
16502+
{
16503+
assert_eq!(value.to_string(), "'foo'");
16504+
} else {
16505+
panic!("Expected default column option, got {default_option}");
16506+
}
16507+
} else {
16508+
panic!("Expected create table statement");
16509+
}
16510+
}

0 commit comments

Comments
 (0)