]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Skip expression-based index reflection for SQLite
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 9 Jan 2019 16:42:02 +0000 (11:42 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 10 Jan 2019 02:07:05 +0000 (21:07 -0500)
Reflection of an index based on SQL expressions are now skipped with a
warning, in the same way as that of the Postgresql dialect, where we currently
do not support reflecting indexes that have SQL expressions within them.
Previously, an index with columns of None were produced which would break
tools like Alembic.

Fixes: #4431
Change-Id: I1363ade912d206b42669331e2be2bb6f444b65a2
(cherry picked from commit 08994cb97c501a3cf984fd827eba9aa9614b9dd3)

doc/build/changelog/unreleased_12/4431.rst [new file with mode: 0644]
lib/sqlalchemy/dialects/sqlite/base.py
lib/sqlalchemy/testing/requirements.py
lib/sqlalchemy/testing/suite/test_reflection.py
test/requirements.py

diff --git a/doc/build/changelog/unreleased_12/4431.rst b/doc/build/changelog/unreleased_12/4431.rst
new file mode 100644 (file)
index 0000000..9aea496
--- /dev/null
@@ -0,0 +1,9 @@
+.. change::
+   :tags: bug, sqlite
+   :tickets: 4431
+
+   Reflection of an index based on SQL expressions are now skipped with a
+   warning, in the same way as that of the Postgresql dialect, where we currently
+   do not support reflecting indexes that have SQL expressions within them.
+   Previously, an index with columns of None were produced which would break
+   tools like Alembic.
index 875ea13bbf00d02680876f1a7a55411845a401e6..e4efd44ee2c0e9f6fff5850d7e91742a58f45631 100644 (file)
@@ -1737,17 +1737,24 @@ class SQLiteDialect(default.DefaultDialect):
                 "sqlite_autoindex"
             ):
                 continue
-
             indexes.append(dict(name=row[1], column_names=[], unique=row[2]))
 
         # loop thru unique indexes to get the column names.
-        for idx in indexes:
+        for idx in list(indexes):
             pragma_index = self._get_table_pragma(
                 connection, "index_info", idx["name"]
             )
 
             for row in pragma_index:
-                idx["column_names"].append(row[2])
+                if row[2] is None:
+                    util.warn(
+                        "Skipped unsupported reflection of "
+                        "expression-based index %s" % idx["name"]
+                    )
+                    indexes.remove(idx)
+                    break
+                else:
+                    idx["column_names"].append(row[2])
         return indexes
 
     @reflection.cache
index 067607c232eca5c79d20172bf37629d4c21c98ad..ac4767a36ae2779dd8805216afcf2e21b33727bb 100644 (file)
@@ -453,6 +453,11 @@ class SuiteRequirements(Requirements):
     def index_reflection(self):
         return exclusions.open()
 
+    @property
+    def indexes_with_expressions(self):
+        """target database supports CREATE INDEX against SQL expressions."""
+        return exclusions.closed()
+
     @property
     def unique_constraint_reflection(self):
         """target dialect supports reflection of unique constraints"""
index 96bd188eedee657d9d818fccc2435d792eada9f2..d589c046fbf52ba41ae6dbe6c0abc753d9de06f2 100644 (file)
@@ -6,6 +6,7 @@ from .. import assert_raises_message
 from .. import config
 from .. import engines
 from .. import eq_
+from .. import expect_warnings
 from .. import fixtures
 from .. import is_
 from ..schema import Column
@@ -792,6 +793,35 @@ class ComponentReflectionTest(fixtures.TablesTest):
     def test_get_noncol_index_pk(self):
         self._test_get_noncol_index("noncol_idx_test_pk", "noncol_idx_pk")
 
+    @testing.requires.indexes_with_expressions
+    @testing.provide_metadata
+    def test_reflect_expression_based_indexes(self):
+        Table(
+            "t",
+            self.metadata,
+            Column("x", String(30)),
+            Column("y", String(30)),
+        )
+        event.listen(
+            self.metadata,
+            "after_create",
+            DDL("CREATE INDEX t_idx ON t(lower(x), lower(y))"),
+        )
+        event.listen(
+            self.metadata, "after_create", DDL("CREATE INDEX t_idx_2 ON t(x)")
+        )
+        self.metadata.create_all()
+
+        insp = inspect(self.metadata.bind)
+
+        with expect_warnings(
+            "Skipped unsupported reflection of expression-based index t_idx"
+        ):
+            eq_(
+                insp.get_indexes("t"),
+                [{"name": "t_idx_2", "column_names": ["x"], "unique": 0}],
+            )
+
     @testing.requires.unique_constraint_reflection
     def test_get_unique_constraints(self):
         self._test_get_unique_constraints()
index b11a2b452593f84bcaa1837d8ae8c4efb81493b1..895554ed7b2b2c2d5276e1a2dfdec496498a711e 100644 (file)
@@ -456,6 +456,10 @@ class DefaultRequirements(SuiteRequirements):
             "postgresql", "sqlite", "oracle", self._mariadb_102
         )
 
+    @property
+    def indexes_with_expressions(self):
+        return only_on(["postgresql", "sqlite>=3.9.0"])
+
     @property
     def temp_table_names(self):
         """target dialect supports listing of temporary table names"""