Skip to content

Commit a17be25

Browse files
isaacparker0ayman-sigma
authored andcommitted
Fix parsing cast operator after parenthesized DEFAULT expression (apache#2168)
1 parent 87c7f03 commit a17be25

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"
@@ -8854,19 +8866,15 @@ impl<'a> Parser<'a> {
88548866
} else if self.parse_keyword(Keyword::NULL) {
88558867
Ok(Some(ColumnOption::Null))
88568868
} else if self.parse_keyword(Keyword::DEFAULT) {
8857-
Ok(Some(ColumnOption::Default(
8858-
self.parse_column_option_expr()?,
8859-
)))
8869+
Ok(Some(ColumnOption::Default(self.parse_expr()?)))
88608870
} else if dialect_of!(self is ClickHouseDialect| GenericDialect)
88618871
&& self.parse_keyword(Keyword::MATERIALIZED)
88628872
{
8863-
Ok(Some(ColumnOption::Materialized(
8864-
self.parse_column_option_expr()?,
8865-
)))
8873+
Ok(Some(ColumnOption::Materialized(self.parse_expr()?)))
88668874
} else if dialect_of!(self is ClickHouseDialect| GenericDialect)
88678875
&& self.parse_keyword(Keyword::ALIAS)
88688876
{
8869-
Ok(Some(ColumnOption::Alias(self.parse_column_option_expr()?)))
8877+
Ok(Some(ColumnOption::Alias(self.parse_expr()?)))
88708878
} else if dialect_of!(self is ClickHouseDialect| GenericDialect)
88718879
&& self.parse_keyword(Keyword::EPHEMERAL)
88728880
{
@@ -8875,9 +8883,7 @@ impl<'a> Parser<'a> {
88758883
if matches!(self.peek_token().token, Token::Comma | Token::RParen) {
88768884
Ok(Some(ColumnOption::Ephemeral(None)))
88778885
} else {
8878-
Ok(Some(ColumnOption::Ephemeral(Some(
8879-
self.parse_column_option_expr()?,
8880-
))))
8886+
Ok(Some(ColumnOption::Ephemeral(Some(self.parse_expr()?))))
88818887
}
88828888
} else if self.parse_keywords(&[Keyword::PRIMARY, Keyword::KEY]) {
88838889
let characteristics = self.parse_constraint_characteristics()?;
@@ -8999,7 +9005,7 @@ impl<'a> Parser<'a> {
89999005
} else if self.parse_keywords(&[Keyword::ON, Keyword::UPDATE])
90009006
&& dialect_of!(self is MySqlDialect | GenericDialect)
90019007
{
9002-
let expr = self.parse_column_option_expr()?;
9008+
let expr = self.parse_expr()?;
90039009
Ok(Some(ColumnOption::OnUpdate(expr)))
90049010
} else if self.parse_keyword(Keyword::GENERATED) {
90059011
self.parse_optional_column_option_generated()
@@ -9017,9 +9023,7 @@ impl<'a> Parser<'a> {
90179023
} else if self.parse_keyword(Keyword::SRID)
90189024
&& dialect_of!(self is MySqlDialect | GenericDialect)
90199025
{
9020-
Ok(Some(ColumnOption::Srid(Box::new(
9021-
self.parse_column_option_expr()?,
9022-
))))
9026+
Ok(Some(ColumnOption::Srid(Box::new(self.parse_expr()?))))
90239027
} else if self.parse_keyword(Keyword::IDENTITY)
90249028
&& dialect_of!(self is MsSqlDialect | GenericDialect)
90259029
{
@@ -9061,31 +9065,6 @@ impl<'a> Parser<'a> {
90619065
}
90629066
}
90639067

9064-
/// When parsing some column option expressions we need to revert to [ParserState::Normal] since
9065-
/// `NOT NULL` is allowed as an alias for `IS NOT NULL`.
9066-
/// In those cases we use this helper instead of calling [Parser::parse_expr] directly.
9067-
///
9068-
/// For example, consider these `CREATE TABLE` statements:
9069-
/// ```sql
9070-
/// CREATE TABLE foo (abc BOOL DEFAULT (42 NOT NULL) NOT NULL);
9071-
/// ```
9072-
/// vs
9073-
/// ```sql
9074-
/// CREATE TABLE foo (abc BOOL NOT NULL);
9075-
/// ```
9076-
///
9077-
/// In the first we should parse the inner portion of `(42 NOT NULL)` as [Expr::IsNotNull],
9078-
/// whereas is both statements that trailing `NOT NULL` should only be parsed as a
9079-
/// [ColumnOption::NotNull].
9080-
fn parse_column_option_expr(&mut self) -> Result<Expr, ParserError> {
9081-
if self.peek_token_ref().token == Token::LParen {
9082-
let expr: Expr = self.with_state(ParserState::Normal, |p| p.parse_prefix())?;
9083-
Ok(expr)
9084-
} else {
9085-
Ok(self.parse_expr()?)
9086-
}
9087-
}
9088-
90899068
pub(crate) fn parse_tag(&mut self) -> Result<Tag, ParserError> {
90909069
let name = self.parse_object_name(false)?;
90919070
self.expect_token(&Token::Eq)?;

tests/sqlparser_common.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17402,6 +17402,12 @@ fn test_parse_not_null_in_column_options() {
1740217402
);
1740317403
}
1740417404

17405+
#[test]
17406+
fn test_parse_default_expr_with_operators() {
17407+
all_dialects().verified_stmt("CREATE TABLE t (c INT DEFAULT (1 + 2) + 3)");
17408+
all_dialects().verified_stmt("CREATE TABLE t (c INT DEFAULT (1 + 2) + 3 NOT NULL)");
17409+
}
17410+
1740517411
#[test]
1740617412
fn test_parse_default_with_collate_column_option() {
1740717413
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)