summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Bodley <4567245+john-bodley@users.noreply.github.com>2022-09-29 21:58:58 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2022-10-03 18:08:08 -0400
commit9829bc43d69ea5e714014f5ac5f036a94d13bc08 (patch)
tree059391d5d165b1840588ad83eee50035f1e05c68
parentbb76f256fc235e2ffcdfa79cdf33a810eac8f7b2 (diff)
downloadsqlalchemy-9829bc43d69ea5e714014f5ac5f036a94d13bc08.tar.gz
adjust MySQL view reflection for non-standard MySQL variants
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. Fixes: #8588 Closes: #8589 Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/8589 Pull-request-sha: d85b2c5b51e45cec543c9ae9d62d6d659b063354 Change-Id: I173137f0bf68639cad0d5c329055475b40ddb5e4
-rw-r--r--doc/build/changelog/unreleased_14/8588.rst10
-rw-r--r--lib/sqlalchemy/dialects/mysql/base.py2
-rw-r--r--lib/sqlalchemy/dialects/mysql/reflection.py5
-rw-r--r--test/dialect/mysql/test_reflection.py17
4 files changed, 31 insertions, 3 deletions
diff --git a/doc/build/changelog/unreleased_14/8588.rst b/doc/build/changelog/unreleased_14/8588.rst
new file mode 100644
index 000000000..879b8b290
--- /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 a3e99514b..e8ddb6d1e 100644
--- a/lib/sqlalchemy/dialects/mysql/base.py
+++ b/lib/sqlalchemy/dialects/mysql/base.py
@@ -3162,7 +3162,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 e7a6b157f..44bc62179 100644
--- a/lib/sqlalchemy/dialects/mysql/reflection.py
+++ b/lib/sqlalchemy/dialects/mysql/reflection.py
@@ -74,6 +74,9 @@ class MySQLTableDefinitionParser:
pass
return state
+ def _check_view(self, sql: str) -> bool:
+ return bool(self._re_is_view.match(sql))
+
def _parse_constraints(self, line):
"""Parse a KEY or CONSTRAINT line.
@@ -409,6 +412,8 @@ class MySQLTableDefinitionParser:
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 f815a7b3c..8f093f134 100644
--- a/test/dialect/mysql/test_reflection.py
+++ b/test/dialect/mysql/test_reflection.py
@@ -1285,8 +1285,6 @@ class ReflectionTest(fixtures.TestBase, AssertsCompiledSQL):
class RawReflectionTest(fixtures.TestBase):
- __backend__ = True
-
def setup_test(self):
dialect = mysql.dialect()
self.parser = _reflection.MySQLTableDefinitionParser(
@@ -1412,3 +1410,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: str, expected: bool) -> None:
+ is_(self.parser._check_view(sql), expected)