Improved mysql-init_connect-t.cpp for PMC-10003

PMC-10003: Retrieved a resultset while running a simple command using async_send_simple_command() .
async_send_simple_command() is used by ProxySQL to configure the connection, thus it
shouldn't retrieve any resultset.
A common issue for triggering this error is to have configure mysql-init_connect to
run a statement that returns a resultset.
PRS_3888_3903^2
René Cannaò 4 years ago
parent afb079899c
commit 227e045aa4

@ -2062,7 +2062,7 @@ bool MySQL_Session::handler_again___status_SETTING_INIT_CONNECT(int *_rc) {
previous_status.pop();
NEXT_IMMEDIATE_NEW(st);
} else {
if (rc==-1) {
if (rc==-1 || rc==-2) {
// the command failed
int myerr=mysql_errno(myconn->mysql);
MyHGM->p_update_mysql_error_counter(
@ -2077,11 +2077,18 @@ bool MySQL_Session::handler_again___status_SETTING_INIT_CONNECT(int *_rc) {
// client error, serious
detected_broken_connection(__FILE__ , __LINE__ , __func__ , "while setting INIT CONNECT", myconn, myerr, mysql_error(myconn->mysql));
//if ((myds->myconn->reusable==true) && ((myds->myprot.prot_status & SERVER_STATUS_IN_TRANS)==0)) {
if ((myds->myconn->reusable==true) && myds->myconn->IsActiveTransaction()==false && myds->myconn->MultiplexDisabled()==false) {
retry_conn=true;
if (rc != -2) { // see PMC-10003
if ((myds->myconn->reusable==true) && myds->myconn->IsActiveTransaction()==false && myds->myconn->MultiplexDisabled()==false) {
retry_conn=true;
}
}
myds->destroy_MySQL_Connection_From_Pool(false);
myds->fd=0;
if (rc==-2) {
// Here we handle PMC-10003
// and we terminate the session
retry_conn=false;
}
if (retry_conn) {
myds->DSS=STATE_NOT_INITIALIZED;
//previous_status.push(PROCESSING_QUERY);

@ -2616,7 +2616,13 @@ int MySQL_Connection::async_send_simple_command(short event, char *stmt, unsigne
if (MyRS) {
// this is a severe mistake, we shouldn't have reach here
// for now we do not assert but report the error
proxy_error("Retrieved a resultset while running a simple command. This is an error!! Simple command: %s\n", stmt);
// PMC-10003: Retrieved a resultset while running a simple command using async_send_simple_command() .
// async_send_simple_command() is used by ProxySQL to configure the connection, thus it
// shouldn't retrieve any resultset.
// A common issue for triggering this error is to have configure mysql-init_connect to
// run a statement that returns a resultset.
proxy_error2(10003, "PMC-10003: Retrieved a resultset while running a simple command. This is an error!! Simple command: %s\n", stmt);
return -2;
}
if (async_state_machine==ASYNC_QUERY_END) {
compute_unknown_transaction_status();

@ -11,6 +11,12 @@
#include "command_line.h"
#include "utils.h"
/*
This TAP test validate the use of mysql-init_connect.
It uses 2 valid init_connect, and 2 invalid ones that trigger PMC-10003.
It also sets a value that causes a syntax error
*/
inline unsigned long long monotonic_time() {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
@ -23,7 +29,7 @@ int main(int argc, char** argv) {
if(cl.getEnv())
return exit_status();
plan(7);
plan(8);
MYSQL* mysqladmin = mysql_init(NULL);
if (!mysqladmin)
@ -40,8 +46,8 @@ int main(int argc, char** argv) {
return exit_status();
if (!mysql_real_connect(mysql, cl.host, cl.username, cl.password, NULL, cl.port, NULL, 0)) {
fprintf(stderr, "Failed to connect to database: Error: %s\n",
mysql_error(mysql));
fprintf(stderr, "Failed to connect to database: Error: %s\n",
mysql_error(mysql));
return exit_status();
}
diag("Setting mysql-init_connect to DO 1");
@ -59,7 +65,7 @@ int main(int argc, char** argv) {
unsigned long long num_rows = mysql_num_rows(res);
ok(num_rows == 1, "mysql_num_rows() , expected: 1 , actual: %llu", num_rows);
while ((row = mysql_fetch_row(res))) {
ok(strcmp(row[0],"100")==0, "row: expected: \"100\" , actual: \"%s\"", row[0]);
ok(strcmp(row[0],"100")==0, "row: expected: \"100\" , actual: \"%s\"", row[0]);
}
mysql_free_result(res);
}
@ -71,15 +77,11 @@ int main(int argc, char** argv) {
{
const char *q = "SELECT /* create_new_connection=1 */ 200";
diag("Running query: %s", q);
MYSQL_QUERY(mysql, q);
res = mysql_store_result(mysql);
MYSQL_ROW row;
unsigned long long num_rows = mysql_num_rows(res);
ok(num_rows == 1, "mysql_num_rows() , expected: 1 , actual: %llu", num_rows);
while ((row = mysql_fetch_row(res))) {
ok(strcmp(row[0],"200")==0, "row: expected: \"200\" , actual: \"%s\"", row[0]);
}
mysql_free_result(res);
int rc=mysql_query(mysql,q);
ok(rc!=0, "Query should fail. Error: %s", mysql_error(mysql));
if (rc==0)
return exit_status();
mysql_close(mysql);
}
diag("Setting mysql-init_connect to SELECT SLEEP(3)");
@ -87,25 +89,78 @@ int main(int argc, char** argv) {
MYSQL_QUERY(mysqladmin, "load mysql variables to runtime");
{
// reconnect
MYSQL* mysql = mysql_init(NULL);
if (!mysql)
return exit_status();
if (!mysql_real_connect(mysql, cl.host, cl.username, cl.password, NULL, cl.port, NULL, 0)) {
fprintf(stderr, "Failed to connect to database: Error: %s\n",
mysql_error(mysql));
return exit_status();
}
const char *q = "SELECT /* create_new_connection=1 */ 300";
diag("Running query: %s", q);
unsigned long long begin = monotonic_time();
int rc=mysql_query(mysql,q);
ok(rc!=0, "Query should fail. Error: %s", mysql_error(mysql));
if (rc==0)
return exit_status();
mysql_close(mysql);
unsigned long long end = monotonic_time();
unsigned long time_diff_ms = (end-begin)/1000;
ok(time_diff_ms>2900 && time_diff_ms < 3200 , "Total query execution time should be around 3 seconds. Actual : %llums", time_diff_ms);
}
diag("Setting mysql-init_connect to Syntax Error");
MYSQL_QUERY(mysqladmin, "UPDATE global_variables SET variable_value='Syntax Error' WHERE variable_name = 'mysql-init_connect'");
MYSQL_QUERY(mysqladmin, "load mysql variables to runtime");
{
// reconnect
MYSQL* mysql = mysql_init(NULL);
if (!mysql)
return exit_status();
if (!mysql_real_connect(mysql, cl.host, cl.username, cl.password, NULL, cl.port, NULL, 0)) {
fprintf(stderr, "Failed to connect to database: Error: %s\n",
mysql_error(mysql));
return exit_status();
}
const char *q = "SELECT /* create_new_connection=1 */ 400";
diag("Running query: %s", q);
int rc=mysql_query(mysql,q);
ok(rc!=0, "Query should fail. Error: %s", mysql_error(mysql));
if (rc==0)
return exit_status();
mysql_close(mysql);
}
diag("Setting mysql-init_connect to DO 1");
MYSQL_QUERY(mysqladmin, "UPDATE global_variables SET variable_value='DO 1' WHERE variable_name = 'mysql-init_connect'");
MYSQL_QUERY(mysqladmin, "load mysql variables to runtime");
{
// reconnect
MYSQL* mysql = mysql_init(NULL);
if (!mysql)
return exit_status();
if (!mysql_real_connect(mysql, cl.host, cl.username, cl.password, NULL, cl.port, NULL, 0)) {
fprintf(stderr, "Failed to connect to database: Error: %s\n",
mysql_error(mysql));
return exit_status();
}
const char *q = "SELECT /* create_new_connection=1 */ 500";
diag("Running query: %s", q);
MYSQL_QUERY(mysql, q);
res = mysql_store_result(mysql);
MYSQL_ROW row;
unsigned long long num_rows = mysql_num_rows(res);
ok(num_rows == 1, "mysql_num_rows() , expected: 1 , actual: %llu", num_rows);
while ((row = mysql_fetch_row(res))) {
ok(strcmp(row[0],"300")==0, "row: expected: \"300\" , actual: \"%s\"", row[0]);
ok(strcmp(row[0],"500")==0, "row: expected: \"500\" , actual: \"%s\"", row[0]);
}
mysql_free_result(res);
unsigned long long end = monotonic_time();
unsigned long time_diff_ms = (end-begin)/1000;
ok(time_diff_ms>2900 && time_diff_ms < 3200 , "Total query execution time should be around 3 seconds. Actual : %llums", time_diff_ms);
}
mysql_close(mysql);
mysql_close(mysqladmin);
return exit_status();

Loading…
Cancel
Save