Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add --contains-pkgs=.. option to history list and info #1980

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions dnf5/commands/history/arguments.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,16 @@ class IgnoreExtrasOption : public libdnf5::cli::session::BoolOption {

std::function<std::vector<std::string>(const char * arg)> create_history_id_autocomplete(Context & ctx);

class HistoryContainsPkgsOption : public libdnf5::cli::session::AppendStringListOption {
public:
explicit HistoryContainsPkgsOption(libdnf5::cli::session::Command & command)
: AppendStringListOption(
command,
"contains-pkgs",
'\0',
_("Show only transactions containing packages with specified names. List option, supports globs."),
_("PACKAGE_NAME,...")) {}
};

} // namespace dnf5

Expand Down
19 changes: 16 additions & 3 deletions dnf5/commands/history/history_info.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ void HistoryInfoCommand::set_argument_parser() {
auto & ctx = get_context();
transaction_specs->get_arg()->set_complete_hook_func(create_history_id_autocomplete(ctx));
reverse = std::make_unique<ReverseOption>(*this);
contains_pkgs = std::make_unique<HistoryContainsPkgsOption>(*this);
}

void HistoryInfoCommand::run() {
Expand All @@ -49,15 +50,27 @@ void HistoryInfoCommand::run() {
transactions = list_transactions_from_specs(history, ts_specs);
}

if (!contains_pkgs->get_value().empty()) {
history.filter_transactions_by_pkg_names(transactions, contains_pkgs->get_value());
}

if (reverse->get_value()) {
std::sort(transactions.begin(), transactions.end(), std::greater{});
} else {
std::sort(transactions.begin(), transactions.end());
}

for (auto ts : transactions) {
libdnf5::cli::output::print_transaction_info(ts);
std::cout << std::endl;
if (!transactions.empty()) {
for (auto ts : transactions) {
libdnf5::cli::output::print_transaction_info(ts);
std::cout << std::endl;
}
} else {
if (ts_specs.empty()) {
std::cout << _("No match found, history info defaults to considering only the last transaction, specify "
"\"1..last\" range to search all transactions.")
<< std::endl;
}
}
}

Expand Down
1 change: 1 addition & 0 deletions dnf5/commands/history/history_info.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class HistoryInfoCommand : public Command {

std::unique_ptr<TransactionSpecArguments> transaction_specs{nullptr};
std::unique_ptr<ReverseOption> reverse{nullptr};
std::unique_ptr<HistoryContainsPkgsOption> contains_pkgs{nullptr};
};

} // namespace dnf5
Expand Down
7 changes: 5 additions & 2 deletions dnf5/commands/history/history_list.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ along with libdnf. If not, see <https://www.gnu.org/licenses/>.

#include <libdnf5-cli/output/transactionlist.hpp>

#include <iostream>


namespace dnf5 {

Expand All @@ -35,6 +33,7 @@ void HistoryListCommand::set_argument_parser() {

transaction_specs = std::make_unique<TransactionSpecArguments>(*this);
reverse = std::make_unique<ReverseOption>(*this);
contains_pkgs = std::make_unique<HistoryContainsPkgsOption>(*this);
}

void HistoryListCommand::run() {
Expand All @@ -48,6 +47,10 @@ void HistoryListCommand::run() {
transactions = list_transactions_from_specs(history, transaction_specs->get_value());
}

if (!contains_pkgs->get_value().empty()) {
history.filter_transactions_by_pkg_names(transactions, contains_pkgs->get_value());
}

if (reverse->get_value()) {
std::sort(transactions.begin(), transactions.end(), std::greater{});
} else {
Expand Down
1 change: 1 addition & 0 deletions dnf5/commands/history/history_list.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class HistoryListCommand : public Command {

std::unique_ptr<TransactionSpecArguments> transaction_specs{nullptr};
std::unique_ptr<ReverseOption> reverse{nullptr};
std::unique_ptr<HistoryContainsPkgsOption> contains_pkgs{nullptr};
};


Expand Down
2 changes: 2 additions & 0 deletions doc/changes_from_dnf4.7.rst
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,8 @@ Changes to individual commands
* Dropped. The functionality is replaced by the ``--help`` option.

``history``
* Subcommands are now mandatory: ``dnf history`` has to be now ``dnf5 history list``.
* The ``history`` commands now only accept transaction ID arguments; to filter by packages, use the ``--contains-pkgs=PACKAGE_NAME,...`` option, available for ``list`` and ``info``.
* ``undo`` subcommand now accepts ``--ignore-extras`` and ``--ignore-installed`` like original ``history replay`` command.
* ``store`` subcommand now creates a directory with transaction JSON file instead of a single transaction JSON file directly.
* ``store`` subcommand's ``--output`` option now accepts a directory path instead of a file. The default is ``./transaction``.
Expand Down
2 changes: 1 addition & 1 deletion doc/commands/group.8.rst
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ Options for ``list`` and ``info``
``--hidden``
| Show also hidden groups.

``--contains-pkgs``
``--contains-pkgs=PACKAGE_NAME,...``
| Show only groups containing packages with specified names. List option, supports globs.


Expand Down
4 changes: 4 additions & 0 deletions doc/commands/history.8.rst
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ Options for ``list`` and ``info``
``--reverse``
| Reverse the order of transactions in the output.

``--contains-pkgs=PACKAGE_NAME,...``
| Show only transactions containing packages with specified names.
| This is a list option. Globs are supported.


Options for ``undo``, ``rollback`` and ``redo``
===============================================
Expand Down
7 changes: 7 additions & 0 deletions include/libdnf5/transaction/transaction_history.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,13 @@ class LIBDNF_API TransactionHistory {
/// @return Mapped transaction id -> count.
std::unordered_map<int64_t, int64_t> get_transaction_item_counts(const std::vector<Transaction> & transactions);

/// Filter out transactions that don't contain any rpm with matching name
///
/// @param transactions Vector of Transactions to filter
/// @param pkg_names Vector of rpm package names to match
void filter_transactions_by_pkg_names(
std::vector<Transaction> & transactions, const std::vector<std::string> & pkg_names);

private:
/// Create a new Transaction object.
LIBDNF_LOCAL libdnf5::transaction::Transaction new_transaction();
Expand Down
66 changes: 66 additions & 0 deletions libdnf5/transaction/db/trans.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -300,4 +300,70 @@ std::unordered_map<int64_t, int64_t> TransactionDbUtils::transactions_item_count
return id_to_count;
}

static constexpr const char * SQL_TRANS_CONTAINS_RPM_NAME = R"**(
SELECT DISTINCT
"t"."id"
FROM
"trans_item" "ti"
JOIN
"trans" "t" ON ("ti"."trans_id" = "t"."id")
JOIN
"rpm" "i" USING ("item_id")
JOIN
"pkg_name" ON "i"."name_id" = "pkg_name"."id"
)**";

void TransactionDbUtils::filter_transactions_by_pkg_names(
const BaseWeakPtr & base, std::vector<Transaction> & transactions, const std::vector<std::string> & pkg_names) {
libdnf_assert(!pkg_names.empty(), "Cannot filter transactions, no package names provided.");

auto conn = transaction_db_connect(*base);

std::string sql = SQL_TRANS_CONTAINS_RPM_NAME;

sql += "WHERE (\"pkg_name\".\"name\" GLOB ?";
for (size_t i = 1; i < pkg_names.size(); i++) {
sql += "OR \"pkg_name\".\"name\" GLOB ?";
}
sql += ")";

// Limit to only selected transactions
if (!transactions.empty()) {
sql += "AND \"t\".\"id\" IN (";
for (size_t i = 0; i < transactions.size(); ++i) {
if (i == 0) {
sql += "?";
} else {
sql += ", ?";
}
}
sql += ")";
}

auto query = libdnf5::utils::SQLite3::Query(*conn, sql);

for (size_t i = 0; i < pkg_names.size(); ++i) {
// bind indexes from 1
query.bind(static_cast<int>(i + 1), pkg_names[i]);
}

for (size_t i = 0; i < transactions.size(); ++i) {
query.bind(static_cast<int>(i + pkg_names.size() + 1), transactions[i].get_id());
}

std::vector<int> ids_to_keep;
while (query.step() == libdnf5::utils::SQLite3::Statement::StepResult::ROW) {
ids_to_keep.push_back(query.get<int>("id"));
}

transactions.erase(
std::remove_if(
transactions.begin(),
transactions.end(),
[&ids_to_keep](auto trans) {
return std::find(ids_to_keep.begin(), ids_to_keep.end(), trans.get_id()) == ids_to_keep.end();
}),
transactions.end());
}

} // namespace libdnf5::transaction
4 changes: 4 additions & 0 deletions libdnf5/transaction/db/trans.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ class TransactionDbUtils {
/// Get transaction item count for history transactions specified by transaction ids.
static std::unordered_map<int64_t, int64_t> transactions_item_counts(
const BaseWeakPtr & base, const std::vector<Transaction> & transactions);

/// Filter out transactions that don't contain any rpm with name from pkg_names
static void filter_transactions_by_pkg_names(
const BaseWeakPtr & base, std::vector<Transaction> & transactions, const std::vector<std::string> & pkg_names);
};

} // namespace libdnf5::transaction
Expand Down
5 changes: 5 additions & 0 deletions libdnf5/transaction/transaction_history.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,9 @@ std::unordered_map<int64_t, int64_t> TransactionHistory::get_transaction_item_co
return TransactionDbUtils::transactions_item_counts(p_impl->base, transactions);
}

void TransactionHistory::filter_transactions_by_pkg_names(
std::vector<Transaction> & transactions, const std::vector<std::string> & pkg_names) {
TransactionDbUtils::filter_transactions_by_pkg_names(p_impl->base, transactions, pkg_names);
}

} // namespace libdnf5::transaction
Loading