From c6f442b4d132e354a938554ab88a1cc2c5cecf0d Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Tue, 24 Mar 2026 09:11:42 +0000 Subject: [PATCH] feat(ffto): record PostgreSQL FF errors in stats_pgsql_errors --- include/PgSQLFFTO.hpp | 7 +++++++ lib/PgSQLFFTO.cpp | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/include/PgSQLFFTO.hpp b/include/PgSQLFFTO.hpp index dfb6e048b..73e3ab420 100644 --- a/include/PgSQLFFTO.hpp +++ b/include/PgSQLFFTO.hpp @@ -118,6 +118,13 @@ private: * @param rows_sent Number of rows returned. */ void report_query_stats(const std::string& query, unsigned long long duration_us, uint64_t affected_rows = 0, uint64_t rows_sent = 0); + + /** + * @brief Records an error from a PostgreSQL ErrorResponse into stats_pgsql_errors. + * @param payload Pointer to the ErrorResponse message payload. + * @param len Length of the payload. + */ + void report_error(const unsigned char* payload, size_t len); }; #endif // PGSQL_FFTO_HPP diff --git a/lib/PgSQLFFTO.cpp b/lib/PgSQLFFTO.cpp index 20c0e6ded..a0a7f6d87 100644 --- a/lib/PgSQLFFTO.cpp +++ b/lib/PgSQLFFTO.cpp @@ -10,12 +10,14 @@ #define SPOOKYV2 #endif #include "c_tokenizer.h" +#include "PgSQLErrorFields.h" #include #include #include #include extern class PgSQL_Query_Processor* GloPgQPro; +extern PgSQL_HostGroups_Manager* PgHGM; /** * @brief Parses the PostgreSQL CommandComplete ('C') message payload to extract row counts. @@ -266,6 +268,7 @@ void PgSQLFFTO::process_server_message(char type, const unsigned char* payload, unsigned long long duration = monotonic_time() - m_query_start_time; report_query_stats(m_current_query, duration, m_affected_rows, m_rows_sent); } + report_error(payload, len); clear_current_query(); m_pending_queries.clear(); m_state = IDLE; @@ -310,6 +313,46 @@ void PgSQLFFTO::report_query_stats(const std::string& query, unsigned long long if (fst_cmnt) free(fst_cmnt); } +void PgSQLFFTO::report_error(const unsigned char* payload, size_t len) { + if (!m_session || !PgHGM) return; + if (!m_session->client_myds || !m_session->client_myds->myconn || + !m_session->client_myds->myconn->userinfo) return; + + PgSQLErrorResult err = pgsql_parse_error_response(payload, len); + if (!err.parsed) return; + + auto* ui = m_session->client_myds->myconn->userinfo; + if (!ui->username || !ui->schemaname) return; + + // Build a null-terminated copy of the error message + std::string msg(err.message ? err.message : "", err.message_len); + + // Get backend server info if available + const char* hostname = ""; + int port = 0; + int hostgroup = m_session->current_hostgroup; + if (m_session->mybe && m_session->mybe->server_myds && + m_session->mybe->server_myds->myconn && + m_session->mybe->server_myds->myconn->parent) { + auto* parent = m_session->mybe->server_myds->myconn->parent; + hostname = parent->address ? parent->address : ""; + port = parent->port; + hostgroup = parent->myhgc->hid; + } + + const char* client_addr = "unknown"; + if (m_session->client_myds->addr.addr) { + client_addr = m_session->client_myds->addr.addr; + } + + // ui->schemaname and ui->dbname are the same field (union in PgSQL_Connection_userinfo) + PgHGM->add_pgsql_errors( + hostgroup, hostname, port, + ui->username, client_addr, ui->schemaname, + err.sqlstate, msg.c_str() + ); +} + std::size_t PgSQLFFTO::get_buffered_size() const { return m_client_buffer.size() + m_server_buffer.size(); }