Skip to content

Commit 99910dd

Browse files
committed
feat: add support for special bang not operator !a and raise error for a! factorial operator in Hive dialect
1 parent 38f1e57 commit 99910dd

File tree

6 files changed

+117
-6
lines changed

6 files changed

+117
-6
lines changed

src/ast/operator.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ pub enum UnaryOperator {
5151
PGPrefixFactorial,
5252
/// Absolute value, e.g. `@ -9` (PostgreSQL-specific)
5353
PGAbs,
54+
/// Unary logical not operator: e.g. `! false` (Hive-specific)
55+
BangNot,
5456
}
5557

5658
impl fmt::Display for UnaryOperator {
@@ -65,6 +67,7 @@ impl fmt::Display for UnaryOperator {
6567
UnaryOperator::PGPostfixFactorial => "!",
6668
UnaryOperator::PGPrefixFactorial => "!!",
6769
UnaryOperator::PGAbs => "@",
70+
UnaryOperator::BangNot => "!",
6871
})
6972
}
7073
}

src/dialect/hive.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,8 @@ impl Dialect for HiveDialect {
5151
fn require_interval_qualifier(&self) -> bool {
5252
true
5353
}
54+
55+
fn supports_bang_not_operator(&self) -> bool {
56+
true
57+
}
5458
}

src/dialect/mod.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,11 @@ pub trait Dialect: Debug + Any {
575575
false
576576
}
577577

578+
/// Returns true if the dialect supports `a!` expressions
579+
fn supports_factorial_operator(&self) -> bool {
580+
false
581+
}
582+
578583
/// Returns true if this dialect supports treating the equals operator `=` within a `SelectItem`
579584
/// as an alias assignment operator, rather than a boolean expression.
580585
/// For example: the following statements are equivalent for such a dialect:
@@ -590,6 +595,11 @@ pub trait Dialect: Debug + Any {
590595
fn supports_try_convert(&self) -> bool {
591596
false
592597
}
598+
599+
/// Returns true if the dialect supports `!a` expressions
600+
fn supports_bang_not_operator(&self) -> bool {
601+
false
602+
}
593603
}
594604

595605
/// This represents the operators for which precedence must be defined

src/dialect/postgresql.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,11 @@ impl Dialect for PostgreSqlDialect {
191191
fn supports_explain_with_utility_options(&self) -> bool {
192192
true
193193
}
194+
195+
/// see <https://www.postgresql.org/docs/13/functions-math.html>
196+
fn supports_factorial_operator(&self) -> bool {
197+
true
198+
}
194199
}
195200

196201
pub fn parse_comment(parser: &mut Parser) -> Result<Statement, ParserError> {

src/parser/mod.rs

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1175,6 +1175,14 @@ impl<'a> Parser<'a> {
11751175
),
11761176
})
11771177
}
1178+
Token::ExclamationMark if self.dialect.supports_bang_not_operator() => {
1179+
Ok(Expr::UnaryOp {
1180+
op: UnaryOperator::BangNot,
1181+
expr: Box::new(
1182+
self.parse_subexpr(self.dialect.prec_value(Precedence::UnaryNot))?,
1183+
),
1184+
})
1185+
}
11781186
tok @ Token::DoubleExclamationMark
11791187
| tok @ Token::PGSquareRoot
11801188
| tok @ Token::PGCubeRoot
@@ -1268,7 +1276,6 @@ impl<'a> Parser<'a> {
12681276
}
12691277
_ => self.expected("an expression", next_token),
12701278
}?;
1271-
12721279
if self.parse_keyword(Keyword::COLLATE) {
12731280
Ok(Expr::Collate {
12741281
expr: Box::new(expr),
@@ -2028,6 +2035,13 @@ impl<'a> Parser<'a> {
20282035
}
20292036
}
20302037

2038+
pub fn parse_bang_not(&mut self) -> Result<Expr, ParserError> {
2039+
Ok(Expr::UnaryOp {
2040+
op: UnaryOperator::BangNot,
2041+
expr: Box::new(self.parse_subexpr(self.dialect.prec_value(Precedence::UnaryNot))?),
2042+
})
2043+
}
2044+
20312045
/// Parses fulltext expressions [`sqlparser::ast::Expr::MatchAgainst`]
20322046
///
20332047
/// # Errors
@@ -2800,11 +2814,40 @@ impl<'a> Parser<'a> {
28002814
format: None,
28012815
})
28022816
} else if Token::ExclamationMark == tok {
2803-
// PostgreSQL factorial operation
2804-
Ok(Expr::UnaryOp {
2805-
op: UnaryOperator::PGPostfixFactorial,
2806-
expr: Box::new(expr),
2807-
})
2817+
if self.dialect.supports_factorial_operator() {
2818+
match expr {
2819+
Expr::Value(_) | Expr::Identifier(_) | Expr::Nested(_) | Expr::BinaryOp{..} => Ok(Expr::UnaryOp {
2820+
op: UnaryOperator::PGPostfixFactorial,
2821+
expr: Box::new(expr),
2822+
}),
2823+
_ => {
2824+
self.expected(
2825+
"Value or Identifier or Nested or BinaryOp struct before factorial operator(!)", self.peek_token())
2826+
},
2827+
}
2828+
} else if self.dialect.supports_bang_not_operator() {
2829+
let token = self.next_token();
2830+
match token.token {
2831+
Token::Word(_) | Token::Number(..) => Ok(Expr::UnaryOp {
2832+
op: UnaryOperator::BangNot,
2833+
expr: Box::new(expr),
2834+
}),
2835+
_ => {
2836+
parser_err!(
2837+
"current dialect support bang not operator, but with wrong synx",
2838+
tok.location
2839+
)
2840+
}
2841+
}
2842+
} else {
2843+
parser_err!(
2844+
format!(
2845+
"current dialect: {:?} does not support factorial operator or bang not operator",
2846+
self.dialect
2847+
),
2848+
self.peek_token().location
2849+
)
2850+
}
28082851
} else if Token::LBracket == tok {
28092852
if dialect_of!(self is PostgreSqlDialect | DuckDbDialect | GenericDialect) {
28102853
self.parse_subscript(expr)

tests/sqlparser_common.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11460,3 +11460,49 @@ fn test_try_convert() {
1146011460
all_dialects_where(|d| d.supports_try_convert() && !d.convert_type_before_value());
1146111461
dialects.verified_expr("TRY_CONVERT('foo', VARCHAR(MAX))");
1146211462
}
11463+
11464+
#[test]
11465+
fn parse_bang_not() {
11466+
let dialects = all_dialects_where(|d| d.supports_bang_not_operator());
11467+
let sql = "SELECT !a, !(b > 3)";
11468+
let Select { projection, .. } = dialects.verified_only_select(sql);
11469+
11470+
for (i, (op, expr)) in [
11471+
(
11472+
UnaryOperator::BangNot,
11473+
Box::new(Expr::Identifier(Ident::new("a"))),
11474+
),
11475+
(
11476+
UnaryOperator::BangNot,
11477+
Box::new(Expr::Nested(Box::new(Expr::BinaryOp {
11478+
left: Box::new(Expr::Identifier(Ident::new("b"))),
11479+
op: BinaryOperator::Gt,
11480+
right: Box::new(Expr::Value(Value::Number("3".parse().unwrap(), false))),
11481+
}))),
11482+
),
11483+
]
11484+
.into_iter()
11485+
.enumerate()
11486+
{
11487+
assert_eq!(
11488+
SelectItem::UnnamedExpr(Expr::UnaryOp { op: op, expr }),
11489+
projection[i]
11490+
)
11491+
}
11492+
11493+
let sql = "SELECT a!";
11494+
assert_eq!(
11495+
dialects.parse_sql_statements(sql).unwrap_err(),
11496+
ParserError::ParserError(
11497+
"current dialect support bang not operator, but with wrong synx".to_string()
11498+
)
11499+
);
11500+
11501+
let sql = "SELECT !a";
11502+
assert_eq!(
11503+
all_dialects_where(|d| !d.supports_bang_not_operator())
11504+
.parse_sql_statements(sql)
11505+
.unwrap_err(),
11506+
ParserError::ParserError("Expected: an expression, found: !".to_string())
11507+
);
11508+
}

0 commit comments

Comments
 (0)