Skip to content

Commit 112be4e

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 dd650b8 commit 112be4e

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 {
@@ -17608,30 +17615,6 @@ mod tests {
1760817615
assert!(Parser::parse_sql(&MySqlDialect {}, sql).is_err());
1760917616
}
1761017617

17611-
#[test]
17612-
fn test_parse_not_null_in_column_options() {
17613-
let canonical = concat!(
17614-
"CREATE TABLE foo (",
17615-
"abc INT DEFAULT (42 IS NOT NULL) NOT NULL,",
17616-
" def INT,",
17617-
" def_null BOOL GENERATED ALWAYS AS (def IS NOT NULL) STORED,",
17618-
" CHECK (abc IS NOT NULL)",
17619-
")"
17620-
);
17621-
all_dialects().verified_stmt(canonical);
17622-
all_dialects().one_statement_parses_to(
17623-
concat!(
17624-
"CREATE TABLE foo (",
17625-
"abc INT DEFAULT (42 NOT NULL) NOT NULL,",
17626-
" def INT,",
17627-
" def_null BOOL GENERATED ALWAYS AS (def NOT NULL) STORED,",
17628-
" CHECK (abc NOT NULL)",
17629-
")"
17630-
),
17631-
canonical,
17632-
);
17633-
}
17634-
1763517618
#[test]
1763617619
fn test_placeholder_invalid_whitespace() {
1763717620
for w in [" ", "/*invalid*/"] {

tests/sqlparser_common.rs

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

0 commit comments

Comments
 (0)