From aa80a1f9c8ee3c235f7fe5d092816b61aa5beb66 Mon Sep 17 00:00:00 2001 From: Wazir Ahmed Date: Wed, 27 Aug 2025 13:45:49 +0530 Subject: [PATCH 1/4] Handle `SHOW STATUS LIKE 'Ssl_version'` in ProxySQL without any backend Signed-off-by: Wazir Ahmed --- lib/MySQL_Session.cpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index 4acacac0f..c7ae143ae 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -56,6 +56,8 @@ using json = nlohmann::json; #define SELECT_VARIABLE_IDENTITY_LEN 17 #define SELECT_VARIABLE_IDENTITY_LIMIT1 "SELECT @@IDENTITY LIMIT 1" #define SELECT_VARIABLE_IDENTITY_LIMIT1_LEN 25 +#define SHOW_STATUS_LIKE_SSL_VERSION "SHOW STATUS LIKE 'Ssl_version" +#define SHOW_STATUS_LIKE_SSL_VERSION_LEN 29 #define EXPMARIA @@ -1394,6 +1396,34 @@ bool MySQL_Session::handler_special_queries(PtrSize_t *pkt) { l_free(pkt->size, pkt->ptr); return true; } + + // Handle SHOW STATUS LIKE 'Ssl_version%' + if ((pkt->size >= SHOW_STATUS_LIKE_SSL_VERSION_LEN + 5) && (strncasecmp(SHOW_STATUS_LIKE_SSL_VERSION, (char*)pkt->ptr + 5, SHOW_STATUS_LIKE_SSL_VERSION_LEN) == 0)) { + SQLite3_result* resultset = new SQLite3_result(2); + resultset->add_column_definition(SQLITE_TEXT, "Variable_name"); + resultset->add_column_definition(SQLITE_TEXT, "Value"); + + const char* ssl_version = ""; + if (client_myds->encrypted && client_myds->ssl) { + ssl_version = SSL_get_version(client_myds->ssl); + } + + char* pta[2]; + pta[0] = (char*)"Ssl_version"; + pta[1] = (char*)ssl_version; + resultset->add_row(pta); + + bool deprecate_eof_active = client_myds->myconn->options.client_flag & CLIENT_DEPRECATE_EOF; + SQLite3_to_MySQL(resultset, NULL, 0, &client_myds->myprot, false, deprecate_eof_active); + delete resultset; + + if (mirror == false) { + RequestEnd(NULL); + } + l_free(pkt->size, pkt->ptr); + return true; + } + // 'LOAD DATA LOCAL INFILE' is unsupported. We report an specific error to inform clients about this fact. For more context see #833. if ( (pkt->size >= 22 + 5) && (strncasecmp((char *)"LOAD DATA LOCAL INFILE",(char *)pkt->ptr+5, 22)==0) ) { if (mysql_thread___enable_load_data_local_infile == false) { From 8ebd7e48ae58bf02fa40b4e1ae0bea801db41f52 Mon Sep 17 00:00:00 2001 From: Wazir Ahmed Date: Wed, 6 Aug 2025 13:26:05 +0530 Subject: [PATCH 2/4] TAP: Move init_mysql_conn() and run_q() to utils Signed-off-by: Wazir Ahmed --- test/tap/tap/utils.cpp | 35 +++++++++++++++++++ test/tap/tap/utils.h | 3 ++ test/tap/tests/admin_various_commands2-t.cpp | 4 --- test/tap/tests/admin_various_commands3-t.cpp | 4 --- test/tap/tests/firewall_commands1-t.cpp | 4 --- test/tap/tests/kill_connection-t.cpp | 4 --- test/tap/tests/kill_connection2-t.cpp | 4 --- test/tap/tests/kill_connection3-t.cpp | 4 --- test/tap/tests/mysql-fast_forward-t.cpp | 4 --- test/tap/tests/mysql-mirror1-t.cpp | 5 --- .../mysql-protocol_compression_level-t.cpp | 29 +++------------ test/tap/tests/test_cluster1-t.cpp | 5 --- 12 files changed, 43 insertions(+), 62 deletions(-) diff --git a/test/tap/tap/utils.cpp b/test/tap/tap/utils.cpp index 89a522ec7..7fbe4d074 100644 --- a/test/tap/tap/utils.cpp +++ b/test/tap/tap/utils.cpp @@ -2316,3 +2316,38 @@ bool get_env_bool(const char* envname, bool envdefault) { return (bool) res; }; + +MYSQL* init_mysql_conn(char* host, int port, char* user, char* pass, bool ssl, bool cmp) { + diag("Creating MySQL conn host=\"%s\" port=\"%d\" user=\"%s\" ssl=\"%d\" cmp=\"%d\"", host, port, user, ssl, cmp); + + MYSQL* mysql = mysql_init(NULL); + + if (!mysql) { + return nullptr; + } + if (cmp) { + if (mysql_options(mysql, MYSQL_OPT_COMPRESS, nullptr)) { + return nullptr; + } + } + + int cflags = 0; + + if (ssl) { + if (mysql_ssl_set(mysql, NULL, NULL, NULL, NULL, NULL)) { + return nullptr; + } + cflags |= CLIENT_SSL; + } + + if (!mysql_real_connect(mysql, host, user, pass, NULL, port, NULL, cflags)) { + return nullptr; + } + + return mysql; +} + +int run_q(MYSQL *mysql, const char *q) { + MYSQL_QUERY_T(mysql,q); + return 0; +} diff --git a/test/tap/tap/utils.h b/test/tap/tap/utils.h index f492431e2..36dc54c17 100644 --- a/test/tap/tap/utils.h +++ b/test/tap/tap/utils.h @@ -1002,4 +1002,7 @@ const char* get_env_str(const char* envname, const char* envdefault); int get_env_int(const char* envname, int envdefault); bool get_env_bool(const char* envname, bool envdefault); +MYSQL* init_mysql_conn(char* host, int port, char* user, char* pass, bool ssl=false, bool cmp=false); +int run_q(MYSQL *mysql, const char *q); + #endif // #define UTILS_H diff --git a/test/tap/tests/admin_various_commands2-t.cpp b/test/tap/tests/admin_various_commands2-t.cpp index 94081b1e6..a412abc27 100644 --- a/test/tap/tests/admin_various_commands2-t.cpp +++ b/test/tap/tests/admin_various_commands2-t.cpp @@ -103,10 +103,6 @@ void add_commands_set1(std::vector& queries, std::string m, bool wi } } -int run_q(MYSQL *mysql, const char *q) { - MYSQL_QUERY(mysql,q); - return 0; -} int main() { CommandLine cl; diff --git a/test/tap/tests/admin_various_commands3-t.cpp b/test/tap/tests/admin_various_commands3-t.cpp index db86720d8..ad1c763ea 100644 --- a/test/tap/tests/admin_various_commands3-t.cpp +++ b/test/tap/tests/admin_various_commands3-t.cpp @@ -37,10 +37,6 @@ std::vector vals = { 100, 345, 800 }; std::vector queries = {}; -int run_q(MYSQL *mysql, const char *q) { - MYSQL_QUERY(mysql,q); - return 0; -} int main() { CommandLine cl; diff --git a/test/tap/tests/firewall_commands1-t.cpp b/test/tap/tests/firewall_commands1-t.cpp index 6aa56cf61..366bcd1e0 100644 --- a/test/tap/tests/firewall_commands1-t.cpp +++ b/test/tap/tests/firewall_commands1-t.cpp @@ -51,10 +51,6 @@ std::vector queries = { "LOAD MYSQL FIREWALL TO RUNTIME", }; -int run_q(MYSQL *mysql, const char *q) { - MYSQL_QUERY(mysql,q); - return 0; -} int main() { CommandLine cl; diff --git a/test/tap/tests/kill_connection-t.cpp b/test/tap/tests/kill_connection-t.cpp index 5f964a959..b6e2c76ef 100644 --- a/test/tap/tests/kill_connection-t.cpp +++ b/test/tap/tests/kill_connection-t.cpp @@ -22,10 +22,6 @@ This test verifies a variety of things: const int NUM_CONNS = 5; -int run_q(MYSQL *mysql, const char *q) { - MYSQL_QUERY(mysql,q); - return 0; -} int main(int argc, char** argv) { CommandLine cl; diff --git a/test/tap/tests/kill_connection2-t.cpp b/test/tap/tests/kill_connection2-t.cpp index 947c9d9cf..58989cafa 100644 --- a/test/tap/tests/kill_connection2-t.cpp +++ b/test/tap/tests/kill_connection2-t.cpp @@ -18,10 +18,6 @@ This test verifies that client connections are dropped because of: const int NUM_CONNS = 5; -int run_q(MYSQL *mysql, const char *q) { - MYSQL_QUERY(mysql,q); - return 0; -} MYSQL * conns[NUM_CONNS]; unsigned long mythreadid[NUM_CONNS]; diff --git a/test/tap/tests/kill_connection3-t.cpp b/test/tap/tests/kill_connection3-t.cpp index 1c023ffb5..e4230f014 100644 --- a/test/tap/tests/kill_connection3-t.cpp +++ b/test/tap/tests/kill_connection3-t.cpp @@ -18,10 +18,6 @@ This test verifies that client connections are dropped because of: const int NUM_CONNS = 35; -int run_q(MYSQL *mysql, const char *q) { - MYSQL_QUERY(mysql,q); - return 0; -} MYSQL * conns[NUM_CONNS]; unsigned long mythreadid[NUM_CONNS]; diff --git a/test/tap/tests/mysql-fast_forward-t.cpp b/test/tap/tests/mysql-fast_forward-t.cpp index 20b1b6df4..4a37ad0e9 100644 --- a/test/tap/tests/mysql-fast_forward-t.cpp +++ b/test/tap/tests/mysql-fast_forward-t.cpp @@ -18,10 +18,6 @@ This test verifies that client connections are dropped because of: const int NUM_CONNS = 35; const int RPI = 50; -int run_q(MYSQL *mysql, const char *q) { - MYSQL_QUERY(mysql,q); - return 0; -} MYSQL * conns[NUM_CONNS]; unsigned long mythreadid[NUM_CONNS]; diff --git a/test/tap/tests/mysql-mirror1-t.cpp b/test/tap/tests/mysql-mirror1-t.cpp index 54e63488f..12fe407c1 100644 --- a/test/tap/tests/mysql-mirror1-t.cpp +++ b/test/tap/tests/mysql-mirror1-t.cpp @@ -24,11 +24,6 @@ This test also triggers: const int NUM_CONNS = 15; const int RPI = 20; // rows per insert -int run_q(MYSQL *mysql, const char *q) { - MYSQL_QUERY(mysql,q); - return 0; -} - MYSQL * conns[NUM_CONNS]; unsigned long mythreadid[NUM_CONNS]; diff --git a/test/tap/tests/mysql-protocol_compression_level-t.cpp b/test/tap/tests/mysql-protocol_compression_level-t.cpp index 354d11744..307db5382 100644 --- a/test/tap/tests/mysql-protocol_compression_level-t.cpp +++ b/test/tap/tests/mysql-protocol_compression_level-t.cpp @@ -88,25 +88,6 @@ uint64_t measure_avg_query_time( return avg; } -MYSQL* init_mysql_conn(char* host, char* user, char* pass, int port, bool cmp=false) { - diag("Creating MySQL conn host=\"%s\" port=\"%d\" cmp=\"%d\"", user, port, cmp); - - MYSQL* mysql = mysql_init(NULL); - - if (!mysql) { - return nullptr; - } - if (cmp) { - if (mysql_options(mysql, MYSQL_OPT_COMPRESS, nullptr)) { - return nullptr; - } - } - if (!mysql_real_connect(mysql, host, user, pass, NULL, port, NULL, 0)) { - return nullptr; - } - - return mysql; -} const char version_comment_query[] { "select @@version_comment limit 1" }; @@ -140,13 +121,13 @@ int main(int argc, char** argv) { return EXIT_FAILURE; } - MYSQL* admin = init_mysql_conn(cl.host, cl.admin_username, cl.admin_password, cl.admin_port); + MYSQL* admin = init_mysql_conn(cl.host, cl.admin_port, cl.admin_username, cl.admin_password); if (!admin) { fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(admin)); return EXIT_FAILURE; } - MYSQL* proxy = init_mysql_conn(cl.host, cl.username, cl.password, cl.port); + MYSQL* proxy = init_mysql_conn(cl.host, cl.port, cl.username, cl.password); if (!proxy) { fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(admin)); return EXIT_FAILURE; @@ -185,17 +166,17 @@ int main(int argc, char** argv) { " (SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4) as u4;" ); - MYSQL* proxy_cmp = init_mysql_conn(cl.host, cl.username, cl.password, cl.port, true); + MYSQL* proxy_cmp = init_mysql_conn(cl.host, cl.port, cl.username, cl.password, false, true); if (!proxy_cmp) { fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxy_cmp)); return EXIT_FAILURE; } - MYSQL* mysql = init_mysql_conn(cl.host, cl.username, cl.password, cl.mysql_port, false); + MYSQL* mysql = init_mysql_conn(cl.host, cl.mysql_port, cl.username, cl.password, false, false); if (!mysql) { fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(mysql)); return EXIT_FAILURE; } - MYSQL* mysql_cmp = init_mysql_conn(cl.host, cl.username, cl.password, cl.mysql_port, true); + MYSQL* mysql_cmp = init_mysql_conn(cl.host, cl.mysql_port, cl.username, cl.password, false, true); if (!mysql_cmp) { fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(mysql_cmp)); return EXIT_FAILURE; diff --git a/test/tap/tests/test_cluster1-t.cpp b/test/tap/tests/test_cluster1-t.cpp index 089c4bd82..2495ae9cb 100644 --- a/test/tap/tests/test_cluster1-t.cpp +++ b/test/tap/tests/test_cluster1-t.cpp @@ -26,11 +26,6 @@ * 127.0.0.1:26009 : satellite node6 */ -int run_q(MYSQL *mysql, const char *q) { - MYSQL_QUERY(mysql,q); - return 0; -} - void get_time(std::string& s) { time_t __timer; char __buffer[30]; From fe0f5c2b4dbff29a60a6c6dc6c14025fd1fed683 Mon Sep 17 00:00:00 2001 From: Wazir Ahmed Date: Thu, 7 Aug 2025 13:58:58 +0530 Subject: [PATCH 3/4] TAP: Update `check_query_count()` to fetch count from all hostgroups Signed-off-by: Wazir Ahmed --- test/tap/tap/utils.cpp | 22 ++++++++++++++-------- test/tap/tap/utils.h | 4 ++-- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/test/tap/tap/utils.cpp b/test/tap/tap/utils.cpp index 7fbe4d074..e0c93ef1e 100644 --- a/test/tap/tap/utils.cpp +++ b/test/tap/tap/utils.cpp @@ -2168,11 +2168,14 @@ void check_conn_count(MYSQL* admin, const string& conn_type, uint32_t conn_num, void check_query_count(MYSQL* admin, uint32_t queries, uint32_t hg) { const string queries_s { to_string(queries) }; - const string hg_s { to_string(hg) }; - const string select_hg_queries { - "SELECT Queries FROM stats_mysql_connection_pool WHERE hostgroup=" + to_string(hg) - }; + string select_hg_queries; + if (hg == -1) { + select_hg_queries = "SELECT SUM(Queries) FROM stats_mysql_connection_pool"; + } else { + select_hg_queries = "SELECT Queries FROM stats_mysql_connection_pool WHERE hostgroup=" + to_string(hg); + } + const string check_queries { "SELECT IIF((" + select_hg_queries + ")=" + queries_s + ",'TRUE','FALSE')" }; @@ -2193,11 +2196,14 @@ void check_query_count(MYSQL* admin, vector queries, uint32_t hg) { } ) }; - const string hg_s { to_string(hg) }; - const string select_hg_queries { - "SELECT Queries FROM stats_mysql_connection_pool WHERE hostgroup=" + to_string(hg) - }; + string select_hg_queries; + if (hg == -1) { + select_hg_queries = "SELECT SUM(Queries) FROM stats_mysql_connection_pool"; + } else { + select_hg_queries = "SELECT Queries FROM stats_mysql_connection_pool WHERE hostgroup=" + to_string(hg); + } + const string check_queries { "SELECT IIF((" + select_hg_queries + ") IN (" + queries_s + "),'TRUE','FALSE')" }; diff --git a/test/tap/tap/utils.h b/test/tap/tap/utils.h index 36dc54c17..cea8c548f 100644 --- a/test/tap/tap/utils.h +++ b/test/tap/tap/utils.h @@ -969,8 +969,8 @@ struct srv_addr_t { // Helpers using 'wait_for_cond' on 'stats_mysql_connection' void check_conn_count(MYSQL* admin, const std::string& conn_type, uint32_t conn_num, int32_t hg=-1); -void check_query_count(MYSQL* admin, uint32_t queries, uint32_t hg); -void check_query_count(MYSQL* admin, std::vector queries, uint32_t hg); +void check_query_count(MYSQL* admin, uint32_t queries, uint32_t hg=-1); +void check_query_count(MYSQL* admin, std::vector queries, uint32_t hg=-1); /** * @brief Fetches the ProxySQL nodes configured in the supplied instance. From 6807feb16d9f87c9c941cda0bf595e9ae67d580f Mon Sep 17 00:00:00 2001 From: Wazir Ahmed Date: Wed, 27 Aug 2025 18:13:09 +0530 Subject: [PATCH 4/4] Add TAP test for `SHOW STATUS LIKE 'Ssl_version'` Signed-off-by: Wazir Ahmed --- test/tap/tests/mysql-show_ssl_version-t.cpp | 88 +++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 test/tap/tests/mysql-show_ssl_version-t.cpp diff --git a/test/tap/tests/mysql-show_ssl_version-t.cpp b/test/tap/tests/mysql-show_ssl_version-t.cpp new file mode 100644 index 000000000..c6276486a --- /dev/null +++ b/test/tap/tests/mysql-show_ssl_version-t.cpp @@ -0,0 +1,88 @@ +/** + * @file mysql-show_ssl_version-t.cpp + * @brief This TAP test verifies that 'SHOW STATUS LIKE 'Ssl_version'' is handled by ProxySQL + * without backend connection and returns appropriate SSL version information. + */ + +#include +#include +#include +#include + +#include +#include "mysql.h" + +#include "tap.h" +#include "command_line.h" +#include "utils.h" + +using std::string; + +int main(int argc, char** argv) { + CommandLine cl; + if (cl.getEnv()) { + diag("Failed to get the required environmental variables."); + return exit_status(); + } + + std::vector ssl_version_queries = { + "SHOW STATUS LIKE 'Ssl_version'", + "SHOW STATUS LIKE 'ssl_version'", + "SHOW STATUS LIKE 'Ssl_version%'", + "show status LIKE 'Ssl_version'", + "show status LIKE 'ssl_version'", + "show status LIKE 'Ssl_version%'", + "show status like 'Ssl_version'", + "show status like 'ssl_version'", + "show status like 'Ssl_version%'", + }; + + int num_plans = ssl_version_queries.size() + 1; // +1 for query count check + plan(num_plans); + + MYSQL* admin = init_mysql_conn(cl.host, cl.admin_port, cl.admin_username, cl.admin_password); + if (!admin) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(admin)); + return exit_status(); + } + + MYSQL_QUERY_T(admin, "SELECT 1 FROM stats.stats_mysql_connection_pool_reset"); + MYSQL_RES* reset_result = mysql_store_result(admin); + mysql_free_result(reset_result); + + MYSQL* proxy = init_mysql_conn(cl.host, cl.port, cl.username, cl.password, true); + if (!proxy) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxy)); + return exit_status(); + } + + if (!mysql_get_ssl_cipher(proxy)) { + diag("Connection is not SSL"); + return exit_status(); + } + + for (auto& query : ssl_version_queries) { + int rc = run_q(proxy, query.c_str()); + if (rc == 0) { + string var_name; + string var_value; + + MYSQL_RES* result = mysql_store_result(proxy); + MYSQL_ROW row = mysql_fetch_row(result); + if (row) { + var_name = row[0]; + var_value = row[1]; + } + + ok((var_name == "Ssl_version" && var_value.find("TLS") == 0), "Ssl_version returned by ProxySQL: %s", var_value.c_str()); + mysql_free_result(result); + } + } + + check_query_count(admin, 0); + + mysql_close(proxy); + mysql_close(admin); + + return exit_status(); +}