From c20ec64ad8b8281d41c0a47abaef1c5f19edb6fe Mon Sep 17 00:00:00 2001 From: "James E. Blair" Date: Mon, 31 Oct 2016 14:15:34 -0700 Subject: [PATCH] Fix error when deleting project When deleting a project, an error like: delete statement on table change_conflict expected to delete X rows might occur. This is because the self-referential many-to-many relationship of change conflicts was trying to delete entries from the secondary table twice. Handle the case of deleting a project better. Change-Id: Ib919e36ede14a3a1054fcd6b95ea43ccae046d3e --- gertty/db.py | 19 +++++++++++++++++++ gertty/sync.py | 3 ++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/gertty/db.py b/gertty/db.py index b96abe7..ad25ecb 100644 --- a/gertty/db.py +++ b/gertty/db.py @@ -200,6 +200,25 @@ class Project(object): self.subscribed = subscribed self.description = description + def delete(self): + # This unusual delete method is to accomodate the + # self-referential many-to-many relationship from + # change_conflict. With the default cascade configuration, + # the entry from the association table is deleted regardless + # of whether the change being deleted is change1 or change2. + # However, when both changes are deleted at once (only likely + # to happen when the entire project is deleted), SQLAlchemy + # cascades through both relationships and attempts to delete + # the entry twice. Since we rarely delete projects, we add a + # special case here to delete a project's changes one at a + # time to avoid this situation. + session = Session.object_session(self) + for c in self.changes: + session.delete(c) + session.flush() + session.expire_all() + session.delete(self) + def createChange(self, *args, **kw): session = Session.object_session(self) args = [self] + list(args) diff --git a/gertty/sync.py b/gertty/sync.py index 29ebf63..b11522a 100644 --- a/gertty/sync.py +++ b/gertty/sync.py @@ -236,7 +236,8 @@ class SyncProjectListTask(Task): local_keys = set(local.keys()) for name in local_keys-remote_keys: - session.delete(local[name]) + self.log.info("Deleted project %s", name) + local[name].delete() for name in remote_keys-local_keys: p = remote[name]