]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Explicitly test for Connection in dialect.has_table()
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 14 Apr 2021 16:12:08 +0000 (12:12 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 14 Apr 2021 16:14:12 +0000 (12:14 -0400)
The :meth:`_engine.Dialect.has_table` method now raises an informative
exception if a non-Connection is passed to it, as this incorrect behavior
seems to be common.  This method is not intended for external use outside
of a dialect.  Please use the :meth:`.Inspector.has_table` method
or for cross-compatibility with older SQLAlchemy versions, the
:meth:`_engine.Engine.has_table` method.

Fixes: #5780
Fixes: #6062
Fixes: #6260
Change-Id: I9b2439675167019b68d682edee3dcdcfce836987

12 files changed:
doc/build/changelog/unreleased_14/assert_ht_connection.rst [new file with mode: 0644]
lib/sqlalchemy/dialects/firebird/base.py
lib/sqlalchemy/dialects/mssql/base.py
lib/sqlalchemy/dialects/mysql/base.py
lib/sqlalchemy/dialects/oracle/base.py
lib/sqlalchemy/dialects/postgresql/base.py
lib/sqlalchemy/dialects/sqlite/base.py
lib/sqlalchemy/dialects/sybase/base.py
lib/sqlalchemy/engine/default.py
lib/sqlalchemy/engine/interfaces.py
lib/sqlalchemy/engine/reflection.py
test/engine/test_execute.py

diff --git a/doc/build/changelog/unreleased_14/assert_ht_connection.rst b/doc/build/changelog/unreleased_14/assert_ht_connection.rst
new file mode 100644 (file)
index 0000000..8e79c6f
--- /dev/null
@@ -0,0 +1,10 @@
+.. change::
+    :tags: bug, engine
+
+    The :meth:`_engine.Dialect.has_table` method now raises an informative
+    exception if a non-Connection is passed to it, as this incorrect behavior
+    seems to be common.  This method is not intended for external use outside
+    of a dialect.  Please use the :meth:`.Inspector.has_table` method
+    or for cross-compatibility with older SQLAlchemy versions, the
+    :meth:`_engine.Engine.has_table` method.
+
index fcf0c31d39c7f8b7ba7a34efe2f10218c853d6b9..1f0c66ffb95d4cc0dfcff23a30a42e91020a4032 100644 (file)
@@ -685,6 +685,7 @@ class FBDialect(default.DefaultDialect):
     def has_table(self, connection, table_name, schema=None):
         """Return ``True`` if the given table exists, ignoring
         the `schema`."""
+        self._ensure_has_table_connection(connection)
 
         tblqry = """
         SELECT 1 AS has_table FROM rdb$database
index 1fef42c53e9e261cbb608574ba5e07632ac9aaec..e0932248702da8abe75f71a2716d8c43d61fb3b3 100644 (file)
@@ -2799,6 +2799,7 @@ class MSDialect(default.DefaultDialect):
 
     @_db_plus_owner
     def has_table(self, connection, tablename, dbname, owner, schema):
+        self._ensure_has_table_connection(connection)
         if tablename.startswith("#"):  # temporary table
             tables = ischema.mssql_temp_table_columns
 
index 3966126e2d670750c910e452fce185d1d84f5484..f45d5ec919a65367c5d723b6ac55c60face3ed48 100644 (file)
@@ -2809,6 +2809,8 @@ class MySQLDialect(default.DefaultDialect):
         return connection.exec_driver_sql("SELECT DATABASE()").scalar()
 
     def has_table(self, connection, table_name, schema=None):
+        self._ensure_has_table_connection(connection)
+
         if schema is None:
             schema = self.default_schema_name
 
index 11ad61675e5aec08c4dc4ab74742d9f3b53e668c..6496c4f71f51f8b60fb3ae48c88f5673c4fafc6e 100644 (file)
@@ -1596,6 +1596,8 @@ class OracleDialect(default.DefaultDialect):
         raise NotImplementedError("implemented by cx_Oracle dialect")
 
     def has_table(self, connection, table_name, schema=None):
+        self._ensure_has_table_connection(connection)
+
         if not schema:
             schema = self.default_schema_name
         cursor = connection.execute(
index d0915a0c9752736150c84c686af886f8db75c652..3b419ed16948815c089c6d4cdc554b89e913a391 100644 (file)
@@ -3303,6 +3303,7 @@ class PGDialect(default.DefaultDialect):
         return bool(cursor.first())
 
     def has_table(self, connection, table_name, schema=None):
+        self._ensure_has_table_connection(connection)
         # seems like case gets folded in pg_class...
         if schema is None:
             cursor = connection.execute(
index 83c2a8ea755f6d3d7560174ce1b2bda8f361d173..28901d02823e4748f4df5637f0791e9c8b2fef8c 100644 (file)
@@ -1996,6 +1996,8 @@ class SQLiteDialect(default.DefaultDialect):
         return [row[0] for row in rs]
 
     def has_table(self, connection, table_name, schema=None):
+        self._ensure_has_table_connection(connection)
+
         info = self._get_table_pragma(
             connection, "table_info", table_name, schema=schema
         )
index fd5e5b3b6367cbcae23b8fc62bb15c33d7e1fc41..7c10973e62c54bb109eb251684ab811f62faadbb 100644 (file)
@@ -1089,6 +1089,8 @@ class SybaseDialect(default.DefaultDialect):
         return [v["name"] for v in views]
 
     def has_table(self, connection, table_name, schema=None):
+        self._ensure_has_table_connection(connection)
+
         try:
             self.get_table_id(connection, table_name, schema)
         except exc.NoSuchTableError:
index d45b6d7a7efb4ed0ed8f156c608662a9b79caa79..375a93a484a6d3580c19716cfb06a68651a4c5ce 100644 (file)
@@ -22,6 +22,7 @@ import weakref
 from . import characteristics
 from . import cursor as _cursor
 from . import interfaces
+from .base import Connection
 from .. import event
 from .. import exc
 from .. import pool
@@ -323,6 +324,19 @@ class DefaultDialect(interfaces.Dialect):
         self._encoder = codecs.getencoder(self.encoding)
         self._decoder = processors.to_unicode_processor_factory(self.encoding)
 
+    def _ensure_has_table_connection(self, arg):
+
+        if not isinstance(arg, Connection):
+            raise exc.ArgumentError(
+                "The argument passed to Dialect.has_table() should be a "
+                "%s, got %s. "
+                "Additionally, the Dialect.has_table() method is for "
+                "internal dialect "
+                "use only; please use "
+                "``inspect(some_engine).has_table(<tablename>>)`` "
+                "for public API use." % (Connection, type(arg))
+            )
+
     @util.memoized_property
     def _supports_statement_cache(self):
         return (
index 5e6cc524e9cec467e9d2ca76e06adcde01c11cf2..47348ba767860bc76ac6215a14db7a35062581a4 100644 (file)
@@ -467,12 +467,25 @@ class Dialect(object):
         raise NotImplementedError()
 
     def has_table(self, connection, table_name, schema=None, **kw):
-        """Check the existence of a particular table in the database.
+        """For internal dialect use, check the existence of a particular table
+        in the database.
+
+        Given a :class:`_engine.Connection` object, a string table_name and
+        optional schema name, return True if the given table exists in the
+        database, False otherwise.
+
+        This method serves as the underlying implementation of the
+        public facing :meth:`.Inspector.has_table` method, and is also used
+        internally to implement the "checkfirst" behavior for methods like
+        :meth:`_schema.Table.create` and :meth:`_schema.MetaData.create_all`.
+
+        .. note:: This method is used internally by SQLAlchemy, and is
+           published so that third-party dialects may provide an
+           implementation. It is **not** the public API for checking for table
+           presence. Please use the :meth:`.Inspector.has_table` method.
+           Alternatively, for legacy cross-compatibility, the
+           :meth:`_engine.Engine.has_table` method may be used.
 
-        Given a :class:`_engine.Connection` object and a string
-        `table_name`, return True if the given table (possibly within
-        the specified `schema`) exists in the database, False
-        otherwise.
         """
 
         raise NotImplementedError()
index cff209575280a9cc40f0b99f52a121f8dc533d7e..2cdd9ac3bebbb8de73ff120d1a86ad3af2cd09f6 100644 (file)
@@ -274,7 +274,8 @@ class Inspector(object):
         :param table_name: name of the table to check
         :param schema: schema name to query, if not the default schema.
 
-        .. versionadded:: 1.4
+        .. versionadded:: 1.4 - the :meth:`.Inspector.has_table` method
+           replaces the :meth:`_engine.Engine.has_table` method.
 
         """
         # TODO: info_cache?
index 0de5ed124354887b879b5fa165bc33c9578c4051..0b65b3055c6f693135ed462ec1127edeaec46a22 100644 (file)
@@ -261,6 +261,13 @@ class ExecuteTest(fixtures.TablesTest):
             (4, "sally"),
         ]
 
+    def test_dialect_has_table_assertion(self):
+        with expect_raises_message(
+            tsa.exc.ArgumentError,
+            r"The argument passed to Dialect.has_table\(\) should be a",
+        ):
+            testing.db.dialect.has_table(testing.db, "some_table")
+
     def test_exception_wrapping_dbapi(self):
         with testing.db.connect() as conn:
             # engine does not have exec_driver_sql