]> git.ipfire.org Git - thirdparty/sqlalchemy/alembic.git/commitdiff
ensure heads remain unique in topological
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 20 May 2021 18:17:53 +0000 (14:17 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 20 May 2021 18:17:53 +0000 (14:17 -0400)
Fixed regression where a revision file that contained its own down revision
as a dependency would cause an endless loop in the traversal logic.

Change-Id: Ie8bd3b4d9f81e4bda131ffdbabe7a90c4a715054
Fixes: #843
alembic/script/revision.py
docs/build/unreleased/843.rst [new file with mode: 0644]
tests/test_revision.py
tests/test_version_traversal.py

index ef03d2e54ab2c6f9ab0f17b72be2e6bd050ba478..f2055663bf0ecd8025d81497be9ed9df42721263 100644 (file)
@@ -833,7 +833,6 @@ class RevisionMap(object):
                 key=inserted_order.index,
             )
         )
-
         ancestors_by_idx = [get_ancestors(rev_id) for rev_id in current_heads]
 
         output = []
@@ -864,11 +863,15 @@ class RevisionMap(object):
 
                 candidate_rev = id_to_rev[candidate]
 
-                # immediate ancestor nodes
+                # immediate ancestor nodes, use a set to uniquify
+                _u = set()
                 heads_to_add = [
                     r
                     for r in candidate_rev._normalized_down_revisions
-                    if r in todo and r not in current_heads
+                    if r in todo
+                    and r not in current_heads
+                    and r not in _u
+                    and (_u.add(r) or True)
                 ]
 
                 if not heads_to_add:
diff --git a/docs/build/unreleased/843.rst b/docs/build/unreleased/843.rst
new file mode 100644 (file)
index 0000000..02af2b5
--- /dev/null
@@ -0,0 +1,6 @@
+.. change::
+    :tags: bug, regression, versioning
+    :tickets: 843
+
+    Fixed regression where a revision file that contained its own down revision
+    as a dependency would cause an endless loop in the traversal logic.
index 31590e3a5e60e9313b68bf27f7400c95ecf57624..ef1202a08aef931866375c53e197c07dfea5da86 100644 (file)
@@ -1347,6 +1347,16 @@ class GraphWithLoopTest(DownIterateTest, InvalidRevisionMapTest):
         )
         self._assert_raises_revision_map_loop(map_, "a")
 
+    def test_revision_dupe_head(self):
+        r1 = Revision("user_foo", None)
+        r2 = Revision("user", "user_foo", dependencies="user_foo")
+
+        self.map = RevisionMap(lambda: [r1, r2])
+
+        self._assert_iteration("heads", None, ["user", "user_foo"])
+
+        eq_(self.map._topological_sort([r1, r2], [r2]), ["user", "user_foo"])
+
     def test_revision_map_no_loop_w_overlapping_substrings(self):
         r1 = Revision("user_foo", None)
         r2 = Revision("user", "user_foo")
index 3895607fc560a0bf17a3c5f5889a0fb22f7a0824..d3709d2a9dec03b988e7f148356b35c98b38377e 100644 (file)
@@ -1125,6 +1125,29 @@ class DependsOnBranchTestThree(MigrationTest):
         )
 
 
+class DependsOnOwnDownrevTest(MigrationTest):
+    @classmethod
+    def setup_class(cls):
+        """
+        test #843
+        """
+        cls.env = env = staging_env()
+        cls.a1 = env.generate_revision("a1", "->a1", head="base")
+        cls.a2 = env.generate_revision("a2", "->a2", depends_on="a1")
+
+    @classmethod
+    def teardown_class(cls):
+        clear_staging_env()
+
+    def test_traverse(self):
+        self._assert_upgrade(
+            self.a2.revision,
+            None,
+            [self.up_(self.a1), self.up_(self.a2)],
+            set(["a2"]),
+        )
+
+
 class DependsOnBranchTestFour(MigrationTest):
     @classmethod
     def setup_class(cls):