From 87e861c360b6abaaff935ce177ae2a94df85e176 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Thu, 29 Dec 2016 19:10:35 +0100 Subject: [PATCH] client side implemetation for MDEV-10340: int STDCALL mysql_reset_connection(MYSQL *mysql) --- include/mariadb_com.h | 4 +- include/mysql.h | 4 ++ include/mysql/client_plugin.h | 1 + libmariadb/CMakeLists.txt | 3 ++ libmariadb/mariadb_lib.c | 45 +++++++++++++++++- plugins/connection/aurora.c | 3 +- plugins/connection/replication.c | 3 +- unittest/libmariadb/connection.c | 50 ++++++++++++++++++++ unittest/libmariadb/misc.c | 78 +++++++++++++++++++++++++++++++- unittest/libmariadb/ps_bugs.c | 29 ++++++++++++ 10 files changed, 215 insertions(+), 5 deletions(-) diff --git a/include/mariadb_com.h b/include/mariadb_com.h index 770179f9..cdf7db84 100644 --- a/include/mariadb_com.h +++ b/include/mariadb_com.h @@ -90,7 +90,9 @@ enum enum_server_command COM_STMT_RESET = 26, COM_SET_OPTION = 27, COM_STMT_FETCH = 28, - COM_DAEMON, + COM_DAEMON= 29, + COM_UNSUPPORTED= 30, + COM_RESET_CONNECTION = 31, COM_MULTI = 254, COM_END }; diff --git a/include/mysql.h b/include/mysql.h index 7c4fd4ba..6374552b 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -673,6 +673,8 @@ int STDCALL mysql_read_query_result_start(my_bool *ret, MYSQL *mysql); int STDCALL mysql_read_query_result_cont(my_bool *ret, MYSQL *mysql, int status); +int STDCALL mysql_reset_connection_start(int *ret, MYSQL *mysql); +int STDCALL mysql_reset_connection_cont(int *ret, MYSQL *mysql, int status); int STDCALL mysql_session_track_get_next(MYSQL *mysql, enum enum_session_state_type type, const char **data, size_t *length); int STDCALL mysql_session_track_get_first(MYSQL *mysql, enum enum_session_state_type type, const char **data, size_t *length); int STDCALL mysql_stmt_prepare_start(int *ret, MYSQL_STMT *stmt,const char *query, size_t length); @@ -696,6 +698,7 @@ int STDCALL mysql_stmt_send_long_data_start(my_bool *ret, MYSQL_STMT *stmt, size_t len); int STDCALL mysql_stmt_send_long_data_cont(my_bool *ret, MYSQL_STMT *stmt, int status); +int STDCALL mysql_reset_connection(MYSQL *mysql); /* API function calls (used by dynmic plugins) */ struct st_mariadb_api { @@ -813,6 +816,7 @@ struct st_mariadb_api { int (STDCALL *mysql_stmt_next_result)(MYSQL_STMT *stmt); my_bool (STDCALL *mysql_stmt_more_results)(MYSQL_STMT *stmt); int (STDCALL *mariadb_stmt_execute_direct)(MYSQL_STMT *stmt, const char *stmtstr, size_t length); + int (STDCALL *mysql_reset_connection)(MYSQL *mysql); }; /* these methods can be overwritten by db plugins */ diff --git a/include/mysql/client_plugin.h b/include/mysql/client_plugin.h index dd9fc684..eb2cea0a 100644 --- a/include/mysql/client_plugin.h +++ b/include/mysql/client_plugin.h @@ -100,6 +100,7 @@ typedef struct st_ma_connection_plugin int (*set_connection)(MYSQL *mysql,enum enum_server_command command, const char *arg, size_t length, my_bool skipp_check, void *opt_arg); my_bool (*reconnect)(MYSQL *mysql); + int (*reset)(MYSQL *mysql); } MARIADB_CONNECTION_PLUGIN; #define MARIADB_DB_DRIVER(a) ((a)->ext_db) diff --git a/libmariadb/CMakeLists.txt b/libmariadb/CMakeLists.txt index c45a47b9..8422dbd4 100644 --- a/libmariadb/CMakeLists.txt +++ b/libmariadb/CMakeLists.txt @@ -90,6 +90,7 @@ SET(MARIADB_LIB_SYMBOLS mysql_real_escape_string mysql_real_query mysql_refresh + mysql_reset_connection mysql_rollback mysql_row_seek mysql_row_tell @@ -201,6 +202,8 @@ SET(MARIADB_NONBLOCK_SYMBOLS mysql_next_result_start mysql_ping_cont mysql_ping_start + mysql_reset_connection_start + mysql_reset_connection_cont mysql_query_cont mysql_query_start mysql_read_query_result_cont diff --git a/libmariadb/mariadb_lib.c b/libmariadb/mariadb_lib.c index 2d7a4ed2..15d6c6c8 100644 --- a/libmariadb/mariadb_lib.c +++ b/libmariadb/mariadb_lib.c @@ -2258,6 +2258,10 @@ mysql_fetch_row(MYSQL_RES *res) { if (!res) return 0; + if (res->handle) + if (res->handle->status != MYSQL_STATUS_USE_RESULT && + res->handle->status != MYSQL_STATUS_GET_RESULT) + return 0; if (!res->data) { /* Unbufferred fetch */ if (!res->eof) @@ -3873,6 +3877,44 @@ mysql_get_parameters(void) return &mariadb_internal_parameters; } +int STDCALL mysql_reset_connection(MYSQL *mysql) +{ + int rc; + + /* check if connection handler is active */ + if (IS_CONNHDLR_ACTIVE(mysql)) + { + if (mysql->extension->conn_hdlr->plugin && mysql->extension->conn_hdlr->plugin->reset) + return(mysql->extension->conn_hdlr->plugin->reset(mysql)); + } + + /* skip result sets */ + if (mysql->status == MYSQL_STATUS_USE_RESULT || + mysql->status == MYSQL_STATUS_GET_RESULT || + mysql->status & SERVER_MORE_RESULTS_EXIST) + { + mthd_my_skip_result(mysql); + mysql->status= MYSQL_STATUS_READY; + } + + rc= ma_simple_command(mysql, COM_RESET_CONNECTION, 0, 0, 0, 0); + if (rc && mysql->options.reconnect) + { + /* There is no big sense in resetting but we need reconnect */ + rc= ma_simple_command(mysql, COM_RESET_CONNECTION,0,0,0,0); + } + if (rc) + return 1; + + /* reset the connection in all active statements */ + ma_invalidate_stmts(mysql, "mysql_reset_connection()"); + free_old_query(mysql); + mysql->status= MYSQL_STATUS_READY; + mysql->affected_rows= ~(my_ulonglong)0; + mysql->insert_id= 0; + return 0; +} + #undef STDCALL /* API functions for usage in dynamic plugins */ struct st_mariadb_api MARIADB_API= @@ -3990,7 +4032,8 @@ struct st_mariadb_api MARIADB_API= mysql_stmt_field_count, mysql_stmt_next_result, mysql_stmt_more_results, - mariadb_stmt_execute_direct + mariadb_stmt_execute_direct, + mysql_reset_connection }; /* diff --git a/plugins/connection/aurora.c b/plugins/connection/aurora.c index 58301e5d..342acf41 100644 --- a/plugins/connection/aurora.c +++ b/plugins/connection/aurora.c @@ -76,7 +76,8 @@ MARIADB_CONNECTION_PLUGIN _mysql_client_plugin_declaration_ = aurora_close, NULL, aurora_command, - aurora_reconnect + aurora_reconnect, + NULL }; struct st_mariadb_api *mariadb_api= NULL; diff --git a/plugins/connection/replication.c b/plugins/connection/replication.c index 97b36a0a..101edaa8 100644 --- a/plugins/connection/replication.c +++ b/plugins/connection/replication.c @@ -69,7 +69,8 @@ MARIADB_CONNECTION_PLUGIN _mysql_client_plugin_declaration_ = repl_close, repl_set_options, repl_command, - NULL + NULL, + NULL }; typedef struct st_conn_repl { diff --git a/unittest/libmariadb/connection.c b/unittest/libmariadb/connection.c index 44427821..b51702ab 100644 --- a/unittest/libmariadb/connection.c +++ b/unittest/libmariadb/connection.c @@ -1006,7 +1006,57 @@ static int test_unix_socket_close(MYSQL *unused __attribute__((unused))) return OK; } +static int test_reset(MYSQL *mysql) +{ + int rc; + MYSQL_RES *res; + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE t1 (a int)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "INSERT INTO t1 VALUES (1),(2),(3)"); + check_mysql_rc(rc, mysql); + + FAIL_IF(mysql_affected_rows(mysql) != 3, "Expected 3 rows"); + + rc= mysql_reset_connection(mysql); + check_mysql_rc(rc, mysql); + + FAIL_IF(mysql_affected_rows(mysql) != ~(unsigned long)0, "Expected 0 rows"); + + rc= mysql_query(mysql, "SELECT a FROM t1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "SELECT 1 FROM DUAL"); + FAIL_IF(!rc, "Error expected"); + + rc= mysql_reset_connection(mysql); + check_mysql_rc(rc, mysql); + + res= mysql_store_result(mysql); + FAIL_IF(res, "expected no result"); + + rc= mysql_query(mysql, "SELECT a FROM t1"); + check_mysql_rc(rc, mysql); + + res= mysql_use_result(mysql); + FAIL_IF(!res, "expected result"); + + rc= mysql_reset_connection(mysql); + check_mysql_rc(rc, mysql); + + FAIL_IF(mysql_fetch_row(res), "expected error"); + + mysql_free_result(res); + + return OK; +} + struct my_tests_st my_tests[] = { + {"test_reset", test_reset, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_unix_socket_close", test_unix_socket_close, TEST_CONNECTION_NONE, 0, NULL, NULL}, {"test_sess_track_db", test_sess_track_db, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_get_options", test_get_options, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, diff --git a/unittest/libmariadb/misc.c b/unittest/libmariadb/misc.c index d991090e..bada8e9b 100644 --- a/unittest/libmariadb/misc.c +++ b/unittest/libmariadb/misc.c @@ -1180,8 +1180,84 @@ static int test_server_status(MYSQL *mysql) return OK; } +static int test_wl6797(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + int rc; + const char *stmt_text; + my_ulonglong res; + + if (mysql_get_server_version(mysql) < 50703 || + (mariadb_connection(mysql) && mysql_get_server_version(mysql) < 100203)) + { + diag("Skipping test_wl6797: " + "tested feature does not exist in versions before MySQL 5.7.3 and MariaDB 10.2\n"); + return OK; + } + /* clean up the session */ + rc= mysql_reset_connection(mysql); + FAIL_UNLESS(rc == 0, ""); + + /* do prepare of a query */ + mysql_query(mysql, "use test"); + mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + mysql_query(mysql, "CREATE TABLE t1 (a int)"); + + stmt= mysql_stmt_init(mysql); + stmt_text= "INSERT INTO t1 VALUES (1), (2)"; + + rc= mysql_stmt_prepare(stmt, stmt_text, (ulong)strlen(stmt_text)); + check_mysql_rc(rc, mysql); + + /* Execute the insert statement */ + rc= mysql_stmt_execute(stmt); + check_mysql_rc(rc, mysql); + + /* + clean the session this should remove the prepare statement + from the cache. + */ + rc= mysql_reset_connection(mysql); + FAIL_UNLESS(rc == 0, ""); + + /* this below stmt should report error */ + rc= mysql_stmt_execute(stmt); + FAIL_IF(rc == 0, ""); + + /* + bug#17653288: MYSQL_RESET_CONNECTION DOES NOT RESET LAST_INSERT_ID + */ + + mysql_query(mysql, "DROP TABLE IF EXISTS t2"); + rc= mysql_query(mysql, "CREATE TABLE t2 (a int NOT NULL PRIMARY KEY"\ + " auto_increment)"); + check_mysql_rc(rc, mysql); + rc= mysql_query(mysql, "INSERT INTO t2 VALUES (null)"); + check_mysql_rc(rc, mysql); + res= mysql_insert_id(mysql); + FAIL_UNLESS(res == 1, ""); + rc= mysql_reset_connection(mysql); + FAIL_UNLESS(rc == 0, ""); + res= mysql_insert_id(mysql); + FAIL_UNLESS(res == 0, ""); + + rc= mysql_query(mysql, "INSERT INTO t2 VALUES (last_insert_id(100))"); + check_mysql_rc(rc, mysql); + res= mysql_insert_id(mysql); + FAIL_UNLESS(res == 100, ""); + rc= mysql_reset_connection(mysql); + FAIL_UNLESS(rc == 0, ""); + res= mysql_insert_id(mysql); + FAIL_UNLESS(res == 0, ""); + + mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + mysql_query(mysql, "DROP TABLE IF EXISTS t2"); + mysql_stmt_close(stmt); + return OK; +} + struct my_tests_st my_tests[] = { - + {"test_wl6797", test_wl6797, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_server_status", test_server_status, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_read_timeout", test_read_timeout, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_zerofill", test_zerofill, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, diff --git a/unittest/libmariadb/ps_bugs.c b/unittest/libmariadb/ps_bugs.c index 478e25b7..5dc06e6d 100644 --- a/unittest/libmariadb/ps_bugs.c +++ b/unittest/libmariadb/ps_bugs.c @@ -4501,7 +4501,36 @@ static int test_conc217(MYSQL *mysql) return OK; } +static int test_conc208(MYSQL *mysql) +{ + MYSQL_STMT *stmt= mysql_stmt_init(mysql); + int rc; + int data; + MYSQL_BIND bind; + + rc= mysql_stmt_prepare(stmt, "SELECT \"100\" UNION SELECT \"88\" UNION SELECT \"389789\"", -1); + check_stmt_rc(rc, stmt); + + memset(&bind, 0, sizeof(MYSQL_BIND)); + bind.buffer_type= MYSQL_TYPE_LONG; + bind.buffer= (void *)&data; + + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + + rc= mysql_stmt_bind_result(stmt, &bind); + + while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) + { + diag("data=%d", data); + FAIL_IF(data != 100 && data != 88 && data != 389789, "Wrong value"); + } + mysql_stmt_close(stmt); + return OK; +} + struct my_tests_st my_tests[] = { + {"test_conc208", test_conc208, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_conc217", test_conc217, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_conc205", test_conc205, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_conc198", test_conc198, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},