Skip to content

Commit

Permalink
Add overlay expr (#594)
Browse files Browse the repository at this point in the history
* Add PLACING keyword to keywords list

* Add high level overlay expr to Expr enum

* Add parsing logic for overlay op

* add ovleray and is not true/false/unknown tests
  • Loading branch information
ayushdg committed Aug 26, 2022
1 parent 95fbb55 commit 6f55dea
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 4 deletions.
28 changes: 26 additions & 2 deletions src/ast/mod.rs
Expand Up @@ -348,6 +348,13 @@ pub enum Expr {
trim_where: Option<TrimWhereField>,
trim_what: Option<Box<Expr>>,
},
/// OVERLAY(<expr> PLACING <expr> FROM <expr>[ FOR <expr> ]
Overlay {
expr: Box<Expr>,
overlay_what: Box<Expr>,
overlay_from: Box<Expr>,
overlay_for: Option<Box<Expr>>,
},
/// `expr COLLATE collation`
Collate {
expr: Box<Expr>,
Expand Down Expand Up @@ -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, ")")
Expand Down
1 change: 1 addition & 0 deletions src/keywords.rs
Expand Up @@ -382,6 +382,7 @@ define_keywords!(
PERCENTILE_DISC,
PERCENT_RANK,
PERIOD,
PLACING,
PLANS,
PORTION,
POSITION,
Expand Down
23 changes: 23 additions & 0 deletions src/parser.rs
Expand Up @@ -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(),
Expand Down Expand Up @@ -884,6 +885,28 @@ impl<'a> Parser<'a> {
})
}

pub fn parse_overlay_expr(&mut self) -> Result<Expr, ParserError> {
// 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<Expr, ParserError> {
Expand Down
65 changes: 63 additions & 2 deletions tests/sqlparser_common.rs
Expand Up @@ -3973,6 +3973,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(
Expand Down Expand Up @@ -5357,21 +5389,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);
Expand Down

0 comments on commit 6f55dea

Please sign in to comment.