#include <biboumi.h> #ifdef PQ_FOUND #include <utils/scopeguard.hpp> #include <utils/tolower.hpp> #include <database/query.hpp> #include <database/postgresql_engine.hpp> #include <database/postgresql_statement.hpp> #include <logger/logger.hpp> #include <cstring> #include <database/database.hpp> PostgresqlEngine::PostgresqlEngine(PGconn*const conn): conn(conn) {} PostgresqlEngine::~PostgresqlEngine() { PQfinish(this->conn); } static void logging_notice_processor(void*, const char* original) { if (original && std::strlen(original) > 0) { std::string message{original, std::strlen(original) - 1}; log_warning("PostgreSQL: ", message); } } std::unique_ptr<DatabaseEngine> PostgresqlEngine::open(const std::string& conninfo) { PGconn* con = PQconnectdb(conninfo.data()); if (!con) { log_error("Failed to allocate a Postgresql connection"); throw std::runtime_error(""); } const auto status = PQstatus(con); if (status != CONNECTION_OK) { const char* errmsg = PQerrorMessage(con); log_error("Postgresql connection failed: ", errmsg); PQfinish(con); throw std::runtime_error("failed to open connection."); } PQsetNoticeProcessor(con, &logging_notice_processor, nullptr); return std::make_unique<PostgresqlEngine>(con); } std::map<std::string, std::string> PostgresqlEngine::get_all_columns_from_table(const std::string& table_name) { const auto query = "SELECT column_name, data_type from information_schema.columns where table_name='" + table_name + "'"; auto statement = this->prepare(query); std::map<std::string, std::string> columns; while (statement->step() == StepResult::Row) columns[utils::tolower(statement->get_column_text(0))] = utils::tolower(statement->get_column_text(1)); return columns; } std::tuple<bool, std::string> PostgresqlEngine::raw_exec(const std::string& query) { #ifdef DEBUG_SQL_QUERIES log_debug("SQL QUERY: ", query); const auto timer = make_sql_timer(); #endif PGresult* res = PQexec(this->conn, query.data()); auto sg = utils::make_scope_guard([res](){ PQclear(res); }); auto res_status = PQresultStatus(res); if (res_status != PGRES_COMMAND_OK) return std::make_tuple(false, PQresultErrorMessage(res)); return std::make_tuple(true, std::string{}); } std::unique_ptr<Statement> PostgresqlEngine::prepare(const std::string& query) { return std::make_unique<PostgresqlStatement>(query, this->conn); } void PostgresqlEngine::extract_last_insert_rowid(Statement& statement) { this->last_inserted_rowid = statement.get_column_int64(0); } std::string PostgresqlEngine::get_returning_id_sql_string(const std::string& col_name) { return " RETURNING " + col_name; } std::string PostgresqlEngine::id_column_type() const { return "SERIAL"; } std::string PostgresqlEngine::datetime_column_type() const { return "TIMESTAMP"; } void PostgresqlEngine::convert_date_format(DatabaseEngine& db) { const auto table_name = Database::muc_log_lines.get_name(); const std::string column_name = Database::Date::name; const std::string query = "ALTER TABLE " + table_name + " ALTER COLMUN " + column_name + " SET DATA TYPE timestamp USING to_timestamp(" + column_name + ")"; auto result = db.raw_exec(query); if (!std::get<bool>(result)) log_error("Failed to execute query: ", std::get<std::string>(result)); } std::string PostgresqlEngine::escape_param_number(int nb) const { return "to_timestamp(" + DatabaseEngine::escape_param_number(nb) + ")"; } void PostgresqlEngine::init_session() { const auto res = this->raw_exec("SET SESSION TIME ZONE 'UTC'"); if (!std::get<bool>(res)) log_debug("Failed to set UTC timezone: ", std::get<std::string>(res)); } long double PostgresqlEngine::epoch_to_floating_value(long double seconds) const { return seconds; } #endif