summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2015-05-03 20:17:19 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2015-05-03 20:17:19 -0400
commitc75be37640a19990d385a8059b61319af6818358 (patch)
treea6bf6bf34e0e7bd3ee09d2396a47f1feb7923dbc
parentb35dbff602362a88a2580aaa8a26adfad15a837e (diff)
downloadalembic-c75be37640a19990d385a8059b61319af6818358.tar.gz
- Fixed bug where the case of multiple mergepoints that all
have the identical set of ancestor revisions would fail to be upgradable, producing an assertion failure. Merge points were previously assumed to always require at least an UPDATE in alembic_revision from one of the previous revs to the new one, however in this case, if one of the mergepoints has already been reached, the remaining mergepoints have no row to UPDATE therefore they must do an INSERT of their target version. fixes #297
-rw-r--r--alembic/migration.py15
-rw-r--r--docs/build/changelog.rst13
-rw-r--r--tests/test_version_traversal.py149
3 files changed, 170 insertions, 7 deletions
diff --git a/alembic/migration.py b/alembic/migration.py
index 9bd34ed..d94db2e 100644
--- a/alembic/migration.py
+++ b/alembic/migration.py
@@ -670,14 +670,15 @@ class RevisionStep(MigrationStep):
if not downrevs:
# is a base
return True
- elif len(downrevs) == 1:
- if downrevs[0] in heads:
- return False
- else:
- return True
else:
- # is a merge point
- return False
+ # none of our downrevs are present, so...
+ # we have to insert our version. This is true whether
+ # or not there is only one downrev, or multiple (in the latter
+ # case, we're a merge point.)
+ if not heads.intersection(downrevs):
+ return True
+ else:
+ return False
def should_merge_branches(self, heads):
if not self.is_upgrade:
diff --git a/docs/build/changelog.rst b/docs/build/changelog.rst
index 8e3824c..c5d4f6f 100644
--- a/docs/build/changelog.rst
+++ b/docs/build/changelog.rst
@@ -7,6 +7,19 @@ Changelog
:version: 0.7.6
.. change::
+ :tags: feature, versioning
+ :tickets: 297
+
+ Fixed bug where the case of multiple mergepoints that all
+ have the identical set of ancestor revisions would fail to be
+ upgradable, producing an assertion failure. Merge points were
+ previously assumed to always require at least an UPDATE in
+ alembic_revision from one of the previous revs to the new one,
+ however in this case, if one of the mergepoints has already
+ been reached, the remaining mergepoints have no row to UPDATE therefore
+ they must do an INSERT of their target version.
+
+ .. change::
:tags: feature, autogenerate
:tickets: 296
diff --git a/tests/test_version_traversal.py b/tests/test_version_traversal.py
index 410995e..198c7c6 100644
--- a/tests/test_version_traversal.py
+++ b/tests/test_version_traversal.py
@@ -434,6 +434,155 @@ class BranchFrom3WayMergepointTest(MigrationTest):
)
+class TwinMergeTest(MigrationTest):
+ """Test #297, where we have two mergepoints from the same set of
+ originating branches.
+
+ """
+ @classmethod
+ def setup_class(cls):
+ """
+
+ 33e21c000cfe -> 178d4e761bbd (head),
+ 2bef33cb3a58, 3904558db1c6, 968330f320d -> 33e21c000cfe (mergepoint)
+ 46c99f866004 -> 18f46b42410d (head),
+ 2bef33cb3a58, 3904558db1c6, 968330f320d -> 46c99f866004 (mergepoint)
+ f0fa4315825 -> 3904558db1c6 (branchpoint),
+
+ --------------------------
+
+ A -> B2 (branchpoint),
+
+ B1, B2, B3 -> C1 (mergepoint)
+ B1, B2, B3 -> C2 (mergepoint)
+
+ C1 -> D1 (head),
+
+ C2 -> D2 (head),
+
+
+ """
+ cls.env = env = staging_env()
+
+ cls.a = env.generate_revision(
+ 'a', 'a'
+ )
+ cls.b1 = env.generate_revision('b1', 'b1',
+ head=cls.a.revision)
+ cls.b2 = env.generate_revision('b2', 'b2',
+ splice=True,
+ head=cls.a.revision)
+ cls.b3 = env.generate_revision('b3', 'b3',
+ splice=True,
+ head=cls.a.revision)
+
+ cls.c1 = env.generate_revision(
+ 'c1', 'c1',
+ head=(cls.b1.revision, cls.b2.revision, cls.b3.revision))
+
+ cls.c2 = env.generate_revision(
+ 'c2', 'c2',
+ splice=True,
+ head=(cls.b1.revision, cls.b2.revision, cls.b3.revision))
+
+ cls.d1 = env.generate_revision(
+ 'd1', 'd1', head=cls.c1.revision)
+
+ cls.d2 = env.generate_revision(
+ 'd2', 'd2', head=cls.c2.revision)
+
+ def test_upgrade(self):
+ head = HeadMaintainer(mock.Mock(), [self.a.revision])
+
+ steps = [
+ (self.up_(self.b3), ('b3',)),
+ (self.up_(self.b1), ('b1', 'b3',)),
+ (self.up_(self.b2), ('b1', 'b2', 'b3',)),
+ (self.up_(self.c2), ('c2',)),
+ (self.up_(self.d2), ('d2',)),
+ (self.up_(self.c1), ('c1', 'd2')),
+ (self.up_(self.d1), ('d1', 'd2')),
+ ]
+ for step, assert_ in steps:
+ head.update_to_step(step)
+ eq_(head.heads, set(assert_))
+
+
+class NotQuiteTwinMergeTest(MigrationTest):
+ """Test a variant of #297.
+
+ """
+ @classmethod
+ def setup_class(cls):
+ """
+ A -> B2 (branchpoint),
+
+ B1, B2 -> C1 (mergepoint)
+ B2, B3 -> C2 (mergepoint)
+
+ C1 -> D1 (head),
+
+ C2 -> D2 (head),
+
+
+ """
+ cls.env = env = staging_env()
+
+ cls.a = env.generate_revision(
+ 'a', 'a'
+ )
+ cls.b1 = env.generate_revision('b1', 'b1',
+ head=cls.a.revision)
+ cls.b2 = env.generate_revision('b2', 'b2',
+ splice=True,
+ head=cls.a.revision)
+ cls.b3 = env.generate_revision('b3', 'b3',
+ splice=True,
+ head=cls.a.revision)
+
+ cls.c1 = env.generate_revision(
+ 'c1', 'c1',
+ head=(cls.b1.revision, cls.b2.revision))
+
+ cls.c2 = env.generate_revision(
+ 'c2', 'c2',
+ splice=True,
+ head=(cls.b2.revision, cls.b3.revision))
+
+ cls.d1 = env.generate_revision(
+ 'd1', 'd1', head=cls.c1.revision)
+
+ cls.d2 = env.generate_revision(
+ 'd2', 'd2', head=cls.c2.revision)
+
+ def test_upgrade(self):
+ head = HeadMaintainer(mock.Mock(), [self.a.revision])
+
+ """
+ upgrade a -> b2, b2
+ upgrade a -> b3, b3
+ upgrade b2, b3 -> c2, c2
+ upgrade c2 -> d2, d2
+ upgrade a -> b1, b1
+ upgrade b1, b2 -> c1, c1
+ upgrade c1 -> d1, d1
+ """
+
+ steps = [
+ (self.up_(self.b2), ('b2',)),
+ (self.up_(self.b3), ('b2', 'b3',)),
+ (self.up_(self.c2), ('c2',)),
+ (self.up_(self.d2), ('d2',)),
+
+ (self.up_(self.b1), ('b1', 'd2',)),
+ (self.up_(self.c1), ('c1', 'd2')),
+ (self.up_(self.d1), ('d1', 'd2')),
+ ]
+ for step, assert_ in steps:
+ head.update_to_step(step)
+ eq_(head.heads, set(assert_))
+
+
class DependsOnBranchTestOne(MigrationTest):
@classmethod