From 3326e1348183dc6b07fdd55bf646e9f250796d17 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 17 Apr 2015 18:30:35 -0400 Subject: [PATCH] lib: Add diff API This is a *third* implementation of rpm database diffs in the code, but it is now a public introspectable shared library API. Further commits will change the command line tools to use this, and then after that we'll further deduplicate the `db diff` from this code. --- src/lib/rpmostree-db.c | 133 +++++++++++++++++++++++++++++++++++ src/lib/rpmostree-db.h | 11 +++ tests/manual/test-dbquery.py | 8 +++ 3 files changed, 152 insertions(+) diff --git a/src/lib/rpmostree-db.c b/src/lib/rpmostree-db.c index 8364fdea..fba36792 100644 --- a/src/lib/rpmostree-db.c +++ b/src/lib/rpmostree-db.c @@ -140,3 +140,136 @@ rpm_ostree_db_query (OstreeRepo *repo, return query_all_packages_in_sack (rsack); } + +/** + * rpm_ostree_db_diff: + * @repo: An OSTree repository + * @orig_ref: Original ref (branch or commit) + * @new_ref: New ref (branch or commit) + * @query: (allow-none): A query, currently must be %NULL + * @out_removed: (out) (transfer container) (element-type RpmOstreePackage): Return location for removed packages + * @out_added: (out) (transfer container) (element-type RpmOstreePackage): Return location for added packages + * @out_modified_old: (out) (transfer container) (element-type RpmOstreePackage): Return location for modified old packages + * @out_modified_new: (out) (transfer container) (element-type RpmOstreePackage): Return location for modified new packages + * + * Compute the RPM package delta between two commits. Currently you + * must use %NULL for the @query parameter; in a future version this + * function may allow looking at a subset of the packages. + * + * The @out_modified_old and @out_modified_new arrays will always be + * the same length, and indicies will refer to the same base package + * name. It is possible in RPM databases to have multiple packages + * installed with the same name; in this case, the behavior will + * depend on whether the package set is transitioning from 1 -> N or N + * -> 1. In the former case, an arbitrary single instance of one of + * the new packages will be in @out_modified_new. If the latter, then + * multiple entries with the same name will be returned in + * the array @out_modified_old, with each having a reference to the + * single corresponding new package. + */ +gboolean +rpm_ostree_db_diff (OstreeRepo *repo, + const char *orig_ref, + const char *new_ref, + GVariant *query, + GPtrArray **out_removed, + GPtrArray **out_added, + GPtrArray **out_modified_old, + GPtrArray **out_modified_new, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + g_autoptr(RpmOstreeRefSack) orig_sack = NULL; + g_autoptr(RpmOstreeRefSack) new_sack = NULL; + _cleanup_hypackagelist_ HyPackageList orig_pkglist = NULL; + _cleanup_hypackagelist_ HyPackageList new_pkglist = NULL; + g_autoptr(GPtrArray) ret_removed = g_ptr_array_new_with_free_func (g_object_unref); + g_autoptr(GPtrArray) ret_added = g_ptr_array_new_with_free_func (g_object_unref); + g_autoptr(GPtrArray) ret_modified_old = g_ptr_array_new_with_free_func (g_object_unref); + g_autoptr(GPtrArray) ret_modified_new = g_ptr_array_new_with_free_func (g_object_unref); + guint i; + HyPackage pkg; + + g_return_val_if_fail (query == NULL, FALSE); + g_return_val_if_fail (out_removed != NULL && out_added != NULL && + out_modified_old != NULL && out_modified_new != NULL, FALSE); + + orig_sack = get_refsack_for_commit (repo, orig_ref, cancellable, error); + if (!orig_sack) + goto out; + + { _cleanup_hyquery_ HyQuery query = hy_query_create (orig_sack->sack); + hy_query_filter (query, HY_PKG_REPONAME, HY_EQ, HY_SYSTEM_REPO_NAME); + orig_pkglist = hy_query_run (query); + } + + new_sack = get_refsack_for_commit (repo, new_ref, cancellable, error); + if (!new_sack) + goto out; + + { _cleanup_hyquery_ HyQuery query = hy_query_create (new_sack->sack); + hy_query_filter (query, HY_PKG_REPONAME, HY_EQ, HY_SYSTEM_REPO_NAME); + new_pkglist = hy_query_run (query); + } + + FOR_PACKAGELIST(pkg, new_pkglist, i) + { + _cleanup_hyquery_ HyQuery query = NULL; + _cleanup_hypackagelist_ HyPackageList pkglist = NULL; + guint count; + HyPackage oldpkg; + + query = hy_query_create (orig_sack->sack); + hy_query_filter (query, HY_PKG_NAME, HY_EQ, hy_package_get_name (pkg)); + hy_query_filter (query, HY_PKG_EVR, HY_NEQ, hy_package_get_evr (pkg)); + hy_query_filter (query, HY_PKG_REPONAME, HY_EQ, HY_SYSTEM_REPO_NAME); + pkglist = hy_query_run (query); + + count = hy_packagelist_count (pkglist); + if (count > 0) + { + /* See comment above about transitions from N -> 1 */ + oldpkg = hy_packagelist_get (pkglist, 0); + + g_ptr_array_add (ret_modified_old, _rpm_ostree_package_new (orig_sack, oldpkg)); + g_ptr_array_add (ret_modified_new, _rpm_ostree_package_new (new_sack, pkg)); + } + } + + FOR_PACKAGELIST(pkg, orig_pkglist, i) + { + _cleanup_hyquery_ HyQuery query = NULL; + _cleanup_hypackagelist_ HyPackageList pkglist = NULL; + + query = hy_query_create (new_sack->sack); + hy_query_filter (query, HY_PKG_NAME, HY_EQ, hy_package_get_name (pkg)); + hy_query_filter (query, HY_PKG_REPONAME, HY_EQ, HY_SYSTEM_REPO_NAME); + pkglist = hy_query_run (query); + + if (hy_packagelist_count (pkglist) == 0) + g_ptr_array_add (ret_removed, _rpm_ostree_package_new (orig_sack, pkg)); + } + + FOR_PACKAGELIST(pkg, new_pkglist, i) + { + _cleanup_hyquery_ HyQuery query = NULL; + _cleanup_hypackagelist_ HyPackageList pkglist = NULL; + + query = hy_query_create (orig_sack->sack); + hy_query_filter (query, HY_PKG_NAME, HY_EQ, hy_package_get_name (pkg)); + hy_query_filter (query, HY_PKG_REPONAME, HY_EQ, HY_SYSTEM_REPO_NAME); + pkglist = hy_query_run (query); + + if (hy_packagelist_count (pkglist) == 0) + g_ptr_array_add (ret_added, _rpm_ostree_package_new (new_sack, pkg)); + } + + ret = TRUE; + *out_removed = g_steal_pointer (&ret_removed); + *out_added = g_steal_pointer (&ret_added); + *out_modified_old = g_steal_pointer (&ret_modified_old); + *out_modified_new = g_steal_pointer (&ret_modified_new); + out: + return ret; +} diff --git a/src/lib/rpmostree-db.h b/src/lib/rpmostree-db.h index 14a91de8..3527b0e9 100644 --- a/src/lib/rpmostree-db.h +++ b/src/lib/rpmostree-db.h @@ -31,4 +31,15 @@ _RPMOSTREE_EXTERN GPtrArray *rpm_ostree_db_query (OstreeRepo *repo GCancellable *cancellable, GError **error); +_RPMOSTREE_EXTERN gboolean rpm_ostree_db_diff (OstreeRepo *repo, + const char *orig_ref, + const char *new_ref, + GVariant *query, + GPtrArray **out_removed, + GPtrArray **out_added, + GPtrArray **out_modified_old, + GPtrArray **out_modified_new, + GCancellable *cancellable, + GError **error); + G_END_DECLS diff --git a/tests/manual/test-dbquery.py b/tests/manual/test-dbquery.py index e20db4f3..ed87b85c 100644 --- a/tests/manual/test-dbquery.py +++ b/tests/manual/test-dbquery.py @@ -12,3 +12,11 @@ print "Package list: " for p in qr: print p.get_nevra() +_,removed,added,modold,modnew = RpmOstree.db_diff(r, ref + '^', ref, None, None) +for p in removed: + print "D " + p.get_nevra() +for p in added: + print "A " + p.get_nevra() +for o,n in zip(modold, modnew): + print "M {0} {1} -> {2}".format(o.get_name(), o.get_evr(), n.get_evr()) +