From 8e7863b603ca37cb9d24e874dc555f754289e86d Mon Sep 17 00:00:00 2001 From: Maha Rana Yadavalli <271375718+maharanay22@users.noreply.github.com> Date: Fri, 16 Oct 2026 20:33:56 -0400 Subject: [PATCH] Fail the PostgreSQL connection when the configured schema doesn't exist The handler ran SET search_path TO "", public without checking that the schema exists. PostgreSQL accepts a search_path entry for a missing schema, so a mistyped or wrong-cased schema (for example "sales" when the real schema is "Sales") made every unqualified query silently read tables from public, and check_connection still reported success. Look the schema up in pg_namespace before setting search_path and raise InvalidSchemaName when it is missing, so connect() and check_connection() fail with a clear message. Also close the connection when setup fails after psycopg.connect() succeeded. --- .../postgres_handler/postgres_handler.py | 8 ++++++ tests/unit/handlers/test_postgres.py | 28 ++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/mindsdb/integrations/handlers/postgres_handler/postgres_handler.py b/mindsdb/integrations/handlers/postgres_handler/postgres_handler.py index a3456a8e95..40b756f39f 100644 --- a/mindsdb/integrations/handlers/postgres_handler/postgres_handler.py +++ b/mindsdb/integrations/handlers/postgres_handler/postgres_handler.py @@ -221,11 +221,19 @@ def connect(self): schema = self.connection_args.get("schema") if schema: with self.connection.cursor() as cur: + cur.execute("SELECT 1 FROM pg_namespace WHERE nspname = %s;", (schema,)) + if cur.fetchone() is None: + raise psycopg.errors.InvalidSchemaName( + f'Schema "{schema}" does not exist in database "{self.database}". ' + "Schema names are case-sensitive." + ) cur.execute(f'SET search_path TO "{schema}", public;') self.connection.commit() return self.connection except psycopg.Error as e: logger.error(f"Error connecting to PostgreSQL {self.database}, {e}!") + if self.is_connected: + self.connection.close() self.is_connected = False raise diff --git a/tests/unit/handlers/test_postgres.py b/tests/unit/handlers/test_postgres.py index a0e3adc133..4f74914078 100644 --- a/tests/unit/handlers/test_postgres.py +++ b/tests/unit/handlers/test_postgres.py @@ -294,9 +294,35 @@ def test_connect_with_schema_sets_search_path_after_connection(self): self.assertTrue(self.handler.is_connected) self.assertIsNotNone(connection) - mock_cursor.execute.assert_called_once_with('SET search_path TO "my_schema", public;') + mock_cursor.execute.assert_called_with('SET search_path TO "my_schema", public;') self.mock_connect.return_value.commit.assert_called_once() + def test_connect_with_missing_schema_fails(self): + """ + Tests that a schema that does not exist (for example, wrong case) fails the connection + instead of silently falling back to the public schema. + """ + self.tearDown() + self.setUp() + self.handler.connection_args["schema"] = "sales" + + mock_cursor = MockCursorContextManager() + mock_cursor.fetchone = MagicMock(return_value=None) + mock_conn = self.mock_connect.return_value + mock_conn.cursor.return_value = mock_cursor + + with self.assertRaises(psycopg.errors.InvalidSchemaName) as ctx: + self.handler.connect() + + self.assertIn('Schema "sales" does not exist', str(ctx.exception)) + mock_cursor.execute.assert_called_once_with("SELECT 1 FROM pg_namespace WHERE nspname = %s;", ("sales",)) + self.assertFalse(self.handler.is_connected) + mock_conn.close.assert_called_once() + + response = self.handler.check_connection() + self.assertFalse(response.success) + self.assertIn('Schema "sales" does not exist', response.error_message) + def test_make_connection_args_applies_overrides(self): handler = self.handler handler.connection_args = OrderedDict(