From 99da008d0402e9c093f89c98e11a2d0d3091a77e Mon Sep 17 00:00:00 2001 From: creazyfrog Date: Sun, 17 May 2026 00:52:48 -0700 Subject: [PATCH 1/2] [BUGFIX] Warn instead of raise when schema absent from get_schema_names() in test_connection (#10499) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `TableAsset.test_connection` raised TestConnectionError immediately when the configured schema was not present in `inspector.get_schema_names()`. On SQL Server (and other backends where users lack VIEW DEFINITION permission), get_schema_names() only returns schemas the connected user can describe—not every schema they can access. This caused false-positive failures: users with only SELECT permission on a table could not call test_connection even though the table was reachable. Fix: replace the hard raise with a LOGGER.warning and let the subsequent table-level SELECT 1 query act as the definitive accessibility check. If the schema or table is genuinely absent the SQL error surfaces a clear TestConnectionError; if it is accessible the connection is confirmed. Also adds two unit tests covering both the "schema absent but table reachable" and "schema absent and table inaccessible" paths. Fixes #10499 --- .../datasource/fluent/sql_datasource.py | 14 +++- .../datasource/fluent/test_sql_datasources.py | 75 +++++++++++++++++++ 2 files changed, 86 insertions(+), 3 deletions(-) diff --git a/great_expectations/datasource/fluent/sql_datasource.py b/great_expectations/datasource/fluent/sql_datasource.py index 86ce699de110..52596cda2811 100644 --- a/great_expectations/datasource/fluent/sql_datasource.py +++ b/great_expectations/datasource/fluent/sql_datasource.py @@ -1171,9 +1171,17 @@ def test_connection(self) -> None: effective_schema = self._effective_schema_name if effective_schema and effective_schema not in schema_names: - raise TestConnectionError( # noqa: TRY003 # FIXME CoP - f'Attempt to connect to table: "{self.qualified_name}" failed because the schema ' - f'"{effective_schema}" does not exist.' + # Some backends (e.g. SQL Server / MSSQL) only list schemas the connected + # user has VIEW DEFINITION on via get_schema_names(). A user with only + # SELECT permission on a table can still access the schema, so a missing + # entry here does not reliably indicate the schema is absent. Log a warning + # and fall through to the table-level access check, which will surface a + # clear error if the schema or table genuinely does not exist (issue #10499). + LOGGER.warning( + f'Schema "{effective_schema}" was not found in the list returned by ' + "inspector.get_schema_names(). This can occur when the connected user " + "lacks VIEW DEFINITION permission on the schema (e.g. SQL Server). " + "Proceeding to validate table accessibility directly." ) try: diff --git a/tests/datasource/fluent/test_sql_datasources.py b/tests/datasource/fluent/test_sql_datasources.py index f3d0b773a9ac..b8d56aba7a54 100644 --- a/tests/datasource/fluent/test_sql_datasources.py +++ b/tests/datasource/fluent/test_sql_datasources.py @@ -473,5 +473,80 @@ def test_table_name_serialization_preserves_quotes( assert serialized["table_name"] == serialized_name +@pytest.mark.unit +class TestTableAssetTestConnection: + """Tests for TableAsset.test_connection schema-listing behaviour (issue #10499).""" + + def _make_table_asset(self, schema_name: str) -> TableAsset: + """Return a TableAsset with a fixed datasource whose schema_ matches schema_name.""" + ds = SQLDatasource( + name="my_datasource", + connection_string="sqlite:///", + ) + asset = ds.add_table_asset(name="my_asset", table_name="my_table") + # Override schema_name directly on the asset so _effective_schema_name returns it. + object.__setattr__(asset, "schema_name", schema_name) + return asset + + def test_schema_absent_from_get_schema_names_does_not_raise( + self, + monkeypatch: pytest.MonkeyPatch, + ) -> None: + """When inspector.get_schema_names() omits the schema (e.g. because the + connected SQL Server user lacks VIEW DEFINITION on that schema), test_connection + should NOT raise. It should log a warning and let the table-level query decide + whether the asset is reachable (issue #10499).""" + asset = self._make_table_asset("my_schema") + + mock_engine = mock.MagicMock() + mock_inspector = mock.MagicMock() + # get_schema_names() returns only system schemas — omits "my_schema". + mock_inspector.get_schema_names.return_value = ["dbo", "guest", "information_schema"] + mock_conn = mock.MagicMock() + mock_engine.connect.return_value.__enter__ = mock.MagicMock(return_value=mock_conn) + mock_engine.connect.return_value.__exit__ = mock.MagicMock(return_value=False) + + with ( + mock.patch( + "great_expectations.datasource.fluent.sql_datasource.TableAsset.datasource", + new_callable=mock.PropertyMock, + return_value=mock.MagicMock(get_engine=mock.MagicMock(return_value=mock_engine)), + ), + mock.patch("great_expectations.datasource.fluent.sql_datasource.sa.inspect", return_value=mock_inspector), + ): + # Should not raise even though "my_schema" is not in schema_names. + asset.test_connection() + + def test_schema_absent_and_table_inaccessible_raises( + self, + monkeypatch: pytest.MonkeyPatch, + ) -> None: + """When the schema is absent from get_schema_names() AND the table query also + fails, test_connection must still surface a TestConnectionError so the caller + knows the asset is not reachable.""" + from great_expectations.datasource.fluent.interfaces import TestConnectionError + + asset = self._make_table_asset("nonexistent_schema") + + mock_engine = mock.MagicMock() + mock_inspector = mock.MagicMock() + mock_inspector.get_schema_names.return_value = ["dbo"] + mock_conn = mock.MagicMock() + mock_conn.execute.side_effect = Exception("schema or table not found") + mock_engine.connect.return_value.__enter__ = mock.MagicMock(return_value=mock_conn) + mock_engine.connect.return_value.__exit__ = mock.MagicMock(return_value=False) + + with ( + mock.patch( + "great_expectations.datasource.fluent.sql_datasource.TableAsset.datasource", + new_callable=mock.PropertyMock, + return_value=mock.MagicMock(get_engine=mock.MagicMock(return_value=mock_engine)), + ), + mock.patch("great_expectations.datasource.fluent.sql_datasource.sa.inspect", return_value=mock_inspector), + ): + with pytest.raises(TestConnectionError): + asset.test_connection() + + if __name__ == "__main__": pytest.main([__file__, "-vv"]) From db42cb8a0a94e46025d2897b662931b4d99a717b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 17 May 2026 07:55:23 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/datasource/fluent/test_sql_datasources.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/datasource/fluent/test_sql_datasources.py b/tests/datasource/fluent/test_sql_datasources.py index b8d56aba7a54..c92f17f31796 100644 --- a/tests/datasource/fluent/test_sql_datasources.py +++ b/tests/datasource/fluent/test_sql_datasources.py @@ -512,7 +512,10 @@ def test_schema_absent_from_get_schema_names_does_not_raise( new_callable=mock.PropertyMock, return_value=mock.MagicMock(get_engine=mock.MagicMock(return_value=mock_engine)), ), - mock.patch("great_expectations.datasource.fluent.sql_datasource.sa.inspect", return_value=mock_inspector), + mock.patch( + "great_expectations.datasource.fluent.sql_datasource.sa.inspect", + return_value=mock_inspector, + ), ): # Should not raise even though "my_schema" is not in schema_names. asset.test_connection() @@ -542,7 +545,10 @@ def test_schema_absent_and_table_inaccessible_raises( new_callable=mock.PropertyMock, return_value=mock.MagicMock(get_engine=mock.MagicMock(return_value=mock_engine)), ), - mock.patch("great_expectations.datasource.fluent.sql_datasource.sa.inspect", return_value=mock_inspector), + mock.patch( + "great_expectations.datasource.fluent.sql_datasource.sa.inspect", + return_value=mock_inspector, + ), ): with pytest.raises(TestConnectionError): asset.test_connection()