From 21cdef31a20a2487f92390c04815fac77d31471f Mon Sep 17 00:00:00 2001 From: Jia-Xuan Liu Date: Fri, 2 Aug 2024 21:30:07 +0800 Subject: [PATCH 1/5] support parsing empty map literal syntax --- src/parser/mod.rs | 14 ++++++++------ tests/sqlparser_common.rs | 2 ++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index da9ca2672..463707fe7 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -2352,12 +2352,14 @@ impl<'a> Parser<'a> { /// [map]: https://duckdb.org/docs/sql/data_types/map.html#creating-maps fn parse_duckdb_map_literal(&mut self) -> Result { self.expect_token(&Token::LBrace)?; - - let fields = self.parse_comma_separated(Self::parse_duckdb_map_field)?; - - self.expect_token(&Token::RBrace)?; - - Ok(Expr::Map(Map { entries: fields })) + if self.peek_token().token == Token::RBrace { + let _ = self.next_token(); // consume } + Ok(Expr::Map(Map { entries: vec![] })) + } else { + let fields = self.parse_comma_separated(Self::parse_duckdb_map_field)?; + self.expect_token(&Token::RBrace)?; + Ok(Expr::Map(Map { entries: fields })) + } } /// Parse a field for a duckdb [map] diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 44e245254..7ec017269 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -10282,6 +10282,8 @@ fn test_map_syntax() { }), }, ); + + check("MAP {}", Expr::Map(Map { entries: vec![] })); } #[test] From 0763f52277fa9cfbb993cdcdbe4bec345fc3d6cd Mon Sep 17 00:00:00 2001 From: Jia-Xuan Liu Date: Sat, 3 Aug 2024 22:17:26 +0800 Subject: [PATCH 2/5] extract parse_comma_separated0 --- src/parser/mod.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 463707fe7..566d3e7d6 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -2939,7 +2939,7 @@ impl<'a> Parser<'a> { Expr::InList { expr: Box::new(expr), list: if self.dialect.supports_in_empty_list() { - self.parse_comma_separated0(Parser::parse_expr)? + self.parse_comma_separated0(Parser::parse_expr, self.options.trailing_commas, Token::RParen)? } else { self.parse_comma_separated(Parser::parse_expr)? }, @@ -3481,17 +3481,18 @@ impl<'a> Parser<'a> { } /// Parse a comma-separated list of 0+ items accepted by `F` - pub fn parse_comma_separated0(&mut self, f: F) -> Result, ParserError> + /// - [trailing_commas]: support trailing_commas or not + /// - [end_token]: expected end token for the closure (e.g. [Token::RParen], [Token::RBrace] ...) + pub fn parse_comma_separated0(&mut self, f: F, trailing_commas: bool, end_token: Token) -> Result, ParserError> where F: FnMut(&mut Parser<'a>) -> Result, { - // () - if matches!(self.peek_token().token, Token::RParen) { + if self.peek_token().token == end_token { return Ok(vec![]); } - // (,) - if self.options.trailing_commas - && matches!(self.peek_tokens(), [Token::Comma, Token::RParen]) + + if trailing_commas + && self.peek_tokens() == [Token::Comma, end_token] { let _ = self.consume_token(&Token::Comma); return Ok(vec![]); @@ -4061,7 +4062,7 @@ impl<'a> Parser<'a> { }) }; self.expect_token(&Token::LParen)?; - let args = self.parse_comma_separated0(parse_function_param)?; + let args = self.parse_comma_separated0(parse_function_param, self.options.trailing_commas, Token::RParen)?; self.expect_token(&Token::RParen)?; let return_type = if self.parse_keyword(Keyword::RETURNS) { @@ -10715,7 +10716,7 @@ impl<'a> Parser<'a> { } if self.consume_token(&Token::LParen) { - let interpolations = self.parse_comma_separated0(|p| p.parse_interpolation())?; + let interpolations = self.parse_comma_separated0(|p| p.parse_interpolation(), self.options.trailing_commas, Token::RParen)?; self.expect_token(&Token::RParen)?; // INTERPOLATE () and INTERPOLATE ( ... ) variants return Ok(Some(Interpolate { From 5d816549acc35c71ebbbd3c5957cce37232be6b9 Mon Sep 17 00:00:00 2001 From: Jia-Xuan Liu Date: Sat, 3 Aug 2024 22:22:17 +0800 Subject: [PATCH 3/5] use parse_comma_separated0 for parsing array and map elements --- src/parser/mod.rs | 55 +++++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 566d3e7d6..e23beacea 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1849,17 +1849,9 @@ impl<'a> Parser<'a> { /// Parses an array expression `[ex1, ex2, ..]` /// if `named` is `true`, came from an expression like `ARRAY[ex1, ex2]` pub fn parse_array_expr(&mut self, named: bool) -> Result { - if self.peek_token().token == Token::RBracket { - let _ = self.next_token(); // consume ] - Ok(Expr::Array(Array { - elem: vec![], - named, - })) - } else { - let exprs = self.parse_comma_separated(Parser::parse_expr)?; - self.expect_token(&Token::RBracket)?; - Ok(Expr::Array(Array { elem: exprs, named })) - } + let exprs = self.parse_comma_separated0(Parser::parse_expr, false, Token::RBracket)?; + self.expect_token(&Token::RBracket)?; + Ok(Expr::Array(Array { elem: exprs, named })) } pub fn parse_listagg_on_overflow(&mut self) -> Result, ParserError> { @@ -2352,14 +2344,10 @@ impl<'a> Parser<'a> { /// [map]: https://duckdb.org/docs/sql/data_types/map.html#creating-maps fn parse_duckdb_map_literal(&mut self) -> Result { self.expect_token(&Token::LBrace)?; - if self.peek_token().token == Token::RBrace { - let _ = self.next_token(); // consume } - Ok(Expr::Map(Map { entries: vec![] })) - } else { - let fields = self.parse_comma_separated(Self::parse_duckdb_map_field)?; - self.expect_token(&Token::RBrace)?; - Ok(Expr::Map(Map { entries: fields })) - } + let fields = + self.parse_comma_separated0(Self::parse_duckdb_map_field, false, Token::RBrace)?; + self.expect_token(&Token::RBrace)?; + Ok(Expr::Map(Map { entries: fields })) } /// Parse a field for a duckdb [map] @@ -2939,7 +2927,11 @@ impl<'a> Parser<'a> { Expr::InList { expr: Box::new(expr), list: if self.dialect.supports_in_empty_list() { - self.parse_comma_separated0(Parser::parse_expr, self.options.trailing_commas, Token::RParen)? + self.parse_comma_separated0( + Parser::parse_expr, + self.options.trailing_commas, + Token::RParen, + )? } else { self.parse_comma_separated(Parser::parse_expr)? }, @@ -3483,7 +3475,12 @@ impl<'a> Parser<'a> { /// Parse a comma-separated list of 0+ items accepted by `F` /// - [trailing_commas]: support trailing_commas or not /// - [end_token]: expected end token for the closure (e.g. [Token::RParen], [Token::RBrace] ...) - pub fn parse_comma_separated0(&mut self, f: F, trailing_commas: bool, end_token: Token) -> Result, ParserError> + pub fn parse_comma_separated0( + &mut self, + f: F, + trailing_commas: bool, + end_token: Token, + ) -> Result, ParserError> where F: FnMut(&mut Parser<'a>) -> Result, { @@ -3491,9 +3488,7 @@ impl<'a> Parser<'a> { return Ok(vec![]); } - if trailing_commas - && self.peek_tokens() == [Token::Comma, end_token] - { + if trailing_commas && self.peek_tokens() == [Token::Comma, end_token] { let _ = self.consume_token(&Token::Comma); return Ok(vec![]); } @@ -4062,7 +4057,11 @@ impl<'a> Parser<'a> { }) }; self.expect_token(&Token::LParen)?; - let args = self.parse_comma_separated0(parse_function_param, self.options.trailing_commas, Token::RParen)?; + let args = self.parse_comma_separated0( + parse_function_param, + self.options.trailing_commas, + Token::RParen, + )?; self.expect_token(&Token::RParen)?; let return_type = if self.parse_keyword(Keyword::RETURNS) { @@ -10716,7 +10715,11 @@ impl<'a> Parser<'a> { } if self.consume_token(&Token::LParen) { - let interpolations = self.parse_comma_separated0(|p| p.parse_interpolation(), self.options.trailing_commas, Token::RParen)?; + let interpolations = self.parse_comma_separated0( + |p| p.parse_interpolation(), + self.options.trailing_commas, + Token::RParen, + )?; self.expect_token(&Token::RParen)?; // INTERPOLATE () and INTERPOLATE ( ... ) variants return Ok(Some(Interpolate { From 4479ecd2c7e76f15d2e0101cf977b79a32ff3b03 Mon Sep 17 00:00:00 2001 From: Jia-Xuan Liu Date: Sat, 3 Aug 2024 22:26:16 +0800 Subject: [PATCH 4/5] fix the doc --- src/parser/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index e23beacea..2179fff50 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -3473,8 +3473,8 @@ impl<'a> Parser<'a> { } /// Parse a comma-separated list of 0+ items accepted by `F` - /// - [trailing_commas]: support trailing_commas or not - /// - [end_token]: expected end token for the closure (e.g. [Token::RParen], [Token::RBrace] ...) + /// * `trailing_commas` - support trailing_commas or not + /// * `end_token` - expected end token for the closure (e.g. [Token::RParen], [Token::RBrace] ...) pub fn parse_comma_separated0( &mut self, f: F, From 239ef53e46676699c8ecb23f1ba1356a1105431a Mon Sep 17 00:00:00 2001 From: Jia-Xuan Liu Date: Sun, 4 Aug 2024 17:31:47 +0800 Subject: [PATCH 5/5] roll back trailing_commas --- src/parser/mod.rs | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 2179fff50..fe8acb4f2 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1849,7 +1849,7 @@ impl<'a> Parser<'a> { /// Parses an array expression `[ex1, ex2, ..]` /// if `named` is `true`, came from an expression like `ARRAY[ex1, ex2]` pub fn parse_array_expr(&mut self, named: bool) -> Result { - let exprs = self.parse_comma_separated0(Parser::parse_expr, false, Token::RBracket)?; + let exprs = self.parse_comma_separated0(Parser::parse_expr, Token::RBracket)?; self.expect_token(&Token::RBracket)?; Ok(Expr::Array(Array { elem: exprs, named })) } @@ -2344,8 +2344,7 @@ impl<'a> Parser<'a> { /// [map]: https://duckdb.org/docs/sql/data_types/map.html#creating-maps fn parse_duckdb_map_literal(&mut self) -> Result { self.expect_token(&Token::LBrace)?; - let fields = - self.parse_comma_separated0(Self::parse_duckdb_map_field, false, Token::RBrace)?; + let fields = self.parse_comma_separated0(Self::parse_duckdb_map_field, Token::RBrace)?; self.expect_token(&Token::RBrace)?; Ok(Expr::Map(Map { entries: fields })) } @@ -2927,11 +2926,7 @@ impl<'a> Parser<'a> { Expr::InList { expr: Box::new(expr), list: if self.dialect.supports_in_empty_list() { - self.parse_comma_separated0( - Parser::parse_expr, - self.options.trailing_commas, - Token::RParen, - )? + self.parse_comma_separated0(Parser::parse_expr, Token::RParen)? } else { self.parse_comma_separated(Parser::parse_expr)? }, @@ -3473,12 +3468,10 @@ impl<'a> Parser<'a> { } /// Parse a comma-separated list of 0+ items accepted by `F` - /// * `trailing_commas` - support trailing_commas or not /// * `end_token` - expected end token for the closure (e.g. [Token::RParen], [Token::RBrace] ...) pub fn parse_comma_separated0( &mut self, f: F, - trailing_commas: bool, end_token: Token, ) -> Result, ParserError> where @@ -3488,7 +3481,7 @@ impl<'a> Parser<'a> { return Ok(vec![]); } - if trailing_commas && self.peek_tokens() == [Token::Comma, end_token] { + if self.options.trailing_commas && self.peek_tokens() == [Token::Comma, end_token] { let _ = self.consume_token(&Token::Comma); return Ok(vec![]); } @@ -4057,11 +4050,7 @@ impl<'a> Parser<'a> { }) }; self.expect_token(&Token::LParen)?; - let args = self.parse_comma_separated0( - parse_function_param, - self.options.trailing_commas, - Token::RParen, - )?; + let args = self.parse_comma_separated0(parse_function_param, Token::RParen)?; self.expect_token(&Token::RParen)?; let return_type = if self.parse_keyword(Keyword::RETURNS) { @@ -10715,11 +10704,8 @@ impl<'a> Parser<'a> { } if self.consume_token(&Token::LParen) { - let interpolations = self.parse_comma_separated0( - |p| p.parse_interpolation(), - self.options.trailing_commas, - Token::RParen, - )?; + let interpolations = + self.parse_comma_separated0(|p| p.parse_interpolation(), Token::RParen)?; self.expect_token(&Token::RParen)?; // INTERPOLATE () and INTERPOLATE ( ... ) variants return Ok(Some(Interpolate {