Merge lp:~cjwatson/launchpad/getpublishedbinaries-sorting into lp:launchpad

Proposed by Colin Watson
Status: Merged
Merged at revision: 17425
Proposed branch: lp:~cjwatson/launchpad/getpublishedbinaries-sorting
Merge into: lp:launchpad
Diff against target: 752 lines (+198/-148)
12 files modified
lib/lp/archivepublisher/tests/test_ftparchive.py (+2/-2)
lib/lp/archivepublisher/tests/test_publisher.py (+1/-1)
lib/lp/registry/doc/distroseries.txt (+2/-2)
lib/lp/soyuz/doc/archive.txt (+23/-23)
lib/lp/soyuz/interfaces/archive.py (+23/-4)
lib/lp/soyuz/model/archive.py (+110/-102)
lib/lp/soyuz/scripts/tests/test_initialize_distroseries.py (+3/-3)
lib/lp/soyuz/stories/ppa/xx-ppa-packages.txt (+1/-1)
lib/lp/soyuz/stories/webservice/xx-binary-package-publishing.txt (+1/-1)
lib/lp/soyuz/stories/webservice/xx-source-package-publishing.txt (+1/-1)
lib/lp/soyuz/tests/test_archive.py (+28/-6)
lib/lp/soyuz/tests/test_packagecopyjob.py (+3/-2)
To merge this branch: bzr merge lp:~cjwatson/launchpad/getpublishedbinaries-sorting
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+255822@code.launchpad.net

Commit message

Improve performance of Archive.getPublishedSources and Archive.getPublishedBinaries, including adding order_by_date options which provide a reasonable way for applications to catch up with new publications.

Description of the change

Improve performance of Archive.getPublishedSources and Archive.getPublishedBinaries, including adding order_by_date options which provide a reasonable way for applications to catch up with new publications.

It would be nice to insert StormRangeFactory in here too, providing a stable way to slice the returned collection. As explained in the linked bug, though, that is currently difficult, and this is enough to provide a safe API provided that the status filter is omitted (so that entries don't disappear from the collection during iteration).

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/archivepublisher/tests/test_ftparchive.py'
--- lib/lp/archivepublisher/tests/test_ftparchive.py 2014-11-24 16:03:20 +0000
+++ lib/lp/archivepublisher/tests/test_ftparchive.py 2015-04-13 17:14:35 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2013 Canonical Ltd. This software is licensed under the1# Copyright 2009-2015 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Tests for ftparchive.py"""4"""Tests for ftparchive.py"""
@@ -308,7 +308,7 @@
308 self._publisher = SamplePublisher(self._archive)308 self._publisher = SamplePublisher(self._archive)
309 fa = self._setUpFTPArchiveHandler()309 fa = self._setUpFTPArchiveHandler()
310 pubs = self._archive.getAllPublishedBinaries(310 pubs = self._archive.getAllPublishedBinaries(
311 name="pmount", status=PackagePublishingStatus.PUBLISHED,311 name=u"pmount", status=PackagePublishingStatus.PUBLISHED,
312 distroarchseries=self._distribution.getSeries("hoary")["hppa"])312 distroarchseries=self._distribution.getSeries("hoary")["hppa"])
313 for pub in pubs:313 for pub in pubs:
314 pub.changeOverride(new_phased_update_percentage=30).setPublished()314 pub.changeOverride(new_phased_update_percentage=30).setPublished()
315315
=== modified file 'lib/lp/archivepublisher/tests/test_publisher.py'
--- lib/lp/archivepublisher/tests/test_publisher.py 2015-03-18 17:51:16 +0000
+++ lib/lp/archivepublisher/tests/test_publisher.py 2015-04-13 17:14:35 +0000
@@ -407,7 +407,7 @@
407 for pu_build in pu_i386.builds:407 for pu_build in pu_i386.builds:
408 pu_build.publish()408 pu_build.publish()
409409
410 publications = archive.getAllPublishedBinaries(name="bin-i386")410 publications = archive.getAllPublishedBinaries(name=u"bin-i386")
411411
412 self.assertEqual(1, publications.count())412 self.assertEqual(1, publications.count())
413 self.assertEqual(413 self.assertEqual(
414414
=== modified file 'lib/lp/registry/doc/distroseries.txt'
--- lib/lp/registry/doc/distroseries.txt 2014-11-27 22:13:36 +0000
+++ lib/lp/registry/doc/distroseries.txt 2015-04-13 17:14:35 +0000
@@ -342,11 +342,11 @@
342 >>> humpy.getPublishedSources('pmount').count()342 >>> humpy.getPublishedSources('pmount').count()
343 1343 1
344 >>> hoary.main_archive.getAllPublishedBinaries(344 >>> hoary.main_archive.getAllPublishedBinaries(
345 ... distroarchseries=hoary['i386'], name='pmount',345 ... distroarchseries=hoary['i386'], name=u'pmount',
346 ... status=PackagePublishingStatus.PUBLISHED).count()346 ... status=PackagePublishingStatus.PUBLISHED).count()
347 1347 1
348 >>> humpy.main_archive.getAllPublishedBinaries(348 >>> humpy.main_archive.getAllPublishedBinaries(
349 ... distroarchseries=humpy['i386'], name='pmount').count()349 ... distroarchseries=humpy['i386'], name=u'pmount').count()
350 1350 1
351351
352Check if the attributes of an DRSPR instance for the just initialized352Check if the attributes of an DRSPR instance for the just initialized
353353
=== modified file 'lib/lp/soyuz/doc/archive.txt'
--- lib/lp/soyuz/doc/archive.txt 2014-07-15 02:12:04 +0000
+++ lib/lp/soyuz/doc/archive.txt 2015-04-13 17:14:35 +0000
@@ -295,46 +295,46 @@
295295
296'name' filter supporting partial string matching and 'not-found':296'name' filter supporting partial string matching and 'not-found':
297297
298 >>> cprov_archive.getPublishedOnDiskBinaries(name='pmou').count()298 >>> cprov_archive.getPublishedOnDiskBinaries(name=u'pmou').count()
299 1299 1
300 >>> cprov_archive.getAllPublishedBinaries(name='pmou').count()300 >>> cprov_archive.getAllPublishedBinaries(name=u'pmou').count()
301 2301 2
302 >>> cprov_archive.getPublishedOnDiskBinaries(name='foo').count()302 >>> cprov_archive.getPublishedOnDiskBinaries(name=u'foo').count()
303 0303 0
304 >>> cprov_archive.getAllPublishedBinaries(name='foo').count()304 >>> cprov_archive.getAllPublishedBinaries(name=u'foo').count()
305 0305 0
306306
307Combining 'name' filter and 'exact_match' flag:307Combining 'name' filter and 'exact_match' flag:
308308
309 >>> cprov_archive.getAllPublishedBinaries(309 >>> cprov_archive.getAllPublishedBinaries(
310 ... name='pmou', exact_match=True).count()310 ... name=u'pmou', exact_match=True).count()
311 0311 0
312 >>> cprov_archive.getAllPublishedBinaries(312 >>> cprov_archive.getAllPublishedBinaries(
313 ... name='pmount', exact_match=True).count()313 ... name=u'pmount', exact_match=True).count()
314 2314 2
315 >>> cprov_archive.getPublishedOnDiskBinaries(315 >>> cprov_archive.getPublishedOnDiskBinaries(
316 ... name='pmou', exact_match=True).count()316 ... name=u'pmou', exact_match=True).count()
317 0317 0
318 >>> cprov_archive.getPublishedOnDiskBinaries(318 >>> cprov_archive.getPublishedOnDiskBinaries(
319 ... name='pmount', exact_match=True).count()319 ... name=u'pmount', exact_match=True).count()
320 1320 1
321321
322It's possible to associate 'name' and 'version' filters:322It's possible to associate 'name' and 'version' filters:
323323
324 >>> cprov_archive.getPublishedOnDiskBinaries(324 >>> cprov_archive.getPublishedOnDiskBinaries(
325 ... name='moz', version='1.0').count()325 ... name=u'moz', version='1.0').count()
326 2326 2
327327
328 >>> cprov_archive.getAllPublishedBinaries(328 >>> cprov_archive.getAllPublishedBinaries(
329 ... name='moz', version='1.0').count()329 ... name=u'moz', version='1.0').count()
330 2330 2
331331
332 >>> cprov_archive.getPublishedOnDiskBinaries(332 >>> cprov_archive.getPublishedOnDiskBinaries(
333 ... name='moz', version='666').count()333 ... name=u'moz', version='666').count()
334 0334 0
335335
336 >>> cprov_archive.getAllPublishedBinaries(336 >>> cprov_archive.getAllPublishedBinaries(
337 ... name='moz', version='666').count()337 ... name=u'moz', version='666').count()
338 0338 0
339339
340Both methods do not support passing the 'version' filter if the 'name'340Both methods do not support passing the 'version' filter if the 'name'
@@ -429,44 +429,44 @@
429Associating 'name' and 'status' filters:429Associating 'name' and 'status' filters:
430430
431 >>> status_lookup = cprov_archive.getPublishedOnDiskBinaries(431 >>> status_lookup = cprov_archive.getPublishedOnDiskBinaries(
432 ... name='pmount', status=active_status)432 ... name=u'pmount', status=active_status)
433 >>> status_lookup.count()433 >>> status_lookup.count()
434 1434 1
435435
436 >>> status_lookup = cprov_archive.getAllPublishedBinaries(436 >>> status_lookup = cprov_archive.getAllPublishedBinaries(
437 ... name='pmount', status=active_status)437 ... name=u'pmount', status=active_status)
438 >>> status_lookup.count()438 >>> status_lookup.count()
439 2439 2
440440
441 >>> status_lookup = cprov_archive.getPublishedOnDiskBinaries(441 >>> status_lookup = cprov_archive.getPublishedOnDiskBinaries(
442 ... name='foo', status=active_status)442 ... name=u'foo', status=active_status)
443 >>> status_lookup.count()443 >>> status_lookup.count()
444 0444 0
445445
446 >>> status_lookup = cprov_archive.getAllPublishedBinaries(446 >>> status_lookup = cprov_archive.getAllPublishedBinaries(
447 ... name='foo', status=active_status)447 ... name=u'foo', status=active_status)
448 >>> status_lookup.count()448 >>> status_lookup.count()
449 0449 0
450450
451Associating 'name', 'version' and 'status' filters:451Associating 'name', 'version' and 'status' filters:
452452
453 >>> status_lookup = cprov_archive.getPublishedOnDiskBinaries(453 >>> status_lookup = cprov_archive.getPublishedOnDiskBinaries(
454 ... name='pmount', version='0.1-1', status=active_status)454 ... name=u'pmount', version='0.1-1', status=active_status)
455 >>> status_lookup.count()455 >>> status_lookup.count()
456 1456 1
457457
458 >>> status_lookup = cprov_archive.getAllPublishedBinaries(458 >>> status_lookup = cprov_archive.getAllPublishedBinaries(
459 ... name='pmount', version='0.1-1', status=active_status)459 ... name=u'pmount', version='0.1-1', status=active_status)
460 >>> status_lookup.count()460 >>> status_lookup.count()
461 2461 2
462462
463 >>> status_lookup = cprov_archive.getPublishedOnDiskBinaries(463 >>> status_lookup = cprov_archive.getPublishedOnDiskBinaries(
464 ... name='pmount', version='666', status=active_status)464 ... name=u'pmount', version='666', status=active_status)
465 >>> status_lookup.count()465 >>> status_lookup.count()
466 0466 0
467467
468 >>> status_lookup = cprov_archive.getAllPublishedBinaries(468 >>> status_lookup = cprov_archive.getAllPublishedBinaries(
469 ... name='pmount', version='666', status=active_status)469 ... name=u'pmount', version='666', status=active_status)
470 >>> status_lookup.count()470 >>> status_lookup.count()
471 0471 0
472472
@@ -474,13 +474,13 @@
474and 'exact_match' flag:474and 'exact_match' flag:
475475
476 >>> status_lookup = cprov_archive.getAllPublishedBinaries(476 >>> status_lookup = cprov_archive.getAllPublishedBinaries(
477 ... name='pmount', version='0.1-1', distroarchseries=warty_i386,477 ... name=u'pmount', version='0.1-1', distroarchseries=warty_i386,
478 ... status=active_status, exact_match=True)478 ... status=active_status, exact_match=True)
479 >>> status_lookup.count()479 >>> status_lookup.count()
480 1480 1
481481
482 >>> status_lookup = cprov_archive.getAllPublishedBinaries(482 >>> status_lookup = cprov_archive.getAllPublishedBinaries(
483 ... name='pmount', version='0.1-1',483 ... name=u'pmount', version='0.1-1',
484 ... distroarchseries=[warty_i386, warty_hppa],484 ... distroarchseries=[warty_i386, warty_hppa],
485 ... status=active_status, exact_match=True)485 ... status=active_status, exact_match=True)
486 >>> status_lookup.count()486 >>> status_lookup.count()
@@ -592,7 +592,7 @@
592 >>> cprov_archive.number_of_binaries592 >>> cprov_archive.number_of_binaries
593 3593 3
594 >>> cprov_archive.getAllPublishedBinaries(594 >>> cprov_archive.getAllPublishedBinaries(
595 ... name='mozilla-firefox')[0].supersede()595 ... name=u'mozilla-firefox')[0].supersede()
596596
597 >>> cprov_archive.number_of_binaries597 >>> cprov_archive.number_of_binaries
598 2598 2
599599
=== modified file 'lib/lp/soyuz/interfaces/archive.py'
--- lib/lp/soyuz/interfaces/archive.py 2014-08-14 10:08:28 +0000
+++ lib/lp/soyuz/interfaces/archive.py 2015-04-13 17:14:35 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2013 Canonical Ltd. This software is licensed under the1# Copyright 2009-2015 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Archive interfaces."""4"""Archive interfaces."""
@@ -449,6 +449,12 @@
449 "than or equal to this date."),449 "than or equal to this date."),
450 required=False),450 required=False),
451 component_name=TextLine(title=_("Component name"), required=False),451 component_name=TextLine(title=_("Component name"), required=False),
452 order_by_date=Bool(
453 title=_("Order by creation date"),
454 description=_("Return newest results first. This is recommended "
455 "for applications that need to catch up with "
456 "publications since their last run."),
457 required=False),
452 )458 )
453 # Really returns ISourcePackagePublishingHistory, see below for459 # Really returns ISourcePackagePublishingHistory, see below for
454 # patch to avoid circular import.460 # patch to avoid circular import.
@@ -457,7 +463,7 @@
457 def api_getPublishedSources(name=None, version=None, status=None,463 def api_getPublishedSources(name=None, version=None, status=None,
458 distroseries=None, pocket=None,464 distroseries=None, pocket=None,
459 exact_match=False, created_since_date=None,465 exact_match=False, created_since_date=None,
460 component_name=None):466 component_name=None, order_by_date=False):
461 """All `ISourcePackagePublishingHistory` target to this archive."""467 """All `ISourcePackagePublishingHistory` target to this archive."""
462 # It loads additional related objects only needed in the API call468 # It loads additional related objects only needed in the API call
463 # context (i.e. security checks and entries marshalling).469 # context (i.e. security checks and entries marshalling).
@@ -465,7 +471,8 @@
465 def getPublishedSources(name=None, version=None, status=None,471 def getPublishedSources(name=None, version=None, status=None,
466 distroseries=None, pocket=None,472 distroseries=None, pocket=None,
467 exact_match=False, created_since_date=None,473 exact_match=False, created_since_date=None,
468 eager_load=False, component_name=None):474 eager_load=False, component_name=None,
475 order_by_date=False):
469 """All `ISourcePackagePublishingHistory` target to this archive.476 """All `ISourcePackagePublishingHistory` target to this archive.
470477
471 :param name: source name filter (exact match or SQL LIKE controlled478 :param name: source name filter (exact match or SQL LIKE controlled
@@ -482,6 +489,9 @@
482 is greater than or equal to this date.489 is greater than or equal to this date.
483 :param component_name: component filter. Only return source packages490 :param component_name: component filter. Only return source packages
484 that are in this component.491 that are in this component.
492 :param order_by_date: Order publications by descending creation date
493 and then by descending ID. This is suitable for applications
494 that need to catch up with publications since their last run.
485495
486 :return: SelectResults containing `ISourcePackagePublishingHistory`,496 :return: SelectResults containing `ISourcePackagePublishingHistory`,
487 ordered by name. If there are multiple results for the same497 ordered by name. If there are multiple results for the same
@@ -1110,6 +1120,12 @@
1110 description=_("Return ordered results by default, but specifying "1120 description=_("Return ordered results by default, but specifying "
1111 "False will return results more quickly."),1121 "False will return results more quickly."),
1112 required=False, readonly=True),1122 required=False, readonly=True),
1123 order_by_date=Bool(
1124 title=_("Order by creation date"),
1125 description=_("Return newest results first. This is recommended "
1126 "for applications that need to catch up with "
1127 "publications since their last run."),
1128 required=False),
1113 )1129 )
1114 # Really returns ISourcePackagePublishingHistory, see below for1130 # Really returns ISourcePackagePublishingHistory, see below for
1115 # patch to avoid circular import.1131 # patch to avoid circular import.
@@ -1119,7 +1135,7 @@
1119 def getAllPublishedBinaries(name=None, version=None, status=None,1135 def getAllPublishedBinaries(name=None, version=None, status=None,
1120 distroarchseries=None, pocket=None,1136 distroarchseries=None, pocket=None,
1121 exact_match=False, created_since_date=None,1137 exact_match=False, created_since_date=None,
1122 ordered=True):1138 ordered=True, order_by_date=False):
1123 """All `IBinaryPackagePublishingHistory` target to this archive.1139 """All `IBinaryPackagePublishingHistory` target to this archive.
11241140
1125 :param name: binary name filter (exact match or SQL LIKE controlled1141 :param name: binary name filter (exact match or SQL LIKE controlled
@@ -1137,6 +1153,9 @@
1137 False then the results will be unordered. This will make the1153 False then the results will be unordered. This will make the
1138 operation much quicker to return results if you don't care about1154 operation much quicker to return results if you don't care about
1139 ordering.1155 ordering.
1156 :param order_by_date: Order publications by descending creation date
1157 and then by descending ID. This is suitable for applications
1158 that need to catch up with publications since their last run.
11401159
1141 :return: A collection containing `BinaryPackagePublishingHistory`.1160 :return: A collection containing `BinaryPackagePublishingHistory`.
1142 """1161 """
11431162
=== modified file 'lib/lp/soyuz/model/archive.py'
--- lib/lp/soyuz/model/archive.py 2015-03-12 13:59:27 +0000
+++ lib/lp/soyuz/model/archive.py 2015-04-13 17:14:35 +0000
@@ -97,7 +97,6 @@
97 )97 )
98from lp.services.database.sqlbase import (98from lp.services.database.sqlbase import (
99 cursor,99 cursor,
100 quote_like,
101 SQLBase,100 SQLBase,
102 sqlvalues,101 sqlvalues,
103 )102 )
@@ -521,13 +520,16 @@
521 def api_getPublishedSources(self, name=None, version=None, status=None,520 def api_getPublishedSources(self, name=None, version=None, status=None,
522 distroseries=None, pocket=None,521 distroseries=None, pocket=None,
523 exact_match=False, created_since_date=None,522 exact_match=False, created_since_date=None,
524 component_name=None):523 order_by_date=False, component_name=None):
525 """See `IArchive`."""524 """See `IArchive`."""
526 # 'eager_load' and 'include_removed' arguments are always True525 # 'eager_load' and 'include_removed' arguments are always True
527 # for API calls.526 # for API calls.
528 published_sources = self.getPublishedSources(527 published_sources = self.getPublishedSources(
529 name, version, status, distroseries, pocket, exact_match,528 name=name, version=version, status=status,
530 created_since_date, True, component_name, True)529 distroseries=distroseries, pocket=pocket, exact_match=exact_match,
530 created_since_date=created_since_date, eager_load=True,
531 component_name=component_name, order_by_date=order_by_date,
532 include_removed=True)
531533
532 def load_api_extra_objects(rows):534 def load_api_extra_objects(rows):
533 """Load extra related-objects needed by API calls."""535 """Load extra related-objects needed by API calls."""
@@ -565,20 +567,23 @@
565 distroseries=None, pocket=None,567 distroseries=None, pocket=None,
566 exact_match=False, created_since_date=None,568 exact_match=False, created_since_date=None,
567 eager_load=False, component_name=None,569 eager_load=False, component_name=None,
568 include_removed=True):570 order_by_date=False, include_removed=True):
569 """See `IArchive`."""571 """See `IArchive`."""
570 # clauses contains literal sql expressions for things that don't work572 clauses = [SourcePackagePublishingHistory.archiveID == self.id]
571 # easily in storm : this method was migrated from sqlobject but some573
572 # callers are problematic. (Migrate them and test to see).574 if order_by_date:
573 clauses = [575 order_by = [
574 SourcePackagePublishingHistory.archiveID == self.id,576 Desc(SourcePackagePublishingHistory.datecreated),
575 SourcePackagePublishingHistory.sourcepackagereleaseID ==577 Desc(SourcePackagePublishingHistory.id)]
576 SourcePackageRelease.id,578 else:
577 SourcePackagePublishingHistory.sourcepackagenameID ==579 order_by = [
578 SourcePackageName.id]580 SourcePackageName.name,
579 orderBy = [581 Desc(SourcePackagePublishingHistory.id)]
580 SourcePackageName.name,582
581 Desc(SourcePackagePublishingHistory.id)]583 if not order_by_date or name is not None:
584 clauses.append(
585 SourcePackagePublishingHistory.sourcepackagenameID ==
586 SourcePackageName.id)
582587
583 if name is not None:588 if name is not None:
584 if type(name) in (str, unicode):589 if type(name) in (str, unicode):
@@ -590,14 +595,19 @@
590 elif len(name) != 0:595 elif len(name) != 0:
591 clauses.append(SourcePackageName.name.is_in(name))596 clauses.append(SourcePackageName.name.is_in(name))
592597
598 if not order_by_date or version is not None:
599 clauses.append(
600 SourcePackagePublishingHistory.sourcepackagereleaseID ==
601 SourcePackageRelease.id)
602
593 if version is not None:603 if version is not None:
594 if name is None:604 if name is None:
595 raise VersionRequiresName(605 raise VersionRequiresName(
596 "The 'version' parameter can be used only together with"606 "The 'version' parameter can be used only together with"
597 " the 'name' parameter.")607 " the 'name' parameter.")
598 clauses.append(SourcePackageRelease.version == version)608 clauses.append(SourcePackageRelease.version == version)
599 else:609 elif not order_by_date:
600 orderBy.insert(1, Desc(SourcePackageRelease.version))610 order_by.insert(1, Desc(SourcePackageRelease.version))
601611
602 if component_name is not None:612 if component_name is not None:
603 clauses.extend(613 clauses.extend(
@@ -635,7 +645,7 @@
635645
636 store = Store.of(self)646 store = Store.of(self)
637 resultset = store.find(647 resultset = store.find(
638 SourcePackagePublishingHistory, *clauses).order_by(*orderBy)648 SourcePackagePublishingHistory, *clauses).order_by(*order_by)
639 if not eager_load:649 if not eager_load:
640 return resultset650 return resultset
641651
@@ -747,38 +757,46 @@
747 def _getBinaryPublishingBaseClauses(757 def _getBinaryPublishingBaseClauses(
748 self, name=None, version=None, status=None, distroarchseries=None,758 self, name=None, version=None, status=None, distroarchseries=None,
749 pocket=None, exact_match=False, created_since_date=None,759 pocket=None, exact_match=False, created_since_date=None,
750 ordered=True, include_removed=True):760 ordered=True, order_by_date=False, include_removed=True,
751 """Base clauses and clauseTables for binary publishing queries.761 need_bpr=False):
762 """Base clauses for binary publishing queries.
752763
753 Returns a list of 'clauses' (to be joined in the callsite) and764 Returns a list of 'clauses' (to be joined in the callsite).
754 a list of clauseTables required according to the given arguments.
755 """765 """
756 clauses = ["""766 clauses = [BinaryPackagePublishingHistory.archiveID == self.id]
757 BinaryPackagePublishingHistory.archive = %s AND767
758 BinaryPackagePublishingHistory.binarypackagerelease =768 if order_by_date:
759 BinaryPackageRelease.id AND769 ordered = False
760 BinaryPackagePublishingHistory.binarypackagename =770
761 BinaryPackageName.id771 if order_by_date:
762 """ % sqlvalues(self)]772 order_by = [
763 clauseTables = ['BinaryPackageRelease', 'BinaryPackageName']773 Desc(BinaryPackagePublishingHistory.datecreated),
764 if ordered:774 Desc(BinaryPackagePublishingHistory.id)]
765 orderBy = ['BinaryPackageName.name',775 elif ordered:
766 '-BinaryPackagePublishingHistory.id']776 order_by = [
777 BinaryPackageName.name,
778 Desc(BinaryPackagePublishingHistory.id)]
767 else:779 else:
768 # Strictly speaking, this is ordering, but it's an indexed780 # Strictly speaking, this is ordering, but it's an indexed
769 # ordering so it will be quick. It's needed so that we can781 # ordering so it will be quick. It's needed so that we can
770 # batch results on the webservice.782 # batch results on the webservice.
771 orderBy = ['-BinaryPackagePublishingHistory.id']783 order_by = [Desc(BinaryPackagePublishingHistory.id)]
784
785 if ordered or name is not None:
786 clauses.append(
787 BinaryPackagePublishingHistory.binarypackagenameID ==
788 BinaryPackageName.id)
772789
773 if name is not None:790 if name is not None:
774 if exact_match:791 if exact_match:
775 clauses.append("""792 clauses.append(BinaryPackageName.name == name)
776 BinaryPackageName.name=%s
777 """ % sqlvalues(name))
778 else:793 else:
779 clauses.append("""794 clauses.append(BinaryPackageName.name.contains_string(name))
780 BinaryPackageName.name LIKE '%%' || %s || '%%'795
781 """ % quote_like(name))796 if need_bpr or ordered or version is not None:
797 clauses.append(
798 BinaryPackagePublishingHistory.binarypackagereleaseID ==
799 BinaryPackageRelease.id)
782800
783 if version is not None:801 if version is not None:
784 if name is None:802 if name is None:
@@ -786,115 +804,105 @@
786 "The 'version' parameter can be used only together with"804 "The 'version' parameter can be used only together with"
787 " the 'name' parameter.")805 " the 'name' parameter.")
788806
789 clauses.append("""807 clauses.append(BinaryPackageRelease.version == version)
790 BinaryPackageRelease.version = %s
791 """ % sqlvalues(version))
792 elif ordered:808 elif ordered:
793 orderBy.insert(1, Desc(BinaryPackageRelease.version))809 order_by.insert(1, Desc(BinaryPackageRelease.version))
794810
795 if status is not None:811 if status is not None:
796 try:812 try:
797 status = tuple(status)813 status = tuple(status)
798 except TypeError:814 except TypeError:
799 status = (status, )815 status = (status, )
800 clauses.append("""816 clauses.append(BinaryPackagePublishingHistory.status.is_in(status))
801 BinaryPackagePublishingHistory.status IN %s
802 """ % sqlvalues(status))
803817
804 if distroarchseries is not None:818 if distroarchseries is not None:
805 try:819 try:
806 distroarchseries = tuple(distroarchseries)820 distroarchseries = tuple(distroarchseries)
807 except TypeError:821 except TypeError:
808 distroarchseries = (distroarchseries, )822 distroarchseries = (distroarchseries, )
809 # XXX cprov 20071016: there is no sqlrepr for DistroArchSeries823 clauses.append(
810 # uhmm, how so ?824 BinaryPackagePublishingHistory.distroarchseriesID.is_in(
811 das_ids = "(%s)" % ", ".join(str(d.id) for d in distroarchseries)825 [d.id for d in distroarchseries]))
812 clauses.append("""
813 BinaryPackagePublishingHistory.distroarchseries IN %s
814 """ % das_ids)
815826
816 if pocket is not None:827 if pocket is not None:
817 clauses.append("""828 clauses.append(BinaryPackagePublishingHistory.pocket == pocket)
818 BinaryPackagePublishingHistory.pocket = %s
819 """ % sqlvalues(pocket))
820829
821 if created_since_date is not None:830 if created_since_date is not None:
822 clauses.append(831 clauses.append(
823 "BinaryPackagePublishingHistory.datecreated >= %s"832 BinaryPackagePublishingHistory.datecreated >=
824 % sqlvalues(created_since_date))833 created_since_date)
825834
826 if not include_removed:835 if not include_removed:
827 clauses.append(836 clauses.append(BinaryPackagePublishingHistory.dateremoved == None)
828 "BinaryPackagePublishingHistory.dateremoved IS NULL")
829837
830 return clauses, clauseTables, orderBy838 return clauses, order_by
831839
832 def getAllPublishedBinaries(self, name=None, version=None, status=None,840 def getAllPublishedBinaries(self, name=None, version=None, status=None,
833 distroarchseries=None, pocket=None,841 distroarchseries=None, pocket=None,
834 exact_match=False, created_since_date=None,842 exact_match=False, created_since_date=None,
835 ordered=True, include_removed=True):843 ordered=True, order_by_date=False,
844 include_removed=True):
836 """See `IArchive`."""845 """See `IArchive`."""
837 clauses, clauseTables, orderBy = self._getBinaryPublishingBaseClauses(846 clauses, order_by = self._getBinaryPublishingBaseClauses(
838 name=name, version=version, status=status, pocket=pocket,847 name=name, version=version, status=status, pocket=pocket,
839 distroarchseries=distroarchseries, exact_match=exact_match,848 distroarchseries=distroarchseries, exact_match=exact_match,
840 created_since_date=created_since_date, ordered=ordered,849 created_since_date=created_since_date, ordered=ordered,
841 include_removed=include_removed)850 order_by_date=order_by_date, include_removed=include_removed)
842851
843 all_binaries = BinaryPackagePublishingHistory.select(852 return Store.of(self).find(
844 ' AND '.join(clauses), clauseTables=clauseTables,853 BinaryPackagePublishingHistory, *clauses).order_by(*order_by)
845 orderBy=orderBy)
846
847 return all_binaries
848854
849 def getPublishedOnDiskBinaries(self, name=None, version=None, status=None,855 def getPublishedOnDiskBinaries(self, name=None, version=None, status=None,
850 distroarchseries=None, pocket=None,856 distroarchseries=None, pocket=None,
851 exact_match=False,857 exact_match=False):
852 created_since_date=None):
853 """See `IArchive`."""858 """See `IArchive`."""
854 clauses, clauseTables, orderBy = self._getBinaryPublishingBaseClauses(859 # Circular imports.
860 from lp.registry.model.distroseries import DistroSeries
861 from lp.soyuz.model.distroarchseries import DistroArchSeries
862
863 clauses, order_by = self._getBinaryPublishingBaseClauses(
855 name=name, version=version, status=status, pocket=pocket,864 name=name, version=version, status=status, pocket=pocket,
856 distroarchseries=distroarchseries, exact_match=exact_match,865 distroarchseries=distroarchseries, exact_match=exact_match,
857 created_since_date=created_since_date)866 need_bpr=True)
858867
859 clauses.append("""868 clauses.extend([
860 BinaryPackagePublishingHistory.distroarchseries =869 BinaryPackagePublishingHistory.distroarchseriesID ==
861 DistroArchSeries.id AND870 DistroArchSeries.id,
862 DistroArchSeries.distroseries = DistroSeries.id871 DistroArchSeries.distroseriesID == DistroSeries.id,
863 """)872 ])
864 clauseTables.extend(['DistroSeries', 'DistroArchSeries'])873
874 store = Store.of(self)
865875
866 # Retrieve only the binaries published for the 'nominated architecture876 # Retrieve only the binaries published for the 'nominated architecture
867 # independent' (usually i386) in the distroseries in question.877 # independent' (usually i386) in the distroseries in question.
868 # It includes all architecture-independent binaries only once and the878 # It includes all architecture-independent binaries only once and the
869 # architecture-specific built for 'nominatedarchindep'.879 # architecture-specific built for 'nominatedarchindep'.
870 nominated_arch_independent_clause = ["""880 nominated_arch_independent_clauses = clauses + [
871 DistroSeries.nominatedarchindep =881 DistroSeries.nominatedarchindepID ==
872 BinaryPackagePublishingHistory.distroarchseries882 BinaryPackagePublishingHistory.distroarchseriesID,
873 """]883 ]
874 nominated_arch_independent_query = ' AND '.join(884 nominated_arch_independents = store.find(
875 clauses + nominated_arch_independent_clause)885 BinaryPackagePublishingHistory,
876 nominated_arch_independents = BinaryPackagePublishingHistory.select(886 *nominated_arch_independent_clauses)
877 nominated_arch_independent_query, clauseTables=clauseTables)
878887
879 # Retrieve all architecture-specific binary publications except888 # Retrieve all architecture-specific binary publications except
880 # 'nominatedarchindep' (already included in the previous query).889 # 'nominatedarchindep' (already included in the previous query).
881 no_nominated_arch_independent_clause = ["""890 no_nominated_arch_independent_clauses = clauses + [
882 DistroSeries.nominatedarchindep !=891 DistroSeries.nominatedarchindepID !=
883 BinaryPackagePublishingHistory.distroarchseries AND892 BinaryPackagePublishingHistory.distroarchseriesID,
884 BinaryPackageRelease.architecturespecific = true893 BinaryPackageRelease.architecturespecific == True,
885 """]894 ]
886 no_nominated_arch_independent_query = ' AND '.join(895 no_nominated_arch_independents = store.find(
887 clauses + no_nominated_arch_independent_clause)896 BinaryPackagePublishingHistory,
888 no_nominated_arch_independents = (897 *no_nominated_arch_independent_clauses)
889 BinaryPackagePublishingHistory.select(
890 no_nominated_arch_independent_query, clauseTables=clauseTables))
891898
892 # XXX cprov 20071016: It's not possible to use the same ordering899 # XXX cprov 20071016: It's not possible to use the same ordering
893 # schema returned by self._getBinaryPublishingBaseClauses.900 # schema returned by self._getBinaryPublishingBaseClauses.
894 # It results in:901 # It results in:
895 # ERROR: missing FROM-clause entry for table "binarypackagename"902 # ERROR: missing FROM-clause entry for table "binarypackagename"
896 unique_binary_publications = nominated_arch_independents.union(903 unique_binary_publications = nominated_arch_independents.union(
897 no_nominated_arch_independents).orderBy("id")904 no_nominated_arch_independents).order_by(
905 BinaryPackagePublishingHistory.id)
898906
899 return unique_binary_publications907 return unique_binary_publications
900908
901909
=== modified file 'lib/lp/soyuz/scripts/tests/test_initialize_distroseries.py'
--- lib/lp/soyuz/scripts/tests/test_initialize_distroseries.py 2014-11-06 01:42:35 +0000
+++ lib/lp/soyuz/scripts/tests/test_initialize_distroseries.py 2015-04-13 17:14:35 +0000
@@ -1,4 +1,4 @@
1# Copyright 2010-2014 Canonical Ltd. This software is licensed under the1# Copyright 2010-2015 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Test the initialize_distroseries script machinery."""4"""Test the initialize_distroseries script machinery."""
@@ -581,9 +581,9 @@
581 self.assertEqual(581 self.assertEqual(
582 parent_udev_pubs.count(), child_udev_pubs.count())582 parent_udev_pubs.count(), child_udev_pubs.count())
583 parent_arch_udev_pubs = parent.main_archive.getAllPublishedBinaries(583 parent_arch_udev_pubs = parent.main_archive.getAllPublishedBinaries(
584 distroarchseries=parent[parent_das.architecturetag], name='udev')584 distroarchseries=parent[parent_das.architecturetag], name=u'udev')
585 child_arch_udev_pubs = child.main_archive.getAllPublishedBinaries(585 child_arch_udev_pubs = child.main_archive.getAllPublishedBinaries(
586 distroarchseries=child[parent_das.architecturetag], name='udev')586 distroarchseries=child[parent_das.architecturetag], name=u'udev')
587 self.assertEqual(587 self.assertEqual(
588 parent_arch_udev_pubs.count(), child_arch_udev_pubs.count())588 parent_arch_udev_pubs.count(), child_arch_udev_pubs.count())
589 # And the binary package, and linked source package look fine too.589 # And the binary package, and linked source package look fine too.
590590
=== modified file 'lib/lp/soyuz/stories/ppa/xx-ppa-packages.txt'
--- lib/lp/soyuz/stories/ppa/xx-ppa-packages.txt 2014-07-24 09:37:03 +0000
+++ lib/lp/soyuz/stories/ppa/xx-ppa-packages.txt 2015-04-13 17:14:35 +0000
@@ -137,7 +137,7 @@
137 >>> cprov = getUtility(IPersonSet).getByName('cprov')137 >>> cprov = getUtility(IPersonSet).getByName('cprov')
138 >>> cprov_ppa = cprov.archive138 >>> cprov_ppa = cprov.archive
139 >>> pmount_i386_pub = cprov_ppa.getAllPublishedBinaries(139 >>> pmount_i386_pub = cprov_ppa.getAllPublishedBinaries(
140 ... name='pmount', version='0.1-1')[1]140 ... name=u'pmount', version='0.1-1')[1]
141 >>> print pmount_i386_pub.displayname141 >>> print pmount_i386_pub.displayname
142 pmount 0.1-1 in warty i386142 pmount 0.1-1 in warty i386
143 >>> from lp.soyuz.enums import PackagePublishingStatus143 >>> from lp.soyuz.enums import PackagePublishingStatus
144144
=== modified file 'lib/lp/soyuz/stories/webservice/xx-binary-package-publishing.txt'
--- lib/lp/soyuz/stories/webservice/xx-binary-package-publishing.txt 2015-04-08 13:19:19 +0000
+++ lib/lp/soyuz/stories/webservice/xx-binary-package-publishing.txt 2015-04-13 17:14:35 +0000
@@ -159,7 +159,7 @@
159 >>> australia = getUtility(ICountrySet)['AU']159 >>> australia = getUtility(ICountrySet)['AU']
160160
161 >>> firefox_db = cprov_db.archive.getAllPublishedBinaries(161 >>> firefox_db = cprov_db.archive.getAllPublishedBinaries(
162 ... name='mozilla-firefox')[0]162 ... name=u'mozilla-firefox')[0]
163 >>> firefox_db.archive.updatePackageDownloadCount(163 >>> firefox_db.archive.updatePackageDownloadCount(
164 ... firefox_db.binarypackagerelease, date(2010, 2, 21), australia, 10)164 ... firefox_db.binarypackagerelease, date(2010, 2, 21), australia, 10)
165 >>> firefox_db.archive.updatePackageDownloadCount(165 >>> firefox_db.archive.updatePackageDownloadCount(
166166
=== modified file 'lib/lp/soyuz/stories/webservice/xx-source-package-publishing.txt'
--- lib/lp/soyuz/stories/webservice/xx-source-package-publishing.txt 2014-07-24 09:37:03 +0000
+++ lib/lp/soyuz/stories/webservice/xx-source-package-publishing.txt 2015-04-13 17:14:35 +0000
@@ -216,7 +216,7 @@
216216
217 >>> login("admin@canonical.com")217 >>> login("admin@canonical.com")
218 >>> for bin in cprov_ppa.getAllPublishedBinaries(218 >>> for bin in cprov_ppa.getAllPublishedBinaries(
219 ... name="testwebservice-bin"):219 ... name=u"testwebservice-bin"):
220 ... if bin.status != PackagePublishingStatus.DELETED:220 ... if bin.status != PackagePublishingStatus.DELETED:
221 ... print "%s is not deleted when it should be" % bin.displayname221 ... print "%s is not deleted when it should be" % bin.displayname
222 ... else:222 ... else:
223223
=== modified file 'lib/lp/soyuz/tests/test_archive.py'
--- lib/lp/soyuz/tests/test_archive.py 2015-03-12 13:59:27 +0000
+++ lib/lp/soyuz/tests/test_archive.py 2015-04-13 17:14:35 +0000
@@ -2187,14 +2187,24 @@
2187 component_name='universe')2187 component_name='universe')
2188 self.assertEqual('universe', filtered.component.name)2188 self.assertEqual('universe', filtered.component.name)
21892189
21902190 def test_order_by_date(self):
2191class GetPublishedSourcesWebServiceTests(TestCaseWithFactory):2191 archive = self.factory.makeArchive()
2192 dates = [self.factory.getUniqueDate() for _ in range(5)]
2193 # Make sure the ID ordering and date ordering don't match so that we
2194 # can spot a date-ordered result.
2195 pubs = [
2196 self.factory.makeSourcePackagePublishingHistory(
2197 archive=archive, date_uploaded=dates[(i + 1) % 5])
2198 for i in range(5)]
2199 self.assertEqual(
2200 [pubs[i] for i in (3, 2, 1, 0, 4)],
2201 list(archive.getPublishedSources(order_by_date=True)))
2202
2203
2204class TestGetPublishedSourcesWebService(TestCaseWithFactory):
21922205
2193 layer = LaunchpadFunctionalLayer2206 layer = LaunchpadFunctionalLayer
21942207
2195 def setUp(self):
2196 super(GetPublishedSourcesWebServiceTests, self).setUp()
2197
2198 def createTestingPPA(self):2208 def createTestingPPA(self):
2199 """Creates and populates a PPA for API performance tests.2209 """Creates and populates a PPA for API performance tests.
22002210
@@ -2208,7 +2218,6 @@
2208 # XXX cprov 2014-04-22: currently the target archive owner cannot2218 # XXX cprov 2014-04-22: currently the target archive owner cannot
2209 # 'addSource' to a `PackageUpload` ('launchpad.Edit'). It seems2219 # 'addSource' to a `PackageUpload` ('launchpad.Edit'). It seems
2210 # too restrive to me.2220 # too restrive to me.
2211 from zope.security.proxy import removeSecurityProxy
2212 with person_logged_in(ppa.owner):2221 with person_logged_in(ppa.owner):
2213 for i in range(5):2222 for i in range(5):
2214 upload = self.factory.makePackageUpload(2223 upload = self.factory.makePackageUpload(
@@ -2814,6 +2823,19 @@
2814 publications,2823 publications,
2815 [first_publication, middle_publication, later_publication])2824 [first_publication, middle_publication, later_publication])
28162825
2826 def test_order_by_date(self):
2827 archive = self.factory.makeArchive()
2828 dates = [self.factory.getUniqueDate() for _ in range(5)]
2829 # Make sure the ID ordering and date ordering don't match so that we
2830 # can spot a date-ordered result.
2831 pubs = [
2832 self.factory.makeBinaryPackagePublishingHistory(
2833 archive=archive, datecreated=dates[(i + 1) % 5])
2834 for i in range(5)]
2835 self.assertEqual(
2836 [pubs[i] for i in (3, 2, 1, 0, 4)],
2837 list(archive.getAllPublishedBinaries(order_by_date=True)))
2838
28172839
2818class TestRemovingPermissions(TestCaseWithFactory):2840class TestRemovingPermissions(TestCaseWithFactory):
28192841
28202842
=== modified file 'lib/lp/soyuz/tests/test_packagecopyjob.py'
--- lib/lp/soyuz/tests/test_packagecopyjob.py 2014-08-14 09:54:33 +0000
+++ lib/lp/soyuz/tests/test_packagecopyjob.py 2015-04-13 17:14:35 +0000
@@ -1,4 +1,4 @@
1# Copyright 2010-2014 Canonical Ltd. This software is licensed under the1# Copyright 2010-2015 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Tests for sync package jobs."""4"""Tests for sync package jobs."""
@@ -1392,7 +1392,8 @@
1392 copied_sources = target_archive.getPublishedSources(1392 copied_sources = target_archive.getPublishedSources(
1393 name=u"copyme", version="2.8-1")1393 name=u"copyme", version="2.8-1")
1394 self.assertNotEqual(0, copied_sources.count())1394 self.assertNotEqual(0, copied_sources.count())
1395 copied_binaries = target_archive.getAllPublishedBinaries(name="copyme")1395 copied_binaries = target_archive.getAllPublishedBinaries(
1396 name=u"copyme")
1396 self.assertNotEqual(0, copied_binaries.count())1397 self.assertNotEqual(0, copied_binaries.count())
13971398
1398 # Check that files were unembargoed.1399 # Check that files were unembargoed.