Split out of #2461, where this was raised as an open question.
MySQL's -> / ->> bind more tightly than every arithmetic, shift and bitwise operator: the
right-hand side is a quoted JSON path, and col->path is defined as equivalent to
JSON_EXTRACT(col, path)
(docs). sqlparser gives the
arrow operators Precedence::PgOther (21), which sits below + / - / *, so the arrow
under-binds on both sides:
| SQL |
sqlparser (MySqlDialect, GenericDialect) |
MySQL |
c -> '$.a' + 1 |
c -> ('$.a' + 1) |
(c -> '$.a') + 1 |
c ->> '$.a' + 1 |
c ->> ('$.a' + 1) |
(c ->> '$.a') + 1 |
c -> '$.a' * 2 |
c -> ('$.a' * 2) |
(c -> '$.a') * 2 |
1 + c -> '$.a' |
(1 + c) -> '$.a' |
1 + (c -> '$.a') |
Comparisons are already correct, since Eq (20) is below PgOther (21):
c -> '$.a' = 1 → ((c -> '$.a') = 1).
As in the sibling issues, Display for Expr::BinaryOp emits no parentheses, so a mis-grouped tree
round-trips to the original SQL — verified_expr / verified_stmt cannot catch this, only a test
asserting on the tree can.
Why this is not a fix to the shared row
PgOther = 21 is correct for PostgreSQL. In gram.y the arrow shares one left-associative level
with | and generic operators, below + / -:
%left Op OPERATOR RIGHT_ARROW '|'
and PostgreSqlDialect behaves accordingly today (a -> b + c → a -> (b + c)). So the two engines
genuinely disagree about where the arrow sits, and a single shared precedence row cannot be right for
both.
Possible approaches
- Add a dedicated
Precedence variant for the arrow operators, defaulting to the current
PgOther value so PostgreSQL and every other dialect are unchanged, and have MySqlDialect place
it above MulDivModOp. This is the narrowest option.
- Give
MySqlDialect its own prec_value the way PostgreSqlDialect has one. That duplicates the
whole table, and would also move @>, <@ and CustomBinaryOperator, which is probably not
intended.
I'd lean towards (1), but I don't want to presume — happy to implement whichever a maintainer
prefers.
Two open questions for whoever picks this up:
- Should
GenericDialect follow MySQL here? It currently produces the same grouping as MySQL, but
Generic is a permissive superset, so this seems like a judgment call rather than a clear bug.
- How high should the MySQL arrow sit exactly? Since the right operand is lexically a path string in
real MySQL, anything above MulDivModOp gives correct results for valid input; placing it near
DoubleColon would match the grammar most literally.
No existing test pins the current grouping — the only MySQL arrow trees asserted are single-operator
ones inside a CAST (tests/sqlparser_mysql.rs:879-910).
Related: #2436, #2460, #2461.
Split out of #2461, where this was raised as an open question.
MySQL's
->/->>bind more tightly than every arithmetic, shift and bitwise operator: theright-hand side is a quoted JSON path, and
col->pathis defined as equivalent toJSON_EXTRACT(col, path)(docs). sqlparser gives the
arrow operators
Precedence::PgOther(21), which sits below+/-/*, so the arrowunder-binds on both sides:
MySqlDialect,GenericDialect)c -> '$.a' + 1c -> ('$.a' + 1)(c -> '$.a') + 1c ->> '$.a' + 1c ->> ('$.a' + 1)(c ->> '$.a') + 1c -> '$.a' * 2c -> ('$.a' * 2)(c -> '$.a') * 21 + c -> '$.a'(1 + c) -> '$.a'1 + (c -> '$.a')Comparisons are already correct, since
Eq(20) is belowPgOther(21):c -> '$.a' = 1→((c -> '$.a') = 1).As in the sibling issues,
DisplayforExpr::BinaryOpemits no parentheses, so a mis-grouped treeround-trips to the original SQL —
verified_expr/verified_stmtcannot catch this, only a testasserting on the tree can.
Why this is not a fix to the shared row
PgOther= 21 is correct for PostgreSQL. Ingram.ythe arrow shares one left-associative levelwith
|and generic operators, below+/-:and
PostgreSqlDialectbehaves accordingly today (a -> b + c→a -> (b + c)). So the two enginesgenuinely disagree about where the arrow sits, and a single shared precedence row cannot be right for
both.
Possible approaches
Precedencevariant for the arrow operators, defaulting to the currentPgOthervalue so PostgreSQL and every other dialect are unchanged, and haveMySqlDialectplaceit above
MulDivModOp. This is the narrowest option.MySqlDialectits ownprec_valuethe wayPostgreSqlDialecthas one. That duplicates thewhole table, and would also move
@>,<@andCustomBinaryOperator, which is probably notintended.
I'd lean towards (1), but I don't want to presume — happy to implement whichever a maintainer
prefers.
Two open questions for whoever picks this up:
GenericDialectfollow MySQL here? It currently produces the same grouping as MySQL, butGeneric is a permissive superset, so this seems like a judgment call rather than a clear bug.
real MySQL, anything above
MulDivModOpgives correct results for valid input; placing it nearDoubleColonwould match the grammar most literally.No existing test pins the current grouping — the only MySQL arrow trees asserted are single-operator
ones inside a
CAST(tests/sqlparser_mysql.rs:879-910).Related: #2436, #2460, #2461.