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

Ouverte
#2,819 2 commentaires 0 réactions 0 personnes assignées Voir sur GitHub

Les mainteneurs répondent en général sous 1 jour

Personne n'a encore pris cette issue.

  • #2830 par @compiler-segfault — fermée sans fusion

Évaluation

Difficulté
3/5
Temps estimé
1-2 jours
Accessibilité débutants
72/100
Type d'issue
Bug
Clarté
Clairement spécifiée
Activité
Active
Stack technique
rust

Piste de recherche

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.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Description

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

Langage dominant
Rust
Étoiles
3.5k
Forks
805
Merge moyen
2 j 3 h
PR mergées (30 j)
116

Préparer son environnement

  • Aucun Dockerfile ni fichier Docker Compose
  • Propose un modèle de pull request
  • Aucun guide de contribution

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Autres issues de apache/datafusion-sqlparser-rs

Toutes les issues de apache/datafusion-sqlparser-rs

Issues similaires

Plus d'issues Rust

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.