]> 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:06:57 +0000 (21:06 -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

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 1eea2b6c65dbf643a4dab07bf21430c08dff7135..424f91fa690465f9f1b63a2ee513a2e46c302441 100644 (file)
@@ -1941,17 +1941,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 941a9458b78dbd0296cc097d92acd4211cb59146..ce9954cfd6526d88fa6f9463f6f1f4111cfafa41 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 d95a3c58bc6457594c1f3f429da9e319515e25e0..c70169acfaacede0bec9b56f1423d24e6cf41048 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"""