fix(mysql)!: qualify column reference in NULLS LAST CASE simulation#7641
Conversation
… avoid 1052 ambiguity When transpiling ORDER BY <col> from a NULLS-LAST default dialect (e.g. DuckDB) to MySQL, the simulated CASE WHEN <col> IS NULL THEN 1 ELSE 0 END, <col> inlines the raw expression. Inside the CASE, MySQL does not apply ORDER BY's alias-first column resolution, so unqualified references collide with same-named columns in joined base tables (error 1052). Resolve the column against the enclosing SELECT projection and substitute the qualified source where one uniquely exists.
|
Hey @brdbry, thank you for the PR. The transpilation bug is legit, but I don't like the solution because it is quite complicated and only solves a single instance of the problem: a simple qualified Shouldn't we replace the |
|
Closing this due to inactivity, feel free to reopen when ready. |
… simulation Generalises the NULLS FIRST/LAST CASE WHEN <expr> IS NULL simulation to replace a bare ORDER BY name with the full sub-AST of the matching SELECT projection (Alias-stripped), not just qualified-Column projections. Fixes MySQL 1052 ambiguity and also handles expression projections aliased to the same name (e.g. SELECT (-1) * col AS col ... ORDER BY col) which the prior fix left unresolved. Per @georgesittas review on tobymao#7641.
|
Hi @georgesittas, sorry for the delay. I pushed a new commit but don't seem to be able to reopen |
|
No worries, happy to continue iterating. Reopened it. |
|
Thanks - how does it look to you now - anything else you'd like to change? |
|
I'll take another look soon. |
georgesittas
left a comment
There was a problem hiding this comment.
Have you verified whether the same bug exists in T-SQL?
… TSQL coverage Per @georgesittas review on tobymao#7641: - Window-ancestor guard removed; ORDER BY-in-window resolves in the same FROM scope, no carve-out needed (all existing window-context simulation tests still pass). - Projection lookup rewritten as a comprehension + uniqueness check. - TSQL test mirrors the MySQL subtests; the base-generator fix already covers it since TSQL has NULL_ORDERING_SUPPORTED = None.
|
thanks @georgesittas, changes pushed. test for TSQL added |
georgesittas
left a comment
There was a problem hiding this comment.
I'll get this in and clean it up. Thanks for the PR!
Problem
When transpiling
ORDER BY <col>from aNULL_ORDERING = "nulls_are_last"source dialect (e.g. DuckDB) into MySQL, the generator emits aCASE WHEN <col> IS NULL THEN 1 ELSE 0 END, <col>shim to simulateNULLS LAST(MySQL has no nativeNULLS FIRST/LASTsyntax). The shim inlines the rawORDER BYexpression verbatim — typically a bare column name.That's fine for a single-table query, but in a multi-table
FROMwhere the same unqualified column name exists in more than one table, MySQL raises:…even when there is an unambiguous
SELECT-list projection. MySQL'sORDER BYdoes apply alias-first column resolution against theSELECTlist — but inside theCASEexpression inORDER BY, that alias-first rule does not apply. The bare reference falls through to base-table resolution and collides.Reproducer
Output today (rejected by MySQL 8.0):
After this PR:
Verified against a running MySQL 8.0 container: the pre-fix SQL errors with
1052, the post-fix SQL succeeds.Fix
New helper
Generator._qualified_for_null_ordering_simulationon the base generator walks from theOrderednode to its enclosingSelectand looks for a single qualified-column projection whoseoutput_namematches the bareORDER BYcolumn. If exactly one is found (whether the projection is plaine.employee_idore.employee_id AS <alias>), the simulation substitutes that qualified column inside both theCASEand the trailing sort key. In every other case (already-qualifiedORDER BY, positional/ordinalORDER BY, expressionORDER BY, no enclosingSelect, ambiguous projection match, inside a window function), the helper returnsNoneand the existing behaviour is preserved.Single change:
sqlglot/generator.py.Test
Added
TestMySQL.test_null_ordering_simulation_qualifies_ambiguous_columnsintests/dialects/test_mysql.pycovering three scenarios: the reproducer (multi-tableLEFT JOIN, unqualifiedORDER BY), an aliased projection (SELECT e.employee_id AS emp ... ORDER BY emp), and an already-qualifiedORDER BY(no change in output).Full suite passes locally (
1107 passed, 18002 subtests passed).Scope / out-of-scope
The fix lives in the base
Generator.ordered_sqlsimulation branch, so any dialect withNULL_ORDERING_SUPPORTED is None(currently MySQL and MSSQL/TSQL) benefits in one place. I've only added a MySQL-focused test and PR title because the reported bug is specifically the MySQL1052symptom; happy to broaden the test coverage to TSQL or any other affected dialect in a follow-up if maintainers want it.The fix is deliberately conservative: it only qualifies when a single qualified-column projection unambiguously matches, and never strips or rewrites already-qualified
ORDER BYreferences. I did not modify the parser to track explicit-vs-implicitNULLordering onOrderednodes (broader ripple) or add any per-dialect toggle.Context
Same area of the codebase as #6655 (UPDATE … FROM → UPDATE … JOIN translation) which I authored earlier this year.