From 6d9b7f16a61ae136db2afea1aa8bed5001649b6c Mon Sep 17 00:00:00 2001 From: Alexander Beedie Date: Thu, 30 Oct 2025 14:55:00 +0400 Subject: [PATCH] Make `BitwiseNot` ("~") available for all dialects, not just PostgreSQL --- src/ast/operator.rs | 50 ++++++++++++++++++------------------- src/parser/mod.rs | 6 +++-- tests/sqlparser_common.rs | 19 ++++++++++++++ tests/sqlparser_postgres.rs | 2 -- 4 files changed, 48 insertions(+), 29 deletions(-) diff --git a/src/ast/operator.rs b/src/ast/operator.rs index d0bb05e3c..58c401f7d 100644 --- a/src/ast/operator.rs +++ b/src/ast/operator.rs @@ -33,35 +33,35 @@ use super::display_separated; #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub enum UnaryOperator { + /// `@-@` Length or circumference (PostgreSQL/Redshift geometric operator) + /// see + AtDashAt, + /// Unary logical not operator: e.g. `! false` (Hive-specific) + BangNot, + /// Bitwise Not, e.g. `~9` + BitwiseNot, + /// `@@` Center (PostgreSQL/Redshift geometric operator) + /// see + DoubleAt, + /// `#` Number of points in path or polygon (PostgreSQL/Redshift geometric operator) + /// see + Hash, /// Plus, e.g. `+9` Plus, /// Minus, e.g. `-9` Minus, /// Not, e.g. `NOT(true)` Not, - /// Bitwise Not, e.g. `~9` (PostgreSQL-specific) - PGBitwiseNot, - /// Square root, e.g. `|/9` (PostgreSQL-specific) - PGSquareRoot, + /// Absolute value, e.g. `@ -9` (PostgreSQL-specific) + PGAbs, /// Cube root, e.g. `||/27` (PostgreSQL-specific) PGCubeRoot, /// Factorial, e.g. `9!` (PostgreSQL-specific) PGPostfixFactorial, /// Factorial, e.g. `!!9` (PostgreSQL-specific) PGPrefixFactorial, - /// Absolute value, e.g. `@ -9` (PostgreSQL-specific) - PGAbs, - /// Unary logical not operator: e.g. `! false` (Hive-specific) - BangNot, - /// `#` Number of points in path or polygon (PostgreSQL/Redshift geometric operator) - /// see - Hash, - /// `@-@` Length or circumference (PostgreSQL/Redshift geometric operator) - /// see - AtDashAt, - /// `@@` Center (PostgreSQL/Redshift geometric operator) - /// see - DoubleAt, + /// Square root, e.g. `|/9` (PostgreSQL-specific) + PGSquareRoot, /// `?-` Is horizontal? (PostgreSQL/Redshift geometric operator) /// see QuestionDash, @@ -73,19 +73,19 @@ pub enum UnaryOperator { impl fmt::Display for UnaryOperator { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(match self { - UnaryOperator::Plus => "+", + UnaryOperator::AtDashAt => "@-@", + UnaryOperator::BangNot => "!", + UnaryOperator::BitwiseNot => "~", + UnaryOperator::DoubleAt => "@@", + UnaryOperator::Hash => "#", UnaryOperator::Minus => "-", UnaryOperator::Not => "NOT", - UnaryOperator::PGBitwiseNot => "~", - UnaryOperator::PGSquareRoot => "|/", + UnaryOperator::PGAbs => "@", UnaryOperator::PGCubeRoot => "||/", UnaryOperator::PGPostfixFactorial => "!", UnaryOperator::PGPrefixFactorial => "!!", - UnaryOperator::PGAbs => "@", - UnaryOperator::BangNot => "!", - UnaryOperator::Hash => "#", - UnaryOperator::AtDashAt => "@-@", - UnaryOperator::DoubleAt => "@@", + UnaryOperator::PGSquareRoot => "|/", + UnaryOperator::Plus => "+", UnaryOperator::QuestionDash => "?-", UnaryOperator::QuestionPipe => "?|", }) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 1248c918f..9a01e510b 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1633,7 +1633,6 @@ impl<'a> Parser<'a> { | tok @ Token::PGSquareRoot | tok @ Token::PGCubeRoot | tok @ Token::AtSign - | tok @ Token::Tilde if dialect_is!(dialect is PostgreSqlDialect) => { let op = match tok { @@ -1641,7 +1640,6 @@ impl<'a> Parser<'a> { Token::PGSquareRoot => UnaryOperator::PGSquareRoot, Token::PGCubeRoot => UnaryOperator::PGCubeRoot, Token::AtSign => UnaryOperator::PGAbs, - Token::Tilde => UnaryOperator::PGBitwiseNot, _ => unreachable!(), }; Ok(Expr::UnaryOp { @@ -1651,6 +1649,10 @@ impl<'a> Parser<'a> { ), }) } + Token::Tilde => Ok(Expr::UnaryOp { + op: UnaryOperator::BitwiseNot, + expr: Box::new(self.parse_subexpr(self.dialect.prec_value(Precedence::PlusMinus))?), + }), tok @ Token::Sharp | tok @ Token::AtDashAt | tok @ Token::AtAt diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 99b7ac3fa..f1ba5df04 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -17613,3 +17613,22 @@ fn test_parse_alter_user() { } verified_stmt("ALTER USER u1 SET DEFAULT_SECONDARY_ROLES=('ALL'), PASSWORD='secret', WORKLOAD_IDENTITY=(TYPE=AWS, ARN='arn:aws:iam::123456789:r1/')"); } + +#[test] +fn parse_generic_unary_ops() { + let unary_ops = &[ + ("~", UnaryOperator::BitwiseNot), + ("-", UnaryOperator::Minus), + ("+", UnaryOperator::Plus), + ]; + for (str_op, op) in unary_ops { + let select = verified_only_select(&format!("SELECT {}expr", &str_op)); + assert_eq!( + UnnamedExpr(UnaryOp { + op: *op, + expr: Box::new(Identifier(Ident::new("expr"))), + }), + select.projection[0] + ); + } +} diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index bcc154287..9ba0fb978 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -2144,13 +2144,11 @@ fn parse_ampersand_arobase() { #[test] fn parse_pg_unary_ops() { let pg_unary_ops = &[ - ("~", UnaryOperator::PGBitwiseNot), ("|/", UnaryOperator::PGSquareRoot), ("||/", UnaryOperator::PGCubeRoot), ("!!", UnaryOperator::PGPrefixFactorial), ("@", UnaryOperator::PGAbs), ]; - for (str_op, op) in pg_unary_ops { let select = pg().verified_only_select(&format!("SELECT {}a", &str_op)); assert_eq!(