1 /*
2 * Copyright (C) 2017-2018 Red Hat, Inc.
3 *
4 * Licensed under the GNU Lesser General Public License Version 2.1
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include "../../utils/bgettext/bgettext-lib.h"
22 #include "../../utils/tinyformat/tinyformat.hpp"
23
24 #include "CompsEnvironmentItem.hpp"
25 #include "CompsGroupItem.hpp"
26 #include "RPMItem.hpp"
27 #include "Transaction.hpp"
28 #include "TransactionItem.hpp"
29
30 namespace libdnf {
31
Transaction(SQLite3Ptr conn)32 swdb_private::Transaction::Transaction(SQLite3Ptr conn)
33 : libdnf::Transaction(conn)
34 {
35 }
36
37 void
begin()38 swdb_private::Transaction::begin()
39 {
40 if (id != 0) {
41 throw std::runtime_error(_("Transaction has already began!"));
42 }
43 dbInsert();
44 saveItems();
45 }
46
47 void
finish(TransactionState state)48 swdb_private::Transaction::finish(TransactionState state)
49 {
50 // save states to the database before checking for UNKNOWN state
51 for (auto i : getItems()) {
52 i->saveState();
53 }
54
55 for (auto i : getItems()) {
56 if (i->getState() == TransactionItemState::UNKNOWN) {
57 throw std::runtime_error(
58 tfm::format(_("TransactionItem state is not set: %s"), i->getItem()->toStr()));
59 }
60 }
61
62 setState(state);
63 dbUpdate();
64 }
65
66 void
dbInsert()67 swdb_private::Transaction::dbInsert()
68 {
69 const char *sql =
70 "INSERT INTO "
71 " trans ("
72 " dt_begin, "
73 " dt_end, "
74 " rpmdb_version_begin, "
75 " rpmdb_version_end, "
76 " releasever, "
77 " user_id, "
78 " cmdline, "
79 " state, "
80 " comment, "
81 " id "
82 " ) "
83 "VALUES "
84 " (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
85 SQLite3::Statement query(*conn.get(), sql);
86 query.bindv(getDtBegin(),
87 getDtEnd(),
88 getRpmdbVersionBegin(),
89 getRpmdbVersionEnd(),
90 getReleasever(),
91 getUserId(),
92 getCmdline(),
93 static_cast< int >(getState()),
94 getComment());
95 if (getId() > 0) {
96 query.bind(9, getId());
97 }
98 query.step();
99 setId(conn->lastInsertRowID());
100
101 // add used software - has to be added at initialization state
102 if (!softwarePerformedWith.empty()) {
103 sql = R"**(
104 INSERT OR REPLACE INTO
105 trans_with (
106 trans_id,
107 item_id
108 )
109 VALUES
110 (?, ?)
111 )**";
112 SQLite3::Statement swQuery(*conn.get(), sql);
113 bool first = true;
114 for (auto software : softwarePerformedWith) {
115 if (!first) {
116 swQuery.reset();
117 }
118 first = false;
119 // save the item to create a database id
120 software->save();
121 swQuery.bindv(getId(), software->getId());
122 swQuery.step();
123 }
124 }
125 }
126
127 void
dbUpdate()128 swdb_private::Transaction::dbUpdate()
129 {
130 const char *sql =
131 "UPDATE "
132 " trans "
133 "SET "
134 " dt_begin=?, "
135 " dt_end=?, "
136 " rpmdb_version_begin=?, "
137 " rpmdb_version_end=?, "
138 " releasever=?, "
139 " user_id=?, "
140 " cmdline=?, "
141 " state=?, "
142 " comment=? "
143 "WHERE "
144 " id = ?";
145 SQLite3::Statement query(*conn.get(), sql);
146 query.bindv(getDtBegin(),
147 getDtEnd(),
148 getRpmdbVersionBegin(),
149 getRpmdbVersionEnd(),
150 getReleasever(),
151 getUserId(),
152 getCmdline(),
153 static_cast< int >(getState()),
154 getComment(),
155 getId());
156 query.step();
157 }
158
159 TransactionItemPtr
addItem(std::shared_ptr<Item> item,const std::string & repoid,TransactionItemAction action,TransactionItemReason reason)160 swdb_private::Transaction::addItem(std::shared_ptr< Item > item,
161 const std::string &repoid,
162 TransactionItemAction action,
163 TransactionItemReason reason)
164 {
165 for (auto & i : items) {
166 if (i->getItem()->toStr() != item->toStr()) {
167 continue;
168 }
169 if (i->getRepoid() != repoid) {
170 continue;
171 }
172 if (i->getAction() != action) {
173 continue;
174 }
175 if (reason > i->getReason()) {
176 // use the more significant reason
177 i->setReason(reason);
178 }
179 // don't add duplicates to the list
180 // return an existing transaction item if exists
181 return i;
182 }
183 auto trans_item = std::make_shared< TransactionItem >(this);
184 trans_item->setItem(item);
185 trans_item->setRepoid(repoid);
186 trans_item->setAction(action);
187 trans_item->setReason(reason);
188 items.push_back(trans_item);
189 return trans_item;
190 }
191
192 void
saveItems()193 swdb_private::Transaction::saveItems()
194 {
195 // TODO: remove all existing items from the database first?
196 for (auto i : items) {
197 i->save();
198 }
199
200 /* this has to be done in a separate loop to make sure
201 * that all the items already have ID assigned
202 */
203 for (auto i : items) {
204 i->saveReplacedBy();
205 }
206 }
207
208 /**
209 * Loader for the transaction items.
210 * \return list of transaction items associated with the transaction
211 */
212 std::vector< TransactionItemPtr >
getItems()213 swdb_private::Transaction::getItems()
214 {
215 if (items.empty()) {
216 items = libdnf::Transaction::getItems();
217 }
218 return items;
219 }
220
221 /**
222 * Append software to softwarePerformedWith list.
223 * Software is saved to the database using save method and therefore
224 * all the software has to be added before transaction is saved.
225 * \param software RPMItem used to perform the transaction
226 */
227 void
addSoftwarePerformedWith(std::shared_ptr<RPMItem> software)228 swdb_private::Transaction::addSoftwarePerformedWith(std::shared_ptr< RPMItem > software)
229 {
230 softwarePerformedWith.insert(software);
231 }
232
233 /**
234 * Save console output line for current transaction to the database. Transaction has
235 * to be saved in advance, otherwise an exception will be thrown.
236 * \param fileDescriptor UNIX file descriptor index (1 = stdout, 2 = stderr).
237 * \param line console output content
238 */
239 void
addConsoleOutputLine(int fileDescriptor,const std::string & line)240 swdb_private::Transaction::addConsoleOutputLine(int fileDescriptor, const std::string &line)
241 {
242 if (!getId()) {
243 throw std::runtime_error(_("Can't add console output to unsaved transaction"));
244 }
245
246 const char *sql = R"**(
247 INSERT INTO
248 console_output (
249 trans_id,
250 file_descriptor,
251 line
252 )
253 VALUES
254 (?, ?, ?);
255 )**";
256 SQLite3::Statement query(*conn, sql);
257 query.bindv(getId(), fileDescriptor, line);
258 query.step();
259 }
260
261 } // namespace libdnf
262