Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add overlay expr #594

Merged
merged 4 commits into from Aug 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 @@ -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(
Expand Down Expand Up @@ -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);
Expand Down