Skip to content

Commit

Permalink
hive: support for special not expression !a and raise error for `a!…
Browse files Browse the repository at this point in the history
…` factorial operator
  • Loading branch information
wugeer committed Oct 12, 2024
1 parent 749b061 commit c0fac44
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 2 deletions.
3 changes: 3 additions & 0 deletions src/ast/operator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ pub enum UnaryOperator {
PGPrefixFactorial,
/// Absolute value, e.g. `@ -9` (PostgreSQL-specific)
PGAbs,
/// Special Not, e.g. `! false` (Hive-specific)
SpecialNot,
}

impl fmt::Display for UnaryOperator {
Expand All @@ -65,6 +67,7 @@ impl fmt::Display for UnaryOperator {
UnaryOperator::PGPostfixFactorial => "!",
UnaryOperator::PGPrefixFactorial => "!!",
UnaryOperator::PGAbs => "@",
UnaryOperator::SpecialNot => "!",
})
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/dialect/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,11 @@ pub trait Dialect: Debug + Any {
fn supports_asc_desc_in_column_definition(&self) -> bool {
false
}

/// Returns true if the dialect supports `a!` expressions
fn supports_factorial_operator(&self) -> bool {
false
}
}

/// This represents the operators for which precedence must be defined
Expand Down
5 changes: 5 additions & 0 deletions src/dialect/postgresql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,11 @@ impl Dialect for PostgreSqlDialect {
fn supports_explain_with_utility_options(&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> {
Expand Down
38 changes: 36 additions & 2 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1073,6 +1073,7 @@ impl<'a> Parser<'a> {
}))
}
Keyword::NOT => self.parse_not(),

Keyword::MATCH if dialect_of!(self is MySqlDialect | GenericDialect) => {
self.parse_match_against()
}
Expand Down Expand Up @@ -1174,6 +1175,14 @@ impl<'a> Parser<'a> {
),
})
}
Token::ExclamationMark if !self.dialect.supports_factorial_operator() => {
Ok(Expr::UnaryOp {
op: UnaryOperator::SpecialNot,
expr: Box::new(
self.parse_subexpr(self.dialect.prec_value(Precedence::UnaryNot))?,
),
})
}
tok @ Token::DoubleExclamationMark
| tok @ Token::PGSquareRoot
| tok @ Token::PGCubeRoot
Expand Down Expand Up @@ -1267,7 +1276,6 @@ impl<'a> Parser<'a> {
}
_ => self.expected("an expression", next_token),
}?;

if self.parse_keyword(Keyword::COLLATE) {
Ok(Expr::Collate {
expr: Box::new(expr),
Expand Down Expand Up @@ -2024,6 +2032,13 @@ impl<'a> Parser<'a> {
}
}

pub fn parse_special_not(&mut self) -> Result<Expr, ParserError> {
Ok(Expr::UnaryOp {
op: UnaryOperator::SpecialNot,
expr: Box::new(self.parse_subexpr(self.dialect.prec_value(Precedence::UnaryNot))?),
})
}

/// Parses fulltext expressions [`sqlparser::ast::Expr::MatchAgainst`]
///
/// # Errors
Expand Down Expand Up @@ -2797,8 +2812,27 @@ impl<'a> Parser<'a> {
})
} else if Token::ExclamationMark == tok {
// PostgreSQL factorial operation
match expr {
Expr::Value(_) | Expr::Identifier(_) => {
if !self.dialect.supports_factorial_operator() {
return parser_err!(
format!(
"current dialect: {:?} does not support factorial operator",
self.dialect
),
self.peek_token().location
);
}
}
_ => {}
};
let op = if !self.dialect.supports_factorial_operator() {
UnaryOperator::SpecialNot
} else {
UnaryOperator::PGPostfixFactorial
};
Ok(Expr::UnaryOp {
op: UnaryOperator::PGPostfixFactorial,
op,
expr: Box::new(expr),
})
} else if Token::LBracket == tok {
Expand Down
27 changes: 27 additions & 0 deletions tests/sqlparser_hive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,33 @@ fn parse_use() {
);
}

#[test]
fn parse_hive_unary_ops() {
let hive_unary_ops = &[("!", UnaryOperator::SpecialNot)];

for (str_op, op) in hive_unary_ops {
let select = hive().verified_only_select(&format!("SELECT {}a", &str_op));
assert_eq!(
SelectItem::UnnamedExpr(Expr::UnaryOp {
op: *op,
expr: Box::new(Expr::Identifier(Ident::new("a"))),
}),
select.projection[0]
);
}
}

#[test]
fn parse_factorial_operator() {
let res = hive().parse_sql_statements("select a!");
assert_eq!(
ParserError::ParserError(
"current dialect: HiveDialect does not support factorial operator".to_string()
),
res.unwrap_err()
);
}

fn hive() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(HiveDialect {})],
Expand Down

0 comments on commit c0fac44

Please sign in to comment.