You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by hu...@apache.org on 2023/04/28 17:23:11 UTC

[superset] 01/01: move dynamic schema to Postgres class

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

hugh pushed a commit to branch fix-connect-eng
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 3d612d438d0ee6f46775b2f97a05a5e83e97ef2c
Author: hughhhh <hu...@gmail.com>
AuthorDate: Fri Apr 28 12:22:52 2023 -0500

    move dynamic schema to Postgres class
---
 superset/db_engine_specs/postgres.py | 95 ++++++++++++++++++++++++------------
 1 file changed, 64 insertions(+), 31 deletions(-)

diff --git a/superset/db_engine_specs/postgres.py b/superset/db_engine_specs/postgres.py
index 99a927541e..7364965cba 100644
--- a/superset/db_engine_specs/postgres.py
+++ b/superset/db_engine_specs/postgres.py
@@ -95,7 +95,6 @@ class PostgresBaseEngineSpec(BaseEngineSpec):
     engine = ""
     engine_name = "PostgreSQL"
 
-    supports_dynamic_schema = True
     supports_catalog = True
 
     _time_grain_expressions = {
@@ -167,25 +166,6 @@ class PostgresBaseEngineSpec(BaseEngineSpec):
         ),
     }
 
-    @classmethod
-    def adjust_engine_params(
-        cls,
-        uri: URL,
-        connect_args: Dict[str, Any],
-        catalog: Optional[str] = None,
-        schema: Optional[str] = None,
-    ) -> Tuple[URL, Dict[str, Any]]:
-        if not schema:
-            return uri, connect_args
-
-        options = parse_options(connect_args)
-        options["search_path"] = schema
-        connect_args["options"] = " ".join(
-            f"-c{key}={value}" for key, value in options.items()
-        )
-
-        return uri, connect_args
-
     @classmethod
     def get_schema_from_engine_params(
         cls,
@@ -205,17 +185,19 @@ class PostgresBaseEngineSpec(BaseEngineSpec):
         to determine the schema for a non-qualified table in a query. In cases like
         that we raise an exception.
         """
-        options = parse_options(connect_args)
-        if search_path := options.get("search_path"):
-            schemas = search_path.split(",")
-            if len(schemas) > 1:
-                raise Exception(
-                    "Multiple schemas are configured in the search path, which means "
-                    "Superset is unable to determine the schema of unqualified table "
-                    "names and enforce permissions."
-                )
-            return schemas[0]
-
+        options = re.split(r"-c\s?", connect_args.get("options", ""))
+        for option in options:
+            if "=" not in option:
+                continue
+            key, value = option.strip().split("=", 1)
+            if key.strip() == "search_path":
+                if "," in value:
+                    raise Exception(
+                        "Multiple schemas are configured in the search path, which means "
+                        "Superset is unable to determine the schema of unqualified table "
+                        "names and enforce permissions."
+                    )
+                return value.strip()
         return None
 
     @classmethod
@@ -268,6 +250,57 @@ class PostgresEngineSpec(PostgresBaseEngineSpec, BasicParametersMixin):
         ),
     )
 
+    @classmethod
+    def get_schema_from_engine_params(
+        cls,
+        sqlalchemy_uri: URL,
+        connect_args: Dict[str, Any],
+    ) -> Optional[str]:
+        """
+        Return the configured schema.
+
+        While Postgres doesn't support connecting directly to a given schema, it allows
+        users to specify a "search path" that is used to resolve non-qualified table
+        names; this can be specified in the database ``connect_args``.
+
+        One important detail is that the search path can be a comma separated list of
+        schemas. While this is supported by the SQLAlchemy dialect, it shouldn't be used
+        in Superset because it breaks schema-level permissions, since it's impossible
+        to determine the schema for a non-qualified table in a query. In cases like
+        that we raise an exception.
+        """
+        options = parse_options(connect_args)
+        if search_path := options.get("search_path"):
+            schemas = search_path.split(",")
+            if len(schemas) > 1:
+                raise Exception(
+                    "Multiple schemas are configured in the search path, which means "
+                    "Superset is unable to determine the schema of unqualified table "
+                    "names and enforce permissions."
+                )
+            return schemas[0]
+
+        return None
+
+    @classmethod
+    def adjust_engine_params(
+        cls,
+        uri: URL,
+        connect_args: Dict[str, Any],
+        catalog: Optional[str] = None,
+        schema: Optional[str] = None,
+    ) -> Tuple[URL, Dict[str, Any]]:
+        if not schema:
+            return uri, connect_args
+
+        options = parse_options(connect_args)
+        options["search_path"] = schema
+        connect_args["options"] = " ".join(
+            f"-c{key}={value}" for key, value in options.items()
+        )
+
+        return uri, connect_args
+
     @classmethod
     def get_allow_cost_estimate(cls, extra: Dict[str, Any]) -> bool:
         return True