Migrated issue, originally created by Michael Bayer (@zzzeek)
class TwinMergeTest(MigrationTest):
"""Test #XYZ, 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_))
#!
Traceback (most recent call last):
File "/Users/classic/dev/alembic/tests/test_version_traversal.py", line 507, in test_upgrade
head.update_to_step(step)
File "/Users/classic/dev/alembic/alembic/migration.py", line 495, in update_to_step
from_, to_ = step.update_version_num(self.heads)
File "/Users/classic/dev/alembic/alembic/migration.py", line 709, in update_version_num
"Can't do an UPDATE because downrevision is ambiguous"
AssertionError: Can't do an UPDATE because downrevision is ambiguous
Assume a tree like this:
#!
A -> B1,
A -> B2,
A -> B3,
B1, B2, B3 -> C1
B1, B2, B3 -> C2
C1 -> D1 (head),
C2 -> D2 (head),
The steps to upgrade from A to D1/D2 come out normally:
#!
upgrade a -> b3, b3
upgrade a -> b1, b1
upgrade a -> b2, b2
upgrade b1, b2, b3 -> c2, c2
upgrade c2 -> d2, d2
upgrade b1, b2, b3 -> c1, c1
upgrade c1 -> d1, d1
The transitions come out as:
#!
DEBUG:alembic.migration:update a to b3
DEBUG:alembic.migration:new branch insert b1
DEBUG:alembic.migration:new branch insert b2
DEBUG:alembic.migration:merge, delete ['b1', 'b2'], update b3 to c2
DEBUG:alembic.migration:update c2 to d2
It then crashes on b1, b2, b3 -> c1, because it thinks it's supposed to to an UPDATE, but there is no row to UPDATE because all three of b1, b2, b3 are gone. What it really should do here is an INSERT of c1.
The internal mechanics ask the question, "if we have more than one anscestor, we're a MERGE point, therefore we definitely aren't INSERTing an identifier". They also assert that, "if we have only one anscestor, we're not a MERGE point, so if our ansestor is in the current heads, we do an UPDATE and if it isn't, we do an INSERT". The logic here can be simplified, such that, "if none of our ancestors are in the current heads, we do an INSERT":
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: