mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-10-09 13:40:22 +00:00
hive: support for special not expression !a
and raise error for a!
factorial operator (#1472)
Co-authored-by: Ifeanyi Ubah <ify1992@yahoo.com>
This commit is contained in:
parent
334a5bf354
commit
e857787309
6 changed files with 142 additions and 3 deletions
|
@ -51,6 +51,8 @@ pub enum UnaryOperator {
|
|||
PGPrefixFactorial,
|
||||
/// Absolute value, e.g. `@ -9` (PostgreSQL-specific)
|
||||
PGAbs,
|
||||
/// Unary logical not operator: e.g. `! false` (Hive-specific)
|
||||
BangNot,
|
||||
}
|
||||
|
||||
impl fmt::Display for UnaryOperator {
|
||||
|
@ -65,6 +67,7 @@ impl fmt::Display for UnaryOperator {
|
|||
UnaryOperator::PGPostfixFactorial => "!",
|
||||
UnaryOperator::PGPrefixFactorial => "!!",
|
||||
UnaryOperator::PGAbs => "@",
|
||||
UnaryOperator::BangNot => "!",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,4 +51,9 @@ impl Dialect for HiveDialect {
|
|||
fn require_interval_qualifier(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// See Hive <https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=27362061#Tutorial-BuiltInOperators>
|
||||
fn supports_bang_not_operator(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -575,6 +575,11 @@ pub trait Dialect: Debug + Any {
|
|||
false
|
||||
}
|
||||
|
||||
/// Returns true if the dialect supports `a!` expressions
|
||||
fn supports_factorial_operator(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Returns true if this dialect supports treating the equals operator `=` within a `SelectItem`
|
||||
/// as an alias assignment operator, rather than a boolean expression.
|
||||
/// For example: the following statements are equivalent for such a dialect:
|
||||
|
@ -591,6 +596,11 @@ pub trait Dialect: Debug + Any {
|
|||
false
|
||||
}
|
||||
|
||||
/// Returns true if the dialect supports `!a` syntax for boolean `NOT` expressions.
|
||||
fn supports_bang_not_operator(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Returns true if the dialect supports the `LISTEN` statement
|
||||
fn supports_listen(&self) -> bool {
|
||||
false
|
||||
|
|
|
@ -201,6 +201,11 @@ impl Dialect for PostgreSqlDialect {
|
|||
fn supports_notify(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// see <https://www.postgresql.org/docs/13/functions-math.html>
|
||||
fn supports_factorial_operator(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_comment(parser: &mut Parser) -> Result<Statement, ParserError> {
|
||||
|
|
|
@ -1194,6 +1194,14 @@ impl<'a> Parser<'a> {
|
|||
),
|
||||
})
|
||||
}
|
||||
Token::ExclamationMark if self.dialect.supports_bang_not_operator() => {
|
||||
Ok(Expr::UnaryOp {
|
||||
op: UnaryOperator::BangNot,
|
||||
expr: Box::new(
|
||||
self.parse_subexpr(self.dialect.prec_value(Precedence::UnaryNot))?,
|
||||
),
|
||||
})
|
||||
}
|
||||
tok @ Token::DoubleExclamationMark
|
||||
| tok @ Token::PGSquareRoot
|
||||
| tok @ Token::PGCubeRoot
|
||||
|
@ -1287,7 +1295,6 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
_ => self.expected("an expression", next_token),
|
||||
}?;
|
||||
|
||||
if self.parse_keyword(Keyword::COLLATE) {
|
||||
Ok(Expr::Collate {
|
||||
expr: Box::new(expr),
|
||||
|
@ -2818,8 +2825,7 @@ impl<'a> Parser<'a> {
|
|||
data_type: self.parse_data_type()?,
|
||||
format: None,
|
||||
})
|
||||
} else if Token::ExclamationMark == tok {
|
||||
// PostgreSQL factorial operation
|
||||
} else if Token::ExclamationMark == tok && self.dialect.supports_factorial_operator() {
|
||||
Ok(Expr::UnaryOp {
|
||||
op: UnaryOperator::PGPostfixFactorial,
|
||||
expr: Box::new(expr),
|
||||
|
|
|
@ -11532,3 +11532,113 @@ fn test_select_top() {
|
|||
dialects.verified_stmt("SELECT TOP 3 DISTINCT * FROM tbl");
|
||||
dialects.verified_stmt("SELECT TOP 3 DISTINCT a, b, c FROM tbl");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_bang_not() {
|
||||
let dialects = all_dialects_where(|d| d.supports_bang_not_operator());
|
||||
let sql = "SELECT !a, !(b > 3)";
|
||||
let Select { projection, .. } = dialects.verified_only_select(sql);
|
||||
|
||||
for (i, expr) in [
|
||||
Box::new(Expr::Identifier(Ident::new("a"))),
|
||||
Box::new(Expr::Nested(Box::new(Expr::BinaryOp {
|
||||
left: Box::new(Expr::Identifier(Ident::new("b"))),
|
||||
op: BinaryOperator::Gt,
|
||||
right: Box::new(Expr::Value(Value::Number("3".parse().unwrap(), false))),
|
||||
}))),
|
||||
]
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
{
|
||||
assert_eq!(
|
||||
SelectItem::UnnamedExpr(Expr::UnaryOp {
|
||||
op: UnaryOperator::BangNot,
|
||||
expr
|
||||
}),
|
||||
projection[i]
|
||||
)
|
||||
}
|
||||
|
||||
let sql_statements = ["SELECT a!", "SELECT a ! b", "SELECT a ! as b"];
|
||||
|
||||
for &sql in &sql_statements {
|
||||
assert_eq!(
|
||||
dialects.parse_sql_statements(sql).unwrap_err(),
|
||||
ParserError::ParserError("No infix parser for token ExclamationMark".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
let sql_statements = ["SELECT !a", "SELECT !a b", "SELECT !a as b"];
|
||||
let dialects = all_dialects_where(|d| !d.supports_bang_not_operator());
|
||||
|
||||
for &sql in &sql_statements {
|
||||
assert_eq!(
|
||||
dialects.parse_sql_statements(sql).unwrap_err(),
|
||||
ParserError::ParserError("Expected: an expression, found: !".to_string())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_factorial_operator() {
|
||||
let dialects = all_dialects_where(|d| d.supports_factorial_operator());
|
||||
let sql = "SELECT a!, (b + c)!";
|
||||
let Select { projection, .. } = dialects.verified_only_select(sql);
|
||||
|
||||
for (i, expr) in [
|
||||
Box::new(Expr::Identifier(Ident::new("a"))),
|
||||
Box::new(Expr::Nested(Box::new(Expr::BinaryOp {
|
||||
left: Box::new(Expr::Identifier(Ident::new("b"))),
|
||||
op: BinaryOperator::Plus,
|
||||
right: Box::new(Expr::Identifier(Ident::new("c"))),
|
||||
}))),
|
||||
]
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
{
|
||||
assert_eq!(
|
||||
SelectItem::UnnamedExpr(Expr::UnaryOp {
|
||||
op: UnaryOperator::PGPostfixFactorial,
|
||||
expr
|
||||
}),
|
||||
projection[i]
|
||||
)
|
||||
}
|
||||
|
||||
let sql_statements = ["SELECT !a", "SELECT !a b", "SELECT !a as b"];
|
||||
|
||||
for &sql in &sql_statements {
|
||||
assert_eq!(
|
||||
dialects.parse_sql_statements(sql).unwrap_err(),
|
||||
ParserError::ParserError("Expected: an expression, found: !".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
let sql_statements = ["SELECT a!", "SELECT a ! b", "SELECT a ! as b"];
|
||||
|
||||
// Due to the exclamation mark, which is both part of the `bang not` operator
|
||||
// and the `factorial` operator, additional filtering not supports
|
||||
// `bang not` operator is required here.
|
||||
let dialects =
|
||||
all_dialects_where(|d| !d.supports_factorial_operator() && !d.supports_bang_not_operator());
|
||||
|
||||
for &sql in &sql_statements {
|
||||
assert_eq!(
|
||||
dialects.parse_sql_statements(sql).unwrap_err(),
|
||||
ParserError::ParserError("No infix parser for token ExclamationMark".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
// Due to the exclamation mark, which is both part of the `bang not` operator
|
||||
// and the `factorial` operator, additional filtering supports
|
||||
// `bang not` operator is required here.
|
||||
let dialects =
|
||||
all_dialects_where(|d| !d.supports_factorial_operator() && d.supports_bang_not_operator());
|
||||
|
||||
for &sql in &sql_statements {
|
||||
assert_eq!(
|
||||
dialects.parse_sql_statements(sql).unwrap_err(),
|
||||
ParserError::ParserError("No infix parser for token ExclamationMark".to_string())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue