Skip to content

Commit ab78bc2

Browse files
committed
hive: support for special not expression !a and raise error for a! factorial operator
1 parent 749b061 commit ab78bc2

File tree

5 files changed

+76
-3
lines changed

5 files changed

+76
-3
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+
/// Special Not, e.g. `! false` (Hive-specific)
55+
SpecialNot,
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::SpecialNot => "!",
6871
})
6972
}
7073
}

src/dialect/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,11 @@ pub trait Dialect: Debug + Any {
561561
fn supports_asc_desc_in_column_definition(&self) -> bool {
562562
false
563563
}
564+
565+
/// Returns true if the dialect supports `a!` expressions
566+
fn supports_factorial_operator(&self) -> bool {
567+
false
568+
}
564569
}
565570

566571
/// 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
@@ -188,6 +188,11 @@ impl Dialect for PostgreSqlDialect {
188188
fn supports_explain_with_utility_options(&self) -> bool {
189189
true
190190
}
191+
192+
/// see <https://www.postgresql.org/docs/13/functions-math.html>
193+
fn supports_factorial_operator(&self) -> bool {
194+
true
195+
}
191196
}
192197

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

src/parser/mod.rs

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,6 +1073,7 @@ impl<'a> Parser<'a> {
10731073
}))
10741074
}
10751075
Keyword::NOT => self.parse_not(),
1076+
10761077
Keyword::MATCH if dialect_of!(self is MySqlDialect | GenericDialect) => {
10771078
self.parse_match_against()
10781079
}
@@ -1174,6 +1175,14 @@ impl<'a> Parser<'a> {
11741175
),
11751176
})
11761177
}
1178+
Token::ExclamationMark if !self.dialect.supports_factorial_operator() => {
1179+
Ok(Expr::UnaryOp {
1180+
op: UnaryOperator::SpecialNot,
1181+
expr: Box::new(
1182+
self.parse_subexpr(self.dialect.prec_value(Precedence::UnaryNot))?,
1183+
),
1184+
})
1185+
}
11771186
tok @ Token::DoubleExclamationMark
11781187
| tok @ Token::PGSquareRoot
11791188
| tok @ Token::PGCubeRoot
@@ -1267,7 +1276,6 @@ impl<'a> Parser<'a> {
12671276
}
12681277
_ => self.expected("an expression", next_token),
12691278
}?;
1270-
12711279
if self.parse_keyword(Keyword::COLLATE) {
12721280
Ok(Expr::Collate {
12731281
expr: Box::new(expr),
@@ -2024,6 +2032,13 @@ impl<'a> Parser<'a> {
20242032
}
20252033
}
20262034

2035+
pub fn parse_special_not(&mut self) -> Result<Expr, ParserError> {
2036+
Ok(Expr::UnaryOp {
2037+
op: UnaryOperator::SpecialNot,
2038+
expr: Box::new(self.parse_subexpr(self.dialect.prec_value(Precedence::UnaryNot))?),
2039+
})
2040+
}
2041+
20272042
/// Parses fulltext expressions [`sqlparser::ast::Expr::MatchAgainst`]
20282043
///
20292044
/// # Errors
@@ -2796,9 +2811,27 @@ impl<'a> Parser<'a> {
27962811
format: None,
27972812
})
27982813
} else if Token::ExclamationMark == tok {
2799-
// PostgreSQL factorial operation
2814+
match expr {
2815+
Expr::Value(_) | Expr::Identifier(_) => {
2816+
if !self.dialect.supports_factorial_operator() {
2817+
return parser_err!(
2818+
format!(
2819+
"current dialect: {:?} does not support factorial operator",
2820+
self.dialect
2821+
),
2822+
self.peek_token().location
2823+
);
2824+
}
2825+
}
2826+
_ => {}
2827+
};
2828+
let op = if !self.dialect.supports_factorial_operator() {
2829+
UnaryOperator::SpecialNot
2830+
} else {
2831+
UnaryOperator::PGPostfixFactorial
2832+
};
28002833
Ok(Expr::UnaryOp {
2801-
op: UnaryOperator::PGPostfixFactorial,
2834+
op,
28022835
expr: Box::new(expr),
28032836
})
28042837
} else if Token::LBracket == tok {

tests/sqlparser_hive.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,33 @@ fn parse_use() {
537537
);
538538
}
539539

540+
#[test]
541+
fn parse_hive_unary_ops() {
542+
let hive_unary_ops = &[("!", UnaryOperator::SpecialNot)];
543+
544+
for (str_op, op) in hive_unary_ops {
545+
let select = hive().verified_only_select(&format!("SELECT {}a", &str_op));
546+
assert_eq!(
547+
SelectItem::UnnamedExpr(Expr::UnaryOp {
548+
op: *op,
549+
expr: Box::new(Expr::Identifier(Ident::new("a"))),
550+
}),
551+
select.projection[0]
552+
);
553+
}
554+
}
555+
556+
#[test]
557+
fn parse_factorial_operator() {
558+
let res = hive().parse_sql_statements("select a!");
559+
assert_eq!(
560+
ParserError::ParserError(
561+
"current dialect: HiveDialect does not support factorial operator".to_string()
562+
),
563+
res.unwrap_err()
564+
);
565+
}
566+
540567
fn hive() -> TestedDialects {
541568
TestedDialects {
542569
dialects: vec![Box::new(HiveDialect {})],

0 commit comments

Comments
 (0)