From 64c53ece00384d5bd0bb13a58f411bbaf5ee7396 Mon Sep 17 00:00:00 2001 From: Ayush Dattagupta Date: Fri, 26 Aug 2022 11:30:22 -0700 Subject: [PATCH 1/4] Add PLACING keyword to keywords list --- src/keywords.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/keywords.rs b/src/keywords.rs index a06ddc48d..2d7cca374 100644 --- a/src/keywords.rs +++ b/src/keywords.rs @@ -382,6 +382,7 @@ define_keywords!( PERCENTILE_DISC, PERCENT_RANK, PERIOD, + PLACING, PLANS, PORTION, POSITION, From 5b3e6e15a31e25b0ea77a3cbe24cfc11e66e141b Mon Sep 17 00:00:00 2001 From: Ayush Dattagupta Date: Fri, 26 Aug 2022 11:30:46 -0700 Subject: [PATCH 2/4] Add high level overlay expr to Expr enum --- src/ast/mod.rs | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 838594da7..4f5fdb2eb 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -348,6 +348,13 @@ pub enum Expr { trim_where: Option, trim_what: Option>, }, + /// OVERLAY( PLACING FROM [ FOR ] + Overlay { + expr: Box, + overlay_what: Box, + overlay_from: Box, + overlay_for: Option>, + }, /// `expr COLLATE collation` Collate { expr: Box, @@ -639,8 +646,25 @@ impl fmt::Display for Expr { if let Some(from_part) = substring_from { write!(f, " FROM {}", from_part)?; } - if let Some(from_part) = substring_for { - write!(f, " FOR {}", from_part)?; + if let Some(for_part) = substring_for { + write!(f, " FOR {}", for_part)?; + } + + write!(f, ")") + } + Expr::Overlay { + expr, + overlay_what, + overlay_from, + overlay_for, + } => { + write!( + f, + "OVERLAY({} PLACING {} FROM {}", + expr, overlay_what, overlay_from + )?; + if let Some(for_part) = overlay_for { + write!(f, " FOR {}", for_part)?; } write!(f, ")") From 66c5ebe4c396b89ff5318a0928e770fb9cd23a9d Mon Sep 17 00:00:00 2001 From: Ayush Dattagupta Date: Fri, 26 Aug 2022 11:30:59 -0700 Subject: [PATCH 3/4] Add parsing logic for overlay op --- src/parser.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/parser.rs b/src/parser.rs index 4cfbdd23b..22ce8418e 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -449,6 +449,7 @@ impl<'a> Parser<'a> { Keyword::EXTRACT => self.parse_extract_expr(), Keyword::POSITION => self.parse_position_expr(), Keyword::SUBSTRING => self.parse_substring_expr(), + Keyword::OVERLAY => self.parse_overlay_expr(), Keyword::TRIM => self.parse_trim_expr(), Keyword::INTERVAL => self.parse_literal_interval(), Keyword::LISTAGG => self.parse_listagg_expr(), @@ -884,6 +885,28 @@ impl<'a> Parser<'a> { }) } + pub fn parse_overlay_expr(&mut self) -> Result { + // PARSE OVERLAY (EXPR PLACING EXPR FROM 1 [FOR 3]) + self.expect_token(&Token::LParen)?; + let expr = self.parse_expr()?; + self.expect_keyword(Keyword::PLACING)?; + let what_expr = self.parse_expr()?; + self.expect_keyword(Keyword::FROM)?; + let from_expr = self.parse_expr()?; + let mut for_expr = None; + if self.parse_keyword(Keyword::FOR) { + for_expr = Some(self.parse_expr()?); + } + self.expect_token(&Token::RParen)?; + + Ok(Expr::Overlay { + expr: Box::new(expr), + overlay_what: Box::new(what_expr), + overlay_from: Box::new(from_expr), + overlay_for: for_expr.map(Box::new), + }) + } + /// TRIM ([WHERE] ['text' FROM] 'text')\ /// TRIM ('text') pub fn parse_trim_expr(&mut self) -> Result { From ea270f76a67c31eaaed357120e23537f1a53781f Mon Sep 17 00:00:00 2001 From: Ayush Dattagupta Date: Fri, 26 Aug 2022 11:31:44 -0700 Subject: [PATCH 4/4] add ovleray and is not true/false/unknown tests --- tests/sqlparser_common.rs | 65 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 65d699b3f..d579fcb7d 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -3950,6 +3950,38 @@ fn parse_substring() { one_statement_parses_to("SELECT SUBSTRING('1' FOR 3)", "SELECT SUBSTRING('1' FOR 3)"); } +#[test] +fn parse_overlay() { + one_statement_parses_to( + "SELECT OVERLAY('abccccde' PLACING 'abc' FROM 3)", + "SELECT OVERLAY('abccccde' PLACING 'abc' FROM 3)", + ); + one_statement_parses_to( + "SELECT OVERLAY('abccccde' PLACING 'abc' FROM 3 FOR 12)", + "SELECT OVERLAY('abccccde' PLACING 'abc' FROM 3 FOR 12)", + ); + assert_eq!( + ParserError::ParserError("Expected PLACING, found: FROM".to_owned()), + parse_sql_statements("SELECT OVERLAY('abccccde' FROM 3)").unwrap_err(), + ); + + let sql = "SELECT OVERLAY('abcdef' PLACING name FROM 3 FOR id + 1) FROM CUSTOMERS"; + let select = verified_only_select(sql); + assert_eq!( + &Expr::Overlay { + expr: Box::new(Expr::Value(Value::SingleQuotedString("abcdef".to_string()))), + overlay_what: Box::new(Expr::Identifier(Ident::new("name"))), + overlay_from: Box::new(Expr::Value(number("3"))), + overlay_for: Some(Box::new(Expr::BinaryOp { + left: Box::new(Expr::Identifier(Ident::new("id"))), + op: BinaryOperator::Plus, + right: Box::new(Expr::Value(number("1"))), + })) + }, + expr_from_projection(only(&select.projection)) + ); +} + #[test] fn parse_trim() { one_statement_parses_to( @@ -5334,21 +5366,50 @@ fn parse_position_negative() { fn parse_is_boolean() { use self::Expr::*; + let sql = "a IS TRUE"; + assert_eq!( + IsTrue(Box::new(Identifier(Ident::new("a")))), + verified_expr(sql) + ); + + let sql = "a IS NOT TRUE"; + assert_eq!( + IsNotTrue(Box::new(Identifier(Ident::new("a")))), + verified_expr(sql) + ); + let sql = "a IS FALSE"; assert_eq!( IsFalse(Box::new(Identifier(Ident::new("a")))), verified_expr(sql) ); - let sql = "a IS TRUE"; + let sql = "a IS NOT FALSE"; assert_eq!( - IsTrue(Box::new(Identifier(Ident::new("a")))), + IsNotFalse(Box::new(Identifier(Ident::new("a")))), + verified_expr(sql) + ); + + let sql = "a IS UNKNOWN"; + assert_eq!( + IsUnknown(Box::new(Identifier(Ident::new("a")))), + verified_expr(sql) + ); + + let sql = "a IS NOT UNKNOWN"; + assert_eq!( + IsNotUnknown(Box::new(Identifier(Ident::new("a")))), verified_expr(sql) ); verified_stmt("SELECT f FROM foo WHERE field IS TRUE"); + verified_stmt("SELECT f FROM foo WHERE field IS NOT TRUE"); verified_stmt("SELECT f FROM foo WHERE field IS FALSE"); + verified_stmt("SELECT f FROM foo WHERE field IS NOT FALSE"); + + verified_stmt("SELECT f FROM foo WHERE field IS UNKNOWN"); + verified_stmt("SELECT f FROM foo WHERE field IS NOT UNKNOWN"); let sql = "SELECT f from foo where field is 0"; let res = parse_sql_statements(sql);