--- /dev/null
+.. change::
+ :tags: bug, mysql, reflection
+ :tickets: 13243
+
+ Narrowed the scope of the internal workaround for MySQL bugs `#88718
+ <https://bugs.mysql.com/bug.php?id=88718>`_ and `#96365
+ <https://bugs.mysql.com/bug.php?id=96365>`_ so that it is only applied
+ where needed: MySQL 8.0.1 through 8.0.13 (where bug 88718 is present), and
+ on systems with ``lower_case_table_names=2`` (where bug 96365 applies,
+ typically macOS). Previously the workaround was applied unconditionally
+ for all MySQL 8.0+ versions, which caused a ``KeyError`` during foreign key
+ reflection when the database user lacked SELECT privileges on referred
+ tables.
# i.e. first connect
_backslash_escapes = True
_server_ansiquotes = False
+ _casing = 0
server_version_info: Tuple[int, ...]
identifier_preparer: MySQLIdentifierPreparer
self._is_mysql and self.server_version_info >= (8, 0, 1)
)
- self._needs_correct_for_88718_96365 = (
- not self.is_mariadb and self.server_version_info >= (8,)
- )
+ self._needs_correct_for_88718_96365 = self.server_version_info >= (
+ 8,
+ ) and (self.server_version_info < (8, 0, 14) or self._casing == 2)
self.delete_returning = (
self.is_mariadb and self.server_version_info >= (10, 0, 5)
yield go
+ @testing.fixture
+ def mysql_version_casing_dialect(self, mysql_version_dialect):
+ """yield a MySQL engine that will simulate a specific version
+ and casing setting.
+
+ Builds on mysql_version_dialect, additionally patching out
+ _detect_casing and _detect_sql_mode so that
+ dialect.initialize() can be called with controlled values.
+
+ """
+ _patchers = []
+
+ def go(server_version, casing):
+ engine = mysql_version_dialect(server_version)
+
+ def _mock_detect_casing(conn):
+ engine.dialect._casing = casing
+
+ def _mock_detect_sql_mode(conn):
+ engine.dialect._sql_mode = ""
+
+ for method, fn in [
+ ("_detect_casing", _mock_detect_casing),
+ ("_detect_sql_mode", _mock_detect_sql_mode),
+ ]:
+ p = mock.patch.object(engine.dialect, method, fn)
+ p.start()
+ _patchers.append(p)
+
+ return engine
+
+ yield go
+
+ for p in _patchers:
+ p.stop()
+
def test_reserved_words_mysql_vs_mariadb(
self, mysql_mariadb_reserved_words
):
c = testing.db.connect().execution_options(isolation_level=value)
eq_(testing.db.dialect.get_isolation_level(c.connection), value)
+ @testing.only_on("mysql")
+ @testing.combinations(
+ ("8.0.12", 0, True),
+ ("8.0.13", 0, True),
+ ("8.0.13", 2, True),
+ ("8.0.14", 0, False),
+ ("8.0.14", 1, False),
+ ("8.0.14", 2, True),
+ ("8.0.32", 0, False),
+ ("8.0.32", 2, True),
+ ("8.4.0", 0, False),
+ ("8.4.0", 2, True),
+ ("5.7.0", 0, False),
+ ("5.7.0", 2, False),
+ argnames="server_version,casing,expected",
+ )
+ def test_needs_correct_for_88718_96365(
+ self, mysql_version_casing_dialect, server_version, casing, expected
+ ):
+ engine = mysql_version_casing_dialect(server_version, casing)
+ engine.connect()
+ is_(engine.dialect._needs_correct_for_88718_96365, expected)
+
class DialectTest(fixtures.TestBase):
__backend__ = True