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

rusackas 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 a114b4cc468 fix(import): skip redundant has_table check when dataset 
import has no data URI (#43808)
a114b4cc468 is described below

commit a114b4cc468347cd4dfae962b4e4d635f11165b6
Author: Kunal <[email protected]>
AuthorDate: Wed Sep 9 01:57:40 2026 +0530

    fix(import): skip redundant has_table check when dataset import has no data 
URI (#43808)
    
    Co-authored-by: Kunal8954 <[email protected]>
    Co-authored-by: Claude Opus 5 <[email protected]>
---
 superset/commands/dataset/importers/v1/utils.py    |  34 +++--
 .../datasets/commands/importers/v1/import_test.py  | 142 +++++++++++++++++++++
 2 files changed, 164 insertions(+), 12 deletions(-)

diff --git a/superset/commands/dataset/importers/v1/utils.py 
b/superset/commands/dataset/importers/v1/utils.py
index 844ac02b8f2..39dd81b476c 100644
--- a/superset/commands/dataset/importers/v1/utils.py
+++ b/superset/commands/dataset/importers/v1/utils.py
@@ -555,19 +555,29 @@ def import_dataset(  # noqa: C901
         except SupersetSecurityException as ex:
             raise DatasetAccessDeniedError() from ex
 
-    try:
-        table_exists = dataset.database.has_table(
-            Table(dataset.table_name, dataset.schema, dataset.catalog),
-        )
-    except Exception:  # pylint: disable=broad-except
-        # MySQL doesn't play nice with GSheets table names
-        logger.warning(
-            "Couldn't check if table %s exists, assuming it does", 
dataset.table_name
-        )
-        table_exists = True
+    # `has_table` opens a live connection to the target database to run a
+    # schema-introspection query. Its result is only ever consulted below to
+    # decide whether to call `load_data`, which itself is a no-op unless
+    # `data_uri` is set - so for imports that don't carry inline data (the
+    # common case when bulk-importing dataset *metadata*, e.g. hundreds of
+    # datasets at once), this was an unconditional, unnecessary round trip
+    # to every target database on every single dataset, and a major
+    # contributor to bulk imports timing out.
+    if data_uri:
+        try:
+            table_exists = dataset.database.has_table(
+                Table(dataset.table_name, dataset.schema, dataset.catalog),
+            )
+        except Exception:  # pylint: disable=broad-except
+            # MySQL doesn't play nice with GSheets table names
+            logger.warning(
+                "Couldn't check if table %s exists, assuming it does",
+                dataset.table_name,
+            )
+            table_exists = True
 
-    if data_uri and (not table_exists or force_data):
-        load_data(data_uri, dataset, dataset.database)
+        if not table_exists or force_data:
+            load_data(data_uri, dataset, dataset.database)
 
     if user:
         from superset.subjects.utils import get_user_subject
diff --git a/tests/unit_tests/datasets/commands/importers/v1/import_test.py 
b/tests/unit_tests/datasets/commands/importers/v1/import_test.py
index c3e65bb1055..bebc5687527 100644
--- a/tests/unit_tests/datasets/commands/importers/v1/import_test.py
+++ b/tests/unit_tests/datasets/commands/importers/v1/import_test.py
@@ -773,6 +773,148 @@ def test_import_dataset_no_folder(mocker: MockerFixture, 
session: Session) -> No
     assert sqla_table.folders is None
 
 
+def test_import_dataset_skips_has_table_check_without_data_uri(
+    mocker: MockerFixture, session: Session
+) -> None:
+    """
+    Importing a dataset with no ``data`` URI should never call
+    ``Database.has_table`` - its result only ever gates ``load_data``, which
+    is already a no-op when there's no data URI to load. Skipping the call
+    avoids an unnecessary round trip to the target database on every
+    imported dataset, which is what made bulk imports of many datasets slow
+    enough to hit the gunicorn worker timeout.
+    """
+    mocker.patch.object(security_manager, "can_access", return_value=True)
+    has_table = mocker.patch.object(Database, "has_table")
+
+    engine = db.session.get_bind()
+    SqlaTable.metadata.create_all(engine)  # pylint: disable=no-member
+
+    database = Database(database_name="my_database", 
sqlalchemy_uri="sqlite://")
+    db.session.add(database)
+    db.session.flush()
+
+    config = {
+        "table_name": "no_data_table",
+        "main_dttm_col": None,
+        "description": None,
+        "default_endpoint": None,
+        "offset": 0,
+        "cache_timeout": None,
+        "schema": None,
+        "sql": None,
+        "params": None,
+        "template_params": None,
+        "filter_select_enabled": False,
+        "fetch_values_predicate": None,
+        "extra": None,
+        "uuid": uuid.uuid4(),
+        "metrics": [],
+        "columns": [],
+        "database_uuid": database.uuid,
+        "database_id": database.id,
+    }
+
+    import_dataset(config)
+
+    has_table.assert_not_called()
+
+
+def test_import_dataset_checks_has_table_with_data_uri(
+    mocker: MockerFixture, session: Session
+) -> None:
+    """
+    When a ``data`` URI is present, ``Database.has_table`` should still be
+    consulted to decide whether the data needs to be (re-)loaded. A table that
+    already exists means the data is there, so it must not be re-loaded.
+    """
+    mocker.patch.object(security_manager, "can_access", return_value=True)
+    has_table = mocker.patch.object(Database, "has_table", return_value=True)
+    load_data = 
mocker.patch("superset.commands.dataset.importers.v1.utils.load_data")
+
+    engine = db.session.get_bind()
+    SqlaTable.metadata.create_all(engine)  # pylint: disable=no-member
+
+    database = Database(database_name="my_database", 
sqlalchemy_uri="sqlite://")
+    db.session.add(database)
+    db.session.flush()
+
+    config = {
+        "table_name": "has_data_table",
+        "main_dttm_col": None,
+        "description": None,
+        "default_endpoint": None,
+        "offset": 0,
+        "cache_timeout": None,
+        "schema": None,
+        "sql": None,
+        "params": None,
+        "template_params": None,
+        "filter_select_enabled": False,
+        "fetch_values_predicate": None,
+        "extra": None,
+        "uuid": uuid.uuid4(),
+        "metrics": [],
+        "columns": [],
+        "database_uuid": database.uuid,
+        "database_id": database.id,
+        "data": "https://example.com/data.csv";,
+    }
+
+    import_dataset(config)
+
+    has_table.assert_called_once()
+    load_data.assert_not_called()
+
+
+def test_import_dataset_loads_data_when_table_is_missing(
+    mocker: MockerFixture, session: Session
+) -> None:
+    """
+    When a ``data`` URI is present and the target table doesn't exist yet, the
+    data must actually be loaded. This is the case ``has_table`` exists to
+    detect, and the one an inverted or misindented condition would silently
+    skip while still satisfying the checks above.
+    """
+    mocker.patch.object(security_manager, "can_access", return_value=True)
+    has_table = mocker.patch.object(Database, "has_table", return_value=False)
+    load_data = 
mocker.patch("superset.commands.dataset.importers.v1.utils.load_data")
+
+    engine = db.session.get_bind()
+    SqlaTable.metadata.create_all(engine)  # pylint: disable=no-member
+
+    database = Database(database_name="my_database", 
sqlalchemy_uri="sqlite://")
+    db.session.add(database)
+    db.session.flush()
+
+    config = {
+        "table_name": "missing_data_table",
+        "main_dttm_col": None,
+        "description": None,
+        "default_endpoint": None,
+        "offset": 0,
+        "cache_timeout": None,
+        "schema": None,
+        "sql": None,
+        "params": None,
+        "template_params": None,
+        "filter_select_enabled": False,
+        "fetch_values_predicate": None,
+        "extra": None,
+        "uuid": uuid.uuid4(),
+        "metrics": [],
+        "columns": [],
+        "database_uuid": database.uuid,
+        "database_id": database.id,
+        "data": "https://example.com/data.csv";,
+    }
+
+    import_dataset(config)
+
+    has_table.assert_called_once()
+    load_data.assert_called_once()
+
+
 def 
test_import_dataset_rejects_non_default_catalog_when_multi_catalog_disabled(
     mocker: MockerFixture, session: Session
 ) -> None:

Reply via email to