From f3b2b29fa432a1047e43d11c29d771f0c3d64cb4 Mon Sep 17 00:00:00 2001 From: Yuval Shkolar Date: Sun, 17 Apr 2022 16:32:23 +0300 Subject: [PATCH 1/8] Add support in the position function --- src/ast/mod.rs | 6 ++++++ src/parser.rs | 47 ++++++++++++++++++++++++++++++++--------------- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 23072592b..62d314467 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -252,6 +252,11 @@ pub enum Expr { field: DateTimeField, expr: Box, }, + /// POSITION( in ) + Position { + expr: Box, + from: Box, + }, /// SUBSTRING( [FROM ] [FOR ]) Substring { expr: Box, @@ -399,6 +404,7 @@ impl fmt::Display for Expr { Expr::Cast { expr, data_type } => write!(f, "CAST({} AS {})", expr, data_type), Expr::TryCast { expr, data_type } => write!(f, "TRY_CAST({} AS {})", expr, data_type), Expr::Extract { field, expr } => write!(f, "EXTRACT({} FROM {})", field, expr), + Expr::Position { expr, from } => write!(f, "POSITION({} IN {})", expr, from), Expr::Collate { expr, collation } => write!(f, "{} COLLATE {}", expr, collation), Expr::Nested(ast) => write!(f, "({})", ast), Expr::Value(v) => write!(f, "{}", v), diff --git a/src/parser.rs b/src/parser.rs index 63b7f3452..da99c43e4 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -423,6 +423,7 @@ impl<'a> Parser<'a> { Keyword::TRY_CAST => self.parse_try_cast_expr(), Keyword::EXISTS => self.parse_exists_expr(), Keyword::EXTRACT => self.parse_extract_expr(), + Keyword::POSITION => self.parse_position_expr(), Keyword::SUBSTRING => self.parse_substring_expr(), Keyword::TRIM => self.parse_trim_expr(), Keyword::INTERVAL => self.parse_literal_interval(), @@ -779,6 +780,14 @@ impl<'a> Parser<'a> { }) } + pub fn parse_position_expr(&mut self) -> Result { + // PARSE SELECT POSITION('@' in field) + self.expect_token(&Token::LParen)?; + let res = self.parse_expr()?; + self.expect_token(&Token::RParen)?; + Ok(res) + } + pub fn parse_substring_expr(&mut self) -> Result { // PARSE SUBSTRING (EXPR [FROM 1] [FOR 3]) self.expect_token(&Token::LParen)?; @@ -1201,23 +1210,31 @@ impl<'a> Parser<'a> { negated, }); } - self.expect_token(&Token::LParen)?; - let in_op = if self.parse_keyword(Keyword::SELECT) || self.parse_keyword(Keyword::WITH) { - self.prev_token(); - Expr::InSubquery { - expr: Box::new(expr), - subquery: Box::new(self.parse_query()?), - negated, - } + if self.peek_token() == Token::LParen { + self.expect_token(&Token::LParen)?; + let in_op = if self.parse_keyword(Keyword::SELECT) || self.parse_keyword(Keyword::WITH) { + self.prev_token(); + Expr::InSubquery { + expr: Box::new(expr), + subquery: Box::new(self.parse_query()?), + negated, + } + } else { + Expr::InList { + expr: Box::new(expr), + list: self.parse_comma_separated(Parser::parse_expr)?, + negated, + } + }; + self.expect_token(&Token::RParen)?; + return Ok(in_op) } else { - Expr::InList { + let from = self.parse_expr()?; + Ok(Expr::Position { expr: Box::new(expr), - list: self.parse_comma_separated(Parser::parse_expr)?, - negated, - } - }; - self.expect_token(&Token::RParen)?; - Ok(in_op) + from: Box::new(from), + }) + } } /// Parses `BETWEEN AND `, assuming the `BETWEEN` keyword was already consumed From c912b6437c53b7c0de5f59da5e37ff91689921b9 Mon Sep 17 00:00:00 2001 From: Yuval Shkolar Date: Sun, 17 Apr 2022 16:54:26 +0300 Subject: [PATCH 2/8] Add Test --- tests/sqlparser_common.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index fa8fc6699..e0ade0afa 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -4567,3 +4567,18 @@ fn parse_time_functions() { // Validating Parenthesis one_statement_parses_to("SELECT CURRENT_DATE", sql); } + +#[test] +fn parse_position() { + let sql = "SELECT POSITION('@' IN field)"; + let select = verified_only_select(sql); + assert_eq!( + &Expr::Position { + expr: Box::new(Expr::Value(Value::SingleQuotedString( + "@".to_string() + ))), + from: Box::new(Expr::Identifier(Ident::new("field"))), + }, + expr_from_projection(only(&select.projection)) + ); +} From 14648da7d03d6d189014a311cbc794108ba295ba Mon Sep 17 00:00:00 2001 From: Yuval Shkolar Date: Sun, 17 Apr 2022 16:59:51 +0300 Subject: [PATCH 3/8] Add lint fixes --- src/parser.rs | 7 ++++--- tests/sqlparser_common.rs | 4 +--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index da99c43e4..5fa2d2e2c 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1210,9 +1210,10 @@ impl<'a> Parser<'a> { negated, }); } - if self.peek_token() == Token::LParen { + if self.peek_token() == Token::LParen { self.expect_token(&Token::LParen)?; - let in_op = if self.parse_keyword(Keyword::SELECT) || self.parse_keyword(Keyword::WITH) { + let in_op = if self.parse_keyword(Keyword::SELECT) || self.parse_keyword(Keyword::WITH) + { self.prev_token(); Expr::InSubquery { expr: Box::new(expr), @@ -1227,7 +1228,7 @@ impl<'a> Parser<'a> { } }; self.expect_token(&Token::RParen)?; - return Ok(in_op) + return Ok(in_op); } else { let from = self.parse_expr()?; Ok(Expr::Position { diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index e0ade0afa..92ae215a2 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -4574,9 +4574,7 @@ fn parse_position() { let select = verified_only_select(sql); assert_eq!( &Expr::Position { - expr: Box::new(Expr::Value(Value::SingleQuotedString( - "@".to_string() - ))), + expr: Box::new(Expr::Value(Value::SingleQuotedString("@".to_string()))), from: Box::new(Expr::Identifier(Ident::new("field"))), }, expr_from_projection(only(&select.projection)) From 380758bdc74eae9bf9e9735fc1b7809b0f94b1ca Mon Sep 17 00:00:00 2001 From: Yuval Shkolar Date: Sun, 17 Apr 2022 17:04:03 +0300 Subject: [PATCH 4/8] Remove if --- src/parser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser.rs b/src/parser.rs index 5fa2d2e2c..8446de67f 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1228,7 +1228,7 @@ impl<'a> Parser<'a> { } }; self.expect_token(&Token::RParen)?; - return Ok(in_op); + Ok(in_op) } else { let from = self.parse_expr()?; Ok(Expr::Position { From 57ec54a4a56a228f0eb8b3c6de2c8983f975dfa7 Mon Sep 17 00:00:00 2001 From: Yuval Shkolar Date: Wed, 20 Apr 2022 14:45:54 +0300 Subject: [PATCH 5/8] Change from to in --- src/ast/mod.rs | 4 ++-- src/parser.rs | 2 +- tests/sqlparser_common.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 62d314467..a99b336ff 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -255,7 +255,7 @@ pub enum Expr { /// POSITION( in ) Position { expr: Box, - from: Box, + r#in: Box, }, /// SUBSTRING( [FROM ] [FOR ]) Substring { @@ -404,7 +404,7 @@ impl fmt::Display for Expr { Expr::Cast { expr, data_type } => write!(f, "CAST({} AS {})", expr, data_type), Expr::TryCast { expr, data_type } => write!(f, "TRY_CAST({} AS {})", expr, data_type), Expr::Extract { field, expr } => write!(f, "EXTRACT({} FROM {})", field, expr), - Expr::Position { expr, from } => write!(f, "POSITION({} IN {})", expr, from), + Expr::Position { expr, r#in } => write!(f, "POSITION({} IN {})", expr, r#in), Expr::Collate { expr, collation } => write!(f, "{} COLLATE {}", expr, collation), Expr::Nested(ast) => write!(f, "({})", ast), Expr::Value(v) => write!(f, "{}", v), diff --git a/src/parser.rs b/src/parser.rs index 8446de67f..482ffad3a 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1233,7 +1233,7 @@ impl<'a> Parser<'a> { let from = self.parse_expr()?; Ok(Expr::Position { expr: Box::new(expr), - from: Box::new(from), + r#in: Box::new(from), }) } } diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 92ae215a2..e12f497b5 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -4575,7 +4575,7 @@ fn parse_position() { assert_eq!( &Expr::Position { expr: Box::new(Expr::Value(Value::SingleQuotedString("@".to_string()))), - from: Box::new(Expr::Identifier(Ident::new("field"))), + r#in: Box::new(Expr::Identifier(Ident::new("field"))), }, expr_from_projection(only(&select.projection)) ); From 6ed3e9b64875ceb0e5b5d3c44daa1925bfd4c812 Mon Sep 17 00:00:00 2001 From: Yuval Shkolar Date: Wed, 20 Apr 2022 15:01:09 +0300 Subject: [PATCH 6/8] Remove special method for position --- src/ast/mod.rs | 2 +- src/parser.rs | 9 --------- tests/sqlparser_common.rs | 13 +++++++++---- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index a99b336ff..140154c0c 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -404,7 +404,7 @@ impl fmt::Display for Expr { Expr::Cast { expr, data_type } => write!(f, "CAST({} AS {})", expr, data_type), Expr::TryCast { expr, data_type } => write!(f, "TRY_CAST({} AS {})", expr, data_type), Expr::Extract { field, expr } => write!(f, "EXTRACT({} FROM {})", field, expr), - Expr::Position { expr, r#in } => write!(f, "POSITION({} IN {})", expr, r#in), + Expr::Position { expr, r#in } => write!(f, "{} IN {}", expr, r#in), Expr::Collate { expr, collation } => write!(f, "{} COLLATE {}", expr, collation), Expr::Nested(ast) => write!(f, "({})", ast), Expr::Value(v) => write!(f, "{}", v), diff --git a/src/parser.rs b/src/parser.rs index 482ffad3a..b2ae24896 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -423,7 +423,6 @@ impl<'a> Parser<'a> { Keyword::TRY_CAST => self.parse_try_cast_expr(), Keyword::EXISTS => self.parse_exists_expr(), Keyword::EXTRACT => self.parse_extract_expr(), - Keyword::POSITION => self.parse_position_expr(), Keyword::SUBSTRING => self.parse_substring_expr(), Keyword::TRIM => self.parse_trim_expr(), Keyword::INTERVAL => self.parse_literal_interval(), @@ -780,14 +779,6 @@ impl<'a> Parser<'a> { }) } - pub fn parse_position_expr(&mut self) -> Result { - // PARSE SELECT POSITION('@' in field) - self.expect_token(&Token::LParen)?; - let res = self.parse_expr()?; - self.expect_token(&Token::RParen)?; - Ok(res) - } - pub fn parse_substring_expr(&mut self) -> Result { // PARSE SUBSTRING (EXPR [FROM 1] [FOR 3]) self.expect_token(&Token::LParen)?; diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index e12f497b5..01624b0ba 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -4573,10 +4573,15 @@ fn parse_position() { let sql = "SELECT POSITION('@' IN field)"; let select = verified_only_select(sql); assert_eq!( - &Expr::Position { - expr: Box::new(Expr::Value(Value::SingleQuotedString("@".to_string()))), - r#in: Box::new(Expr::Identifier(Ident::new("field"))), - }, + &Expr::Function(Function { + name: ObjectName(vec![Ident::new("POSITION")]), + args: vec![FunctionArg::Unnamed(sqlparser::ast::FunctionArgExpr::Expr(Expr::Position { + expr: Box::new(Expr::Value(Value::SingleQuotedString("@".to_string()))), + r#in: Box::new(Expr::Identifier(Ident::new("field"))), + }))], + over: None, + distinct: false, + }), expr_from_projection(only(&select.projection)) ); } From c3acee60a6460dccc98f3dd1d4aef99695207d26 Mon Sep 17 00:00:00 2001 From: Yuval Shkolar Date: Wed, 20 Apr 2022 15:02:42 +0300 Subject: [PATCH 7/8] Fix lint --- tests/sqlparser_common.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 01624b0ba..828b8a756 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -4575,10 +4575,12 @@ fn parse_position() { assert_eq!( &Expr::Function(Function { name: ObjectName(vec![Ident::new("POSITION")]), - args: vec![FunctionArg::Unnamed(sqlparser::ast::FunctionArgExpr::Expr(Expr::Position { + args: vec![FunctionArg::Unnamed(sqlparser::ast::FunctionArgExpr::Expr( + Expr::Position { expr: Box::new(Expr::Value(Value::SingleQuotedString("@".to_string()))), r#in: Box::new(Expr::Identifier(Ident::new("field"))), - }))], + } + ))], over: None, distinct: false, }), From 87af89a3ade131087589cd34a09e5d8a7e594624 Mon Sep 17 00:00:00 2001 From: Yuval Shkolar Date: Sun, 24 Apr 2022 17:39:06 +0300 Subject: [PATCH 8/8] PR Review --- src/ast/mod.rs | 2 +- src/parser.rs | 58 +++++++++++++++++++++++---------------- tests/sqlparser_common.rs | 15 +++------- 3 files changed, 39 insertions(+), 36 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 014507063..89e135756 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -443,7 +443,7 @@ impl fmt::Display for Expr { Expr::Cast { expr, data_type } => write!(f, "CAST({} AS {})", expr, data_type), Expr::TryCast { expr, data_type } => write!(f, "TRY_CAST({} AS {})", expr, data_type), Expr::Extract { field, expr } => write!(f, "EXTRACT({} FROM {})", field, expr), - Expr::Position { expr, r#in } => write!(f, "{} IN {}", expr, r#in), + Expr::Position { expr, r#in } => write!(f, "POSITION({} IN {})", expr, r#in), Expr::Collate { expr, collation } => write!(f, "{} COLLATE {}", expr, collation), Expr::Nested(ast) => write!(f, "({})", ast), Expr::Value(v) => write!(f, "{}", v), diff --git a/src/parser.rs b/src/parser.rs index 852486726..1ca493b98 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -423,6 +423,7 @@ impl<'a> Parser<'a> { Keyword::TRY_CAST => self.parse_try_cast_expr(), Keyword::EXISTS => self.parse_exists_expr(), Keyword::EXTRACT => self.parse_extract_expr(), + Keyword::POSITION => self.parse_position_expr(), Keyword::SUBSTRING => self.parse_substring_expr(), Keyword::TRIM => self.parse_trim_expr(), Keyword::INTERVAL => self.parse_literal_interval(), @@ -779,6 +780,24 @@ impl<'a> Parser<'a> { }) } + pub fn parse_position_expr(&mut self) -> Result { + // PARSE SELECT POSITION('@' in field) + self.expect_token(&Token::LParen)?; + + // Parse the subexpr till the IN keyword + let expr = self.parse_subexpr(Self::BETWEEN_PREC)?; + if self.parse_keyword(Keyword::IN) { + let from = self.parse_expr()?; + self.expect_token(&Token::RParen)?; + Ok(Expr::Position { + expr: Box::new(expr), + r#in: Box::new(from), + }) + } else { + return parser_err!("Position function must include IN keyword".to_string()); + } + } + pub fn parse_substring_expr(&mut self) -> Result { // PARSE SUBSTRING (EXPR [FROM 1] [FOR 3]) self.expect_token(&Token::LParen)?; @@ -1218,32 +1237,23 @@ impl<'a> Parser<'a> { negated, }); } - if self.peek_token() == Token::LParen { - self.expect_token(&Token::LParen)?; - let in_op = if self.parse_keyword(Keyword::SELECT) || self.parse_keyword(Keyword::WITH) - { - self.prev_token(); - Expr::InSubquery { - expr: Box::new(expr), - subquery: Box::new(self.parse_query()?), - negated, - } - } else { - Expr::InList { - expr: Box::new(expr), - list: self.parse_comma_separated(Parser::parse_expr)?, - negated, - } - }; - self.expect_token(&Token::RParen)?; - Ok(in_op) + self.expect_token(&Token::LParen)?; + let in_op = if self.parse_keyword(Keyword::SELECT) || self.parse_keyword(Keyword::WITH) { + self.prev_token(); + Expr::InSubquery { + expr: Box::new(expr), + subquery: Box::new(self.parse_query()?), + negated, + } } else { - let from = self.parse_expr()?; - Ok(Expr::Position { + Expr::InList { expr: Box::new(expr), - r#in: Box::new(from), - }) - } + list: self.parse_comma_separated(Parser::parse_expr)?, + negated, + } + }; + self.expect_token(&Token::RParen)?; + Ok(in_op) } /// Parses `BETWEEN AND `, assuming the `BETWEEN` keyword was already consumed diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index bcc90f629..1a1dee986 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -4594,17 +4594,10 @@ fn parse_position() { let sql = "SELECT POSITION('@' IN field)"; let select = verified_only_select(sql); assert_eq!( - &Expr::Function(Function { - name: ObjectName(vec![Ident::new("POSITION")]), - args: vec![FunctionArg::Unnamed(sqlparser::ast::FunctionArgExpr::Expr( - Expr::Position { - expr: Box::new(Expr::Value(Value::SingleQuotedString("@".to_string()))), - r#in: Box::new(Expr::Identifier(Ident::new("field"))), - } - ))], - over: None, - distinct: false, - }), + &Expr::Position { + expr: Box::new(Expr::Value(Value::SingleQuotedString("@".to_string()))), + r#in: Box::new(Expr::Identifier(Ident::new("field"))), + }, expr_from_projection(only(&select.projection)) ); }