diff --git a/libmariadb/mariadb_lib.c b/libmariadb/mariadb_lib.c index 6b66ddcb..c2a110cf 100644 --- a/libmariadb/mariadb_lib.c +++ b/libmariadb/mariadb_lib.c @@ -1485,6 +1485,44 @@ mysql_real_connect(MYSQL *mysql, const char *host, const char *user, #endif } +struct st_host { + char *host; + int port; +}; + +static void ma_get_host_list(char *host_list, struct st_host *host_info, int default_port) +{ + char *token, *start, *save; + int host_nr= 0; + + start= host_list; + while ((token= strtok_r(start, ",", &save))) + { + char *p; + + /* ipv6 hostname */ + if ((p= strchr(token, ']'))) + { + host_info[host_nr].host= token + 1; + *p++= 0; + token= p; + } + else + host_info[host_nr].host= token; + /* check if port was specified */ + if ((p= strchr(token, ':'))) + { + *p++= 0; + host_info[host_nr].port= atoi(p); + } + else + host_info[host_nr].port= default_port; + host_nr++; + start= NULL; + } + return; +} + MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user, const char *passwd, const char *db, uint port, const char *unix_socket, unsigned long client_flag) @@ -1498,6 +1536,10 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user, const char *scramble_plugin; uint pkt_length, scramble_len, pkt_scramble_len= 0; NET *net= &mysql->net; + my_bool is_multi= 0; + char *host_copy= NULL; + struct st_host *host_list= NULL; + int connect_attempts= 0; if (!mysql->methods) mysql->methods= &MARIADB_DEFAULT_METHODS; @@ -1520,6 +1562,54 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user, mysql->options.my_cnf_file=mysql->options.my_cnf_group=0; } + if (!port) + port=mysql->options.port; + if (!host || !host[0]) + host= mysql->options.host; + + /* check if we have multi hosts */ + if (host && host[0] && strchr(host, ',')) + { + char *p; + size_t host_count= 1; + + is_multi= 1; + + /* don't change original entry, so let's make a copy */ + if (!(host_copy= strdup(host))) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + goto error; + } + + p= host_copy; + + while ((p = strchr(p, ','))) + { + host_count++; + p++; + } + + if (!(host_list= (struct st_host *)calloc(host_count + 1, sizeof(struct st_host)))) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + goto error; + } + + ma_get_host_list(host_copy, host_list, port); + } + +restart: + /* check if we reached end of list */ + if (is_multi) + { + if (!host_list[connect_attempts].host) + goto error; + + host= host_list[connect_attempts].host; + port= host_list[connect_attempts].port; + } + ma_set_connect_attrs(mysql, host); #ifndef WIN32 @@ -1545,8 +1635,6 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user, } if (!db || !db[0]) db=mysql->options.db; - if (!port) - port=mysql->options.port; if (!unix_socket) unix_socket=mysql->options.unix_socket; @@ -1609,6 +1697,11 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user, if (ma_pvio_connect(pvio, &cinfo) != 0) { ma_pvio_close(pvio); + if (is_multi) + { + connect_attempts++; + goto restart; + } goto error; } @@ -1846,10 +1939,16 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user, /* connection established, apply timeouts */ ma_pvio_set_timeout(mysql->net.pvio, PVIO_READ_TIMEOUT, mysql->options.read_timeout); ma_pvio_set_timeout(mysql->net.pvio, PVIO_WRITE_TIMEOUT, mysql->options.write_timeout); + + free(host_list); + free(host_copy); + return(mysql); error: /* Free allocated memory */ + free(host_list); + free(host_copy); end_server(mysql); /* only free the allocated memory, user needs to call mysql_close */ mysql_close_memory(mysql); diff --git a/unittest/libmariadb/connection.c b/unittest/libmariadb/connection.c index bee6bae0..6647aa8a 100644 --- a/unittest/libmariadb/connection.c +++ b/unittest/libmariadb/connection.c @@ -1999,8 +1999,6 @@ static int test_conn_str(MYSQL *my __attribute__((unused))) strcat(conn_str, ";ssl_enforce=1"); } - diag("connection string: %s", conn_str); - if (mariadb_connect(mysql, conn_str)) { diag("host: %s", mysql->host); @@ -2025,7 +2023,9 @@ static int test_conn_str_1(MYSQL *my __attribute__((unused))) return FAIL; fprintf(fp, "[client]\n"); - fprintf(fp, "connection=host=%s;user=%s;password=%s;port=%d;ssl_enforce=1\n", hostname, username, password, port); + fprintf(fp, "connection=host=%s;user=%s;password=%s;port=%d;ssl_enforce=1\n", + hostname ? hostname : "localhost", username ? username : "", + password ? password : "", port); fclose(fp); @@ -2052,7 +2052,95 @@ static int test_conn_str_1(MYSQL *my __attribute__((unused))) return OK; } +static int test_conc365(MYSQL *my __attribute__((unused))) +{ + int rc= OK; + MYSQL *mysql= mysql_init(NULL); + char tmp[1024]; + + snprintf(tmp, sizeof(tmp) - 1, + "host=127.0.0.1:3300,%s;user=%s;password=%s;port=%d", + hostname ? hostname : "localhost", username ? username : "", password ? password : "", + port); + + if (IS_SKYSQL(hostname)) + strcat(tmp, ";ssl_enforce=1"); + + if (!mariadb_connect(mysql, tmp)) + rc= FAIL; + + mysql_close(mysql); + + if (rc) + return rc; + + mysql= mysql_init(NULL); + snprintf(tmp, sizeof(tmp) -1, "127.0.0.1:3300,%s:%d", hostname ? hostname : "localhost", port); + if (!my_test_connect(mysql, tmp, username, + password, schema, port, socketname, 0)) + { + diag("Error: %s", mysql_error(mysql)); + rc= FAIL; + } + + mysql_close(mysql); + + if (rc) + return rc; + + mysql= mysql_init(NULL); + mysql_options(mysql, MARIADB_OPT_HOST, tmp); + if (!my_test_connect(mysql, NULL, username, + password, schema, port, socketname, 0)) + { + diag("Error: %s", mysql_error(mysql)); + rc= FAIL; + } + + mysql_close(mysql); + return rc; +} + +static int test_conc365_reconnect(MYSQL *my) +{ + int rc= OK; + MYSQL *mysql= mysql_init(NULL); + char tmp[1024]; + my_bool reconnect= 1; + + mysql_options(mysql, MYSQL_OPT_RECONNECT, &reconnect); + + snprintf(tmp, sizeof(tmp) - 1, + "host=127.0.0.1:3300,%s;user=%s;password=%s;port=%d", + hostname ? hostname : "localhost", username ? username : "", password ? password : "", + port); + + if (IS_SKYSQL(hostname)) + strcat(tmp, ";ssl_enforce=1"); + + if (!my_test_connect(mysql, tmp, username, + password, schema, port, socketname, CLIENT_REMEMBER_OPTIONS)) + { + diag("Error: %s", mysql_error(mysql)); + rc= FAIL; + } + + sprintf(tmp, "KILL %ld", mysql_thread_id(mysql)); + + rc= mysql_query(my, tmp); + check_mysql_rc(rc, my); + + sleep(3); + rc= mysql_ping(mysql); + check_mysql_rc(rc, my); + + mysql_close(mysql); + return rc; +} + struct my_tests_st my_tests[] = { + {"test_conc365", test_conc365, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_conc365_reconnect", test_conc365_reconnect, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_conn_str", test_conn_str, TEST_CONNECTION_NONE, 0, NULL, NULL}, {"test_conn_str_1", test_conn_str_1, TEST_CONNECTION_NONE, 0, NULL, NULL}, {"test_conc544", test_conc544, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},