viet-nv commented on code in PR #43578:
URL: https://github.com/apache/superset/pull/43578#discussion_r3869448998


##########
superset/sql/parse.py:
##########
@@ -1427,11 +1450,17 @@ def set_limit_value(
         if not isinstance(self._parsed, exp.Query):
             return
 
-        if method == LimitMethod.FORCE_LIMIT:
+        # A ClickHouse `LIMIT ... BY` occupies the very `limit`/`offset` slot 
that
+        # `FORCE_LIMIT` overwrites, so forcing a row cap in place would drop 
the
+        # `BY` grouping and silently change what the query returns. The cap 
can't
+        # be appended alongside it either -- sqlglot rejects ClickHouse's 
native
+        # `LIMIT n BY x LIMIT m` with "Found multiple 'LIMIT' clauses" -- so it
+        # goes on a wrapping query instead, exactly as `WRAP_SQL` does.
+        if method == LimitMethod.FORCE_LIMIT and not self._has_limit_by():
             self._parsed.args["limit"] = exp.Limit(
                 expression=exp.Literal(this=str(limit), is_string=False)
             )
-        elif method == LimitMethod.WRAP_SQL:
+        elif method in {LimitMethod.FORCE_LIMIT, LimitMethod.WRAP_SQL}:
             self._parsed = exp.Select(

Review Comment:
   Thanks — I checked this against a live ClickHouse (26.9.1.52) rather than 
reasoning about it, and `WITH TOTALS` specifically is safe. The docs are 
explicit that it works in subqueries ("You can use `WITH TOTALS` in subqueries, 
including subqueries in the JOIN clause"), and empirically the wrapped form and 
ClickHouse's own `LIMIT n BY x LIMIT m` return byte-identical responses for a 
`GROUP BY … WITH TOTALS … LIMIT 2 BY k` query — same rows, same `"totals"` 
block, same `rows_before_limit_at_least`. Same for `WITH ROLLUP`/`WITH CUBE`, 
which are just extra rows in the data stream, and for inner `ORDER BY`. I've 
added a `WITH TOTALS` + `LIMIT BY` test to pin that down.
   
   Your underlying point was right, though — just not about that clause. Two 
modifiers sqlglot models genuinely aren't subquery-safe, and 83cab07 fixes both 
by preserving them at the outer level as you suggested:
   - **`FORMAT`** was a real bug this PR introduced: `SELECT * FROM (… LIMIT 2 
BY a FORMAT JSON) LIMIT 1001` is a syntax error in ClickHouse.
   - **`SETTINGS`** was silently rescoped — a `SETTINGS` on a subquery binds to 
that subquery only (verified: `SELECT * FROM (SELECT 
getSetting('max_block_size') SETTINGS max_block_size=777)` returns `777`), so 
top-level-only settings like `extremes` would have stopped applying.
   
   Both now ride on the wrapper, which is where the original query had them. 
Row-producing modifiers stay inside, since hoisting those would change the 
result rather than preserve it.
   
   On avoiding the rewrite entirely: I did try emitting ClickHouse's native 
`LIMIT n BY x LIMIT m` (sqlglot *can* generate it via a nested 
`exp.Limit(this=…)`, and it runs fine). It's a dead end because sqlglot can't 
parse it back — `Found multiple 'LIMIT' clauses` — and Superset re-parses its 
own `executed_sql` in the CSV export, streaming export, and `raise_for_access` 
paths, so that form would break all three. Worth an upstream sqlglot fix, but 
not something this PR can depend on.



-- 
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