diff --git a/test/tap/tap/utils.cpp b/test/tap/tap/utils.cpp index f5fc3354c8..8ee7d1f6b8 100644 --- a/test/tap/tap/utils.cpp +++ b/test/tap/tap/utils.cpp @@ -31,6 +31,150 @@ using std::vector; using std::to_string; using nlohmann::json; +thread_local std::vector mysql_conn; +thread_local std::vector> last_query_executed; + +thread_local std::vector mysql_stmt; +thread_local std::vector> last_stmt_executed; + +MYSQL* mysql_init_override(MYSQL* mysql, const char* file, int line) { + MYSQL* result = (*real_mysql_init)(mysql); + if (mysql_conn.capacity() == 0) { + fprintf(stdout, ">> [mysql_init] Override functions attached <<\n"); + mysql_conn.reserve(64); + last_query_executed.reserve(64); + } + return result; +} + +int mysql_query_override(MYSQL* mysql, const char* query, const char* file, int line) { + const int result = (*real_mysql_query)(mysql, query); + if (result == 0) { + mysql_conn.emplace_back(mysql); + last_query_executed.emplace_back(strdup(query),&std::free); + if (mysql_errno(mysql) == 0 && mysql_field_count(mysql) == 0 && mysql_warning_count(mysql) > 0) { + fprintf(stdout, "File %s, Line %d, [mysql_query] A warning was generated during the execution of the query:'%s', warning count:%d\n", + file, line, query, mysql_warning_count(mysql)); + } + } + return result; +} + +MYSQL_RES* mysql_store_result_override(MYSQL* mysql, const char* file, int line) { + MYSQL_RES* result = (*real_mysql_store_result)(mysql); + if (mysql_errno(mysql) == 0 && mysql_warning_count(mysql) > 0) { + size_t idx = -1; + for (size_t i = 0; i < mysql_conn.size(); i++) { + if (mysql_conn[i] == mysql) { + idx = i; + break; + } + } + fprintf(stdout, "File %s, Line %d, [mysql_store_result] A warning was generated during the execution of the query:'%s', warning count:%d\n", + file, line, (idx != -1 ? last_query_executed[idx].get() : ""), mysql_warning_count(mysql)); + } + return result; +} + +void mysql_close_override(MYSQL* mysql, const char* file, int line) { + size_t idx = -1; + for (size_t i = 0; i < mysql_conn.size(); i++) { + if (mysql_conn[i] == mysql) + idx = i; + break; + } + if (idx != -1) { + if (idx != mysql_conn.size() - 1) { + mysql_conn[idx] = mysql_conn.back(); + last_query_executed[idx] = std::move(last_query_executed.back()); + } + mysql_conn.pop_back(); + last_query_executed.pop_back(); + } + (*real_mysql_close)(mysql); +} + +MYSQL_STMT* mysql_stmt_init_override(MYSQL* mysql, const char* file, int line) { + MYSQL_STMT* result = (*real_mysql_stmt_init)(mysql); + if (mysql_stmt.capacity() == 0) { + fprintf(stdout, ">> [mysql_stmt_init] Override functions attached << \n"); + mysql_stmt.reserve(64); + last_stmt_executed.reserve(64); + } + return result; +} + +int mysql_stmt_prepare_override(MYSQL_STMT* stmt, const char* stmt_str, unsigned long length, const char* file, int line) { + const int result = (*real_mysql_stmt_prepare)(stmt, stmt_str, length); + if (result == 0) { + mysql_stmt.emplace_back(stmt); + last_stmt_executed.emplace_back(strdup(stmt_str), &std::free); + // mysql_stmt_warning_count is not available in MySQL connector + if (mysql_stmt_errno(stmt) == 0 && /*mysql_stmt_warning_count(stmt)*/mysql_warning_count(stmt->mysql) > 0) { + fprintf(stdout, "File %s, Line %d, [mysql_stmt_prepare] A warning was generated during the execution of the query:'%s', warning count:%d\n", + file, line, stmt_str, /*mysql_stmt_warning_count(stmt)*/mysql_warning_count(stmt->mysql)); + } + } + return result; +} + +int mysql_stmt_execute_override(MYSQL_STMT* stmt, const char* file, int line) { + const int result = (*real_mysql_stmt_execute)(stmt); + if (result == 0) { + // mysql_stmt_warning_count is not available in MySQL connector + if (mysql_stmt_errno(stmt) == 0 && mysql_stmt_field_count(stmt) == 0 && + /*mysql_stmt_warning_count(stmt)*/mysql_warning_count(stmt->mysql) > 0) { + size_t idx = -1; + for (size_t i = 0; i < mysql_stmt.size(); i++) { + if (mysql_stmt[i] == stmt) { + idx = i; + break; + } + } + fprintf(stdout, "File %s, Line %d, [mysql_stmt_execute] A warning was generated during the execution of the query:'%s', warning count:%d\n", + file, line, (idx != -1 ? last_stmt_executed[idx].get() : ""), /*mysql_stmt_warning_count(stmt)*/mysql_warning_count(stmt->mysql)); + } + } + return result; +} + +int mysql_stmt_store_result_override(MYSQL_STMT* stmt, const char* file, int line) { + const int result = (*real_mysql_stmt_store_result)(stmt); + if (result == 0) { + // mysql_stmt_warning_count is not available in MySQL connector + if (mysql_stmt_errno(stmt) == 0 && /*mysql_stmt_warning_count(stmt)*/mysql_warning_count(stmt->mysql) > 0) { + size_t idx = -1; + for (size_t i = 0; i < mysql_stmt.size(); i++) { + if (mysql_stmt[i] == stmt) { + idx = i; + break; + } + } + fprintf(stdout, "File %s, Line %d, [mysql_stmt_store_result] A warning was generated during the execution of the query:'%s', warning count:%d\n", + file, line, (idx != -1 ? last_stmt_executed[idx].get() : ""), /*mysql_stmt_warning_count(stmt)*/mysql_warning_count(stmt->mysql)); + } + } + return result; +} + +my_bool mysql_stmt_close_override(MYSQL_STMT* stmt, const char* file, int line) { + size_t idx = -1; + for (size_t i = 0; i < mysql_stmt.size(); i++) { + if (mysql_stmt[i] == stmt) + idx = i; + break; + } + if (idx != -1) { + if (idx != mysql_stmt.size() - 1) { + mysql_stmt[idx] = mysql_stmt.back(); + last_stmt_executed[idx] = std::move(last_stmt_executed.back()); + } + mysql_stmt.pop_back(); + last_stmt_executed.pop_back(); + } + return (*real_mysql_stmt_close)(stmt); +} + std::size_t count_matches(const string& str, const string& substr) { std::size_t result = 0; std::size_t pos = 0; diff --git a/test/tap/tap/utils.h b/test/tap/tap/utils.h index da13c69e8f..d9ff6a81a5 100644 --- a/test/tap/tap/utils.h +++ b/test/tap/tap/utils.h @@ -16,6 +16,41 @@ #include "command_line.h" #include "json.hpp" +#ifndef LIBMYSQL_HELPER +/* We are overriding some of the mariadb APIs to extract the warning count and print it in the log. + This override will apply to all TAP tests, except when the TAP test is linked with the MySQL client library (LIBMYSQL_HELPER defined). +*/ +static MYSQL* (*real_mysql_init)(MYSQL* mysql) = &mysql_init; +static int (*real_mysql_query)(MYSQL* mysql, const char* query) = &mysql_query; +static MYSQL_RES* (*real_mysql_store_result)(MYSQL* mysql) = &mysql_store_result; +static void (*real_mysql_close)(MYSQL* mysql) = &mysql_close; +static MYSQL_STMT* (*real_mysql_stmt_init)(MYSQL* mysql) = &mysql_stmt_init; +static int (*real_mysql_stmt_prepare)(MYSQL_STMT* stmt, const char* stmt_str, unsigned long length) = &mysql_stmt_prepare; +static int (*real_mysql_stmt_execute)(MYSQL_STMT* stmt) = &mysql_stmt_execute; +static int (*real_mysql_stmt_store_result)(MYSQL_STMT* stmt) = &mysql_stmt_store_result; +static my_bool (*real_mysql_stmt_close)(MYSQL_STMT* stmt) = &mysql_stmt_close; + +MYSQL* mysql_init_override(MYSQL* mysql, const char* file, int line); +int mysql_query_override(MYSQL* mysql, const char* query, const char* file, int line); +MYSQL_RES* mysql_store_result_override(MYSQL* mysql, const char* file, int line); +void mysql_close_override(MYSQL* mysql, const char* file, int line); +MYSQL_STMT* mysql_stmt_init_override(MYSQL* mysql, const char* file, int line); +int mysql_stmt_prepare_override(MYSQL_STMT* stmt, const char* stmt_str, unsigned long length, const char* file, int line); +int mysql_stmt_execute_override(MYSQL_STMT* stmt, const char* file, int line); +int mysql_stmt_store_result_override(MYSQL_STMT* stmt, const char* file, int line); +my_bool mysql_stmt_close_override(MYSQL_STMT* stmt, const char* file, int line); + +#define mysql_init(mysql) mysql_init_override(mysql,__FILE__,__LINE__) +#define mysql_query(mysql,query) mysql_query_override(mysql,query,__FILE__,__LINE__) +#define mysql_store_result(mysql) mysql_store_result_override(mysql,__FILE__,__LINE__) +#define mysql_close(mysql) mysql_close_override(mysql,__FILE__,__LINE__) +#define mysql_stmt_init(mysql) mysql_stmt_init_override(mysql,__FILE__,__LINE__) +#define mysql_stmt_prepare(stmt,stmt_str,length) mysql_stmt_prepare_override(stmt,stmt_str,length,__FILE__,__LINE__) +#define mysql_stmt_execute(stmt) mysql_stmt_execute_override(stmt,__FILE__,__LINE__) +#define mysql_stmt_store_result(stmt) mysql_stmt_store_result_override(stmt,__FILE__,__LINE__) +#define mysql_stmt_close(stmt) mysql_stmt_close_override(stmt,__FILE__,__LINE__) +#endif + inline std::string get_formatted_time() { time_t __timer; char __buffer[30];