PostgreSQL: unary minus binds more loosely than `^`, so `-1 ^ 0` parses as `-(1 ^ 0)`

未关闭
#2,819 2 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 1 天内回复

还没有人认领这个 Issue。

  • #2830 来自 @compiler-segfault —— 已关闭,未合并

评估

难度
3/5
预计耗时
1-2 天
新手友好度
72/100
Issue 类型
缺陷
描述清晰度
描述清楚
活跃度
活跃
技术栈
rust

调研方向

Read parse_prefix_inner in src/parser/mod.rs and the precedence constants in src/dialect/postgresql.rs; compare the PostgreSQL precedence table cited in the issue. Add or adjust parser coverage for unary plus and minus with ^, including the reproduced expressions. Done means the PostgreSQL dialect produces the expected AST grouping while the generic dialect remains unaffected.

由索引模型根据 Issue 内容生成。

描述

PostgreSQL

Problem

In PostgreSqlDialect, a unary minus binds more loosely than ^. The parser reads -1 ^ 0 as -(1 ^ 0).

In PostgreSQL, unary minus binds more tightly than ^ (operator precedence table). PostgreSQL reads -1 ^ 0 as (-1) ^ 0.

Reproduction

At main (d1c1790e3):

use sqlparser::dialect::PostgreSqlDialect;
use sqlparser::parser::Parser;

let ast = Parser::parse_sql(&PostgreSqlDialect {}, "SELECT -1 ^ 0").unwrap();
println!("{ast:#?}");

The projection expression is (spans removed):

UnaryOp {
    op: Minus,
    expr: BinaryOp {
        left: Value(Number("1", false)),
        op: PGExp,
        right: Value(Number("0", false)),
    },
}

The expected expression is:

BinaryOp {
    left: UnaryOp { op: Minus, expr: Value(Number("1", false)) },
    op: PGExp,
    right: Value(Number("0", false)),
}

SELECT -2 ^ 2 and SELECT +2 ^ 2 parse the same way.

Effect on query results

The grouping changes the result:

Query PostgreSQL 17 DuckDB DataFusion (PostgreSQL dialect)
SELECT -1 ^ 0 1 1 -1
SELECT -2 ^ 2 4 4 -4

DataFusion tracks this in apache/datafusion#27094.

Cause

parse_prefix_inner parses the operand of a unary - or + at Precedence::MulDivModOp (src/parser/mod.rs#L1967-L1979):

tok @ Token::Minus | tok @ Token::Plus => {
    // ...
    Ok(Expr::UnaryOp {
        op,
        expr: Box::new(
            self.parse_subexpr(self.dialect.prec_value(Precedence::MulDivModOp))?,
        ),
    })
}

PostgreSqlDialect sets CARET_PREC (100) above MUL_DIV_MOD_OP_PREC (90) (src/dialect/postgresql.rs#L213-L214). So the operand of the minus also takes the ^ that follows.

For the PostgreSQL dialect, the operand of a unary - or + must be parsed at a precedence above CARET_PREC. In the PostgreSQL table, unary + and - are also above COLLATE and AT.

The generic dialect is not affected in this way: there, Caret is below MulDivModOp.

Related: #814 (precedence of ~).

主要语言
Rust
星标
3.5k
派生
805
平均合并
2 天 3 小时
30 天内合并 PR
116

环境准备

  • 没有 Dockerfile 或 Docker Compose 文件
  • 有 Pull Request 模板
  • 没有贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

apache/datafusion-sqlparser-rs 的其他 Issue

查看 apache/datafusion-sqlparser-rs 的全部 Issue

相似的 Issue

更多 Rust Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。