← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~cjwatson/launchpad:bpph-spn into launchpad:master

 

Colin Watson has proposed merging ~cjwatson/launchpad:bpph-spn into launchpad:master.

Commit message:
Populate BinaryPackagePublishingHistory.sourcepackagename

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/429770

This denormalizes the current `BinaryPackagePublishingHistory.source_package_name` property, allowing us to avoid expensive joins through `BinaryPackageRelease` and `BinaryPackageBuild` when looking up binary publications for a particular source package name.

DB patch: https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/429767
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:bpph-spn into launchpad:master.
diff --git a/lib/lp/scripts/garbo.py b/lib/lp/scripts/garbo.py
index 80c3dcc..348b7a7 100644
--- a/lib/lp/scripts/garbo.py
+++ b/lib/lp/scripts/garbo.py
@@ -25,6 +25,7 @@ import six
 import transaction
 from contrib.glock import GlobalLock, LockAlreadyAcquired
 from psycopg2 import IntegrityError
+from storm.databases.postgres import Returning
 from storm.expr import (
     SQL,
     And,
@@ -127,6 +128,7 @@ from lp.soyuz.interfaces.publishing import active_publishing_status
 from lp.soyuz.model.archive import Archive
 from lp.soyuz.model.archiveauthtoken import ArchiveAuthToken
 from lp.soyuz.model.archivesubscriber import ArchiveSubscriber
+from lp.soyuz.model.binarypackagebuild import BinaryPackageBuild
 from lp.soyuz.model.binarypackagerelease import BinaryPackageRelease
 from lp.soyuz.model.distributionsourcepackagecache import (
     DistributionSourcePackageCache,
@@ -2179,6 +2181,68 @@ class BinaryPackagePublishingHistoryFormatPopulator(TunableLoop):
         transaction.commit()
 
 
+# XXX cjwatson 2022-09-12: Remove this when it is complete.
+class BinaryPackagePublishingHistorySPNPopulator(BulkPruner):
+    """Populate the new BPPH.sourcepackagename column."""
+
+    target_table_class = BinaryPackagePublishingHistory
+
+    ids_to_prune_query = convert_storm_clause_to_string(
+        Select(
+            BinaryPackagePublishingHistory.id,
+            where=And(
+                BinaryPackagePublishingHistory.sourcepackagename == None,
+                BinaryPackagePublishingHistory.binarypackagerelease
+                == BinaryPackageRelease.id,
+                BinaryPackageRelease.build != None,
+            ),
+        )
+    )
+
+    def __call__(self, chunk_size):
+        """See `TunableLoop`."""
+        chunk_size = int(chunk_size + 0.5)
+        ids = [
+            row[0]
+            for row in self.store.execute(
+                SQL(
+                    "SELECT * FROM cursor_fetch(%s, %s) AS f(id integer)",
+                    params=(self.cursor_name, chunk_size),
+                )
+            )
+        ]
+        BPPH = BinaryPackagePublishingHistory
+        update = Returning(
+            BulkUpdate(
+                {
+                    BPPH.sourcepackagenameID: (
+                        SourcePackageRelease.sourcepackagenameID
+                    )
+                },
+                table=BPPH,
+                values=(
+                    BinaryPackageRelease,
+                    BinaryPackageBuild,
+                    SourcePackageRelease,
+                ),
+                where=And(
+                    BPPH.binarypackagerelease == BinaryPackageRelease.id,
+                    BinaryPackageRelease.build == BinaryPackageBuild.id,
+                    BinaryPackageBuild.source_package_release
+                    == SourcePackageRelease.id,
+                    BPPH.id.is_in(ids),
+                ),
+            ),
+            columns=(BPPH.id,),
+        )
+        if ids:
+            updated_ids = list(self.store.execute(update))
+            self._num_removed = len(updated_ids)
+        else:
+            self._num_removed = 0
+        transaction.commit()
+
+
 class BaseDatabaseGarbageCollector(LaunchpadCronScript):
     """Abstract base class to run a collection of TunableLoops."""
 
@@ -2493,6 +2557,7 @@ class DailyDatabaseGarbageCollector(BaseDatabaseGarbageCollector):
         AnswerContactPruner,
         ArchiveArtifactoryColumnsPopulator,
         BinaryPackagePublishingHistoryFormatPopulator,
+        BinaryPackagePublishingHistorySPNPopulator,
         BranchJobPruner,
         BugNotificationPruner,
         BugWatchActivityPruner,
diff --git a/lib/lp/scripts/tests/test_garbo.py b/lib/lp/scripts/tests/test_garbo.py
index 1407924..b33159e 100644
--- a/lib/lp/scripts/tests/test_garbo.py
+++ b/lib/lp/scripts/tests/test_garbo.py
@@ -128,6 +128,7 @@ from lp.soyuz.enums import (
 )
 from lp.soyuz.interfaces.archive import NAMED_AUTH_TOKEN_FEATURE_FLAG
 from lp.soyuz.interfaces.livefs import LIVEFS_FEATURE_FLAG
+from lp.soyuz.interfaces.publishing import IPublishingSet
 from lp.soyuz.model.distributionsourcepackagecache import (
     DistributionSourcePackageCache,
 )
@@ -2520,6 +2521,42 @@ class TestGarbo(FakeAdapterMixin, TestCaseWithFactory):
                 removeSecurityProxy(bpph)._binarypackageformat,
             )
 
+    def test_BinaryPackagePublishingHistorySPNPopulator(self):
+        switch_dbuser("testadmin")
+        bpphs = [
+            self.factory.makeBinaryPackagePublishingHistory() for _ in range(2)
+        ]
+        spns = [bpph.sourcepackagename for bpph in bpphs]
+        for bpph in bpphs:
+            removeSecurityProxy(bpph).sourcepackagename = None
+        ci_build = self.factory.makeCIBuild()
+        wheel_bpr = ci_build.createBinaryPackageRelease(
+            self.factory.makeBinaryPackageName(),
+            "1.0",
+            "test summary",
+            "test description",
+            BinaryPackageFormat.WHL,
+            False,
+        )
+        bpphs.append(
+            getUtility(IPublishingSet).publishBinaries(
+                bpphs[0].archive,
+                bpphs[0].distroseries,
+                bpphs[0].pocket,
+                {wheel_bpr: (None, None, None, None)},
+            )[0]
+        )
+        transaction.commit()
+        self.assertIs(None, bpph.sourcepackagename)
+
+        self.runDaily()
+
+        # Publications with BinaryPackageBuilds are backfilled.
+        for bpph, spn in zip(bpphs, spns):
+            self.assertEqual(spn, bpph.sourcepackagename)
+        # Other publications are left alone.
+        self.assertIsNone(bpphs[2].sourcepackagename)
+
 
 class TestGarboTasks(TestCaseWithFactory):
     layer = LaunchpadZopelessLayer
diff --git a/lib/lp/soyuz/doc/distroarchseriesbinarypackage.rst b/lib/lp/soyuz/doc/distroarchseriesbinarypackage.rst
index ecbb1d7..60957e5 100644
--- a/lib/lp/soyuz/doc/distroarchseriesbinarypackage.rst
+++ b/lib/lp/soyuz/doc/distroarchseriesbinarypackage.rst
@@ -91,6 +91,7 @@ needs to be removed.
     ...     datemadepending=None,
     ...     dateremoved=None,
     ...     archive=hoary_i386.main_archive,
+    ...     sourcepackagename=bpr.build.source_package_name,
     ... )
 
 XXX: noodles 2008-11-06 bug=294585: The dependency on a database id
@@ -139,6 +140,7 @@ needs to be removed.
     ...     datemadepending=None,
     ...     dateremoved=None,
     ...     archive=hoary_i386.main_archive,
+    ...     sourcepackagename=bpr.build.source_package_name,
     ... )
 
 Then, we ensure that grabbing the current release of pmount and the old
diff --git a/lib/lp/soyuz/interfaces/binarypackagebuild.py b/lib/lp/soyuz/interfaces/binarypackagebuild.py
index 2790093..f294889 100644
--- a/lib/lp/soyuz/interfaces/binarypackagebuild.py
+++ b/lib/lp/soyuz/interfaces/binarypackagebuild.py
@@ -37,6 +37,7 @@ from lp.buildmaster.interfaces.packagebuild import (
     IPackageBuildView,
 )
 from lp.buildmaster.interfaces.processor import IProcessor
+from lp.registry.interfaces.sourcepackagename import ISourcePackageName
 from lp.soyuz.interfaces.publishing import ISourcePackagePublishingHistory
 from lp.soyuz.interfaces.sourcepackagerelease import ISourcePackageRelease
 
@@ -86,6 +87,13 @@ class IBinaryPackageBuildView(IPackageBuildView):
         readonly=True,
     )
 
+    source_package_name = Reference(
+        title=_("Source package name"),
+        schema=ISourcePackageName,
+        required=True,
+        readonly=True,
+    )
+
     # Properties
     current_source_publication = exported(
         Reference(
diff --git a/lib/lp/soyuz/interfaces/publishing.py b/lib/lp/soyuz/interfaces/publishing.py
index aa5d14f..5b861a8 100644
--- a/lib/lp/soyuz/interfaces/publishing.py
+++ b/lib/lp/soyuz/interfaces/publishing.py
@@ -963,6 +963,9 @@ class IBinaryPackagePublishingHistoryPublic(IPublishingView):
         ),
         as_of="devel",
     )
+    sourcepackagename = Attribute(
+        "The source package name that built this binary."
+    )
 
     def getOtherPublications():
         """Return remaining publications with the same overrides.
diff --git a/lib/lp/soyuz/model/publishing.py b/lib/lp/soyuz/model/publishing.py
index faec822..1c8e497 100644
--- a/lib/lp/soyuz/model/publishing.py
+++ b/lib/lp/soyuz/model/publishing.py
@@ -791,6 +791,11 @@ class BinaryPackagePublishingHistory(SQLBase, ArchivePublisherBase):
         default=None,
     )
     removal_comment = StringCol(dbName="removal_comment", default=None)
+    sourcepackagename = ForeignKey(
+        foreignKey="SourcePackageName",
+        dbName="sourcepackagename",
+        notNull=False,
+    )
 
     @property
     def binarypackageformat(self):
@@ -845,6 +850,8 @@ class BinaryPackagePublishingHistory(SQLBase, ArchivePublisherBase):
     @property
     def source_package_name(self):
         """See `ISourcePackagePublishingHistory`"""
+        # XXX cjwatson 2022-09-12: Simplify this once self.sourcepackagename
+        # is populated.
         return self.binarypackagerelease.sourcepackagename
 
     @property
@@ -1122,6 +1129,7 @@ class BinaryPackagePublishingHistory(SQLBase, ArchivePublisherBase):
                 archive=debug.archive,
                 phased_update_percentage=new_phased_update_percentage,
                 _channel=removeSecurityProxy(debug)._channel,
+                sourcepackagename=debug.sourcepackagename,
             )
 
         # Append the modified package publishing entry
@@ -1140,6 +1148,11 @@ class BinaryPackagePublishingHistory(SQLBase, ArchivePublisherBase):
             creator=creator,
             phased_update_percentage=new_phased_update_percentage,
             _channel=self._channel,
+            sourcepackagename=(
+                bpr.build.source_package_name
+                if bpr.build is not None
+                else None
+            ),
         )
 
     def copyTo(self, distroseries, pocket, archive):
@@ -1388,6 +1401,7 @@ class PublishingSet:
                 BPPH.phased_update_percentage,
                 BPPH.status,
                 BPPH.datecreated,
+                BPPH.sourcepackagename,
             ),
             [
                 (
@@ -1405,6 +1419,11 @@ class PublishingSet:
                     phased_update_percentage,
                     PackagePublishingStatus.PENDING,
                     UTC_NOW,
+                    (
+                        bpr.build.source_package_name
+                        if bpr.build is not None
+                        else None
+                    ),
                 )
                 for (
                     das,
diff --git a/lib/lp/soyuz/scripts/gina/handlers.py b/lib/lp/soyuz/scripts/gina/handlers.py
index eb4a468..1e0ff96 100644
--- a/lib/lp/soyuz/scripts/gina/handlers.py
+++ b/lib/lp/soyuz/scripts/gina/handlers.py
@@ -1052,6 +1052,7 @@ class BinaryPackagePublisher:
             datemadepending=None,
             dateremoved=None,
             archive=archive,
+            sourcepackagename=binarypackage.build.source_package_name,
         )
 
         log.info(
diff --git a/lib/lp/soyuz/scripts/tests/test_copypackage.py b/lib/lp/soyuz/scripts/tests/test_copypackage.py
index 66ff8b7..eb3d972 100644
--- a/lib/lp/soyuz/scripts/tests/test_copypackage.py
+++ b/lib/lp/soyuz/scripts/tests/test_copypackage.py
@@ -1447,6 +1447,7 @@ class TestDoDirectCopy(BaseDoCopyTests, TestCaseWithFactory):
             priority=bin_i386.priority,
             status=PackagePublishingStatus.PENDING,
             datecreated=UTC_NOW,
+            sourcepackagename=bin_i386.sourcepackagename,
         )
         # Now we can copy the package with binaries.
         copies = self.doCopy(
diff --git a/lib/lp/soyuz/tests/test_publishing.py b/lib/lp/soyuz/tests/test_publishing.py
index 7988905..cd0828d 100644
--- a/lib/lp/soyuz/tests/test_publishing.py
+++ b/lib/lp/soyuz/tests/test_publishing.py
@@ -653,6 +653,9 @@ class SoyuzTestPublisher:
                 archive=archive,
                 phased_update_percentage=phased_update_percentage,
                 _channel=channel,
+                sourcepackagename=(
+                    binarypackagerelease.build.source_package_name
+                ),
             )
             if status == PackagePublishingStatus.PUBLISHED:
                 pub.datepublished = UTC_NOW
diff --git a/lib/lp/soyuz/tests/test_publishing_models.py b/lib/lp/soyuz/tests/test_publishing_models.py
index 47121ce..1c16710 100644
--- a/lib/lp/soyuz/tests/test_publishing_models.py
+++ b/lib/lp/soyuz/tests/test_publishing_models.py
@@ -340,6 +340,10 @@ class TestBinaryPackagePublishingHistory(TestCaseWithFactory):
     def test_source_package_name(self):
         bpph = self.factory.makeBinaryPackagePublishingHistory()
         self.assertEqual(
+            bpph.binarypackagerelease.build.source_package_name,
+            bpph.sourcepackagename,
+        )
+        self.assertEqual(
             bpph.binarypackagerelease.sourcepackagename,
             bpph.source_package_name,
         )
@@ -353,9 +357,14 @@ class TestBinaryPackagePublishingHistory(TestCaseWithFactory):
         with person_logged_in(team_owner):
             bpph = self.factory.makeBinaryPackagePublishingHistory(archive=ppa)
             self.assertEqual(
+                bpph.binarypackagerelease.build.source_package_name,
+                bpph.sourcepackagename,
+            )
+            self.assertEqual(
                 bpph.binarypackagerelease.sourcepackagename,
                 bpph.source_package_name,
             )
+        self.assertRaises(Unauthorized, getattr, bpph, "sourcepackagename")
         self.assertRaises(Unauthorized, getattr, bpph, "source_package_name")
 
     def test_source_package_version(self):