FOSSredder 0.5.0
Loading...
Searching...
No Matches
SqliteTransactionRepository.cpp
Go to the documentation of this file.
1
5
11#include <sqlite3.h>
12#include <stdexcept>
13
15 std::shared_ptr<SqliteDb> db;
16 std::shared_ptr<core::ports::diagnostics::IErrorReporter> errorReporter;
17};
18
20 const std::string& dbPath)
21 : SqliteTransactionRepository(std::make_shared<SqliteDb>(dbPath), nullptr) {
22}
23
25 const std::string& dbPath,
26 std::shared_ptr<core::ports::diagnostics::IErrorReporter> er)
27 : SqliteTransactionRepository(std::make_shared<SqliteDb>(dbPath),
28 std::move(er)) {}
29
31 std::shared_ptr<SqliteDb> db)
32 : SqliteTransactionRepository(std::move(db), nullptr) {}
33
35 std::shared_ptr<SqliteDb> db,
36 std::shared_ptr<core::ports::diagnostics::IErrorReporter> er)
37 : pimpl_(std::make_unique<Impl>()) {
38 if (!db)
39 throw std::runtime_error("db is null");
40 pimpl_->db = std::move(db);
41 pimpl_->errorReporter = std::move(er);
42}
43
45
46namespace {
47
48constexpr auto kSelectTx =
49 "SELECT id, name, booking_date, amount, statement_id,"
50 " status, actor_id, allocatable, contract_id, valuta, created_at, "
51 "updated_at FROM transactions";
52
53void bindNullableText(persistence::StmtGuard& stmt, int column,
54 const std::string& value) {
55 if (value.empty()) {
56 sqlite3_bind_null(stmt.get(), column);
57 return;
58 }
59 stmt.bindText(column, value);
60}
61
62void loadTxProperties(sqlite3* db, Transaction& t) {
63 persistence::StmtGuard s(db, "SELECT property_id FROM transaction_properties "
64 "WHERE transaction_id = ?;");
65 if (!s)
66 return;
67 s.bindText(1, t.id());
68 while (s.step() == SQLITE_ROW)
69 t.addPropertyId(s.columnText(0));
70}
71
72std::shared_ptr<Transaction> readTx(persistence::StmtGuard& s, sqlite3* db) {
73 auto t = std::make_shared<Transaction>();
74 t->setId(s.columnText(0));
75 t->setName(s.columnText(1));
77 t->setAmount(s.columnDouble(3));
79 t->setStatus(static_cast<Transaction::Status>(s.columnInt(5)));
80 t->setActorId(s.columnText(6));
81 t->setAllocatable(s.columnInt(7) != 0);
83 t->setValuta(s.columnText(9));
84 t->setCreatedAt(s.columnText(10));
85 t->setUpdatedAt(s.columnText(11));
86 loadTxProperties(db, *t);
87 return t;
88}
89
90void insertTxProperties(sqlite3* db, const Transaction& t) {
91 persistence::StmtGuard s(db, "INSERT OR IGNORE INTO transaction_properties "
92 "(transaction_id, property_id) VALUES (?, ?);");
93 if (!s)
94 return;
95 for (const auto& pid : t.propertyIds()) {
96 if (pid.empty())
97 continue;
98 s.reset();
99 s.bindText(1, t.id());
100 s.bindText(2, pid);
101 s.step();
102 }
103}
104
105void deleteTxProperties(sqlite3* db, const std::string& txId) {
107 db, "DELETE FROM transaction_properties WHERE transaction_id = ?;");
108 if (!s)
109 return;
110 s.bindText(1, txId);
111 s.step();
112}
113
114} // namespace
115
117 const std::shared_ptr<Transaction>& transaction) {
118 if (!transaction || transaction->id().empty())
119 return;
120 persistence::StmtGuard stmt(pimpl_->db->handle(),
121 "INSERT OR IGNORE INTO transactions"
122 " (id, name, booking_date, amount, statement_id,"
123 " status, actor_id, allocatable, contract_id, "
124 "valuta, created_at, updated_at)"
125 " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);");
126 if (!stmt)
127 return;
128 stmt.bindText(1, transaction->id());
129 stmt.bindText(2, transaction->name());
130 stmt.bindText(3, transaction->bookingDate());
131 stmt.bindDouble(4, transaction->amount());
132 bindNullableText(stmt, 5, transaction->statementId());
133 stmt.bindInt(6, static_cast<int>(transaction->status()));
134 bindNullableText(stmt, 7, transaction->actorId());
135 stmt.bindInt(8, transaction->isAllocatable() ? 1 : 0);
136 bindNullableText(stmt, 9, transaction->contractId());
137 stmt.bindText(10, transaction->valuta());
138 stmt.bindText(11, transaction->createdAt());
139 stmt.bindText(12, transaction->updatedAt());
140 if (stmt.step() == SQLITE_DONE)
141 insertTxProperties(pimpl_->db->handle(), *transaction);
142}
143
144std::vector<std::shared_ptr<Transaction>>
146 std::vector<std::shared_ptr<Transaction>> out;
147 persistence::StmtGuard stmt(pimpl_->db->handle(),
148 std::string(kSelectTx) + " ORDER BY rowid;");
149 if (!stmt)
150 return out;
151 while (stmt.step() == SQLITE_ROW)
152 out.push_back(readTx(stmt, pimpl_->db->handle()));
153 return out;
154}
155
156std::optional<std::shared_ptr<Transaction>>
158 if (id.empty())
159 return std::nullopt;
161 pimpl_->db->handle(), std::string(kSelectTx) + " WHERE id = ? LIMIT 1;");
162 if (!stmt)
163 return std::nullopt;
164 stmt.bindText(1, id);
165 if (stmt.step() != SQLITE_ROW)
166 return std::nullopt;
167 return readTx(stmt, pimpl_->db->handle());
168}
169
171 if (id.empty())
172 return;
173 deleteTxProperties(pimpl_->db->handle(), id);
174 persistence::StmtGuard stmt(pimpl_->db->handle(),
175 "DELETE FROM transactions WHERE id = ?;");
176 if (!stmt)
177 return;
178 stmt.bindText(1, id);
179 stmt.step();
180}
181
183 const std::shared_ptr<Transaction>& transaction) {
184 if (!transaction || transaction->id().empty())
185 return;
187 pimpl_->db->handle(),
188 "UPDATE transactions SET name=?, booking_date=?, amount=?,"
189 " statement_id=?, status=?, actor_id=?, allocatable=?, contract_id=?, "
190 "valuta=?, created_at=?, updated_at=?"
191 " WHERE id=?;");
192 if (!stmt)
193 return;
194 stmt.bindText(1, transaction->name());
195 stmt.bindText(2, transaction->bookingDate());
196 stmt.bindDouble(3, transaction->amount());
197 bindNullableText(stmt, 4, transaction->statementId());
198 stmt.bindInt(5, static_cast<int>(transaction->status()));
199 bindNullableText(stmt, 6, transaction->actorId());
200 stmt.bindInt(7, transaction->isAllocatable() ? 1 : 0);
201 bindNullableText(stmt, 8, transaction->contractId());
202 stmt.bindText(9, transaction->valuta());
203 stmt.bindText(10, transaction->createdAt());
204 stmt.bindText(11, transaction->updatedAt());
205 stmt.bindText(12, transaction->id());
206 stmt.step();
207 deleteTxProperties(pimpl_->db->handle(), transaction->id());
208 insertTxProperties(pimpl_->db->handle(), *transaction);
209}
210
212 const std::shared_ptr<Transaction>& transaction) {
213 if (!transaction || transaction->id().empty())
214 return;
215
217 pimpl_->db->handle(),
218 "INSERT INTO transactions (id, name, booking_date, amount, statement_id, "
219 "status, actor_id, allocatable, contract_id, valuta, created_at, "
220 "updated_at) "
221 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) "
222 "ON CONFLICT(id) DO UPDATE SET "
223 "name = excluded.name, booking_date = excluded.booking_date, amount = "
224 "excluded.amount, "
225 "statement_id = excluded.statement_id, status = excluded.status, "
226 "actor_id = excluded.actor_id, allocatable = excluded.allocatable, "
227 "contract_id = excluded.contract_id, valuta = excluded.valuta, "
228 "created_at = excluded.created_at, updated_at = excluded.updated_at;");
229 if (!stmt) {
230 return;
231 }
232
233 stmt.bindText(1, transaction->id());
234 stmt.bindText(2, transaction->name());
235 stmt.bindText(3, transaction->bookingDate());
236 stmt.bindDouble(4, transaction->amount());
237 bindNullableText(stmt, 5, transaction->statementId());
238 stmt.bindInt(6, static_cast<int>(transaction->status()));
239 bindNullableText(stmt, 7, transaction->actorId());
240 stmt.bindInt(8, transaction->isAllocatable() ? 1 : 0);
241 bindNullableText(stmt, 9, transaction->contractId());
242 stmt.bindText(10, transaction->valuta());
243 stmt.bindText(11, transaction->createdAt());
244 stmt.bindText(12, transaction->updatedAt());
245
246 stmt.step();
247
248 deleteTxProperties(pimpl_->db->handle(), transaction->id());
249 insertTxProperties(pimpl_->db->handle(), *transaction);
250}
251
253 char* err = nullptr;
254 sqlite3_exec(pimpl_->db->handle(),
255 "DELETE FROM transaction_properties; DELETE FROM transactions;",
256 nullptr, nullptr, &err);
257 if (err)
258 sqlite3_free(err);
259}
260
261std::vector<std::shared_ptr<Transaction>>
263 const std::string& contractId) const {
264 std::vector<std::shared_ptr<Transaction>> out;
266 pimpl_->db->handle(), std::string(kSelectTx) + " WHERE contract_id = ?;");
267 if (!stmt)
268 return out;
269 stmt.bindText(1, contractId);
270 while (stmt.step() == SQLITE_ROW)
271 out.push_back(readTx(stmt, pimpl_->db->handle()));
272 return out;
273}
274
276 const std::string& contractId,
277 const std::vector<std::string>& transactionIds) {
278 if (transactionIds.empty())
279 return;
280 std::vector<std::string> ids;
281 ids.reserve(transactionIds.size());
282 for (const auto& id : transactionIds)
283 if (!id.empty())
284 ids.push_back(id);
285 if (ids.empty())
286 return;
287
288 SqliteTransaction tx(pimpl_->db->handle());
289
290 std::string sql = "UPDATE transactions SET contract_id = ? WHERE id IN (";
291 for (size_t i = 0; i < ids.size(); ++i) {
292 if (i)
293 sql += ",";
294 sql += "?";
295 }
296 sql += ");";
297
298 persistence::StmtGuard stmt(pimpl_->db->handle(), sql.c_str());
299 if (!stmt)
300 return;
301
302 stmt.bindText(1, contractId);
303 for (size_t i = 0; i < ids.size(); ++i) {
304 stmt.bindText(static_cast<int>(i + 2), ids[i]);
305 }
306
307 stmt.step();
308 tx.commit();
309}
Declares the lightweight SQLite connection wrapper.
Declares a lightweight SQLite transaction guard.
Declares the SQLite-backed transaction repository.
RAII wrapper for sqlite3_stmt* that auto-finalizes on destruction.
Domain model for transactions.
void commit()
Commit the transaction.
void upsertTransaction(const std::shared_ptr< core::domain::Transaction > &transaction) override
Insert or update a transaction.
void updateTransaction(const std::shared_ptr< core::domain::Transaction > &transaction) override
Update an existing transaction.
void clearTransactions() override
Remove all transactions.
void removeTransaction(const std::string &id) override
Remove a transaction by identifier.
~SqliteTransactionRepository() override
Destroy the repository.
std::vector< std::shared_ptr< core::domain::Transaction > > getTransactions() const override
Retrieve all transactions.
void addTransaction(const std::shared_ptr< core::domain::Transaction > &transaction) override
Insert a new transaction.
SqliteTransactionRepository(const std::string &dbPath)
Create a transaction repository for the database at the given path.
std::optional< std::shared_ptr< core::domain::Transaction > > getTransactionById(const std::string &id) const override
Retrieve a transaction by identifier.
std::vector< std::shared_ptr< core::domain::Transaction > > getTransactionsForContract(const std::string &contractId) const override
Retrieve transactions associated with a given contract.
void assignTransactionsToContract(const std::string &contractId, const std::vector< std::string > &transactionIds) override
Assign transactions to a contract.
void setAmount(MoneyAmount value)
Sets the amount from a value object.
void setStatementId(std::string value)
Sets the statement id.
void setActorId(std::string value)
Sets the actor id.
void setName(EntityName value)
Sets the transaction name from a value object.
void setStatus(Status value)
Sets the transaction status.
void setValuta(std::string value)
Sets the valuta text.
void setContractId(std::string value)
Sets the contract id.
const std::string & id() const noexcept
const std::vector< std::string > & propertyIds() const noexcept
void addPropertyId(std::string value)
Adds a property id when it is not already present.
void setCreatedAt(std::string value)
void setBookingDate(BookingDate value)
Sets the booking date from a value object.
void setId(std::string value)
void setUpdatedAt(std::string value)
void setAllocatable(bool value)
Sets the allocatable flag.
std::string columnText(int col) const noexcept
Definition StmtGuard.h:57
sqlite3_stmt * get() const noexcept
Definition StmtGuard.h:33
void bindInt(int col, int v) noexcept
Definition StmtGuard.h:47
double columnDouble(int col) const noexcept
Definition StmtGuard.h:67
int step() noexcept
Definition StmtGuard.h:37
void reset() noexcept
Definition StmtGuard.h:40
void bindText(int col, const std::string &v) noexcept
Definition StmtGuard.h:44
int columnInt(int col) const noexcept
Definition StmtGuard.h:61
void bindDouble(int col, double v) noexcept
Definition StmtGuard.h:53
std::shared_ptr< core::ports::diagnostics::IErrorReporter > errorReporter