Skip to content

Commit

Permalink
PackageQuery: Add filter_{latest,earliest}_evr_ignore_arch
Browse files Browse the repository at this point in the history
Resolves #1111.

`filter_latest_evr` and `filter_earliest_evr` group packages by
architecture and keep the latest package on each architecture. After
`filter_latest_evr`, a PackageQuery might have anaconda-1-1.noarch as
well as anaconda-2.2.x86_64.

This commit adds `filter_latest_evr_ignore_arch` and
`filter_earliest_evr_ignore_arch` which filter for packages with the
latest versions regardless of their architecture. Filtering the above
anaconda example with `filter_latest_evr_ignore_arch` would result in
just {anaconda-2.2.x86_64} since it is the latest version of the package
for any arch.

This is an ABI-breaking change and thus is targeted at dnf5 5.2.
  • Loading branch information
evan-goode committed Feb 1, 2024
1 parent 6e29ae8 commit c6f7523
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 2 deletions.
14 changes: 14 additions & 0 deletions include/libdnf5/rpm/package_query.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -618,13 +618,27 @@ class PackageQuery : public PackageSet {
// @replaces libdnf/sack/query.hpp:method:addFilter(int keyname, int cmp_type, int match) - cmp_type = HY_PKG_LATEST_PER_ARCH
void filter_latest_evr(int limit = 1);

/// Group packages by `name`. Then within each group, keep packages that correspond with up to `limit` of (all but) latest `evr`s in the group.
///
/// @param limit If `limit` > 0, keep `limit` number `evr`s in each group.
/// If `limit` < 0, keep all **but** `limit` last `evr`s in each group.
/// @since 5.2
void filter_latest_evr_any_arch(int limit = 1);

/// Group packages by `name` and `arch`. Then within each group, keep packages that correspond with up to `limit` of (all but) earliest `evr`s in the group.
///
/// @param limit If `limit` > 0, keep `limit` number `evr`s in each group.
/// If `limit` < 0, keep all **but** `limit` last `evr`s in each group.
/// @since 5.0
void filter_earliest_evr(int limit = 1);

/// Group packages by `name`. Then within each group, keep packages that correspond with up to `limit` of (all but) earliest `evr`s in the group.
///
/// @param limit If `limit` > 0, keep `limit` number `evr`s in each group.
/// If `limit` < 0, keep all **but** `limit` last `evr`s in each group.
/// @since 5.2
void filter_earliest_evr_any_arch(int limit = 1);

/// Group packages by `name` and `arch`. Then within each group, keep packages that belong to a repo with the highest priority (the lowest number).
/// The filter works only on available packages, installed packages are not affected.
///
Expand Down
41 changes: 39 additions & 2 deletions libdnf5/rpm/package_query.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2435,6 +2435,19 @@ static int latest_cmp(const Id * ap, const Id * bp, libdnf5::solv::RpmPool * poo
return *ap - *bp;
}

static int latest_ignore_arch_cmp(const Id * ap, const Id * bp, libdnf5::solv::RpmPool * pool) {
Solvable * sa = pool->id2solvable(*ap);
Solvable * sb = pool->id2solvable(*bp);
int r;
r = sa->name - sb->name;
if (r)
return r;
r = pool->evrcmp(sb->evr, sa->evr, EVRCMP_COMPARE);
if (r)
return r;
return *ap - *bp;
}

static int earliest_cmp(const Id * ap, const Id * bp, libdnf5::solv::RpmPool * pool) {
Solvable * sa = pool->id2solvable(*ap);
Solvable * sb = pool->id2solvable(*bp);
Expand All @@ -2453,11 +2466,27 @@ static int earliest_cmp(const Id * ap, const Id * bp, libdnf5::solv::RpmPool * p
return *ap - *bp;
}

static int earliest_ignore_arch_cmp(const Id * ap, const Id * bp, libdnf5::solv::RpmPool * pool) {
Solvable * sa = pool->id2solvable(*ap);
Solvable * sb = pool->id2solvable(*bp);
int r;
r = sa->name - sb->name;
if (r)
return r;
r = pool->evrcmp(sb->evr, sa->evr, EVRCMP_COMPARE);
if (r > 0)
return -1;
if (r < 0)
return 1;
return *ap - *bp;
}

static void filter_first_sorted_by(
libdnf5::solv::RpmPool & pool,
int limit,
int (*cmp)(const Id * a, const Id * b, libdnf5::solv::RpmPool * pool),
libdnf5::solv::SolvMap & data) {
libdnf5::solv::SolvMap & data,
bool group_by_arch = true) {
libdnf5::solv::IdQueue samename;
for (Id candidate_id : data) {
samename.push_back(candidate_id);
Expand All @@ -2471,7 +2500,7 @@ static void filter_first_sorted_by(
int i;
for (i = 0; i < samename.size(); ++i) {
Solvable * considered = pool.id2solvable(samename[i]);
if (!highest || highest->name != considered->name || highest->arch != considered->arch) {
if (!highest || highest->name != considered->name || (group_by_arch && (highest->arch != considered->arch))) {
/* start of a new block */
if (start_block == -1) {
highest = considered;
Expand All @@ -2492,10 +2521,18 @@ void PackageQuery::filter_latest_evr(int limit) {
filter_first_sorted_by(get_rpm_pool(p_impl->base), limit, latest_cmp, *p_impl);
}

void PackageQuery::filter_latest_evr_any_arch(int limit) {
filter_first_sorted_by(get_rpm_pool(p_impl->base), limit, latest_ignore_arch_cmp, *p_impl, false);
}

void PackageQuery::filter_earliest_evr(int limit) {
filter_first_sorted_by(get_rpm_pool(p_impl->base), limit, earliest_cmp, *p_impl);
}

void PackageQuery::filter_earliest_evr_any_arch(int limit) {
filter_first_sorted_by(get_rpm_pool(p_impl->base), limit, earliest_ignore_arch_cmp, *p_impl, false);
}

static inline bool priority_solvable_cmp_key(const Solvable * first, const Solvable * second) {
if (first->name != second->name) {
return first->name < second->name;
Expand Down
13 changes: 13 additions & 0 deletions test/data/repos-solv/solv-multiarch.repo
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
=Ver: 3.0

=Pkg: foo 1.2 1 x86_64
=Prv: foo = 1.2 1

=Pkg: foo 1.2 2 noarch
=Prv: foo = 1.2 2

=Pkg: bar 4.5 1 noarch
=Prv: bar = 4.5 1

=Pkg: bar 4.5 2 x86_64
=Prv: bar = 4.5 2
50 changes: 50 additions & 0 deletions test/libdnf5/rpm/test_package_query.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,31 @@ void RpmPackageQueryTest::test_filter_latest_evr() {
}
}

void RpmPackageQueryTest::test_filter_latest_evr_ignore_arch() {
add_repo_solv("solv-multiarch");

{
// Result of filter_latest_evr should include a package from each arch
PackageQuery query(base);
query.filter_latest_evr(1);
std::vector<Package> expected = {
get_pkg("foo-0:1.2-1.x86_64"),
get_pkg("foo-0:1.2-2.noarch"),
get_pkg("bar-0:4.5-1.noarch"),
get_pkg("bar-0:4.5-2.x86_64"),
};
CPPUNIT_ASSERT_EQUAL(expected, to_vector(query));
}
{
// Result of filter_latest_evr_ignore_arch should include only the latest
// packages, regardless of arch
PackageQuery query(base);
query.filter_latest_evr_any_arch(1);
std::vector<Package> expected = {get_pkg("foo-0:1.2-2.noarch"), get_pkg("bar-0:4.5-2.x86_64")};
CPPUNIT_ASSERT_EQUAL(expected, to_vector(query));
}
}

void RpmPackageQueryTest::test_filter_earliest_evr() {
add_repo_solv("solv-repo1");
add_repo_solv("solv-24pkgs");
Expand Down Expand Up @@ -173,6 +198,31 @@ void RpmPackageQueryTest::test_filter_earliest_evr() {
}
}

void RpmPackageQueryTest::test_filter_earliest_evr_ignore_arch() {
add_repo_solv("solv-multiarch");

{
// Result of filter_earliest_evr should include a package from each arch
PackageQuery query(base);
query.filter_earliest_evr(1);
std::vector<Package> expected = {
get_pkg("foo-0:1.2-1.x86_64"),
get_pkg("foo-0:1.2-2.noarch"),
get_pkg("bar-0:4.5-1.noarch"),
get_pkg("bar-0:4.5-2.x86_64"),
};
CPPUNIT_ASSERT_EQUAL(expected, to_vector(query));
}
{
// Result of filter_earliest_evr_ignore_arch should include only the earliest
// packages, regardless of arch
PackageQuery query(base);
query.filter_earliest_evr_any_arch(1);
std::vector<Package> expected = {get_pkg("foo-0:1.2-1.x86_64"), get_pkg("bar-0:4.5-1.noarch")};
CPPUNIT_ASSERT_EQUAL(expected, to_vector(query));
}
}

void RpmPackageQueryTest::test_filter_name() {
add_repo_solv("solv-repo1");

Expand Down
4 changes: 4 additions & 0 deletions test/libdnf5/rpm/test_package_query.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ class RpmPackageQueryTest : public BaseTestCase {
#ifndef WITH_PERFORMANCE_TESTS
CPPUNIT_TEST(test_size);
CPPUNIT_TEST(test_filter_latest_evr);
CPPUNIT_TEST(test_filter_latest_evr_ignore_arch);
CPPUNIT_TEST(test_filter_earliest_evr);
CPPUNIT_TEST(test_filter_earliest_evr_ignore_arch);
CPPUNIT_TEST(test_filter_name);
CPPUNIT_TEST(test_filter_name_packgset);
CPPUNIT_TEST(test_filter_nevra_packgset);
Expand Down Expand Up @@ -66,7 +68,9 @@ class RpmPackageQueryTest : public BaseTestCase {

void test_size();
void test_filter_latest_evr();
void test_filter_latest_evr_ignore_arch();
void test_filter_earliest_evr();
void test_filter_earliest_evr_ignore_arch();
void test_filter_name();
void test_filter_name_packgset();
void test_filter_nevra_packgset();
Expand Down

0 comments on commit c6f7523

Please sign in to comment.