Skip to content

Commit 3b9b05e

Browse files
authored
Fix parsing cast operator after parenthesized DEFAULT expression (#2168)
1 parent ed1c4e2 commit 3b9b05e

File tree

3 files changed

+32
-40
lines changed

3 files changed

+32
-40
lines changed

src/parser/mod.rs

Lines changed: 19 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1820,7 +1820,19 @@ impl<'a> Parser<'a> {
18201820
} else if let Some(lambda) = self.try_parse_lambda()? {
18211821
return Ok(lambda);
18221822
} else {
1823-
let exprs = self.parse_comma_separated(Parser::parse_expr)?;
1823+
// Parentheses in expressions switch to "normal" parsing state.
1824+
// This matters for dialects (SQLite, DuckDB) where `NOT NULL` can
1825+
// be an alias for `IS NOT NULL`. In column definitions like:
1826+
//
1827+
// CREATE TABLE t (c INT DEFAULT (42 NOT NULL) NOT NULL)
1828+
//
1829+
// The `(42 NOT NULL)` is an expression with parens, so it parses
1830+
// as `IsNotNull(42)`. The trailing `NOT NULL` is outside those
1831+
// expression parens (the outer parens are CREATE TABLE syntax),
1832+
// so it remains a column constraint.
1833+
let exprs = self.with_state(ParserState::Normal, |p| {
1834+
p.parse_comma_separated(Parser::parse_expr)
1835+
})?;
18241836
match exprs.len() {
18251837
0 => return Err(ParserError::ParserError(
18261838
"Internal parser error: parse_comma_separated returned empty list"
@@ -8823,19 +8835,15 @@ impl<'a> Parser<'a> {
88238835
} else if self.parse_keyword(Keyword::NULL) {
88248836
Ok(Some(ColumnOption::Null))
88258837
} else if self.parse_keyword(Keyword::DEFAULT) {
8826-
Ok(Some(ColumnOption::Default(
8827-
self.parse_column_option_expr()?,
8828-
)))
8838+
Ok(Some(ColumnOption::Default(self.parse_expr()?)))
88298839
} else if dialect_of!(self is ClickHouseDialect| GenericDialect)
88308840
&& self.parse_keyword(Keyword::MATERIALIZED)
88318841
{
8832-
Ok(Some(ColumnOption::Materialized(
8833-
self.parse_column_option_expr()?,
8834-
)))
8842+
Ok(Some(ColumnOption::Materialized(self.parse_expr()?)))
88358843
} else if dialect_of!(self is ClickHouseDialect| GenericDialect)
88368844
&& self.parse_keyword(Keyword::ALIAS)
88378845
{
8838-
Ok(Some(ColumnOption::Alias(self.parse_column_option_expr()?)))
8846+
Ok(Some(ColumnOption::Alias(self.parse_expr()?)))
88398847
} else if dialect_of!(self is ClickHouseDialect| GenericDialect)
88408848
&& self.parse_keyword(Keyword::EPHEMERAL)
88418849
{
@@ -8844,9 +8852,7 @@ impl<'a> Parser<'a> {
88448852
if matches!(self.peek_token().token, Token::Comma | Token::RParen) {
88458853
Ok(Some(ColumnOption::Ephemeral(None)))
88468854
} else {
8847-
Ok(Some(ColumnOption::Ephemeral(Some(
8848-
self.parse_column_option_expr()?,
8849-
))))
8855+
Ok(Some(ColumnOption::Ephemeral(Some(self.parse_expr()?))))
88508856
}
88518857
} else if self.parse_keywords(&[Keyword::PRIMARY, Keyword::KEY]) {
88528858
let characteristics = self.parse_constraint_characteristics()?;
@@ -8968,7 +8974,7 @@ impl<'a> Parser<'a> {
89688974
} else if self.parse_keywords(&[Keyword::ON, Keyword::UPDATE])
89698975
&& dialect_of!(self is MySqlDialect | GenericDialect)
89708976
{
8971-
let expr = self.parse_column_option_expr()?;
8977+
let expr = self.parse_expr()?;
89728978
Ok(Some(ColumnOption::OnUpdate(expr)))
89738979
} else if self.parse_keyword(Keyword::GENERATED) {
89748980
self.parse_optional_column_option_generated()
@@ -8986,9 +8992,7 @@ impl<'a> Parser<'a> {
89868992
} else if self.parse_keyword(Keyword::SRID)
89878993
&& dialect_of!(self is MySqlDialect | GenericDialect)
89888994
{
8989-
Ok(Some(ColumnOption::Srid(Box::new(
8990-
self.parse_column_option_expr()?,
8991-
))))
8995+
Ok(Some(ColumnOption::Srid(Box::new(self.parse_expr()?))))
89928996
} else if self.parse_keyword(Keyword::IDENTITY)
89938997
&& dialect_of!(self is MsSqlDialect | GenericDialect)
89948998
{
@@ -9030,31 +9034,6 @@ impl<'a> Parser<'a> {
90309034
}
90319035
}
90329036

9033-
/// When parsing some column option expressions we need to revert to [ParserState::Normal] since
9034-
/// `NOT NULL` is allowed as an alias for `IS NOT NULL`.
9035-
/// In those cases we use this helper instead of calling [Parser::parse_expr] directly.
9036-
///
9037-
/// For example, consider these `CREATE TABLE` statements:
9038-
/// ```sql
9039-
/// CREATE TABLE foo (abc BOOL DEFAULT (42 NOT NULL) NOT NULL);
9040-
/// ```
9041-
/// vs
9042-
/// ```sql
9043-
/// CREATE TABLE foo (abc BOOL NOT NULL);
9044-
/// ```
9045-
///
9046-
/// In the first we should parse the inner portion of `(42 NOT NULL)` as [Expr::IsNotNull],
9047-
/// whereas is both statements that trailing `NOT NULL` should only be parsed as a
9048-
/// [ColumnOption::NotNull].
9049-
fn parse_column_option_expr(&mut self) -> Result<Expr, ParserError> {
9050-
if self.peek_token_ref().token == Token::LParen {
9051-
let expr: Expr = self.with_state(ParserState::Normal, |p| p.parse_prefix())?;
9052-
Ok(expr)
9053-
} else {
9054-
Ok(self.parse_expr()?)
9055-
}
9056-
}
9057-
90589037
pub(crate) fn parse_tag(&mut self) -> Result<Tag, ParserError> {
90599038
let name = self.parse_object_name(false)?;
90609039
self.expect_token(&Token::Eq)?;

tests/sqlparser_common.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17376,6 +17376,12 @@ fn test_parse_not_null_in_column_options() {
1737617376
);
1737717377
}
1737817378

17379+
#[test]
17380+
fn test_parse_default_expr_with_operators() {
17381+
all_dialects().verified_stmt("CREATE TABLE t (c INT DEFAULT (1 + 2) + 3)");
17382+
all_dialects().verified_stmt("CREATE TABLE t (c INT DEFAULT (1 + 2) + 3 NOT NULL)");
17383+
}
17384+
1737917385
#[test]
1738017386
fn test_parse_default_with_collate_column_option() {
1738117387
let sql = "CREATE TABLE foo (abc TEXT DEFAULT 'foo' COLLATE 'en_US')";

tests/sqlparser_postgres.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,13 @@ fn parse_create_table_with_defaults() {
512512
}
513513
}
514514

515+
#[test]
516+
fn parse_cast_in_default_expr() {
517+
pg().verified_stmt("CREATE TABLE t (c TEXT DEFAULT (foo())::TEXT)");
518+
pg().verified_stmt("CREATE TABLE t (c TEXT DEFAULT (foo())::INT::TEXT)");
519+
pg().verified_stmt("CREATE TABLE t (c TEXT DEFAULT (foo())::TEXT NOT NULL)");
520+
}
521+
515522
#[test]
516523
fn parse_create_table_from_pg_dump() {
517524
let sql = "CREATE TABLE public.customer (

0 commit comments

Comments
 (0)