This is an automated email from the ASF dual-hosted git repository.

betodealmeida pushed a commit to branch rls-splice
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/rls-splice by this push:
     new 0fe8293c7f5 Simplify
0fe8293c7f5 is described below

commit 0fe8293c7f5faeb37c8b6df97f446a9cef215641
Author: Beto Dealmeida <[email protected]>
AuthorDate: Fri May 8 14:12:46 2026 -0400

    Simplify
---
 .../controls/CollectionControl/index.tsx           |  4 ++-
 superset/db_engine_specs/base.py                   | 35 +++---------------
 superset/db_engine_specs/couchbase.py              |  2 ++
 superset/db_engine_specs/pinot.py                  |  2 ++
 superset/db_engine_specs/solr.py                   |  2 ++
 superset/utils/rls.py                              |  2 +-
 tests/unit_tests/db_engine_specs/test_base.py      | 42 ++--------------------
 tests/unit_tests/sql/parse_tests.py                |  9 +++--
 8 files changed, 24 insertions(+), 74 deletions(-)

diff --git 
a/superset-frontend/src/explore/components/controls/CollectionControl/index.tsx 
b/superset-frontend/src/explore/components/controls/CollectionControl/index.tsx
index 0ad904731fa..369642f4142 100644
--- 
a/superset-frontend/src/explore/components/controls/CollectionControl/index.tsx
+++ 
b/superset-frontend/src/explore/components/controls/CollectionControl/index.tsx
@@ -188,7 +188,9 @@ function CollectionControl({
   // Two items can collide when keyAccessor returns falsy and the index
   // fallback is used — breaking dnd-kit reordering and React reconciliation.
   // Assign a stable nanoid per item ref when no key is available.
-  const generatedIdsRef = useRef<WeakMap<CollectionItem, string>>(new 
WeakMap());
+  const generatedIdsRef = useRef<WeakMap<CollectionItem, string>>(
+    new WeakMap(),
+  );
   const itemIds = useMemo(
     () =>
       value.map(item => {
diff --git a/superset/db_engine_specs/base.py b/superset/db_engine_specs/base.py
index 78fc2e78afa..a98c87dd227 100644
--- a/superset/db_engine_specs/base.py
+++ b/superset/db_engine_specs/base.py
@@ -557,13 +557,11 @@ class BaseEngineSpec:  # pylint: 
disable=too-many-public-methods
     # if True, database will be listed as option in the upload file form
     supports_file_upload = True
 
-    # Optional override for the RLS method used by ``get_rls_method``. When 
set,
-    # the engine spec opts into a specific strategy regardless of the
-    # ``allows_subqueries`` / ``allows_alias_in_select`` defaults. Use
-    # ``RLSMethod.AS_PREDICATE_SPLICE`` for engines whose sqlglot dialect can
-    # parse but not faithfully regenerate the SQL — splice mode rewrites the
-    # original query string instead of round-tripping through the generator.
-    rls_method: RLSMethod | None = None
+    # RLS strategy for this engine spec. Override in engine-specific classes as
+    # needed (for example ``RLSMethod.AS_PREDICATE`` for engines that don't
+    # support subquery-based RLS, or ``RLSMethod.AS_PREDICATE_SPLICE`` for
+    # engines where sqlglot generation is not faithful).
+    rls_method = RLSMethod.AS_SUBQUERY
 
     # Is the DB engine spec able to change the default schema? This requires 
implementing  # noqa: E501
     # a custom `adjust_engine_params` method.
@@ -626,29 +624,6 @@ class BaseEngineSpec:  # pylint: 
disable=too-many-public-methods
             else cls.encrypted_extra_sensitive_fields
         )
 
-    @classmethod
-    def get_rls_method(cls) -> RLSMethod:
-        """
-        Returns the RLS method to be used for this engine.
-
-        There are three ways to insert RLS: replacing the table with a subquery
-        that has the RLS (safest, but not supported in all databases), 
appending
-        the RLS to the ``WHERE`` clause via AST transformation, or splicing the
-        RLS into the original SQL string (preserves dialect-specific syntax 
that
-        the sqlglot generator would otherwise transpile).
-
-        Engine specs can opt into a specific strategy by setting the 
class-level
-        ``rls_method`` attribute; otherwise the choice falls back to subquery
-        when supported, and predicate otherwise.
-        """
-        if cls.rls_method is not None:
-            return cls.rls_method
-        return (
-            RLSMethod.AS_SUBQUERY
-            if cls.allows_subqueries and cls.allows_alias_in_select
-            else RLSMethod.AS_PREDICATE
-        )
-
     @classmethod
     def is_oauth2_enabled(cls) -> bool:
         return (
diff --git a/superset/db_engine_specs/couchbase.py 
b/superset/db_engine_specs/couchbase.py
index 9f4b4b82256..8e238906390 100644
--- a/superset/db_engine_specs/couchbase.py
+++ b/superset/db_engine_specs/couchbase.py
@@ -35,6 +35,7 @@ from superset.db_engine_specs.base import (
     DatabaseCategory,
 )
 from superset.errors import ErrorLevel, SupersetError, SupersetErrorType
+from superset.sql.parse import RLSMethod
 from superset.utils.network import is_hostname_valid, is_port_open
 
 
@@ -82,6 +83,7 @@ class CouchbaseEngineSpec(BasicParametersMixin, 
BaseEngineSpec):
     default_driver = "couchbase"
     allows_joins = False
     allows_subqueries = False
+    rls_method = RLSMethod.AS_PREDICATE
     sqlalchemy_uri_placeholder = (
         "couchbase://user:password@host[:port]?truststorepath=value?ssl=value"
     )
diff --git a/superset/db_engine_specs/pinot.py 
b/superset/db_engine_specs/pinot.py
index 7942df79efe..b5b671c3451 100644
--- a/superset/db_engine_specs/pinot.py
+++ b/superset/db_engine_specs/pinot.py
@@ -20,6 +20,7 @@ from sqlalchemy.types import TypeEngine
 
 from superset.constants import TimeGrain
 from superset.db_engine_specs.base import BaseEngineSpec, DatabaseCategory
+from superset.sql.parse import RLSMethod
 
 
 class PinotEngineSpec(BaseEngineSpec):
@@ -30,6 +31,7 @@ class PinotEngineSpec(BaseEngineSpec):
     allows_joins = False
     allows_alias_in_select = False
     allows_alias_in_orderby = False
+    rls_method = RLSMethod.AS_PREDICATE
 
     # pinotdb only sets cursor.description when the response contains
     # columnDataTypes, which Pinot omits for zero-row results.
diff --git a/superset/db_engine_specs/solr.py b/superset/db_engine_specs/solr.py
index 03dca6c2b66..4f08e94ecac 100644
--- a/superset/db_engine_specs/solr.py
+++ b/superset/db_engine_specs/solr.py
@@ -16,6 +16,7 @@
 # under the License.
 
 from superset.db_engine_specs.base import BaseEngineSpec, DatabaseCategory
+from superset.sql.parse import RLSMethod
 
 
 class SolrEngineSpec(BaseEngineSpec):  # pylint: disable=abstract-method
@@ -27,6 +28,7 @@ class SolrEngineSpec(BaseEngineSpec):  # pylint: 
disable=abstract-method
     time_groupby_inline = False
     allows_joins = False
     allows_subqueries = False
+    rls_method = RLSMethod.AS_PREDICATE
 
     metadata = {
         "description": "Apache Solr is an open-source enterprise search 
platform.",
diff --git a/superset/utils/rls.py b/superset/utils/rls.py
index 456b589e365..e7b989f349a 100644
--- a/superset/utils/rls.py
+++ b/superset/utils/rls.py
@@ -46,7 +46,7 @@ def apply_rls(
     #   - append the RLS to the ``WHERE`` clause via AST transformation
     #   - splice the RLS into the original SQL string (preserves 
dialect-specific
     #     syntax that the sqlglot generator would otherwise transpile)
-    method = database.db_engine_spec.get_rls_method()
+    method = database.db_engine_spec.rls_method
 
     # In splice mode predicates stay as raw SQL strings and are inserted 
verbatim
     # into the source query — re-parsing them would force a generator 
round-trip
diff --git a/tests/unit_tests/db_engine_specs/test_base.py 
b/tests/unit_tests/db_engine_specs/test_base.py
index 1b58809b3b2..5d64d9f283d 100644
--- a/tests/unit_tests/db_engine_specs/test_base.py
+++ b/tests/unit_tests/db_engine_specs/test_base.py
@@ -1285,42 +1285,6 @@ def 
test_start_oauth2_dance_falls_back_to_url_for(mocker: MockerFixture) -> None
     assert error.extra["redirect_uri"] == fallback_uri
 
 
-def test_get_rls_method_default_subquery() -> None:
-    """
-    By default, an engine that supports subqueries and aliases-in-select
-    uses the safer subquery RLS strategy.
-    """
-
-    class _Spec(BaseEngineSpec):
-        allows_subqueries = True
-        allows_alias_in_select = True
-
-    assert _Spec.get_rls_method() == RLSMethod.AS_SUBQUERY
-
-
-def test_get_rls_method_default_predicate_when_no_subqueries() -> None:
-    """
-    Engines without subquery / alias-in-select support fall back to the
-    AST predicate strategy.
-    """
-
-    class _Spec(BaseEngineSpec):
-        allows_subqueries = False
-        allows_alias_in_select = True
-
-    assert _Spec.get_rls_method() == RLSMethod.AS_PREDICATE
-
-
-def test_get_rls_method_class_attribute_override() -> None:
-    """
-    Setting ``rls_method`` on an engine spec opts the engine into a specific
-    strategy regardless of the subquery/alias defaults — used by engines whose
-    sqlglot dialect can parse but not faithfully regenerate SQL.
-    """
-
-    class _SpliceSpec(BaseEngineSpec):
-        allows_subqueries = True
-        allows_alias_in_select = True
-        rls_method = RLSMethod.AS_PREDICATE_SPLICE
-
-    assert _SpliceSpec.get_rls_method() == RLSMethod.AS_PREDICATE_SPLICE
+def test_default_rls_method_is_subquery() -> None:
+    """Base engine spec defaults to subquery-based RLS."""
+    assert BaseEngineSpec.rls_method == RLSMethod.AS_SUBQUERY
diff --git a/tests/unit_tests/sql/parse_tests.py 
b/tests/unit_tests/sql/parse_tests.py
index 79a9cec6953..a8de0dcbe39 100644
--- a/tests/unit_tests/sql/parse_tests.py
+++ b/tests/unit_tests/sql/parse_tests.py
@@ -2754,14 +2754,17 @@ def 
test_rls_predicate_splice_inserts_before_comments(sql: str, expected: str) -
     "sql, engine, expected",
     [
         (
-            "SELECT * FROM some_table QUALIFY row_number() OVER (PARTITION BY 
id ORDER BY ts DESC) = 1",
+            "SELECT * FROM some_table QUALIFY row_number() OVER "
+            "(PARTITION BY id ORDER BY ts DESC) = 1",
             "snowflake",
-            "SELECT * FROM some_table WHERE tenant_id = 42 QUALIFY 
row_number() OVER (PARTITION BY id ORDER BY ts DESC) = 1",
+            "SELECT * FROM some_table WHERE tenant_id = 42 "
+            "QUALIFY row_number() OVER (PARTITION BY id ORDER BY ts DESC) = 1",
         ),
         (
             "SELECT sum(v) OVER () FROM some_table WINDOW w AS (PARTITION BY 
id)",
             "postgresql",
-            "SELECT sum(v) OVER () FROM some_table WHERE tenant_id = 42 WINDOW 
w AS (PARTITION BY id)",
+            "SELECT sum(v) OVER () FROM some_table WHERE tenant_id = 42 "
+            "WINDOW w AS (PARTITION BY id)",
         ),
     ],
 )

Reply via email to