From 81d4c3ad749491ca0dabf4a43cfb9ecf719678a5 Mon Sep 17 00:00:00 2001 From: Rahim Kanji Date: Sun, 12 Oct 2025 02:27:55 +0500 Subject: [PATCH 1/4] Added get_pg_backend_state --- include/PgSQL_Connection.h | 1 + lib/PgSQL_Connection.cpp | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/include/PgSQL_Connection.h b/include/PgSQL_Connection.h index 49e17baef..60a393ebd 100644 --- a/include/PgSQL_Connection.h +++ b/include/PgSQL_Connection.h @@ -509,6 +509,7 @@ public: char get_transaction_status_char(); inline int get_backend_pid() { return (pgsql_conn) ? get_pg_backend_pid() : -1; } bool is_pipeline_active() { return (PQpipelineStatus(pgsql_conn) != PQ_PIPELINE_OFF); } + const char* get_pg_backend_state() const; static int char_to_encoding(const char* name) { return pg_char_to_encoding(name); diff --git a/lib/PgSQL_Connection.cpp b/lib/PgSQL_Connection.cpp index 494d300cc..044c3c0bc 100644 --- a/lib/PgSQL_Connection.cpp +++ b/lib/PgSQL_Connection.cpp @@ -2117,6 +2117,25 @@ const char* PgSQL_Connection::get_pg_transaction_status_str() { return "INVALID"; } +const char* PgSQL_Connection::get_pg_backend_state() const { + if (PQstatus(pgsql_conn) != CONNECTION_OK) + return "disconnected"; + + switch (PQtransactionStatus(pgsql_conn)) { + case PQTRANS_IDLE: + return "idle"; + case PQTRANS_ACTIVE: + return "active"; + case PQTRANS_INTRANS: + return "idle in transaction"; + case PQTRANS_INERROR: + return "idle in transaction (aborted)"; + case PQTRANS_UNKNOWN: + default: + return "unknown"; + } +} + bool PgSQL_Connection::handle_copy_out(const PGresult* result, uint64_t* processed_bytes) { if (new_result == true) { From f3ea17938450acd9ee0c3ccf7e2a4a2729568601 Mon Sep 17 00:00:00 2001 From: Rahim Kanji Date: Sun, 12 Oct 2025 17:18:07 +0500 Subject: [PATCH 2/4] Add backend PID/state to stats_pgsql_processlist and create pg_stat_activity-style view - Added `backend_pid` and `backend_state` columns to `stats_pgsql_processlist` to display PostgreSQL backend process ID and connection state. - Created `stats_pgsql_stat_activity` view on top of `stats_pgsql_processlist` with column aliases matching PostgreSQL's `pg_stat_activity` for consistency. --- include/ProxySQL_Admin_Tables_Definitions.h | 3 +- lib/Admin_Bootstrap.cpp | 1 + lib/PgSQL_Thread.cpp | 135 ++++++++------------ lib/ProxySQL_Admin.cpp | 4 +- lib/ProxySQL_Admin_Stats.cpp | 78 ++++++----- 5 files changed, 106 insertions(+), 115 deletions(-) diff --git a/include/ProxySQL_Admin_Tables_Definitions.h b/include/ProxySQL_Admin_Tables_Definitions.h index 4e86b8cda..1c668c3bc 100644 --- a/include/ProxySQL_Admin_Tables_Definitions.h +++ b/include/ProxySQL_Admin_Tables_Definitions.h @@ -297,7 +297,8 @@ #define STATS_SQLITE_TABLE_PGSQL_CONNECTION_POOL_RESET "CREATE TABLE stats_pgsql_connection_pool_reset (hostgroup INT , srv_host VARCHAR , srv_port INT , status VARCHAR , ConnUsed INT , ConnFree INT , ConnOK INT , ConnERR INT , MaxConnUsed INT , Queries INT , Bytes_data_sent INT , Bytes_data_recv INT , Latency_us INT)" #define STATS_SQLITE_TABLE_PGSQL_FREE_CONNECTIONS "CREATE TABLE stats_pgsql_free_connections (fd INT NOT NULL , hostgroup INT NOT NULL , srv_host VARCHAR NOT NULL , srv_port INT NOT NULL , user VARCHAR NOT NULL , database VARCHAR , init_connect VARCHAR , time_zone VARCHAR , sql_mode VARCHAR , idle_ms INT , statistics VARCHAR , pgsql_info VARCHAR)" #define STATS_SQLITE_TABLE_PGSQL_USERS "CREATE TABLE stats_pgsql_users (username VARCHAR PRIMARY KEY , frontend_connections INT NOT NULL , frontend_max_connections INT NOT NULL)" -#define STATS_SQLITE_TABLE_PGSQL_PROCESSLIST "CREATE TABLE stats_pgsql_processlist (ThreadID INT NOT NULL , SessionID INTEGER PRIMARY KEY , user VARCHAR , database VARCHAR , cli_host VARCHAR , cli_port INT , hostgroup INT , l_srv_host VARCHAR , l_srv_port INT , srv_host VARCHAR , srv_port INT , command VARCHAR , time_ms INT NOT NULL , info VARCHAR , status_flags INT , extended_info VARCHAR)" +#define STATS_SQLITE_TABLE_PGSQL_PROCESSLIST "CREATE TABLE stats_pgsql_processlist (ThreadID INT NOT NULL , SessionID INTEGER PRIMARY KEY , user VARCHAR , database VARCHAR , cli_host VARCHAR , cli_port INT , hostgroup INT , l_srv_host VARCHAR , l_srv_port INT , srv_host VARCHAR , srv_port INT , backend_pid INT , backend_state VARCHAR , command VARCHAR , time_ms INT NOT NULL , info VARCHAR , status_flags INT , extended_info VARCHAR)" +#define STATS_SQLITE_TABLE_PGSQL_STAT_ACTIVIY "CREATE VIEW stats_pgsql_stat_activity AS SELECT ThreadID AS thread_id, database AS datname, SessionID AS pid, user AS usename, cli_host AS client_addr, cli_port AS client_port, hostgroup, l_srv_host, l_srv_port, srv_host, srv_port, backend_pid, backend_state AS state, command, time_ms AS duration_ms, info as query, status_flags, extended_info FROM stats_pgsql_processlist" #define STATS_SQLITE_TABLE_PGSQL_ERRORS "CREATE TABLE stats_pgsql_errors (hostgroup INT NOT NULL , hostname VARCHAR NOT NULL , port INT NOT NULL , username VARCHAR NOT NULL , client_address VARCHAR NOT NULL , database VARCHAR NOT NULL , sqlstate VARCHAR NOT NULL , count_star INTEGER NOT NULL , first_seen INTEGER NOT NULL , last_seen INTEGER NOT NULL , last_error VARCHAR NOT NULL DEFAULT '' , PRIMARY KEY (hostgroup, hostname, port, username, database, sqlstate) )" #define STATS_SQLITE_TABLE_PGSQL_ERRORS_RESET "CREATE TABLE stats_pgsql_errors_reset (hostgroup INT NOT NULL , hostname VARCHAR NOT NULL , port INT NOT NULL , username VARCHAR NOT NULL , client_address VARCHAR NOT NULL , database VARCHAR NOT NULL , sqlstate VARCHAR NOT NULL , count_star INTEGER NOT NULL , first_seen INTEGER NOT NULL , last_seen INTEGER NOT NULL , last_error VARCHAR NOT NULL DEFAULT '' , PRIMARY KEY (hostgroup, hostname, port, username, database, sqlstate) )" #define STATS_SQLITE_TABLE_PGSQL_CLIENT_HOST_CACHE "CREATE TABLE stats_pgsql_client_host_cache (client_address VARCHAR NOT NULL , error_count INT NOT NULL , last_updated BIGINT NOT NULL)" diff --git a/lib/Admin_Bootstrap.cpp b/lib/Admin_Bootstrap.cpp index 798e3f6ba..a1bf7f5b6 100644 --- a/lib/Admin_Bootstrap.cpp +++ b/lib/Admin_Bootstrap.cpp @@ -688,6 +688,7 @@ bool ProxySQL_Admin::init(const bootstrap_info_t& bootstrap_info) { insert_into_tables_defs(tables_defs_stats,"stats_pgsql_free_connections", STATS_SQLITE_TABLE_PGSQL_FREE_CONNECTIONS); insert_into_tables_defs(tables_defs_stats,"stats_pgsql_users", STATS_SQLITE_TABLE_PGSQL_USERS); insert_into_tables_defs(tables_defs_stats,"stats_pgsql_processlist", STATS_SQLITE_TABLE_PGSQL_PROCESSLIST); + insert_into_tables_defs(tables_defs_stats,"stats_pgsql_stat_activity", STATS_SQLITE_TABLE_PGSQL_STAT_ACTIVIY); insert_into_tables_defs(tables_defs_stats,"stats_pgsql_errors", STATS_SQLITE_TABLE_PGSQL_ERRORS); insert_into_tables_defs(tables_defs_stats,"stats_pgsql_errors_reset", STATS_SQLITE_TABLE_PGSQL_ERRORS_RESET); insert_into_tables_defs(tables_defs_stats,"stats_pgsql_client_host_cache", STATS_SQLITE_TABLE_PGSQL_CLIENT_HOST_CACHE); diff --git a/lib/PgSQL_Thread.cpp b/lib/PgSQL_Thread.cpp index 502d5e0ce..bcb257724 100644 --- a/lib/PgSQL_Thread.cpp +++ b/lib/PgSQL_Thread.cpp @@ -4536,7 +4536,7 @@ void PgSQL_Threads_Handler::Get_Memory_Stats() { } SQLite3_result* PgSQL_Threads_Handler::SQL3_Processlist() { - const int colnum = 16; + const int colnum = 18; char port[NI_MAXSERV]; proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 4, "Dumping PgSQL Processlist\n"); SQLite3_result* result = new SQLite3_result(colnum); @@ -4551,6 +4551,8 @@ SQLite3_result* PgSQL_Threads_Handler::SQL3_Processlist() { result->add_column_definition(SQLITE_TEXT, "l_srv_port"); result->add_column_definition(SQLITE_TEXT, "srv_host"); result->add_column_definition(SQLITE_TEXT, "srv_port"); + result->add_column_definition(SQLITE_TEXT, "backend_pid"); + result->add_column_definition(SQLITE_TEXT, "backend_state"); result->add_column_definition(SQLITE_TEXT, "command"); result->add_column_definition(SQLITE_TEXT, "time_ms"); result->add_column_definition(SQLITE_TEXT, "info"); @@ -4571,8 +4573,7 @@ SQLite3_result* PgSQL_Threads_Handler::SQL3_Processlist() { if (i < num_threads && pgsql_threads) { thr = (PgSQL_Thread*)pgsql_threads[i].worker; #ifdef IDLE_THREADS - } - else { + } else { if (GloVars.global.idle_threads && pgsql_thread___session_idle_show_processlist && pgsql_threads_idles) { thr = (PgSQL_Thread*)pgsql_threads_idles[i - num_threads].worker; } @@ -4596,8 +4597,7 @@ SQLite3_result* PgSQL_Threads_Handler::SQL3_Processlist() { if (ui) { if (ui->username) { pta[2] = strdup(ui->username); - } - else { + } else { pta[2] = strdup("unauthenticated user"); } if (ui->dbname) { @@ -4628,8 +4628,7 @@ SQLite3_result* PgSQL_Threads_Handler::SQL3_Processlist() { pta[5] = NULL; break; } - } - else { + } else { pta[4] = strdup("mirror_internal"); pta[5] = NULL; } @@ -4637,8 +4636,6 @@ SQLite3_result* PgSQL_Threads_Handler::SQL3_Processlist() { pta[6] = strdup(buf); if (sess->mybe && sess->mybe->server_myds && sess->mybe->server_myds->myconn) { PgSQL_Connection* mc = sess->mybe->server_myds->myconn; - - struct sockaddr addr; socklen_t addr_len = sizeof(struct sockaddr); memset(&addr, 0, addr_len); @@ -4667,8 +4664,7 @@ SQLite3_result* PgSQL_Threads_Handler::SQL3_Processlist() { pta[8] = NULL; break; } - } - else { + } else { pta[7] = NULL; pta[8] = NULL; } @@ -4679,126 +4675,107 @@ SQLite3_result* PgSQL_Threads_Handler::SQL3_Processlist() { pta[10] = strdup(buf); if (sess->CurrentQuery.extended_query_info.stmt_info == NULL) { // text protocol if (mc->query.length) { - pta[13] = (char*)malloc(mc->query.length + 1); - strncpy(pta[13], mc->query.ptr, mc->query.length); - pta[13][mc->query.length] = '\0'; + pta[15] = (char*)malloc(mc->query.length + 1); + strncpy(pta[15], mc->query.ptr, mc->query.length); + pta[15][mc->query.length] = '\0'; + } else { + pta[15] = NULL; } - else { - pta[13] = NULL; - } - } - else { // prepared statement + } else { // prepared statement const PgSQL_STMT_Global_info* si = sess->CurrentQuery.extended_query_info.stmt_info; if (si->query_length) { - pta[13] = (char*)malloc(si->query_length + 1); - strncpy(pta[13], si->query, si->query_length); - pta[13][si->query_length] = '\0'; - } - else { - pta[13] = NULL; + pta[15] = (char*)malloc(si->query_length + 1); + strncpy(pta[15], si->query, si->query_length); + pta[15][si->query_length] = '\0'; + } else { + pta[15] = NULL; } } sprintf(buf, "%d", mc->status_flags); - pta[14] = strdup(buf); + pta[16] = strdup(buf); + sprintf(buf, "%u", mc->get_pg_backend_pid()); + pta[11] = strdup(buf); + sprintf(buf, "%s", mc->get_pg_backend_state()); + pta[12] = strdup(buf); } else { pta[7] = NULL; pta[8] = NULL; pta[9] = NULL; pta[10] = NULL; - pta[13] = NULL; - pta[14] = NULL; + pta[11] = NULL; + pta[12] = NULL; + pta[15] = NULL; + pta[16] = NULL; } switch (sess->status) { case CONNECTING_SERVER: - pta[11] = strdup("Connect"); + pta[13] = strdup("Connect"); break; case PROCESSING_QUERY: if (sess->pause_until > sess->thread->curtime) { - pta[11] = strdup("Delay"); - } - else { - pta[11] = strdup("Query"); + pta[13] = strdup("Delay"); + } else { + pta[13] = strdup("Query"); } break; case WAITING_CLIENT_DATA: - pta[11] = strdup("Sleep"); + pta[13] = strdup("Sleep"); break; case CHANGING_USER_SERVER: - pta[11] = strdup("Changing user server"); + pta[13] = strdup("Changing user server"); break; case CHANGING_USER_CLIENT: - pta[11] = strdup("Change user client"); + pta[13] = strdup("Change user client"); break; case RESETTING_CONNECTION: - pta[11] = strdup("Resetting connection"); + pta[13] = strdup("Resetting connection"); break; case RESETTING_CONNECTION_V2: - pta[11] = strdup("Resetting connection V2"); + pta[13] = strdup("Resetting connection V2"); break; - //case CHANGING_SCHEMA: - // pta[11] = strdup("InitDB"); - // break; case PROCESSING_STMT_EXECUTE: - pta[11] = strdup("Execute"); + pta[13] = strdup("Execute"); break; case PROCESSING_STMT_DESCRIBE: - pta[11] = strdup("Describe"); + pta[13] = strdup("Describe"); break; case PROCESSING_STMT_PREPARE: - pta[11] = strdup("Prepare"); + pta[13] = strdup("Prepare"); break; case CONNECTING_CLIENT: - pta[11] = strdup("Connecting client"); + pta[13] = strdup("Connecting client"); break; case PINGING_SERVER: - pta[11] = strdup("Pinging server"); + pta[13] = strdup("Pinging server"); break; case WAITING_SERVER_DATA: - pta[11] = strdup("Waiting server data"); + pta[13] = strdup("Waiting server data"); break; - //case CHANGING_CHARSET: - // pta[11] = strdup("Changing charset"); - // break; - //case CHANGING_AUTOCOMMIT: - // pta[11] = strdup("Changing autocommit"); - // break; case SETTING_INIT_CONNECT: - pta[11] = strdup("Setting init connect"); + pta[13] = strdup("Setting init connect"); break; - /* - case SETTING_SQL_LOG_BIN: - pta[11]=strdup("Set log bin"); - break; - case SETTING_SQL_MODE: - pta[11]=strdup("Set SQL mode"); - break; - case SETTING_TIME_ZONE: - pta[11]=strdup("Set TZ"); - break; - */ case SETTING_VARIABLE: { int idx = sess->changing_variable_idx; if (idx < PGSQL_NAME_LAST_HIGH_WM) { char buf[128]; sprintf(buf, "Setting variable %s", pgsql_tracked_variables[idx].set_variable_name); - pta[11] = strdup(buf); - } - else { - pta[11] = strdup("Setting variable"); + pta[13] = strdup(buf); + } else { + pta[13] = strdup("Setting variable"); } } break; case FAST_FORWARD: - pta[11] = strdup("Fast forward"); + pta[13] = strdup("Fast forward"); break; case session_status___NONE: - pta[11] = strdup("None"); + pta[13] = strdup("None"); break; default: sprintf(buf, "%d", sess->status); - pta[11] = strdup(buf); + pta[13] = strdup(buf); break; } if (sess->mirror == false) { @@ -4810,24 +4787,22 @@ SQLite3_result* PgSQL_Threads_Handler::SQL3_Processlist() { last_time = sess->thread->curtime; } sprintf(buf, "%llu", (sess->thread->curtime - last_time) / 1000); - } - else { + } else { // for mirror session we only consider the start time sprintf(buf, "%llu", (sess->thread->curtime - sess->start_time) / 1000); } - pta[12] = strdup(buf); + pta[14] = strdup(buf); - pta[15] = NULL; + pta[17] = NULL; if (pgsql_thread___show_processlist_extended) { json j; sess->generate_proxysql_internal_session_json(j); if (pgsql_thread___show_processlist_extended == 2) { std::string s = j.dump(4, ' ', false, json::error_handler_t::replace); - pta[15] = strdup(s.c_str()); - } - else { + pta[17] = strdup(s.c_str()); + } else { std::string s = j.dump(-1, ' ', false, json::error_handler_t::replace); - pta[15] = strdup(s.c_str()); + pta[17] = strdup(s.c_str()); } } result->add_row(pta); diff --git a/lib/ProxySQL_Admin.cpp b/lib/ProxySQL_Admin.cpp index bb4564bb1..eaae6e57c 100644 --- a/lib/ProxySQL_Admin.cpp +++ b/lib/ProxySQL_Admin.cpp @@ -1227,7 +1227,8 @@ bool ProxySQL_Admin::GenericRefreshStatistics(const char *query_no_space, unsign //bool stats_proxysql_servers_status = false; // temporary disabled because not implemented if (strcasestr(query_no_space, "pgsql processlist") || - strcasestr(query_no_space, "stats_pgsql_processlist")) + strcasestr(query_no_space, "stats_pgsql_processlist") || + strcasestr(query_no_space, "stats_pgsql_stat_activity")) // This will match the following usecases: // SHOW PGSQL PROCESSLIST // SHOW FULL PGSQL PROCESSLIST @@ -1914,6 +1915,7 @@ void ProxySQL_Admin::vacuum_stats(bool is_admin) { "stats_pgsql_prepared_statements_info", "stats_mysql_processlist", "stats_pgsql_processlist", + "stats_pgsql_stat_activity", "stats_mysql_query_digest", "stats_mysql_query_digest_reset", "stats_pgsql_query_digest", diff --git a/lib/ProxySQL_Admin_Stats.cpp b/lib/ProxySQL_Admin_Stats.cpp index 1e9c60f12..4fc7f3f56 100644 --- a/lib/ProxySQL_Admin_Stats.cpp +++ b/lib/ProxySQL_Admin_Stats.cpp @@ -946,8 +946,8 @@ void ProxySQL_Admin::stats___pgsql_processlist() { char* query32 = NULL; std::string query32s = ""; - query1 = (char*)"INSERT OR IGNORE INTO stats_pgsql_processlist VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16)"; - query32s = "INSERT OR IGNORE INTO stats_pgsql_processlist VALUES " + generate_multi_rows_query(32, 16); + query1 = (char*)"INSERT OR IGNORE INTO stats_pgsql_processlist VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18)"; + query32s = "INSERT OR IGNORE INTO stats_pgsql_processlist VALUES " + generate_multi_rows_query(32, 18); query32 = (char*)query32s.c_str(); //rc=(*proxy_sqlite3_prepare_v2)(mydb3, query1, -1, &statement1, 0); @@ -967,46 +967,52 @@ void ProxySQL_Admin::stats___pgsql_processlist() { SQLite3_row* r1 = *it; int idx = row_idx % 32; if (row_idx < max_bulk_row_idx) { // bulk - rc = (*proxy_sqlite3_bind_int64)(statement32, (idx * 16) + 1, atoll(r1->fields[0])); ASSERT_SQLITE_OK(rc, statsdb); // ThreadID - rc = (*proxy_sqlite3_bind_int64)(statement32, (idx * 16) + 2, atoll(r1->fields[1])); ASSERT_SQLITE_OK(rc, statsdb); // SessionID - rc = (*proxy_sqlite3_bind_text)(statement32, (idx * 16) + 3, r1->fields[2], -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, statsdb); // user - rc = (*proxy_sqlite3_bind_text)(statement32, (idx * 16) + 4, r1->fields[3], -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, statsdb); // database - rc = (*proxy_sqlite3_bind_text)(statement32, (idx * 16) + 5, r1->fields[4], -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, statsdb); // cli_host + rc = (*proxy_sqlite3_bind_int64)(statement32, (idx * 18) + 1, atoll(r1->fields[0])); ASSERT_SQLITE_OK(rc, statsdb); // ThreadID + rc = (*proxy_sqlite3_bind_int64)(statement32, (idx * 18) + 2, atoll(r1->fields[1])); ASSERT_SQLITE_OK(rc, statsdb); // SessionID + rc = (*proxy_sqlite3_bind_text)(statement32, (idx * 18) + 3, r1->fields[2], -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, statsdb); // user + rc = (*proxy_sqlite3_bind_text)(statement32, (idx * 18) + 4, r1->fields[3], -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, statsdb); // database + rc = (*proxy_sqlite3_bind_text)(statement32, (idx * 18) + 5, r1->fields[4], -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, statsdb); // cli_host if (r1->fields[5]) { - rc = (*proxy_sqlite3_bind_int64)(statement32, (idx * 16) + 6, atoll(r1->fields[5])); ASSERT_SQLITE_OK(rc, statsdb); // cli_port + rc = (*proxy_sqlite3_bind_int64)(statement32, (idx * 18) + 6, atoll(r1->fields[5])); ASSERT_SQLITE_OK(rc, statsdb); // cli_port } else { - rc = (*proxy_sqlite3_bind_null)(statement32, (idx * 16) + 6); ASSERT_SQLITE_OK(rc, statsdb); + rc = (*proxy_sqlite3_bind_null)(statement32, (idx * 18) + 6); ASSERT_SQLITE_OK(rc, statsdb); } if (r1->fields[6]) { - rc = (*proxy_sqlite3_bind_int64)(statement32, (idx * 16) + 7, atoll(r1->fields[6])); ASSERT_SQLITE_OK(rc, statsdb); // hostgroup + rc = (*proxy_sqlite3_bind_int64)(statement32, (idx * 18) + 7, atoll(r1->fields[6])); ASSERT_SQLITE_OK(rc, statsdb); // hostgroup } else { - rc = (*proxy_sqlite3_bind_null)(statement32, (idx * 16) + 8); ASSERT_SQLITE_OK(rc, statsdb); + rc = (*proxy_sqlite3_bind_null)(statement32, (idx * 18) + 7); ASSERT_SQLITE_OK(rc, statsdb); } - rc = (*proxy_sqlite3_bind_text)(statement32, (idx * 16) + 8, r1->fields[7], -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, statsdb); // l_srv_host + rc = (*proxy_sqlite3_bind_text)(statement32, (idx * 18) + 8, r1->fields[7], -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, statsdb); // l_srv_host if (r1->fields[8]) { - rc = (*proxy_sqlite3_bind_int64)(statement32, (idx * 16) + 9, atoll(r1->fields[8])); ASSERT_SQLITE_OK(rc, statsdb); // l_srv_port + rc = (*proxy_sqlite3_bind_int64)(statement32, (idx * 18) + 9, atoll(r1->fields[8])); ASSERT_SQLITE_OK(rc, statsdb); // l_srv_port } else { - rc = (*proxy_sqlite3_bind_null)(statement32, (idx * 16) + 9); ASSERT_SQLITE_OK(rc, statsdb); + rc = (*proxy_sqlite3_bind_null)(statement32, (idx * 18) + 9); ASSERT_SQLITE_OK(rc, statsdb); } - rc = (*proxy_sqlite3_bind_text)(statement32, (idx * 16) + 10, r1->fields[9], -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, statsdb); // srv_host + rc = (*proxy_sqlite3_bind_text)(statement32, (idx * 18) + 10, r1->fields[9], -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, statsdb); // srv_host if (r1->fields[10]) { - rc = (*proxy_sqlite3_bind_int64)(statement32, (idx * 16) + 11, atoll(r1->fields[10])); ASSERT_SQLITE_OK(rc, statsdb); // srv_port + rc = (*proxy_sqlite3_bind_int64)(statement32, (idx * 18) + 11, atoll(r1->fields[10])); ASSERT_SQLITE_OK(rc, statsdb); // srv_port } else { - rc = (*proxy_sqlite3_bind_null)(statement32, (idx * 16) + 11); ASSERT_SQLITE_OK(rc, statsdb); + rc = (*proxy_sqlite3_bind_null)(statement32, (idx * 18) + 11); ASSERT_SQLITE_OK(rc, statsdb); } - rc = (*proxy_sqlite3_bind_text)(statement32, (idx * 16) + 12, r1->fields[11], -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, statsdb); // command - if (r1->fields[12]) { - rc = (*proxy_sqlite3_bind_int64)(statement32, (idx * 16) + 13, atoll(r1->fields[12])); ASSERT_SQLITE_OK(rc, statsdb); // time_ms + if (r1->fields[11]) { + rc = (*proxy_sqlite3_bind_int64)(statement32, (idx * 18) + 12, atoll(r1->fields[11])); ASSERT_SQLITE_OK(rc, statsdb); // backend_pid } else { - rc = (*proxy_sqlite3_bind_null)(statement32, (idx * 16) + 13); ASSERT_SQLITE_OK(rc, statsdb); + rc = (*proxy_sqlite3_bind_null)(statement32, (idx * 18) + 12); ASSERT_SQLITE_OK(rc, statsdb); } - rc = (*proxy_sqlite3_bind_text)(statement32, (idx * 16) + 14, r1->fields[13], -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, statsdb); // info + rc = (*proxy_sqlite3_bind_text)(statement32, (idx * 18) + 13, r1->fields[12], -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, statsdb); // backend_state + rc = (*proxy_sqlite3_bind_text)(statement32, (idx * 18) + 14, r1->fields[13], -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, statsdb); // command if (r1->fields[14]) { - rc = (*proxy_sqlite3_bind_int64)(statement32, (idx * 16) + 15, atoll(r1->fields[14])); ASSERT_SQLITE_OK(rc, statsdb); // status_flags + rc = (*proxy_sqlite3_bind_int64)(statement32, (idx * 18) + 15, atoll(r1->fields[14])); ASSERT_SQLITE_OK(rc, statsdb); // time_ms + } else { + rc = (*proxy_sqlite3_bind_null)(statement32, (idx * 18) + 15); ASSERT_SQLITE_OK(rc, statsdb); + } + rc = (*proxy_sqlite3_bind_text)(statement32, (idx * 18) + 16, r1->fields[15], -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, statsdb); // info + if (r1->fields[16]) { + rc = (*proxy_sqlite3_bind_int64)(statement32, (idx * 18) + 17, atoll(r1->fields[16])); ASSERT_SQLITE_OK(rc, statsdb); // status_flags } else { - rc = (*proxy_sqlite3_bind_null)(statement32, (idx * 16) + 15); ASSERT_SQLITE_OK(rc, statsdb); + rc = (*proxy_sqlite3_bind_null)(statement32, (idx * 18) + 17); ASSERT_SQLITE_OK(rc, statsdb); } - rc = (*proxy_sqlite3_bind_text)(statement32, (idx * 16) + 16, r1->fields[15], -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, statsdb); // extended_info + rc = (*proxy_sqlite3_bind_text)(statement32, (idx * 18) + 18, r1->fields[17], -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, statsdb); // extended_info if (idx == 31) { SAFE_SQLITE3_STEP2(statement32); rc = (*proxy_sqlite3_clear_bindings)(statement32); ASSERT_SQLITE_OK(rc, statsdb); @@ -1026,7 +1032,7 @@ void ProxySQL_Admin::stats___pgsql_processlist() { if (r1->fields[6]) { rc = (*proxy_sqlite3_bind_int64)(statement1, 7, atoll(r1->fields[6])); ASSERT_SQLITE_OK(rc, statsdb); // hostgroup } else { - rc = (*proxy_sqlite3_bind_null)(statement1, 8); ASSERT_SQLITE_OK(rc, statsdb); + rc = (*proxy_sqlite3_bind_null)(statement1, 7); ASSERT_SQLITE_OK(rc, statsdb); } rc = (*proxy_sqlite3_bind_text)(statement1, 8, r1->fields[7], -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, statsdb); // l_srv_host if (r1->fields[8]) { @@ -1040,19 +1046,25 @@ void ProxySQL_Admin::stats___pgsql_processlist() { } else { rc = (*proxy_sqlite3_bind_null)(statement1, 11); ASSERT_SQLITE_OK(rc, statsdb); } - rc = (*proxy_sqlite3_bind_text)(statement1, 12, r1->fields[11], -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, statsdb); // command - if (r1->fields[12]) { - rc = (*proxy_sqlite3_bind_int64)(statement1, 13, atoll(r1->fields[12])); ASSERT_SQLITE_OK(rc, statsdb); // time_ms + if (r1->fields[11]) { + rc = (*proxy_sqlite3_bind_int64)(statement1, 12, atoll(r1->fields[11])); ASSERT_SQLITE_OK(rc, statsdb); // backend_pid } else { - rc = (*proxy_sqlite3_bind_null)(statement1, 13); ASSERT_SQLITE_OK(rc, statsdb); + rc = (*proxy_sqlite3_bind_null)(statement1, 12); ASSERT_SQLITE_OK(rc, statsdb); } - rc = (*proxy_sqlite3_bind_text)(statement1, 14, r1->fields[13], -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, statsdb); // info + rc = (*proxy_sqlite3_bind_text)(statement1, 13, r1->fields[12], -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, statsdb); // backend_state + rc = (*proxy_sqlite3_bind_text)(statement1, 14, r1->fields[13], -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, statsdb); // command if (r1->fields[14]) { - rc = (*proxy_sqlite3_bind_int64)(statement1, 15, atoll(r1->fields[14])); ASSERT_SQLITE_OK(rc, statsdb); // status_flags + rc = (*proxy_sqlite3_bind_int64)(statement1, 15, atoll(r1->fields[14])); ASSERT_SQLITE_OK(rc, statsdb); // time_ms } else { rc = (*proxy_sqlite3_bind_null)(statement1, 15); ASSERT_SQLITE_OK(rc, statsdb); } - rc = (*proxy_sqlite3_bind_text)(statement1, 16, r1->fields[15], -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, statsdb); // extended_info + rc = (*proxy_sqlite3_bind_text)(statement1, 16, r1->fields[15], -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, statsdb); // info + if (r1->fields[16]) { + rc = (*proxy_sqlite3_bind_int64)(statement1, 17, atoll(r1->fields[16])); ASSERT_SQLITE_OK(rc, statsdb); // status_flags + } else { + rc = (*proxy_sqlite3_bind_null)(statement1, 17); ASSERT_SQLITE_OK(rc, statsdb); + } + rc = (*proxy_sqlite3_bind_text)(statement1, 18, r1->fields[17], -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, statsdb); // extended_info SAFE_SQLITE3_STEP2(statement1); rc = (*proxy_sqlite3_clear_bindings)(statement1); ASSERT_SQLITE_OK(rc, statsdb); rc = (*proxy_sqlite3_reset)(statement1); ASSERT_SQLITE_OK(rc, statsdb); From fc4d7f76a03570174673c088e5aa42dcaab6238f Mon Sep 17 00:00:00 2001 From: Rahim Kanji Date: Sun, 12 Oct 2025 18:51:50 +0500 Subject: [PATCH 3/4] Added 'SHOW FULL PGSQL ACTIVITY' and 'SHOW PGSQL ACTIVITY' handling --- lib/Admin_Handler.cpp | 16 +++++++++++++++- lib/ProxySQL_Admin.cpp | 4 ++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/lib/Admin_Handler.cpp b/lib/Admin_Handler.cpp index b653f0bef..3e770bebf 100644 --- a/lib/Admin_Handler.cpp +++ b/lib/Admin_Handler.cpp @@ -3700,9 +3700,23 @@ void admin_session_handler(S* sess, void *_pa, PtrSize_t *pkt) { goto __run_query; } + if (query_no_space_length == strlen("SHOW FULL PGSQL ACTIVITY") && !strncasecmp("SHOW FULL PGSQL ACTIVITY", query_no_space, query_no_space_length)) { + l_free(query_length, query); + query = l_strdup("SELECT * FROM stats_pgsql_stat_activity"); + query_length = strlen(query) + 1; + goto __run_query; + } + if (query_no_space_length == strlen("SHOW PGSQL PROCESSLIST") && !strncasecmp("SHOW PGSQL PROCESSLIST", query_no_space, query_no_space_length)) { l_free(query_length, query); - query = l_strdup("SELECT SessionID, user, database, hostgroup, command, time_ms, SUBSTR(info,0,100) info FROM stats_pgsql_processlist"); + query = l_strdup("SELECT SessionID, user, database, hostgroup, backend_pid, backend_state, command, time_ms, SUBSTR(info,0,100) info FROM stats_pgsql_processlist"); + query_length = strlen(query) + 1; + goto __run_query; + } + + if (query_no_space_length == strlen("SHOW PGSQL ACTIVITY") && !strncasecmp("SHOW PGSQL ACTIVITY", query_no_space, query_no_space_length)) { + l_free(query_length, query); + query = l_strdup("SELECT datname, pid, usename, hostgroup, backend_pid, state, command, duration_ms, SUBSTR(query,0,100) query FROM stats_pgsql_stat_activity"); query_length = strlen(query) + 1; goto __run_query; } diff --git a/lib/ProxySQL_Admin.cpp b/lib/ProxySQL_Admin.cpp index eaae6e57c..42872bc1d 100644 --- a/lib/ProxySQL_Admin.cpp +++ b/lib/ProxySQL_Admin.cpp @@ -1227,12 +1227,16 @@ bool ProxySQL_Admin::GenericRefreshStatistics(const char *query_no_space, unsign //bool stats_proxysql_servers_status = false; // temporary disabled because not implemented if (strcasestr(query_no_space, "pgsql processlist") || + strcasestr(query_no_space, "pgsql activity") || strcasestr(query_no_space, "stats_pgsql_processlist") || strcasestr(query_no_space, "stats_pgsql_stat_activity")) // This will match the following usecases: // SHOW PGSQL PROCESSLIST // SHOW FULL PGSQL PROCESSLIST + // SHOW PGSQL ACTIVITY + // SHOW FULL PGSQL ACTIVITY // SELECT * FROM stats_pgsql_processlist + // SELECT * FROM stats_pgsql_stat_activity { stats_pgsql_processlist = true; refresh = true; } else if (strcasestr(query_no_space,"processlist")) From 0292b780e986f7581a0445c35ec706d8b30b4cf4 Mon Sep 17 00:00:00 2001 From: Rahim Kanji Date: Sun, 12 Oct 2025 19:11:02 +0500 Subject: [PATCH 4/4] Fixed typo --- include/ProxySQL_Admin_Tables_Definitions.h | 2 +- lib/Admin_Bootstrap.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/ProxySQL_Admin_Tables_Definitions.h b/include/ProxySQL_Admin_Tables_Definitions.h index 1c668c3bc..6305c6026 100644 --- a/include/ProxySQL_Admin_Tables_Definitions.h +++ b/include/ProxySQL_Admin_Tables_Definitions.h @@ -298,7 +298,7 @@ #define STATS_SQLITE_TABLE_PGSQL_FREE_CONNECTIONS "CREATE TABLE stats_pgsql_free_connections (fd INT NOT NULL , hostgroup INT NOT NULL , srv_host VARCHAR NOT NULL , srv_port INT NOT NULL , user VARCHAR NOT NULL , database VARCHAR , init_connect VARCHAR , time_zone VARCHAR , sql_mode VARCHAR , idle_ms INT , statistics VARCHAR , pgsql_info VARCHAR)" #define STATS_SQLITE_TABLE_PGSQL_USERS "CREATE TABLE stats_pgsql_users (username VARCHAR PRIMARY KEY , frontend_connections INT NOT NULL , frontend_max_connections INT NOT NULL)" #define STATS_SQLITE_TABLE_PGSQL_PROCESSLIST "CREATE TABLE stats_pgsql_processlist (ThreadID INT NOT NULL , SessionID INTEGER PRIMARY KEY , user VARCHAR , database VARCHAR , cli_host VARCHAR , cli_port INT , hostgroup INT , l_srv_host VARCHAR , l_srv_port INT , srv_host VARCHAR , srv_port INT , backend_pid INT , backend_state VARCHAR , command VARCHAR , time_ms INT NOT NULL , info VARCHAR , status_flags INT , extended_info VARCHAR)" -#define STATS_SQLITE_TABLE_PGSQL_STAT_ACTIVIY "CREATE VIEW stats_pgsql_stat_activity AS SELECT ThreadID AS thread_id, database AS datname, SessionID AS pid, user AS usename, cli_host AS client_addr, cli_port AS client_port, hostgroup, l_srv_host, l_srv_port, srv_host, srv_port, backend_pid, backend_state AS state, command, time_ms AS duration_ms, info as query, status_flags, extended_info FROM stats_pgsql_processlist" +#define STATS_SQLITE_TABLE_PGSQL_STAT_ACTIVITY "CREATE VIEW stats_pgsql_stat_activity AS SELECT ThreadID AS thread_id, database AS datname, SessionID AS pid, user AS usename, cli_host AS client_addr, cli_port AS client_port, hostgroup, l_srv_host, l_srv_port, srv_host, srv_port, backend_pid, backend_state AS state, command, time_ms AS duration_ms, info as query, status_flags, extended_info FROM stats_pgsql_processlist" #define STATS_SQLITE_TABLE_PGSQL_ERRORS "CREATE TABLE stats_pgsql_errors (hostgroup INT NOT NULL , hostname VARCHAR NOT NULL , port INT NOT NULL , username VARCHAR NOT NULL , client_address VARCHAR NOT NULL , database VARCHAR NOT NULL , sqlstate VARCHAR NOT NULL , count_star INTEGER NOT NULL , first_seen INTEGER NOT NULL , last_seen INTEGER NOT NULL , last_error VARCHAR NOT NULL DEFAULT '' , PRIMARY KEY (hostgroup, hostname, port, username, database, sqlstate) )" #define STATS_SQLITE_TABLE_PGSQL_ERRORS_RESET "CREATE TABLE stats_pgsql_errors_reset (hostgroup INT NOT NULL , hostname VARCHAR NOT NULL , port INT NOT NULL , username VARCHAR NOT NULL , client_address VARCHAR NOT NULL , database VARCHAR NOT NULL , sqlstate VARCHAR NOT NULL , count_star INTEGER NOT NULL , first_seen INTEGER NOT NULL , last_seen INTEGER NOT NULL , last_error VARCHAR NOT NULL DEFAULT '' , PRIMARY KEY (hostgroup, hostname, port, username, database, sqlstate) )" #define STATS_SQLITE_TABLE_PGSQL_CLIENT_HOST_CACHE "CREATE TABLE stats_pgsql_client_host_cache (client_address VARCHAR NOT NULL , error_count INT NOT NULL , last_updated BIGINT NOT NULL)" diff --git a/lib/Admin_Bootstrap.cpp b/lib/Admin_Bootstrap.cpp index a1bf7f5b6..320be9633 100644 --- a/lib/Admin_Bootstrap.cpp +++ b/lib/Admin_Bootstrap.cpp @@ -688,7 +688,7 @@ bool ProxySQL_Admin::init(const bootstrap_info_t& bootstrap_info) { insert_into_tables_defs(tables_defs_stats,"stats_pgsql_free_connections", STATS_SQLITE_TABLE_PGSQL_FREE_CONNECTIONS); insert_into_tables_defs(tables_defs_stats,"stats_pgsql_users", STATS_SQLITE_TABLE_PGSQL_USERS); insert_into_tables_defs(tables_defs_stats,"stats_pgsql_processlist", STATS_SQLITE_TABLE_PGSQL_PROCESSLIST); - insert_into_tables_defs(tables_defs_stats,"stats_pgsql_stat_activity", STATS_SQLITE_TABLE_PGSQL_STAT_ACTIVIY); + insert_into_tables_defs(tables_defs_stats,"stats_pgsql_stat_activity", STATS_SQLITE_TABLE_PGSQL_STAT_ACTIVITY); insert_into_tables_defs(tables_defs_stats,"stats_pgsql_errors", STATS_SQLITE_TABLE_PGSQL_ERRORS); insert_into_tables_defs(tables_defs_stats,"stats_pgsql_errors_reset", STATS_SQLITE_TABLE_PGSQL_ERRORS_RESET); insert_into_tables_defs(tables_defs_stats,"stats_pgsql_client_host_cache", STATS_SQLITE_TABLE_PGSQL_CLIENT_HOST_CACHE);