You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by be...@apache.org on 2023/03/22 20:40:22 UTC

[superset] branch master updated: feat(DB engine spec): `get_catalog_names` (#23447)

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

beto pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 8588f81855 feat(DB engine spec): `get_catalog_names` (#23447)
8588f81855 is described below

commit 8588f81855b926876b65e7481f163e0ee47d8bff
Author: Beto Dealmeida <ro...@dealmeida.net>
AuthorDate: Wed Mar 22 13:40:14 2023 -0700

    feat(DB engine spec): `get_catalog_names` (#23447)
---
 superset/db_engine_specs/base.py                   | 22 ++++++++++++++++++++++
 superset/db_engine_specs/postgres.py               | 22 ++++++++++++++++++++++
 .../db_engine_specs/postgres_tests.py              | 18 ++++++++++++++++++
 3 files changed, 62 insertions(+)

diff --git a/superset/db_engine_specs/base.py b/superset/db_engine_specs/base.py
index b8b1662057..f1a46ceb34 100644
--- a/superset/db_engine_specs/base.py
+++ b/superset/db_engine_specs/base.py
@@ -374,6 +374,14 @@ class BaseEngineSpec:  # pylint: disable=too-many-public-methods
     # a custom `adjust_engine_params` method.
     supports_dynamic_schema = False
 
+    # Does the DB support catalogs? A catalog here is a group of schemas, and has
+    # different names depending on the DB: BigQuery calles it a "project", Postgres calls
+    # it a "database", Trino calls it a "catalog", etc.
+    supports_catalog = False
+
+    # Can the catalog be changed on a per-query basis?
+    supports_dynamic_catalog = False
+
     @classmethod
     def supports_url(cls, url: URL) -> bool:
         """
@@ -1091,6 +1099,20 @@ class BaseEngineSpec:  # pylint: disable=too-many-public-methods
         TODO: Improve docstring and refactor implementation in Hive
         """
 
+    @classmethod
+    def get_catalog_names(  # pylint: disable=unused-argument
+        cls,
+        database: Database,
+        inspector: Inspector,
+    ) -> List[str]:
+        """
+        Get all catalogs from database.
+
+        This needs to be implemented per database, since SQLAlchemy doesn't offer an
+        abstraction.
+        """
+        return []
+
     @classmethod
     def get_schema_names(cls, inspector: Inspector) -> List[str]:
         """
diff --git a/superset/db_engine_specs/postgres.py b/superset/db_engine_specs/postgres.py
index fac0b1b1d0..e52e69d7e8 100644
--- a/superset/db_engine_specs/postgres.py
+++ b/superset/db_engine_specs/postgres.py
@@ -23,6 +23,7 @@ from typing import Any, Dict, List, Optional, Pattern, Set, Tuple, TYPE_CHECKING
 from flask_babel import gettext as __
 from sqlalchemy.dialects.postgresql import DOUBLE_PRECISION, ENUM, JSON
 from sqlalchemy.dialects.postgresql.base import PGInspector
+from sqlalchemy.engine.reflection import Inspector
 from sqlalchemy.engine.url import URL
 from sqlalchemy.types import Date, DateTime, String
 
@@ -291,6 +292,27 @@ class PostgresEngineSpec(PostgresBaseEngineSpec, BasicParametersMixin):
     ) -> List[Dict[str, str]]:
         return [{k: str(v) for k, v in row.items()} for row in raw_cost]
 
+    @classmethod
+    def get_catalog_names(
+        cls,
+        database: "Database",
+        inspector: Inspector,
+    ) -> List[str]:
+        """
+        Return all catalogs.
+
+        In Postgres, a catalog is called a "database".
+        """
+        return sorted(
+            catalog
+            for (catalog,) in inspector.bind.execute(
+                """
+SELECT datname FROM pg_database
+WHERE datistemplate = false;
+            """
+            ).fetchall()
+        )
+
     @classmethod
     def get_table_names(
         cls, database: "Database", inspector: PGInspector, schema: Optional[str]
diff --git a/tests/integration_tests/db_engine_specs/postgres_tests.py b/tests/integration_tests/db_engine_specs/postgres_tests.py
index a6145432c2..44fb1c0474 100644
--- a/tests/integration_tests/db_engine_specs/postgres_tests.py
+++ b/tests/integration_tests/db_engine_specs/postgres_tests.py
@@ -17,6 +17,7 @@
 from textwrap import dedent
 from unittest import mock
 
+from flask.ctx import AppContext
 from sqlalchemy import column, literal_column
 from sqlalchemy.dialects import postgresql
 
@@ -24,6 +25,7 @@ from superset.db_engine_specs import load_engine_specs
 from superset.db_engine_specs.postgres import PostgresEngineSpec
 from superset.errors import ErrorLevel, SupersetError, SupersetErrorType
 from superset.models.sql_lab import Query
+from superset.utils.database import get_example_database
 from tests.integration_tests.db_engine_specs.base_tests import TestDbEngineSpec
 from tests.integration_tests.fixtures.certificates import ssl_certificate
 from tests.integration_tests.fixtures.database import default_db_extra
@@ -514,3 +516,19 @@ def test_base_parameters_mixin():
         },
         "required": ["database", "host", "port", "username"],
     }
+
+
+def test_get_catalog_names(app_context: AppContext) -> None:
+    """
+    Test the ``get_catalog_names`` method.
+    """
+    database = get_example_database()
+
+    if database.backend != "postgresql":
+        return
+
+    with database.get_inspector_with_context() as inspector:
+        assert PostgresEngineSpec.get_catalog_names(database, inspector) == [
+            "postgres",
+            "superset",
+        ]