eschutho opened a new pull request, #42899:
URL: https://github.com/apache/superset/pull/42899

   ### SUMMARY
   
   Drill to Detail pagination fails on **Trino/Presto**: navigating to page 2 
of the results produces a query that Trino rejects with a syntax error:
   
   ```
   line 7:11: mismatched input 'OFFSET'. Expecting: <EOF>
   ```
   
   This was reported when using Trino as the query engine. The same query runs 
fine in SQL Lab, because SQL Lab never emits an `OFFSET` — only the automated 
pagination path does.
   
   **Problem**
   
   Superset builds paginated queries with SQLAlchemy's dialect-aware 
construction (`qry.limit(row_limit).offset(row_offset)` in `models/helpers.py`) 
and compiles them in `Database.compile_sqla_query`. In SQLAlchemy the 
*compiled* clause order is decided entirely by the driver's dialect 
(`limit_clause`), not by the order the methods are called.
   
   Trino and Presto require `OFFSET` to appear **before** `LIMIT` (unlike the 
ANSI `LIMIT ... OFFSET` ordering). Whether Superset emits valid SQL therefore 
depends on which driver the connection resolves to:
   
   - The official `trino` package and the legacy `sqlalchemy-trino` package 
both override `limit_clause` to emit `OFFSET ... LIMIT` → correct.
   - **PyHive's** Presto/Trino dialects (`PrestoCompiler` / `TrinoCompiler`) do 
**not** override `limit_clause`; they inherit SQLAlchemy's ANSI `LIMIT ... 
OFFSET` → **rejected by Presto/Trino**.
   
   So any connection that resolves to a PyHive dialect — including any vanilla 
`presto://` connection on stock Superset — produces invalid SQL as soon as an 
offset is applied (Drill to Detail page 2+, server-side pagination, etc.).
   
   Verified directly:
   
   ```python
   >>> from sqlalchemy import table, column, select
   >>> from pyhive.sqlalchemy_presto import PrestoDialect
   >>> q = select([table('t', column('a')).c.a]).limit(50).offset(50)
   >>> str(q.compile(dialect=PrestoDialect(), compile_kwargs={'literal_binds': 
True}))
   'SELECT "t"."a" FROM "t" LIMIT 50 OFFSET 50'   # ← invalid for Presto/Trino
   ```
   
   **Fix**
   
   Guarantee the ordering in Superset rather than depending on the driver:
   
   - Add an `offset_before_limit` engine-spec flag (default `False`, set to 
`True` on `PrestoBaseEngineSpec`, inherited by both Presto and Trino).
   - Add `BaseEngineSpec.apply_offset_before_limit(sql)`, which re-renders the 
statement through sqlglot's dialect-aware generator (which orders `OFFSET` 
before `LIMIT` for these dialects). A cheap textual gate means it only runs 
when the flag is set **and** the SQL is actually in the invalid order, so 
drivers that already emit `OFFSET` first are left byte-for-byte untouched.
   - Call it once at the end of `Database.compile_sqla_query`, the single choke 
point where datasource queries are turned into SQL.
   
   This fixes every automated-offset path (Drill to Detail, samples, server 
pagination) for Presto and Trino, independent of the installed SQLAlchemy 
driver.
   
   ### BEFORE/AFTER
   
   **Before** (page 2, PyHive-backed Trino/Presto): `... LIMIT 50 OFFSET 50` → 
`mismatched input 'OFFSET'`.
   
   **After**: `... OFFSET 50 LIMIT 50` → valid, page 2 loads.
   
   ### TESTING INSTRUCTIONS
   
   1. Connect a Trino (or Presto) database whose connection uses a driver that 
emits ANSI `LIMIT ... OFFSET` (e.g. PyHive).
   2. Open any chart → Drill to Detail → go to page 2 of the results.
   3. Before this change the query fails with `mismatched input 'OFFSET'`; 
after it, the page loads.
   
   Automated coverage added:
   
   - `tests/unit_tests/db_engine_specs/test_trino.py` — 
`apply_offset_before_limit` reorders ANSI ordering, leaves 
already-correct/limit-only SQL untouched, and is a no-op for engines that don't 
set the flag.
   - `tests/unit_tests/models/core_test.py` — end-to-end through 
`compile_sqla_query`, driving a Trino `Database` backed by a dialect that emits 
ANSI ordering and asserting the compiled SQL puts `OFFSET` before `LIMIT`.
   
   ```
   pytest tests/unit_tests/db_engine_specs/test_trino.py 
tests/unit_tests/db_engine_specs/test_presto.py \
          tests/unit_tests/db_engine_specs/test_base.py 
tests/unit_tests/models/core_test.py
   ```
   
   ### ADDITIONAL INFORMATION
   
   - [ ] Has associated issue:
   - [ ] Required feature flags:
   - [ ] Changes UI
   - [ ] Includes DB Migration (follow approval process in 
[SIP-59](https://github.com/apache/superset/issues/13351))
   - [ ] Introduces new feature or API
   - [ ] Removes existing feature or API
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to