apache / apache/datafusion-sqlparser-rs

Bitwise `&` and `->` group differently in the PostgreSQL and MySQL/Generic dialects

オープン 初心者向け
#2,461 コメント 2 件 リアクション 0 件 担当者 0 名 GitHub で見る
主要言語
Rust
スター
3.5k
フォーク
772
平均マージ
4日 9時間
マージ済み PR(30日)
17

説明

`&` and `->` have the same precedence in PostgreSQL but not in the default precedence table, so the
same expression produces two different trees depending on dialect:

| SQL | `PostgreSqlDialect` | `MySqlDialect` / `GenericDialect` |
|---|---|---|
| `a -> b & c` | `((a -> b) & c)` | `(a -> (b & c))` |
| `a -> b \| c` | `((a -> b) \| c)` | `((a -> b) \| c)` |
| `a -> b ^ c` | `(a -> (b ^ c))` | `(a -> (b ^ c))` |
| `a -> b + c` | `(a -> (b + c))` | `(a -> (b + c))` |

`&` is the only row that disagrees.

### Cause

The default table in `src/dialect/mod.rs` has:

```rust
Precedence::Ampersand => 23,
Precedence::Caret => 22,
Precedence::Pipe => 21,
Precedence::Colon => 21,
Precedence::PgOther => 21,
```

`PostgreSqlDialect::prec_value` instead maps `Ampersand`, `Pipe`, `Colon` and `PgOther` all to
`PG_OTHER_PREC` (`src/dialect/postgresql.rs:176-184`), which matches `gram.y`, where `&` is just a
generic `Op` and shares one left-associative level with `->`:

```
%left Op OPERATOR RIGHT_ARROW '|'
```

So `Pipe` already agrees with `PgOther` in the default table (both 21), and `Caret` is legitimately
above it, but `Ampersand` at 23 is left as the sole outlier.

### Candidate fix

```diff
- Precedence::Ampersand => 23,
+ Precedence::Ampersand => 21,
```

The full suite passes unchanged with that applied, so no existing test pins the current grouping —
which is also why this went unnoticed. `Display` for `Expr::BinaryOp` emits no parentheses, so a
mis-grouped tree round-trips to the original SQL and `verified_expr` / `verified_stmt` cannot catch
it; a test would have to assert on the tree.

### Open question, possibly a separate issue

For MySQL the fix above is necessary but not sufficient. MySQL's `->` / `->>` take a quoted JSON path
on the right-hand side, so there is nothing for MySQL to resolve — `col->'$.a' + 1` can only mean
`(col->'$.a') + 1`. sqlparser parses that right operand as a full expression at `PgOther`, giving
`col -> ('$.a' + 1)`, so `->` under-binds in MySQL against `+`, `*`, `^` and friends, not just `&`.
Making that correct probably means a MySQL-specific precedence for the arrow operators rather than
another adjustment to the shared row, so I've kept it out of scope here — happy to split it out if
a maintainer would prefer it tracked separately.

Surfaced while working on #2436. Related: #2460.

コントリビューションガイド

このリポジトリのコントリビューションガイドは索引されていません

調査の方向性

src/dialect/mod.rs のデフォルトの優先順位テーブルから始め、次に src/dialect/postgresql.rs:176-184 の PostgreSqlDialect::prec_value および issue で引用されている gram.y の PostgreSQL ルールと比較します。SQL のラウンドトリップテストではグループ化を検出できないため、`a -> b & c` のツリーレベルの回帰テストを追加し、完全なテストスイートを実行して変更を検証します。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
mysql, postgresql, rust
領域
compilers, databases
issue の種類
バグ
難易度
2/5
見積もり時間
1〜3時間
活発さ
活発
明瞭さ
明確に書かれている
初心者へのやさしさ
78/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。