diff --git a/src/ast/operator.rs b/src/ast/operator.rs index c3bb379d6..d3ec3a301 100644 --- a/src/ast/operator.rs +++ b/src/ast/operator.rs @@ -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 { @@ -65,6 +67,7 @@ impl fmt::Display for UnaryOperator { UnaryOperator::PGPostfixFactorial => "!", UnaryOperator::PGPrefixFactorial => "!!", UnaryOperator::PGAbs => "@", + UnaryOperator::SpecialNot => "!", }) } } diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs index 744f5a8c8..28387ad5f 100644 --- a/src/dialect/mod.rs +++ b/src/dialect/mod.rs @@ -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 diff --git a/src/dialect/postgresql.rs b/src/dialect/postgresql.rs index 2a66705bb..9ffd67a70 100644 --- a/src/dialect/postgresql.rs +++ b/src/dialect/postgresql.rs @@ -188,6 +188,11 @@ impl Dialect for PostgreSqlDialect { fn supports_explain_with_utility_options(&self) -> bool { true } + + /// see + fn supports_factorial_operator(&self) -> bool { + true + } } pub fn parse_comment(parser: &mut Parser) -> Result { diff --git a/src/parser/mod.rs b/src/parser/mod.rs index cd9be1d8f..3eccb9202 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -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() } @@ -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 @@ -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), @@ -2024,6 +2032,13 @@ impl<'a> Parser<'a> { } } + pub fn parse_special_not(&mut self) -> Result { + 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 @@ -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 { diff --git a/tests/sqlparser_hive.rs b/tests/sqlparser_hive.rs index 069500bf6..c12b746df 100644 --- a/tests/sqlparser_hive.rs +++ b/tests/sqlparser_hive.rs @@ -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 {})],