#include "database.h" #include #include #include #include #include Database::Database(const std::string &conninfo) { try { connection_ = std::make_unique(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> Database::query(const std::string &sql) { std::vector> 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 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& 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 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; } }