Skip to content
This repository was archived by the owner on Mar 1, 2026. It is now read-only.

Commit 6497419

Browse files
aditya-jalanHerman Lee
authored andcommitted
Adding new api to query the stage of the mysql_real_connect* state machine
Summary: For nonblocking connect we need a way to query what stage of the mysql_real_connect_nonblocking state machine is at. This will allow users to monitor and take appropriate actions based on the progress of the connection. Reviewed By: lloyd Differential Revision: D23387197
1 parent 1e077f2 commit 6497419

6 files changed

Lines changed: 179 additions & 1 deletion

File tree

‎include/mysql.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,8 @@ int STDCALL mysql_session_track_get_next(MYSQL *mysql,
528528
const char **data, size_t *length);
529529
int STDCALL mysql_resp_attr_find(MYSQL *mysql, const char *lookup,
530530
const char **data, size_t *length);
531+
532+
enum connect_stage STDCALL mysql_get_connect_stage(MYSQL *mysql);
531533
/* local infile support */
532534

533535
#define LOCAL_INFILE_ERROR_LEN 512

‎include/mysql.h.pp‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,21 @@
187187
MYSQL_OPTION_MULTI_STATEMENTS_ON,
188188
MYSQL_OPTION_MULTI_STATEMENTS_OFF
189189
};
190+
enum connect_stage {
191+
CONNECT_STAGE_INVALID = 0,
192+
CONNECT_STAGE_NOT_STARTED,
193+
CONNECT_STAGE_NET_BEGIN_CONNECT,
194+
CONNECT_STAGE_NET_WAIT_CONNECT,
195+
CONNECT_STAGE_NET_COMPLETE_CONNECT,
196+
CONNECT_STAGE_READ_GREETING,
197+
CONNECT_STAGE_PARSE_HANDSHAKE,
198+
CONNECT_STAGE_ESTABLISH_SSL,
199+
CONNECT_STAGE_AUTHENTICATE,
200+
CONNECT_STAGE_PREP_SELECT_DATABASE,
201+
CONNECT_STAGE_PREP_INIT_COMMANDS,
202+
CONNECT_STAGE_SEND_ONE_INIT_COMMAND,
203+
CONNECT_STAGE_COMPLETE
204+
};
190205
enum enum_session_state_type {
191206
SESSION_TRACK_SYSTEM_VARIABLES,
192207
SESSION_TRACK_SCHEMA,
@@ -681,6 +696,7 @@
681696
const char **data, size_t *length);
682697
int mysql_resp_attr_find(MYSQL *mysql, const char *lookup,
683698
const char **data, size_t *length);
699+
enum connect_stage mysql_get_connect_stage(MYSQL *mysql);
684700
void mysql_set_local_infile_handler(
685701
MYSQL *mysql, int (*local_infile_init)(void **, const char *, void *),
686702
int (*local_infile_read)(void *, char *, unsigned int),

‎include/mysql_com.h‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,6 +1070,25 @@ enum enum_mysql_set_option {
10701070
MYSQL_OPTION_MULTI_STATEMENTS_OFF
10711071
};
10721072

1073+
/*
1074+
Describes the current state of Asynchronous connection phase state machine
1075+
*/
1076+
enum connect_stage {
1077+
CONNECT_STAGE_INVALID = 0,
1078+
CONNECT_STAGE_NOT_STARTED,
1079+
CONNECT_STAGE_NET_BEGIN_CONNECT,
1080+
CONNECT_STAGE_NET_WAIT_CONNECT,
1081+
CONNECT_STAGE_NET_COMPLETE_CONNECT,
1082+
CONNECT_STAGE_READ_GREETING,
1083+
CONNECT_STAGE_PARSE_HANDSHAKE,
1084+
CONNECT_STAGE_ESTABLISH_SSL,
1085+
CONNECT_STAGE_AUTHENTICATE,
1086+
CONNECT_STAGE_PREP_SELECT_DATABASE,
1087+
CONNECT_STAGE_PREP_INIT_COMMANDS,
1088+
CONNECT_STAGE_SEND_ONE_INIT_COMMAND,
1089+
CONNECT_STAGE_COMPLETE
1090+
};
1091+
10731092
/**
10741093
Type of state change information that the server can include in the Ok
10751094
packet.

‎libmysql/CMakeLists.txt‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,8 @@ SET(CLIENT_API_NONBLOCKING_FUNCTIONS
189189
mysql_send_query_nonblocking
190190
mysql_store_result_nonblocking
191191
mysql_get_socket_descriptor
192-
192+
mysql_get_connect_stage
193+
mysql_reset_connection_nonblocking
193194
CACHE INTERNAL "Nonblocking functions exported by client API"
194195
)
195196

‎sql-common/client.cc‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4970,6 +4970,45 @@ int STDCALL mysql_get_socket_descriptor(MYSQL *mysql) {
49704970
return -1;
49714971
}
49724972

4973+
static const std::vector<std::pair<csm_function, connect_stage>> stages = {
4974+
{csm_begin_connect, CONNECT_STAGE_NET_BEGIN_CONNECT},
4975+
{csm_wait_connect, CONNECT_STAGE_NET_WAIT_CONNECT},
4976+
{csm_complete_connect, CONNECT_STAGE_NET_COMPLETE_CONNECT},
4977+
{csm_read_greeting, CONNECT_STAGE_READ_GREETING},
4978+
{csm_parse_handshake, CONNECT_STAGE_PARSE_HANDSHAKE},
4979+
{csm_establish_ssl, CONNECT_STAGE_ESTABLISH_SSL},
4980+
{csm_authenticate, CONNECT_STAGE_AUTHENTICATE},
4981+
{csm_prep_select_database, CONNECT_STAGE_PREP_SELECT_DATABASE},
4982+
#if !defined(MYSQL_SERVER)
4983+
{csm_prep_init_commands, CONNECT_STAGE_PREP_INIT_COMMANDS},
4984+
{csm_send_one_init_command, CONNECT_STAGE_SEND_ONE_INIT_COMMAND},
4985+
#endif
4986+
};
4987+
4988+
connect_stage STDCALL mysql_get_connect_stage(MYSQL *mysql) {
4989+
if (mysql) {
4990+
NET *net = &mysql->net;
4991+
if (!net->vio) {
4992+
/* Seems the first stage hasn't started yet or it was unsuccessful, and
4993+
needs to be restarted so return the 1st stage */
4994+
return CONNECT_STAGE_NOT_STARTED;
4995+
}
4996+
4997+
mysql_async_connect *ctx = ASYNC_DATA(mysql)->connect_context;
4998+
if (!ctx) {
4999+
// If context is null and vio->net is set, means connection is complete
5000+
return CONNECT_STAGE_COMPLETE;
5001+
}
5002+
5003+
for (const auto &stage_pair : stages) {
5004+
if (ctx->state_function == stage_pair.first) {
5005+
return stage_pair.second;
5006+
}
5007+
}
5008+
}
5009+
return CONNECT_STAGE_INVALID;
5010+
}
5011+
49735012
/* clang-format off */
49745013
/**
49755014
@page page_protocol_connection_phase_packets_protocol_handshake_response Protocol::HandshakeResponse:

‎testclients/mysql_client_test.cc‎

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23466,6 +23466,106 @@ static void test_bug25584097() {
2346623466
thd.join();
2346723467
}
2346823468

23469+
static void test_get_connect_stage() {
23470+
MYSQL *mysql_async = NULL, *mysql_sync = NULL;
23471+
net_async_status status;
23472+
23473+
myheader("test_get_connect_stage");
23474+
23475+
enum connect_stage cs = mysql_get_connect_stage(mysql_async);
23476+
if (cs != CONNECT_STAGE_INVALID) {
23477+
fprintf(stdout,
23478+
"\n Expected connect_stage to be "
23479+
"CONNECT_STAGE_INVALID for uninitialized mysql");
23480+
exit(1);
23481+
}
23482+
23483+
/* test stages for a nonblocking conneciton */
23484+
if (!(mysql_async = mysql_client_init(NULL))) {
23485+
myerror("mysql_client_init() failed");
23486+
exit(1);
23487+
}
23488+
cs = mysql_get_connect_stage(mysql_async);
23489+
if (cs != CONNECT_STAGE_NOT_STARTED) {
23490+
fprintf(stdout,
23491+
"\n Expected connect_stage to be "
23492+
"CONNECT_STAGE_NET_BEGIN_CONNECT for just initialized connection"
23493+
"but its %d",
23494+
cs);
23495+
exit(1);
23496+
}
23497+
23498+
enum connect_stage cs_prev = cs;
23499+
do {
23500+
status = mysql_real_connect_nonblocking(
23501+
mysql_async, opt_host, opt_user, opt_password, current_db, opt_port,
23502+
opt_unix_socket, CLIENT_MULTI_STATEMENTS);
23503+
cs = mysql_get_connect_stage(mysql_async);
23504+
// Always expect state machine to make forward progress
23505+
if (cs < cs_prev) {
23506+
fprintf(stdout,
23507+
"\n Did not expect connect_stage to be %d, as previous was %d",
23508+
cs, cs_prev);
23509+
exit(1);
23510+
} else if (cs != cs_prev) {
23511+
if (!opt_silent)
23512+
fprintf(stdout,
23513+
"\n Nonblocking connect made transition from stage %d to %d",
23514+
cs_prev, cs);
23515+
}
23516+
cs_prev = cs;
23517+
} while (status == NET_ASYNC_NOT_READY);
23518+
if (status == NET_ASYNC_ERROR) {
23519+
fprintf(stdout, "\n mysql_real_connect_nonblocking() failed. stage:%d", cs);
23520+
exit(1);
23521+
}
23522+
23523+
cs = mysql_get_connect_stage(mysql_async);
23524+
if (cs != CONNECT_STAGE_COMPLETE) {
23525+
fprintf(stdout,
23526+
"\n Expected connect_stage as CONNECT_STAGE_COMPLETE, its %d", cs);
23527+
exit(1);
23528+
}
23529+
if (!opt_silent)
23530+
fprintf(stdout, "\n Nonblocking connect successful. Final stage %d", cs);
23531+
23532+
mysql_close(mysql_async);
23533+
23534+
/* test stages for a blocking conneciton */
23535+
if (!(mysql_sync = mysql_client_init(NULL))) {
23536+
myerror("mysql_client_init() failed");
23537+
exit(1);
23538+
}
23539+
cs = mysql_get_connect_stage(mysql_sync);
23540+
if (cs != CONNECT_STAGE_NOT_STARTED) {
23541+
fprintf(stdout,
23542+
"\n Sync:Expected connect_stage to be "
23543+
"CONNECT_STAGE_NOT_STARTED for just initialized connection"
23544+
"but its %d",
23545+
cs);
23546+
exit(1);
23547+
}
23548+
23549+
if (!opt_silent)
23550+
fprintf(stdout, "\n Starting blocking connect. Starting stage %d", cs);
23551+
if (!(mysql_real_connect(mysql_sync, opt_host, opt_user, opt_password,
23552+
current_db, opt_port, opt_unix_socket, 0))) {
23553+
myerror("connection failed");
23554+
exit(1);
23555+
}
23556+
cs = mysql_get_connect_stage(mysql_sync);
23557+
if (cs != CONNECT_STAGE_COMPLETE) {
23558+
fprintf(stdout,
23559+
"\n Sync:Expected connect_stage as CONNECT_STAGE_COMPLETE, its %d",
23560+
cs);
23561+
exit(1);
23562+
}
23563+
if (!opt_silent)
23564+
fprintf(stdout, "\n Blocking connect successful. Final stage %d", cs);
23565+
23566+
mysql_close(mysql_sync);
23567+
}
23568+
2346923569
static struct my_tests_st my_tests[] = {
2347023570
{"test_bug5194", test_bug5194},
2347123571
{"disable_query_logs", disable_query_logs},
@@ -23782,6 +23882,7 @@ static struct my_tests_st my_tests[] = {
2378223882
{"test_wl13128", test_wl13128},
2378323883
{"test_bug25584097", test_bug25584097},
2378423884
{"test_34556764", test_34556764},
23885+
{"test_get_connect_stage", test_get_connect_stage},
2378523886
{nullptr, nullptr}};
2378623887

2378723888
static struct my_tests_st *get_my_tests() { return my_tests; }

0 commit comments

Comments
 (0)