175 lines
6.7 KiB
C++
175 lines
6.7 KiB
C++
#include "database.h"
|
|
#include <pqxx/pqxx>
|
|
#include <vector>
|
|
#include <unordered_map>
|
|
#include <string>
|
|
#include <iostream>
|
|
|
|
Database::Database(const std::string &conninfo)
|
|
{
|
|
try {
|
|
connection_ = std::make_unique<pqxx::connection>(conninfo);
|
|
if (!connection_->is_open()) {
|
|
throw std::runtime_error("Konnte DB-Verbindung nicht öffnen!");
|
|
}
|
|
} catch (const std::exception &e) {
|
|
std::cerr << "[Database] Fehler beim Verbinden: " << e.what() << std::endl;
|
|
|
|
|
|
throw;
|
|
}
|
|
}
|
|
|
|
std::vector<std::map<std::string, std::string>>
|
|
Database::query(const std::string &sql)
|
|
{
|
|
std::vector<std::map<std::string, std::string>> rows;
|
|
try {
|
|
pqxx::work txn(*connection_);
|
|
pqxx::result r = txn.exec(sql);
|
|
txn.commit();
|
|
|
|
// Pre-allocate memory for better performance
|
|
rows.reserve(r.size());
|
|
|
|
for (const auto& row : r) {
|
|
std::map<std::string, std::string> oneRow;
|
|
|
|
for (auto f = 0u; f < row.size(); f++) {
|
|
const std::string colName = r.column_name(f);
|
|
const char* value = row[f].c_str();
|
|
oneRow.emplace(colName, value ? value : "");
|
|
}
|
|
rows.emplace_back(std::move(oneRow));
|
|
}
|
|
} catch (const std::exception &ex) {
|
|
std::cerr << "[Database] query-Fehler: " << ex.what() << "\nSQL: " << sql << std::endl;
|
|
}
|
|
return rows;
|
|
}
|
|
|
|
void Database::prepare(const std::string &stmtName, const std::string &sql)
|
|
{
|
|
try {
|
|
// Versuche zuerst, das alte Statement zu entfernen, falls es existiert
|
|
try {
|
|
remove(stmtName);
|
|
} catch (...) {
|
|
// Ignoriere Fehler beim Entfernen - das Statement existiert möglicherweise nicht
|
|
}
|
|
|
|
// Erstelle das neue Statement
|
|
pqxx::work txn(*connection_);
|
|
txn.conn().prepare(stmtName, sql);
|
|
txn.commit();
|
|
} catch (const std::exception &ex) {
|
|
std::cerr << "[Database] prepare-Fehler: " << ex.what()
|
|
<< "\nSQL: " << sql << std::endl;
|
|
}
|
|
}
|
|
|
|
Database::FieldList Database::execute(const std::string& stmtName,
|
|
const std::vector<std::string>& params)
|
|
{
|
|
try {
|
|
pqxx::work txn(*connection_);
|
|
|
|
pqxx::result res;
|
|
if (params.empty()) {
|
|
res = txn.exec_prepared(stmtName);
|
|
} else {
|
|
// Kompatibilität für libpqxx 6.x (Ubuntu 22) und 7.x (OpenSUSE Tumbleweed)
|
|
#if PQXX_VERSION_MAJOR >= 7
|
|
pqxx::params p;
|
|
for (const auto& v : params) p.append(v);
|
|
res = txn.exec_prepared(stmtName, p);
|
|
#else
|
|
// Für libpqxx 6.x - verwende exec_params mit variadic template
|
|
if (params.size() == 0) {
|
|
res = txn.exec_prepared(stmtName);
|
|
} else if (params.size() == 1) {
|
|
res = txn.exec_prepared(stmtName, params[0]);
|
|
} else if (params.size() == 2) {
|
|
res = txn.exec_prepared(stmtName, params[0], params[1]);
|
|
} else if (params.size() == 3) {
|
|
res = txn.exec_prepared(stmtName, params[0], params[1], params[2]);
|
|
} else if (params.size() == 4) {
|
|
res = txn.exec_prepared(stmtName, params[0], params[1], params[2], params[3]);
|
|
} else if (params.size() == 5) {
|
|
res = txn.exec_prepared(stmtName, params[0], params[1], params[2], params[3], params[4]);
|
|
} else {
|
|
// Für mehr als 5 Parameter, verwende exec_params mit einzelnen Parametern
|
|
std::string sql = "EXECUTE " + stmtName;
|
|
if (!params.empty()) {
|
|
sql += "(";
|
|
for (size_t i = 0; i < params.size(); ++i) {
|
|
if (i > 0) sql += ", ";
|
|
sql += "$" + std::to_string(i + 1);
|
|
}
|
|
sql += ")";
|
|
}
|
|
|
|
// Konvertiere vector zu einzelnen Parametern für exec_params
|
|
if (params.size() == 6) {
|
|
res = txn.exec_params(sql, params[0], params[1], params[2], params[3], params[4], params[5]);
|
|
} else if (params.size() == 7) {
|
|
res = txn.exec_params(sql, params[0], params[1], params[2], params[3], params[4], params[5], params[6]);
|
|
} else if (params.size() == 8) {
|
|
res = txn.exec_params(sql, params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7]);
|
|
} else if (params.size() == 9) {
|
|
res = txn.exec_params(sql, params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8]);
|
|
} else if (params.size() == 10) {
|
|
res = txn.exec_params(sql, params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7], params[8], params[9]);
|
|
} else {
|
|
// Für noch mehr Parameter, verwende eine einfachere Lösung
|
|
throw std::runtime_error("Zu viele Parameter für prepared statement: " + std::to_string(params.size()));
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
FieldList out;
|
|
out.reserve(res.size());
|
|
for (const auto& row : res) {
|
|
std::unordered_map<std::string, std::string> m;
|
|
m.reserve(row.size()); // Pre-allocate for better performance
|
|
|
|
for (const auto& f : row) {
|
|
// Use string_view for better performance (C++17+)
|
|
const std::string_view name = f.name();
|
|
const char* value = f.c_str();
|
|
m.emplace(name, f.is_null() ? std::string{} : std::string(value));
|
|
}
|
|
out.emplace_back(std::move(m));
|
|
}
|
|
|
|
txn.commit();
|
|
return out;
|
|
} catch (const std::exception& e) {
|
|
std::cerr << "[Database] execute-Fehler: " << e.what()
|
|
<< "\n\nStatement: " << stmtName << std::endl;
|
|
return {};
|
|
}
|
|
}
|
|
|
|
void Database::remove(const std::string &stmtName) {
|
|
pqxx::work txn(*connection_);
|
|
txn.conn().unprepare(stmtName);
|
|
txn.commit();
|
|
}
|
|
|
|
bool Database::isValid() const {
|
|
try {
|
|
if (!connection_ || !connection_->is_open()) {
|
|
return false;
|
|
}
|
|
pqxx::work txn(*connection_);
|
|
txn.exec("SELECT 1"); // Einfacher Ping
|
|
txn.commit();
|
|
return true;
|
|
} catch (const std::exception &ex) {
|
|
std::cerr << "[Database] Verbindung ungültig: " << ex.what() << "\n";
|
|
return false;
|
|
}
|
|
}
|