Skip to content
Open
1 change: 1 addition & 0 deletions fuzz/fuzz_seeds/postgres_2829_unary_minus_caret.sql
Comment thread
Surajsuthar marked this conversation as resolved.
Resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SELECT -1 ^ 0;
4 changes: 3 additions & 1 deletion src/dialect/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1152,7 +1152,7 @@ pub trait Dialect: Debug + Any {
Precedence::Period => 100,
Precedence::DoubleColon => 50,
Precedence::AtTz => 41,
Precedence::MulDivModOp => 40,
Precedence::MulDivModOp | Precedence::UnaryPlusMinus => 40,
Precedence::PlusMinus => 30,
Precedence::Xor => 24,
Precedence::Ampersand => 23,
Expand Down Expand Up @@ -2362,6 +2362,8 @@ pub enum Precedence {
DoubleColon,
/// Timezone operator (e.g. `AT TIME ZONE`).
AtTz,
/// Unary `+` and `-`, bounding their operand.
UnaryPlusMinus,
/// Multiplication / Division / Modulo operators (`*`, `/`, `%`).
MulDivModOp,
/// Addition / Subtraction (`+`, `-`).
Expand Down
2 changes: 2 additions & 0 deletions src/dialect/postgresql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ pub struct PostgreSqlDialect {}
const PERIOD_PREC: u8 = 200;
const DOUBLE_COLON_PREC: u8 = 140;
const BRACKET_PREC: u8 = 130;
const UNARY_PLUS_MINUS_PREC: u8 = 125;
const COLLATE_PREC: u8 = 120;
const AT_TZ_PREC: u8 = 110;
const CARET_PREC: u8 = 100;
Expand Down Expand Up @@ -387,6 +388,7 @@ impl Dialect for PostgreSqlDialect {
Precedence::UnaryNot => NOT_PREC,
Precedence::And => AND_PREC,
Precedence::Or => OR_PREC,
Precedence::UnaryPlusMinus => UNARY_PLUS_MINUS_PREC,
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/dialect/sqlite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ impl Dialect for SQLiteDialect {
Precedence::Period => 100,
Precedence::DoubleColon => 50,
Precedence::AtTz => 41,
Precedence::MulDivModOp => 40,
Precedence::MulDivModOp | Precedence::UnaryPlusMinus => 40,
Precedence::PlusMinus => 30,
Precedence::Xor => 25,
Precedence::Ampersand => 24,
Expand Down
2 changes: 1 addition & 1 deletion src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2011,7 +2011,7 @@ impl<'a> Parser<'a> {
Ok(Expr::UnaryOp {
op,
expr: Box::new(
self.parse_subexpr(self.dialect.prec_value(Precedence::MulDivModOp))?,
self.parse_subexpr(self.dialect.prec_value(Precedence::UnaryPlusMinus))?,
),
})
}
Expand Down
83 changes: 83 additions & 0 deletions tests/sqlparser_postgres.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13200,6 +13200,51 @@ fn parse_alter_table_no_inherit() {
}
}

#[test]
fn parse_pg_prefix_operator_before_multiplication() {
for sql in ["SELECT ~ 1 * 2", "SELECT |/ 25 * 4", "SELECT # 1 * 2"] {
let select = pg().verified_only_select(sql);
let Expr::UnaryOp { expr, .. } = expr_from_projection(&select.projection[0]) else {
panic!("Expected prefix operator expression in {sql}")
};
assert!(
matches!(
expr.as_ref(),
Expr::BinaryOp {
op: BinaryOperator::Multiply,
..
}
),
"{sql}"
);
}
}

#[test]
fn parse_unary_plus_minus_before_pg_exponentiation() {
for (sql, unary_op, operand, exponent) in [
("SELECT -1 ^ 0", UnaryOperator::Minus, "1", "0"),
("SELECT +2 ^ 2", UnaryOperator::Plus, "2", "2"),
] {
let select = pg().verified_only_select(sql);
let Expr::BinaryOp { left, op, right } = expr_from_projection(&select.projection[0]) else {
panic!("Expected exponentiation expression")
};
assert_eq!(*op, BinaryOperator::PGExp);
assert_eq!(
right.as_ref(),
&Expr::Value(number(exponent).with_empty_span())
);
assert_eq!(
left.as_ref(),
&Expr::UnaryOp {
op: unary_op,
expr: Box::new(Expr::Value(number(operand).with_empty_span())),
}
);
}
}
Comment thread
Surajsuthar marked this conversation as resolved.
Resolved

#[test]
fn parse_create_function_cost() {
let stmt = pg_and_generic()
Expand Down Expand Up @@ -13349,6 +13394,44 @@ fn parse_create_function_rows() {
}
}

#[test]
fn parse_unary_minus_before_pg_at_time_zone() {
let select = pg().verified_only_select("SELECT -ts AT TIME ZONE 'UTC'");
let Expr::AtTimeZone { timestamp, .. } = expr_from_projection(&select.projection[0]) else {
panic!("Expected AT TIME ZONE expression")
};
assert_eq!(
timestamp.as_ref(),
&Expr::UnaryOp {
op: UnaryOperator::Minus,
expr: Box::new(Expr::Identifier(Ident::new("ts"))),
}
);
}

#[test]
fn parse_generic_unary_minus_before_caret() {
let statements = Parser::parse_sql(&GenericDialect {}, "SELECT -1 ^ 0").unwrap();
let Statement::Query(query) = &statements[0] else {
panic!("Expected query")
};
let SetExpr::Select(select) = query.body.as_ref() else {
panic!("Expected select")
};
let Expr::BinaryOp { left, op, right } = expr_from_projection(&select.projection[0]) else {
panic!("Expected bitwise expression")
};
assert_eq!(*op, BinaryOperator::BitwiseXor);
assert_eq!(
left.as_ref(),
&Expr::UnaryOp {
op: UnaryOperator::Minus,
expr: Box::new(Expr::Value(number("1").with_empty_span())),
}
);
assert_eq!(right.as_ref(), &Expr::Value(number("0").with_empty_span()));
}

#[test]
fn parse_create_function_support() {
let stmt = pg_and_generic().verified_stmt(
Expand Down
Loading