← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~cjwatson/launchpad:stormify-bugbranch into launchpad:master

 

Colin Watson has proposed merging ~cjwatson/launchpad:stormify-bugbranch into launchpad:master.

Commit message:
Convert BugBranch to Storm

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/380659
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:stormify-bugbranch into launchpad:master.
diff --git a/lib/lp/bugs/model/bug.py b/lib/lp/bugs/model/bug.py
index be4bb07..7ea2576 100644
--- a/lib/lp/bugs/model/bug.py
+++ b/lib/lp/bugs/model/bug.py
@@ -1,4 +1,4 @@
-# Copyright 2009-2019 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2020 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Launchpad bug-related database table classes."""
@@ -59,6 +59,7 @@ from storm.locals import (
     DateTime,
     Int,
     Reference,
+    ReferenceSet,
     )
 from storm.store import (
     EmptyResultSet,
@@ -360,8 +361,8 @@ class Bug(SQLBase, InformationTypeMixin):
     watches = SQLMultipleJoin(
         'BugWatch', joinColumn='bug', orderBy=['bugtracker', 'remotebug'])
     duplicates = SQLMultipleJoin('Bug', joinColumn='duplicateof', orderBy='id')
-    linked_bugbranches = SQLMultipleJoin(
-        'BugBranch', joinColumn='bug', orderBy='id')
+    linked_bugbranches = ReferenceSet(
+        'id', BugBranch.bug_id, order_by=BugBranch.id)
     date_last_message = UtcDateTimeCol(default=None)
     number_of_duplicates = IntCol(notNull=True, default=0)
     message_count = IntCol(notNull=True, default=0)
@@ -1368,7 +1369,8 @@ class Bug(SQLBase, InformationTypeMixin):
 
     def unlinkBranch(self, branch, user):
         """See `IBug`."""
-        bug_branch = BugBranch.selectOneBy(bug=self, branch=branch)
+        bug_branch = Store.of(self).find(
+            BugBranch, BugBranch.bug == self, BugBranch.branch == branch).one()
         if bug_branch is not None:
             self.addChange(BranchUnlinkedFromBug(UTC_NOW, user, branch, self))
             notify(ObjectUnlinkedEvent(branch, self, user=user))
@@ -1385,7 +1387,7 @@ class Bug(SQLBase, InformationTypeMixin):
             branch_ids = [branch.id for branch in linked_branches]
             results = Store.of(self).find(
                 BugBranch,
-                BugBranch.bug == self, In(BugBranch.branchID, branch_ids))
+                BugBranch.bug == self, In(BugBranch.branch_id, branch_ids))
             return results.order_by(BugBranch.id)
 
     def linkMergeProposal(self, merge_proposal, user, check_permissions=True):
diff --git a/lib/lp/bugs/model/bugbranch.py b/lib/lp/bugs/model/bugbranch.py
index 9a8728b..665f177 100644
--- a/lib/lp/bugs/model/bugbranch.py
+++ b/lib/lp/bugs/model/bugbranch.py
@@ -1,16 +1,23 @@
-# Copyright 2009 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2020 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Database classes for linking bugtasks and branches."""
 
+from __future__ import absolute_import, print_function, unicode_literals
+
 __metaclass__ = type
 
-__all__ = ["BugBranch",
-           "BugBranchSet"]
+__all__ = [
+    "BugBranch",
+    "BugBranchSet",
+    ]
 
-from sqlobject import (
-    ForeignKey,
-    IntCol,
+import pytz
+from storm.locals import (
+    DateTime,
+    Int,
+    Reference,
+    Store,
     )
 from zope.interface import implementer
 
@@ -20,23 +27,37 @@ from lp.bugs.interfaces.bugbranch import (
     )
 from lp.registry.interfaces.person import validate_public_person
 from lp.services.database.constants import UTC_NOW
-from lp.services.database.datetimecol import UtcDateTimeCol
 from lp.services.database.interfaces import IStore
-from lp.services.database.sqlbase import SQLBase
+from lp.services.database.stormbase import StormBase
 
 
 @implementer(IBugBranch)
-class BugBranch(SQLBase):
+class BugBranch(StormBase):
     """See `IBugBranch`."""
 
-    datecreated = UtcDateTimeCol(notNull=True, default=UTC_NOW)
-    bug = ForeignKey(dbName="bug", foreignKey="Bug", notNull=True)
-    branch_id = IntCol(dbName="branch", notNull=True)
-    branch = ForeignKey(dbName="branch", foreignKey="Branch", notNull=True)
+    __storm_table__ = 'BugBranch'
+
+    id = Int(primary=True)
+
+    datecreated = DateTime(
+        name='datecreated', tzinfo=pytz.UTC, allow_none=False, default=UTC_NOW)
+    bug_id = Int(name='bug', allow_none=False)
+    bug = Reference(bug_id, 'Bug.id')
+    branch_id = Int(name="branch", allow_none=False)
+    branch = Reference(branch_id, 'Branch.id')
+
+    registrant_id = Int(
+        name='registrant', allow_none=False, validator=validate_public_person)
+    registrant = Reference(registrant_id, 'Person.id')
+
+    def __init__(self, branch, bug, registrant):
+        super(BugBranch, self).__init__()
+        self.branch = branch
+        self.bug = bug
+        self.registrant = registrant
 
-    registrant = ForeignKey(
-        dbName='registrant', foreignKey='Person',
-        storm_validator=validate_public_person, notNull=True)
+    def destroySelf(self):
+        Store.of(self).remove(self)
 
 
 @implementer(IBugBranchSet)
@@ -54,7 +75,7 @@ class BugBranchSet:
 
         visible = get_bug_privacy_filter(user)
         return IStore(BugBranch).find(
-            BugBranch.branchID,
+            BugBranch.branch_id,
             BugBranch.branch_id.is_in(branch_ids),
-            BugTaskFlat.bug_id == BugBranch.bugID,
+            BugTaskFlat.bug_id == BugBranch.bug_id,
             visible).config(distinct=True)
diff --git a/lib/lp/bugs/model/bugtask.py b/lib/lp/bugs/model/bugtask.py
index b682378..2c3b6ef 100644
--- a/lib/lp/bugs/model/bugtask.py
+++ b/lib/lp/bugs/model/bugtask.py
@@ -1,4 +1,4 @@
-# Copyright 2009-2019 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2020 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Classes that implement IBugTask and its related interfaces."""
@@ -1392,7 +1392,7 @@ class BugTaskSet:
                 [(u'bug', unicode(bug_id)) for bug_id in bug_ids],
                 types=[u'specification']).keys())
         bug_ids_with_branches = set(IStore(BugBranch).find(
-                BugBranch.bugID, BugBranch.bugID.is_in(bug_ids)))
+                BugBranch.bug_id, BugBranch.bug_id.is_in(bug_ids)))
         # Badging looks up milestones too : eager load into the storm cache.
         milestoneset = getUtility(IMilestoneSet)
         # And trigger a load:
diff --git a/lib/lp/bugs/model/bugtasksearch.py b/lib/lp/bugs/model/bugtasksearch.py
index 18ea899..39c5cb8 100644
--- a/lib/lp/bugs/model/bugtasksearch.py
+++ b/lib/lp/bugs/model/bugtasksearch.py
@@ -1,4 +1,4 @@
-# Copyright 2009-2019 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2020 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 __metaclass__ = type
@@ -700,11 +700,11 @@ def _build_query(params):
         extra_clauses.append(hw_clause)
 
     def make_branch_clause(branches=None):
-        where = [BugBranch.bugID == BugTaskFlat.bug_id]
+        where = [BugBranch.bug_id == BugTaskFlat.bug_id]
         if branches is not None:
             where.append(
                 search_value_to_storm_where_condition(
-                    BugBranch.branchID, branches))
+                    BugBranch.branch_id, branches))
         return Exists(Select(1, tables=[BugBranch], where=And(*where)))
 
     def make_merge_proposal_clause(merge_proposals=None):
diff --git a/lib/lp/code/model/branch.py b/lib/lp/code/model/branch.py
index cd0b4cc..cd9d69f 100644
--- a/lib/lp/code/model/branch.py
+++ b/lib/lp/code/model/branch.py
@@ -38,7 +38,10 @@ from storm.expr import (
     Select,
     SQL,
     )
-from storm.locals import AutoReload
+from storm.locals import (
+    AutoReload,
+    ReferenceSet,
+    )
 from storm.store import Store
 from zope.component import getUtility
 from zope.event import notify
@@ -441,12 +444,12 @@ class Branch(SQLBase, WebhookTargetMixin, BzrIdentityMixin):
         'Person', joinColumn='branch', otherColumn='person',
         intermediateTable='BranchSubscription', orderBy='name')
 
-    bug_branches = SQLMultipleJoin(
-        'BugBranch', joinColumn='branch', orderBy='id')
+    bug_branches = ReferenceSet(
+        'id', 'BugBranch.branch_id', order_by='BugBranch.id')
 
-    linked_bugs = SQLRelatedJoin(
-        'Bug', joinColumn='branch', otherColumn='bug',
-        intermediateTable='BugBranch', orderBy='id')
+    linked_bugs = ReferenceSet(
+        'id', 'BugBranch.branch_id',
+        'BugBranch.bug_id', 'Bug.id', order_by='Bug.id')
 
     def getLinkedBugTasks(self, user, status_filter=None):
         """See `IBranch`."""
diff --git a/lib/lp/code/model/branchcollection.py b/lib/lp/code/model/branchcollection.py
index 24c23fa..79411cc 100644
--- a/lib/lp/code/model/branchcollection.py
+++ b/lib/lp/code/model/branchcollection.py
@@ -334,7 +334,7 @@ class GenericBranchCollection:
             # need for validity etc in the /branches API call.
             load_related(Person, rows,
                 ['ownerID', 'registrantID', 'reviewerID'])
-            load_referencing(BugBranch, rows, ['branchID'])
+            load_referencing(BugBranch, rows, ['branch_id'])
 
         def cache_permission(branch):
             if self._user:
@@ -549,11 +549,11 @@ class GenericBranchCollection:
             store = IStore(BugBranch)
             rs = store.using(
                 BugBranch,
-                Join(BugTask, BugTask.bugID == BugBranch.bugID),
+                Join(BugTask, BugTask.bugID == BugBranch.bug_id),
             ).find(
                 (BugTask, BugBranch),
-                BugBranch.bugID.is_in(bug_ids),
-                BugBranch.branchID.is_in(source_branch_ids)
+                BugBranch.bug_id.is_in(bug_ids),
+                BugBranch.branch_id.is_in(source_branch_ids)
             )
 
             # Build up a collection of bugtasks for each branch
@@ -731,7 +731,7 @@ class GenericBranchCollection:
         """See `IBranchCollection`."""
         bug_ids = [bug.id for bug in bugs]
         return self._filterBy(
-            [In(BugBranch.bugID, bug_ids)],
+            [In(BugBranch.bug_id, bug_ids)],
             table=BugBranch,
             join=Join(BugBranch, BugBranch.branch == Branch.id),
             symmetric=False)
diff --git a/lib/lp/code/model/tests/test_branch.py b/lib/lp/code/model/tests/test_branch.py
index 2bd0b62..3a04a03 100644
--- a/lib/lp/code/model/tests/test_branch.py
+++ b/lib/lp/code/model/tests/test_branch.py
@@ -1,4 +1,4 @@
-# Copyright 2009-2018 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2020 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Tests for Branches."""
@@ -1590,10 +1590,10 @@ class TestBranchDeletionConsequences(TestCase):
         """break_links allows deleting a branch with a bug."""
         bug1 = self.factory.makeBug()
         bug1.linkBranch(self.branch, self.branch.owner)
-        bug_branch1 = bug1.linked_bugbranches[0]
+        bug_branch1 = bug1.linked_bugbranches.first()
         bug_branch1_id = removeSecurityProxy(bug_branch1).id
         self.branch.destroySelf(break_references=True)
-        self.assertRaises(SQLObjectNotFound, BugBranch.get, bug_branch1_id)
+        self.assertIsNone(IStore(BugBranch).get(BugBranch, bug_branch1_id))
 
     def test_branchWithSpecRequirements(self):
         """Deletion requirements for a branch with a spec are right."""
@@ -2761,7 +2761,7 @@ class TestBranchBugLinks(TestCaseWithFactory):
 
         self.assertEqual(branch.linked_bugs.count(), 1)
 
-        linked_bug = branch.linked_bugs[0]
+        linked_bug = branch.linked_bugs.first()
 
         self.assertEqual(linked_bug.id, bug.id)