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

Aberta
#2,819 2 comentários 0 reações 0 responsáveis Ver no GitHub

Mantenedores costumam responder em até 1 dia

Ninguém assumiu esta issue ainda.

  • #2830 de @compiler-segfault — fechado sem integrar

Avaliação

Dificuldade
3/5
Tempo estimado
1-2 dias
Facilidade para iniciantes
72/100
Tipo de issue
Bug
Clareza
Claramente especificada
Status de atividade
Ativa
Stack de tecnologia
rust

Direção de pesquisa

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.

Escrita pelo modelo de indexação a partir do texto da issue.

Descrição

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 ~).

Linguagem predominante
Rust
Estrelas
3.5k
Forks
805
Merge médio
2d 3h
PRs com merge (30d)
116

Preparar o ambiente

  • Sem Dockerfile nem arquivo Docker Compose
  • Tem um modelo de pull request
  • Sem guia de contribuição

Primeiros passos

  1. Leia a issue inteira e depois o guia de contribuição do projeto.
  2. Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
  3. Faça um fork do repositório e trabalhe em uma branch.
  4. Abra um pull request que referencie o número da issue.

Mais de apache/datafusion-sqlparser-rs

Todas as issues de apache/datafusion-sqlparser-rs

Issues semelhantes

Mais issues de Rust

Receba novas issues na sua caixa de entrada

Um resumo curto de issues do GitHub para quem está começando.