diff --git a/doc/build/changelog/unreleased_14/8588.rst b/doc/build/changelog/unreleased_14/8588.rst new file mode 100644 index 0000000000..879b8b2907 --- /dev/null +++ b/doc/build/changelog/unreleased_14/8588.rst @@ -0,0 +1,10 @@ +.. change:: + :tags: bug, mysql + :tickets: 8588 + + Adjusted the regular expression used to match "CREATE VIEW" when + testing for views to work more flexibly, no longer requiring the + special keyword "ALGORITHM" in the middle, which was intended to be + optional but was not working correctly. The change allows view reflection + to work more completely on MySQL-compatible variants such as StarRocks. + Pull request courtesy John Bodley. \ No newline at end of file diff --git a/lib/sqlalchemy/dialects/mysql/base.py b/lib/sqlalchemy/dialects/mysql/base.py index 111c63bff1..70b60a0a0f 100644 --- a/lib/sqlalchemy/dialects/mysql/base.py +++ b/lib/sqlalchemy/dialects/mysql/base.py @@ -3107,7 +3107,7 @@ class MySQLDialect(default.DefaultDialect): sql = self._show_create_table( connection, None, charset, full_name=full_name ) - if re.match(r"^CREATE (?:ALGORITHM)?.* VIEW", sql): + if parser._check_view(sql): # Adapt views to something table-like. columns = self._describe_table( connection, None, charset, full_name=full_name diff --git a/lib/sqlalchemy/dialects/mysql/reflection.py b/lib/sqlalchemy/dialects/mysql/reflection.py index 27394bbe9f..f536496d46 100644 --- a/lib/sqlalchemy/dialects/mysql/reflection.py +++ b/lib/sqlalchemy/dialects/mysql/reflection.py @@ -70,6 +70,9 @@ class MySQLTableDefinitionParser(object): pass return state + def _check_view(self, sql): + return bool(self._re_is_view.match(sql)) + def _parse_constraints(self, line): """Parse a KEY or CONSTRAINT line. @@ -349,6 +352,8 @@ class MySQLTableDefinitionParser(object): self.preparer._unescape_identifier, ) + self._re_is_view = _re_compile(r"^CREATE(?! TABLE)(\s.*)?\sVIEW") + # `col`,`col2`(32),`col3`(15) DESC # self._re_keyexprs = _re_compile( diff --git a/test/dialect/mysql/test_reflection.py b/test/dialect/mysql/test_reflection.py index 4c763a6483..529d352a2a 100644 --- a/test/dialect/mysql/test_reflection.py +++ b/test/dialect/mysql/test_reflection.py @@ -1122,8 +1122,6 @@ class ReflectionTest(fixtures.TestBase, AssertsCompiledSQL): class RawReflectionTest(fixtures.TestBase): - __backend__ = True - def setup_test(self): dialect = mysql.dialect() self.parser = _reflection.MySQLTableDefinitionParser( @@ -1249,3 +1247,18 @@ class RawReflectionTest(fixtures.TestBase): "SET NULL", ), ) + + @testing.combinations( + ( + "CREATE ALGORITHM=UNDEFINED DEFINER=`scott`@`%` " + "SQL SECURITY DEFINER VIEW `v1` AS SELECT", + True, + ), + ("CREATE VIEW `v1` AS SELECT", True), + ("CREATE TABLE `v1`", False), + ("CREATE TABLE `VIEW`", False), + ("CREATE TABLE `VIEW_THINGS`", False), + ("CREATE TABLE `A VIEW`", False), + ) + def test_is_view(self, sql, expected): + is_(self.parser._check_view(sql), expected)