You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@superset.apache.org by "betodealmeida (via GitHub)" <gi...@apache.org> on 2025/05/16 15:47:06 UTC

[PR] feat: use sqlglot to set limit [superset]

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

   <!---
   Please write the PR title following the conventions at https://www.conventionalcommits.org/en/v1.0.0/
   Example:
   fix(dashboard): load charts correctly
   -->
   
   ### SUMMARY
   <!--- Describe the change below, including rationale and design decisions -->
   
   Part of https://github.com/apache/superset/issues/26786, stacked on https://github.com/apache/superset/pull/33457.
   
   This PR moves the logic of setting the limit in a query to use `sqlglot`, adding the `set_limit_value(limit: int)` method to the `BaseSQLStatement` class.
   
   The method **always** sets the requested limit. It's up to the application to determine if a bigger limit should be forced or not. Before, it had some logic that determined if the limit should be updated or not depending on if the existing limit was smaller than the desired limit. This keeps the method simpler and decoupled from business logic.
   
   One of the main advantages of using `sqlglot` here is that it abstracts all the way a query can be limited:
   
   ```sql
   SELECT * FROM t LIMIT 10;  -- Postgres, Clickhouse, Bigquery, etc.
   SELECT TOP 10 * FROM t;  -- Teradata, MS SQL, Vertica, etc.
   SELECT * FROM t FETCH FIRST 10 ROWS ONLY;  -- Oracle, DB2, Apache Derby
   ```
   
   These are all abstract as a `sqlglot.exp.Limit`, and rendered correctly when the engine is specified (see unit tests).
   
   Previously there was additional logic for Teradata that took in consideration the `SAMPLE` function:
   
   ```sql
   SELECT * FROM t SAMPLE n  -- returns n rows from t
   ```
   
   I removed this logic because `SAMPLE` is not a limit — it limits the number of rows fetched from the table, but **before the projection is applied**. The projection could have a UDTF that generates millions of rows even if only 10 were read from the table. Because of that, I removed the logic. (The tests were using invalid syntax, so I'm not even sure if this feature ever worked!)
   
   ### BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF
   <!--- Skip this if not applicable -->
   
   ### TESTING INSTRUCTIONS
   <!--- Required! What steps can be taken to manually verify the changes? -->
   
   Added tests, and moved old tests to the new pattern.
   
   ### ADDITIONAL INFORMATION
   <!--- Check any relevant boxes with "x" -->
   <!--- HINT: Include "Fixes #nnn" if you are fixing an existing issue -->
   - [ ] Has associated issue:
   - [ ] Required feature flags:
   - [ ] Changes UI
   - [ ] Includes DB Migration (follow approval process in [SIP-59](https://github.com/apache/superset/issues/13351))
     - [ ] Migration is atomic, supports rollback & is backwards-compatible
     - [ ] Confirm DB migration upgrade and downgrade tested
     - [ ] Runtime estimates and downtime expectations provided
   - [ ] Introduces new feature or API
   - [ ] Removes existing feature or API
   


-- 
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: notifications-unsubscribe@superset.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org


Re: [PR] feat: use sqlglot to set limit [superset]

Posted by "betodealmeida (via GitHub)" <gi...@apache.org>.
betodealmeida commented on code in PR #33473:
URL: https://github.com/apache/superset/pull/33473#discussion_r2093301877


##########
superset/sql/parse.py:
##########
@@ -767,6 +781,33 @@ def get_limit_value(self) -> int | None:
 
         return None
 
+    def set_limit_value(self, limit: int) -> None:

Review Comment:
   It's been fun implementing the logic in KQL, but I'm considering writing a SIP to get rid of it, since it's not SQL and double the work.



-- 
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: notifications-unsubscribe@superset.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org


Re: [PR] feat: use sqlglot to set limit [superset]

Posted by "betodealmeida (via GitHub)" <gi...@apache.org>.
betodealmeida commented on code in PR #33473:
URL: https://github.com/apache/superset/pull/33473#discussion_r2093297376


##########
superset/db_engine_specs/teradata.py:
##########
@@ -15,19 +15,16 @@
 # specific language governing permissions and limitations
 # under the License.
 
-from superset.db_engine_specs.base import BaseEngineSpec, LimitMethod
+from superset.db_engine_specs.base import BaseEngineSpec
 
 
 class TeradataEngineSpec(BaseEngineSpec):
     """Dialect for Teradata DB."""
 
     engine = "teradatasql"
     engine_name = "Teradata"
-    limit_method = LimitMethod.WRAP_SQL

Review Comment:
   Ditto here.



-- 
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: notifications-unsubscribe@superset.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org


Re: [PR] feat: use sqlglot to set limit [superset]

Posted by "betodealmeida (via GitHub)" <gi...@apache.org>.
betodealmeida commented on code in PR #33473:
URL: https://github.com/apache/superset/pull/33473#discussion_r2093302910


##########
tests/unit_tests/db_engine_specs/test_teradata.py:
##########
@@ -1,43 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-# pylint: disable=unused-argument, import-outside-toplevel, protected-access
-import pytest
-
-
-@pytest.mark.parametrize(
-    "limit,original,expected",
-    [
-        (100, "SEL TOP 1000 * FROM My_table", "SEL TOP 100 * FROM My_table"),
-        (100, "SEL TOP 1000 * FROM My_table;", "SEL TOP 100 * FROM My_table"),
-        (10000, "SEL TOP 1000 * FROM My_table;", "SEL TOP 1000 * FROM My_table"),
-        (1000, "SEL TOP 1000 * FROM My_table;", "SEL TOP 1000 * FROM My_table"),
-        (100, "SELECT TOP 1000 * FROM My_table", "SELECT TOP 100 * FROM My_table"),
-        (100, "SEL SAMPLE 1000 * FROM My_table", "SEL SAMPLE 100 * FROM My_table"),
-        (10000, "SEL SAMPLE 1000 * FROM My_table", "SEL SAMPLE 1000 * FROM My_table"),

Review Comment:
   This is not even valid syntax... `SAMPLE` goes at the end, not in the projection! The correct would be:
   
   ```sql
   SELECT * FROM My_table SAMPLE 1000
   ```



-- 
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: notifications-unsubscribe@superset.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org


Re: [PR] feat: use sqlglot to set limit [superset]

Posted by "betodealmeida (via GitHub)" <gi...@apache.org>.
betodealmeida merged PR #33473:
URL: https://github.com/apache/superset/pull/33473


-- 
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: notifications-unsubscribe@superset.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org


Re: [PR] feat: use sqlglot to set limit [superset]

Posted by "betodealmeida (via GitHub)" <gi...@apache.org>.
betodealmeida commented on code in PR #33473:
URL: https://github.com/apache/superset/pull/33473#discussion_r2093302910


##########
tests/unit_tests/db_engine_specs/test_teradata.py:
##########
@@ -1,43 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-# pylint: disable=unused-argument, import-outside-toplevel, protected-access
-import pytest
-
-
-@pytest.mark.parametrize(
-    "limit,original,expected",
-    [
-        (100, "SEL TOP 1000 * FROM My_table", "SEL TOP 100 * FROM My_table"),
-        (100, "SEL TOP 1000 * FROM My_table;", "SEL TOP 100 * FROM My_table"),
-        (10000, "SEL TOP 1000 * FROM My_table;", "SEL TOP 1000 * FROM My_table"),
-        (1000, "SEL TOP 1000 * FROM My_table;", "SEL TOP 1000 * FROM My_table"),
-        (100, "SELECT TOP 1000 * FROM My_table", "SELECT TOP 100 * FROM My_table"),
-        (100, "SEL SAMPLE 1000 * FROM My_table", "SEL SAMPLE 100 * FROM My_table"),
-        (10000, "SEL SAMPLE 1000 * FROM My_table", "SEL SAMPLE 1000 * FROM My_table"),

Review Comment:
   This is not even valid syntax... `SAMPLE` goes at the end, not in the projection!



-- 
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: notifications-unsubscribe@superset.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org


Re: [PR] feat: use sqlglot to set limit [superset]

Posted by "betodealmeida (via GitHub)" <gi...@apache.org>.
betodealmeida commented on code in PR #33473:
URL: https://github.com/apache/superset/pull/33473#discussion_r2093303619


##########
tests/unit_tests/db_engine_specs/test_mssql.py:
##########
@@ -254,36 +254,6 @@ def test_cte_query_parsing(original: TypeEngine, expected: str) -> None:
     assert actual == expected
 
 
-@pytest.mark.parametrize(
-    "original,expected,top",
-    [
-        ("SEL TOP 1000 * FROM My_table", "SEL TOP 100 * FROM My_table", 100),
-        ("SEL TOP 1000 * FROM My_table;", "SEL TOP 100 * FROM My_table", 100),
-        ("SEL TOP 1000 * FROM My_table;", "SEL TOP 1000 * FROM My_table", 10000),
-        ("SEL TOP 1000 * FROM My_table;", "SEL TOP 1000 * FROM My_table", 1000),

Review Comment:
   This is invalid syntax... MS SQL doesn't support the `SEL` shortcut for `SELECT`!



-- 
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: notifications-unsubscribe@superset.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org


Re: [PR] feat: use sqlglot to set limit [superset]

Posted by "betodealmeida (via GitHub)" <gi...@apache.org>.
betodealmeida commented on code in PR #33473:
URL: https://github.com/apache/superset/pull/33473#discussion_r2093300060


##########
superset/sql/parse.py:
##########
@@ -412,34 +427,12 @@ def format(self, comments: bool = True) -> str:
         """
         Pretty-format the SQL statement.
         """
-        if self._dialect:
-            try:
-                write = Dialect.get_or_raise(self._dialect)
-                return write.generate(
-                    self._parsed,
-                    copy=False,
-                    comments=comments,
-                    pretty=True,
-                )
-            except ValueError:
-                pass
-
-        return self._fallback_formatting()
-
-    @deprecated(deprecated_in="4.0")

Review Comment:
   This was supposed to be removed in 5.0 and we missed the window, but it doesn't break anything other than not being able to make queries pretty when they don't have a `sqlglot` dialect.
   
   We **need** to remove this in order to modify the `sqlglot` AST inplace.



-- 
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: notifications-unsubscribe@superset.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org


Re: [PR] feat: use sqlglot to set limit [superset]

Posted by "betodealmeida (via GitHub)" <gi...@apache.org>.
betodealmeida commented on code in PR #33473:
URL: https://github.com/apache/superset/pull/33473#discussion_r2093301877


##########
superset/sql/parse.py:
##########
@@ -767,6 +781,33 @@ def get_limit_value(self) -> int | None:
 
         return None
 
+    def set_limit_value(self, limit: int) -> None:

Review Comment:
   It's been fun implementing the logic in KQL, but I'm considering writing a SIP to get rid of it, since it's not SQL and doubles the work.



-- 
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: notifications-unsubscribe@superset.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org


Re: [PR] feat: use sqlglot to set limit [superset]

Posted by "betodealmeida (via GitHub)" <gi...@apache.org>.
betodealmeida commented on code in PR #33473:
URL: https://github.com/apache/superset/pull/33473#discussion_r2093298182


##########
superset/models/core.py:
##########
@@ -762,11 +762,19 @@ def select_star(  # pylint: disable=too-many-arguments
             )
 
     def apply_limit_to_sql(
-        self, sql: str, limit: int = 1000, force: bool = False
+        self,
+        sql: str,
+        limit: int = 1000,
+        force: bool = False,
     ) -> str:
-        if self.db_engine_spec.allow_limit_clause:
-            return self.db_engine_spec.apply_limit_to_sql(sql, limit, self, force=force)
-        return self.db_engine_spec.apply_top_to_sql(sql, limit)
+        script = SQLScript(sql, self.db_engine_spec.engine)
+        statement = script.statements[-1]
+        current_limit = statement.get_limit_value() or float("inf")
+
+        if limit < current_limit or force:

Review Comment:
   Moved the limit application logic here.



-- 
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: notifications-unsubscribe@superset.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org


Re: [PR] feat: use sqlglot to set limit [superset]

Posted by "betodealmeida (via GitHub)" <gi...@apache.org>.
betodealmeida commented on code in PR #33473:
URL: https://github.com/apache/superset/pull/33473#discussion_r2093296438


##########
superset/db_engine_specs/kusto.py:
##########
@@ -105,7 +106,6 @@ def convert_dttm(
 
 
 class KustoKqlEngineSpec(BaseEngineSpec):  # pylint: disable=abstract-method
-    limit_method = LimitMethod.WRAP_SQL

Review Comment:
   Kusto supports limit, no need to wrap the query.



-- 
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: notifications-unsubscribe@superset.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org


Re: [PR] feat: use sqlglot to set limit [superset]

Posted by "betodealmeida (via GitHub)" <gi...@apache.org>.
betodealmeida commented on code in PR #33473:
URL: https://github.com/apache/superset/pull/33473#discussion_r2093296936


##########
superset/db_engine_specs/mssql.py:
##########
@@ -52,10 +52,8 @@
 class MssqlEngineSpec(BaseEngineSpec):
     engine = "mssql"
     engine_name = "Microsoft SQL Server"
-    limit_method = LimitMethod.WRAP_SQL

Review Comment:
   MS SQL supports limit, no need to wrap the query.



-- 
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: notifications-unsubscribe@superset.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org