diff --git a/test/tap/tests/reg_test_3603-stmt_metadata-t.cpp b/test/tap/tests/reg_test_3603-stmt_metadata-t.cpp new file mode 100644 index 000000000..c47c3cf12 --- /dev/null +++ b/test/tap/tests/reg_test_3603-stmt_metadata-t.cpp @@ -0,0 +1,611 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "tap.h" +#include "command_line.h" +#include "utils.h" + +using std::string; + +int update_and_check( + MYSQL_STMT *stmti, MYSQL_STMT *stmts, int64_t id, char *cll_num, char *dst_num, + int64_t* dur, MYSQL_TIME *end_time, char *lst_msg, char *lst_st, int *mapping_id, + char *prov, char *prov_id, MYSQL_TIME *st_time, char* trck_num +) { + int rc; + MYSQL_BIND bindu[13]; + MYSQL_BIND bindsi[1]; + MYSQL_BIND binds[13]; + my_bool is_null_on = 1; + + long unsigned int cll_l = 0; + long unsigned int dst_l = 0; + long unsigned int lst_msg_l = 0; + long unsigned int lst_st_l = 0; + long unsigned int prov_l = 0; + long unsigned int prov_id_l = 0; + long unsigned int trck_num_l = 0; + + int64_t copyid = id; + + memset(bindu, 0, sizeof(bindu)); + memset(bindsi, 0, sizeof(bindsi)); + memset(binds, 0, sizeof(binds)); + + bindu[0].buffer_type= MYSQL_TYPE_VAR_STRING; + bindu[0].buffer= cll_num; + if (cll_num) { + cll_l = strlen(cll_num); + bindu[0].buffer_length= cll_l; + bindu[0].is_null= 0; + bindu[0].length= &cll_l; + } else { + bindu[0].is_null= &is_null_on; + bindu[0].buffer_type= MYSQL_TYPE_NULL; + } + + bindu[1].buffer_type= MYSQL_TYPE_VAR_STRING; + bindu[1].buffer= dst_num; + if (dst_num) { + dst_l = strlen(dst_num); + bindu[1].buffer_length= dst_l; + bindu[1].is_null= 0; + bindu[1].length= &dst_l; + } else { + bindu[1].is_null= &is_null_on; + bindu[1].buffer_type= MYSQL_TYPE_NULL; + } + + bindu[2].buffer_type= MYSQL_TYPE_LONG; + bindu[2].buffer= (char *)dur; + if (dur) { + bindu[2].is_null= 0; + } else { + bindu[2].is_null= &is_null_on; + bindu[2].buffer_type= MYSQL_TYPE_NULL; + } + bindu[2].length= 0; + + bindu[3].buffer_type= MYSQL_TYPE_DATETIME; + bindu[3].buffer= (char *)end_time; + if (end_time) { + bindu[3].is_null= 0; + } else { + bindu[3].is_null= &is_null_on; + bindu[3].buffer_type= MYSQL_TYPE_NULL; + } + bindu[3].length= 0; + + bindu[4].buffer_type= MYSQL_TYPE_VAR_STRING; + bindu[4].buffer= lst_msg; + if (lst_msg) { + lst_msg_l = strlen(lst_msg); + bindu[4].buffer_length= lst_msg_l; + bindu[4].is_null= 0; + bindu[4].length= &lst_msg_l; + } else { + bindu[4].is_null= &is_null_on; + bindu[4].buffer_type= MYSQL_TYPE_NULL; + } + + bindu[5].buffer_type= MYSQL_TYPE_VAR_STRING; + bindu[5].buffer= lst_st; + if (lst_st) { + lst_st_l = strlen(lst_st); + bindu[5].buffer_length= lst_st_l; + bindu[5].is_null= 0; + bindu[5].length= &lst_st_l; + } else { + bindu[5].is_null= &is_null_on; + bindu[5].buffer_type= MYSQL_TYPE_NULL; + } + + bindu[6].buffer_type= MYSQL_TYPE_LONG; + bindu[6].buffer= (char *)mapping_id; + if (mapping_id) { + bindu[6].is_null= 0; + } else { + bindu[6].is_null= &is_null_on; + bindu[6].buffer_type= MYSQL_TYPE_NULL; + } + bindu[6].length= 0; + + bindu[7].buffer_type= MYSQL_TYPE_VAR_STRING; + bindu[7].buffer= prov; + if (prov) { + prov_l = strlen(prov); + bindu[7].buffer_length= prov_l; + bindu[7].is_null= 0; + bindu[7].length= &prov_l; + } else { + bindu[7].is_null= &is_null_on; + bindu[7].buffer_type= MYSQL_TYPE_NULL; + } + + bindu[8].buffer_type= MYSQL_TYPE_VAR_STRING; + bindu[8].buffer= prov_id; + if (prov_id) { + prov_id_l = strlen(prov_id); + bindu[8].buffer_length= prov_id_l; + bindu[8].is_null= 0; + bindu[8].length= &prov_id_l; + } else { + bindu[8].is_null= &is_null_on; + bindu[8].buffer_type= MYSQL_TYPE_NULL; + } + + bindu[9].buffer_type= MYSQL_TYPE_DATETIME; + bindu[9].buffer= (char *)st_time; + if (st_time) { + bindu[9].is_null= 0; + } else { + bindu[9].is_null= &is_null_on; + bindu[9].buffer_type= MYSQL_TYPE_NULL; + } + bindu[9].length= 0; + + bindu[10].buffer_type= MYSQL_TYPE_VAR_STRING; + bindu[10].buffer= trck_num; + if (trck_num) { + trck_num_l = strlen(trck_num); + bindu[10].buffer_length= trck_num_l; + bindu[10].is_null= 0; + bindu[10].length= &trck_num_l; + } else { + bindu[10].is_null= &is_null_on; + bindu[10].buffer_type= MYSQL_TYPE_NULL; + } + + bindu[11].buffer_type= MYSQL_TYPE_LONGLONG; + bindu[11].buffer= (char *)©id; + if (copyid) { + bindu[11].is_null= 0; + } else { + bindu[11].is_null= &is_null_on; + bindu[11].buffer_type= MYSQL_TYPE_NULL; + } + bindu[11].length= 0; + + rc = mysql_stmt_bind_param(stmti, bindu); + if (rc) { + diag("mysql_stmt_bind_param() failed for INSERT with id %ld\n", id); + return exit_status(); + } + + rc = mysql_stmt_execute(stmti); + if (rc) { + diag("mysql_stmt_execute() failed for INSERT with id %ld : %s\n", id, mysql_stmt_error(stmti)); + return exit_status(); + } + + // for SELECT input + bindsi[0].buffer_type= MYSQL_TYPE_LONG; + bindsi[0].buffer= (char *)©id; + bindsi[0].is_null= 0; + bindsi[0].length= 0; + + rc = mysql_stmt_bind_param(stmts, bindsi); + if (rc) { + diag("mysql_stmt_bind_param() failed for SELECT with id %ld\n", id); + return exit_status(); + } + + char cll_buf[256]; + char dst_buf[256]; + char lst_msg_buf[256]; + char lst_st_buf[256]; + char prov_buf[256]; + char prov_id_buf[256]; + char trck_num_buf[256]; + + MYSQL_TIME ts_end_time; + MYSQL_TIME ts_st_time; + memset(&ts_end_time, 0, sizeof(ts_end_time)); + memset(&ts_st_time, 0, sizeof(ts_st_time)); + + unsigned long length[13]; + my_bool is_null[13]; + my_bool error[13]; + + memset(&length, 0, sizeof(length)); + memset(&is_null, 0, sizeof(is_null)); + memset(&error, 0, sizeof(error)); + int64_t id_res; + int i_duration, i_mapping_id, i3_res; + + // for SELECT result + binds[0].buffer_type= MYSQL_TYPE_LONGLONG; + binds[0].buffer= (char *)&id_res; + binds[0].is_null= &is_null[0]; + binds[0].length= &length[0]; + binds[0].error= &error[0]; + + binds[1].buffer_type= MYSQL_TYPE_VAR_STRING; + binds[1].buffer= (char *)cll_buf; + binds[1].buffer_length= sizeof(cll_buf); + binds[1].is_null= &is_null[1]; + binds[1].length= &length[1]; + binds[1].error= &error[1]; + + binds[2].buffer_type= MYSQL_TYPE_VAR_STRING; + binds[2].buffer= (char *)dst_buf; + binds[2].buffer_length= sizeof(dst_buf); + binds[2].is_null= &is_null[2]; + binds[2].length= &length[2]; + binds[2].error= &error[2]; + + binds[3].buffer_type= MYSQL_TYPE_LONG; + binds[3].buffer= (char *)&i_duration; + binds[3].is_null= &is_null[3]; + binds[3].length= &length[3]; + binds[3].error= &error[3]; + + binds[4].buffer_type= MYSQL_TYPE_DATETIME; + binds[4].buffer= (char *)&ts_end_time; + binds[4].is_null= &is_null[4]; + binds[4].length= &length[4]; + binds[4].error= &error[4]; + + binds[5].buffer_type= MYSQL_TYPE_VAR_STRING; + binds[5].buffer= (char *)lst_msg_buf; + binds[5].buffer_length= sizeof(lst_msg_buf); + binds[5].is_null= &is_null[5]; + binds[5].length= &length[5]; + binds[5].error= &error[5]; + + binds[6].buffer_type= MYSQL_TYPE_VAR_STRING; + binds[6].buffer= (char *)lst_st_buf; + binds[6].buffer_length= sizeof(lst_st_buf); + binds[6].is_null= &is_null[6]; + binds[6].length= &length[6]; + binds[6].error= &error[6]; + + binds[7].buffer_type= MYSQL_TYPE_LONG; + binds[7].buffer= (char *)&i_mapping_id; + binds[7].is_null= &is_null[7]; + binds[7].length= &length[7]; + binds[7].error= &error[7]; + + binds[8].buffer_type= MYSQL_TYPE_VAR_STRING; + binds[8].buffer= (char *)prov_buf; + binds[8].buffer_length= sizeof(prov_buf); + binds[8].is_null= &is_null[8]; + binds[8].length= &length[8]; + binds[8].error= &error[8]; + + binds[9].buffer_type= MYSQL_TYPE_VAR_STRING; + binds[9].buffer= (char *)prov_id_buf; + binds[9].buffer_length= sizeof(prov_id_buf); + binds[9].is_null= &is_null[9]; + binds[9].length= &length[9]; + binds[9].error= &error[9]; + + binds[10].buffer_type= MYSQL_TYPE_DATETIME; + binds[10].buffer= (char *)&ts_st_time; + binds[10].is_null= &is_null[10]; + binds[10].length= &length[10]; + binds[10].error= &error[10]; + + binds[11].buffer_type= MYSQL_TYPE_VAR_STRING; + binds[11].buffer= (char *)trck_num_buf; + binds[11].buffer_length= sizeof(trck_num_buf); + binds[11].is_null= &is_null[11]; + binds[11].length= &length[11]; + binds[11].error= &error[11]; + + rc = mysql_stmt_execute(stmts); + if (rc) { + diag("mysql_stmt_execute() failed for SELECT with id %ld : %s", id, mysql_stmt_error(stmts)); + return exit_status(); + } + + rc = mysql_stmt_bind_result(stmts, binds); + if (rc) { + diag("mysql_stmt_bind_result() failed: %s", mysql_stmt_error(stmts)); + return exit_status(); + } + MYSQL_RES *prepare_meta_result; + prepare_meta_result = mysql_stmt_result_metadata(stmts); + if (prepare_meta_result == NULL) { + diag("mysql_stmt_result_metadata() failed: %s", mysql_stmt_error(stmts)); + return exit_status(); + } + + rc = mysql_stmt_store_result(stmts); + if (rc) { + diag("mysql_stmt_store_result() failed: %s", mysql_stmt_error(stmts)); + return exit_status(); + } + + unsigned long long rows_count= mysql_stmt_num_rows(stmts); + ok(rows_count == 1 , "Rows expected: 1 , retrieved: %llu", rows_count); + if (rows_count != 1) { + return 1; + } + rc = mysql_stmt_fetch(stmts); + if (rc) { + diag("mysql_stmt_fetch() failed: %d %s", rc , mysql_stmt_error(stmts)); + } else { + int matches = 0; + char buf1[256], buf2[256]; + + diag("id expected/retrieved: %ld , %ld", copyid, is_null[0] ? 0 : id_res); + if (copyid == (is_null[0] ? 0 : id_res)) + matches++; + { + diag("'call_num' expected/retrieved: %s , %s", cll_num ? cll_num : "NULL" , is_null[1] ? "NULL" : (char *)binds[1].buffer); + if (cll_num == NULL && is_null[1]) { + matches++; + } else { + if (cll_num && !is_null[1] && strcmp(cll_num,(char *)binds[1].buffer)==0) + matches++; + } + } + + { + diag("'dst_num' expected/retrieved: %s , %s", dst_num ? dst_num : "NULL" , is_null[2] ? "NULL" : (char *)binds[2].buffer); + if (dst_num == NULL && is_null[2]) { + matches++; + } else { + if (dst_num && !is_null[2] && strcmp(dst_num,(char *)binds[2].buffer)==0) + matches++; + } + } + + { + sprintf(buf1,"NULL"); + sprintf(buf2,"NULL"); + + if (dur) + sprintf(buf1, "%ld", *dur); + if (!is_null[3]) + sprintf(buf2, "%d", i_duration); + diag("'duration' expected/retrieved: %s , %s", buf1, buf2); + if (strcmp(buf1,buf2)==0) + matches++; + } + + { + if (end_time) { + sprintf(buf1,"%d:%d:%d", end_time->hour, end_time->minute, end_time->second); + } else { + sprintf(buf1,"NULL"); + } + if (is_null[4]) { + sprintf(buf2,"NULL"); + } else { + sprintf(buf2,"%d:%d:%d", ts_end_time.hour, ts_end_time.minute, ts_end_time.second); + } + if (strcmp(buf1,buf2)==0) + matches++; + diag("'end_time' expected/retrieved: %s , %s", buf1, buf2); + sprintf(buf1,"NULL"); + sprintf(buf2,"NULL"); + } + + { + diag("'last_msg' expected/retrieved: %s , %s", lst_msg ? lst_msg : "NULL" , is_null[5] ? "NULL" : (char *)binds[5].buffer); + if (lst_msg == NULL && is_null[5]) { + matches++; + } else { + if (lst_msg && !is_null[5] && strcmp(lst_msg,(char *)binds[5].buffer)==0) + matches++; + } + } + + { + diag("'last_st' expected/retrieved: %s , %s", lst_st ? lst_st : "NULL" , is_null[6] ? "NULL" : (char *)binds[6].buffer); + if (lst_st == NULL && is_null[6]) { + matches++; + } else { + if (lst_st && !is_null[6] && strcmp(lst_st,(char *)binds[6].buffer)==0) + matches++; + } + } + + { + sprintf(buf1,"NULL"); + sprintf(buf2,"NULL"); + + if (mapping_id) + sprintf(buf1, "%d", *mapping_id); + if (!is_null[7]) + sprintf(buf2, "%d", i_mapping_id); + diag("'mapping_id' expected/retrieved: %s , %s", buf1, buf2); + if (strcmp(buf1,buf2)==0) + matches++; + } + + { + diag("'prov' expected/retrieved: %s , %s", prov ? prov : "NULL" , is_null[8] ? "NULL" : (char *)binds[8].buffer); + if (prov == NULL && is_null[8]) { + matches++; + } else { + if (prov && !is_null[8] && strcmp(prov,(char *)binds[8].buffer)==0) + matches++; + } + } + + { + diag("'prov_id' expected/retrieved: %s , %s", prov_id ? prov_id : "NULL" , is_null[9] ? "NULL" : (char *)binds[9].buffer); + if (prov_id == NULL && is_null[9]) { + matches++; + } else { + if (prov_id && !is_null[9] && strcmp(prov_id,(char *)binds[9].buffer)==0) + matches++; + } + } + + { + if (st_time) { + sprintf(buf1,"%d:%d:%d", st_time->hour, st_time->minute, st_time->second); + } else { + sprintf(buf1,"NULL"); + } + if (is_null[10]) { + sprintf(buf2,"NULL"); + } else { + sprintf(buf2,"%d:%d:%d", ts_st_time.hour, ts_st_time.minute, ts_st_time.second); + } + if (strcmp(buf1,buf2)==0) + matches++; + diag("'st_time' expected/retrieved: %s , %s", buf1, buf2); + sprintf(buf1,"NULL"); + sprintf(buf2,"NULL"); + } + + { + diag("'trck_num' expected/retrieved: %s , %s", trck_num ? trck_num : "NULL" , is_null[11] ? "NULL" : (char *)binds[11].buffer); + if (trck_num == NULL && is_null[11]) { + matches++; + } else { + if (trck_num && !is_null[11] && strcmp(trck_num,(char *)binds[11].buffer)==0) + matches++; + } + } + + ok(matches == 12, "Matching columns for ID %ld/%ld: %d (expected 12)\n", id, copyid, matches); + mysql_stmt_free_result(stmts); + mysql_free_result(prepare_meta_result); + } + return 0; + +} + +int main(int argc, char** argv) { + CommandLine cl; + + if (cl.getEnv()) { + diag("Failed to get the required environmental variables."); + return -1; + } + + int np = 4; // init + prepare + np += 5*2; // number of INSERT+SELECT + plan(np); + + MYSQL* proxysql_mysql = mysql_init(NULL); + if (!proxysql_mysql) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_mysql)); + return exit_status(); + } + + if (!mysql_real_connect(proxysql_mysql, cl.host, cl.username, cl.password, NULL, cl.port, NULL, 0)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_mysql)); + return exit_status(); + } + + MYSQL* proxysql_admin = mysql_init(NULL); + if (!proxysql_admin) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_admin)); + return exit_status(); + } + + if (!mysql_real_connect(proxysql_admin, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_admin)); + return exit_status(); + } + + MYSQL_QUERY(proxysql_mysql, "DROP TABLE IF EXISTS test.reg_test_3603"); + MYSQL_QUERY( + proxysql_mysql, + "CREATE TABLE IF NOT EXISTS test.reg_test_3603 (" + " `id` bigint(20) NOT NULL, `cll_num` varchar(255) DEFAULT NULL, `dst_num` varchar(255) DEFAULT NULL," + " `dur` INT DEFAULT NULL, `end_time` DATETIME DEFAULT NULL, `lst_msg` varchar(255) DEFAULT NULL," + " `last_status` varchar(255) DEFAULT NULL, `mapping_id` INT DEFAULT NULL, `prov` varchar(255) DEFAULT NULL," + " `prov_id` varchar(255) DEFAULT NULL, `start_time` DATETIME DEFAULT NULL, `trck_num` varchar(255) DEFAULT NULL, " + " PRIMARY KEY (id)) ENGINE=InnoDB" + ); + MYSQL_QUERY(proxysql_mysql, "INSERT INTO test.reg_test_3603 (id) VALUES (14822133)"); + + int rc; + std::string query_u = + "update test.reg_test_3603 set cll_num=?,dst_num=?,dur=?," + "end_time=?,lst_msg=?,last_status=?,mapping_id=?,prov=?,prov_id=?," + "start_time=?,trck_num=? where id=?"; + + // Force the 'hostgroup' for the 'SELECT' query to avoid replication issues + std::string query_s = "SELECT /*+ ;hostgroup=0 */ * FROM test.reg_test_3603 WHERE id=?"; + + // init and prepare INSERT + MYSQL_STMT *stmti = mysql_stmt_init(proxysql_mysql); + ok(stmti != NULL, "mysql_stmt_init() %s", stmti != NULL ? "succeeded" : "failed"); + if (!stmti) { + return exit_status(); + } + rc = mysql_stmt_prepare(stmti, query_u.c_str(), strlen(query_u.c_str())); + ok(rc==0, "mysql_stmt_prepare() for INSERT %s%s" , rc == 0 ? "succeeded" : "failed: " , rc == 0 ? "" : mysql_error(proxysql_mysql)); + if (rc) { + return exit_status(); + } + + // init and prepare SELECT + MYSQL_STMT *stmts = mysql_stmt_init(proxysql_mysql); + ok(stmts != NULL, "mysql_stmt_init() %s", stmts != NULL ? "succeeded" : "failed"); + if (!stmts) { + return exit_status(); + } + rc = mysql_stmt_prepare(stmts, query_s.c_str(), strlen(query_s.c_str())); + ok(rc==0, "mysql_stmt_prepare() for SELECT %s%s" , rc == 0 ? "succeeded" : "failed: " , rc == 0 ? "" : mysql_error(proxysql_mysql)); + if (rc) { + return exit_status(); + } + + diag("%s",""); + MYSQL_TIME end_time; + MYSQL_TIME st_time; + int64_t i_duration=254; + int i_mapping_id=558; + + memset(&end_time, 0, sizeof(end_time)); + memset(&st_time, 0, sizeof(st_time)); + end_time.time_type=MYSQL_TIMESTAMP_TIME; + end_time.year= 2000; end_time.month= 10; end_time.day= 20; + end_time.hour = 4; end_time.minute = 14; end_time.second=24; end_time.second_part= 10; + st_time.year= 2000; st_time.month= 10; st_time.day= 20; + st_time.hour = 10; st_time.minute = 20; st_time.second=30; st_time.second_part= 10; + + // This two operations should fail due to preservation of NULL values + // ======================================================================== + update_and_check( + stmti, stmts, 14822133, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL + ); + update_and_check( + stmti, stmts, 14822133, (char*)"123456789123", (char*)"123456789123", &i_duration, &end_time, + NULL, (char*)"END", &i_mapping_id, (char*)"T", + (char*)"CAb3f8fb2e3010f60b1d5dbcdfeb10e84d", &st_time, (char*)"912938481238" + ); + // ======================================================================== + + // This three operations should fail ending in a heap-buffer-overflow + // ======================================================================== + update_and_check( + stmti, stmts, 14822133, (char*)"123456789123", (char*)"123456789123", &i_duration, &end_time, + NULL, (char*)"END", &i_mapping_id, (char*)"T", + (char*)"DBc3g6fm7P8383F61b1bbb82878788e849", &st_time, (char*)"912938481238" + ); + update_and_check( + stmti, stmts, 14822133, (char*)"123456789123", NULL, NULL, NULL, + (char*)"aalñjk1982371927831jlasñdjfalsñdfj", (char*)"END", &i_mapping_id, (char*)"T", + (char*)"DBc3g6fm7P8383F61b1bbb82878788e849", &st_time, (char*)"912938481238" + ); + update_and_check( + stmti, stmts, 14822133, (char*)"123456789123", (char*)"123456789123", &i_duration, &end_time, + NULL, (char*)"END", &i_mapping_id, (char*)"T", + (char*)"DBc3g6fm7P8383F61b1bbb82878788e849", &st_time, (char*)"912938481238" + ); + // ======================================================================== + + mysql_close(proxysql_mysql); + mysql_close(proxysql_admin); + + return exit_status(); +}