diff options
-rw-r--r-- | include/hash.h | 2 | ||||
-rw-r--r-- | libmysql/client_settings.h | 1 | ||||
-rw-r--r-- | libmysql/libmysql.c | 33 | ||||
-rw-r--r-- | mysys/hash.c | 26 | ||||
-rw-r--r-- | sql-common/client.c | 41 | ||||
-rw-r--r-- | sql/sql_class.cc | 1 | ||||
-rw-r--r-- | sql/sql_class.h | 13 | ||||
-rw-r--r-- | tests/client_test.c | 30 |
8 files changed, 117 insertions, 30 deletions
diff --git a/include/hash.h b/include/hash.h index c7cc118b7bd..6e6db27cd40 100644 --- a/include/hash.h +++ b/include/hash.h @@ -47,6 +47,7 @@ my_bool _hash_init(HASH *hash, CHARSET_INFO *charset, uint key_length, hash_get_key get_key, void (*free_element)(void*), uint flags CALLER_INFO_PROTO); void hash_free(HASH *tree); +void hash_reset(HASH *hash); byte *hash_element(HASH *hash,uint idx); gptr hash_search(HASH *info,const byte *key,uint length); gptr hash_next(HASH *info,const byte *key,uint length); @@ -56,7 +57,6 @@ my_bool hash_update(HASH *hash,byte *record,byte *old_key,uint old_key_length); void hash_replace(HASH *hash, uint idx, byte *new_row); my_bool hash_check(HASH *hash); /* Only in debug library */ -#define hash_clear(H) bzero((char*) (H),sizeof(*(H))) #define hash_inited(H) ((H)->array.buffer != 0) #ifdef __cplusplus diff --git a/libmysql/client_settings.h b/libmysql/client_settings.h index 5857c0c84d6..552307733ea 100644 --- a/libmysql/client_settings.h +++ b/libmysql/client_settings.h @@ -42,6 +42,7 @@ my_bool handle_local_infile(MYSQL *mysql, const char *net_filename); void mysql_read_default_options(struct st_mysql_options *options, const char *filename,const char *group); +void mysql_detach_stmt_list(LIST **stmt_list); MYSQL * STDCALL cli_mysql_real_connect(MYSQL *mysql,const char *host, const char *user, const char *passwd, const char *db, diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index 6601d3e4ad7..2ad6771cc69 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -662,6 +662,7 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user, const char *passwd, const char *db) { char buff[512],*end=buff; + int rc; DBUG_ENTER("mysql_change_user"); if (!user) @@ -695,18 +696,26 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user, /* Write authentication package */ simple_command(mysql,COM_CHANGE_USER, buff,(ulong) (end-buff),1); - if ((*mysql->methods->read_change_user_result)(mysql, buff, passwd)) - DBUG_RETURN(1); - /* Free old connect information */ - my_free(mysql->user,MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->passwd,MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->db,MYF(MY_ALLOW_ZERO_PTR)); - - /* alloc new connect information */ - mysql->user= my_strdup(user,MYF(MY_WME)); - mysql->passwd=my_strdup(passwd,MYF(MY_WME)); - mysql->db= db ? my_strdup(db,MYF(MY_WME)) : 0; - DBUG_RETURN(0); + rc= (*mysql->methods->read_change_user_result)(mysql, buff, passwd); + + /* + The server will close all statements no matter was the attempt + to change user successful or not. + */ + mysql_detach_stmt_list(&mysql->stmts); + if (rc == 0) + { + /* Free old connect information */ + my_free(mysql->user,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->passwd,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->db,MYF(MY_ALLOW_ZERO_PTR)); + + /* alloc new connect information */ + mysql->user= my_strdup(user,MYF(MY_WME)); + mysql->passwd=my_strdup(passwd,MYF(MY_WME)); + mysql->db= db ? my_strdup(db,MYF(MY_WME)) : 0; + } + DBUG_RETURN(rc); } #if defined(HAVE_GETPWUID) && defined(NO_GETPWUID_DECL) diff --git a/mysys/hash.c b/mysys/hash.c index ce25ae89b63..6f2788ddce7 100644 --- a/mysys/hash.c +++ b/mysys/hash.c @@ -88,6 +88,32 @@ void hash_free(HASH *hash) DBUG_VOID_RETURN; } + +/* + Delete all elements from the hash (the hash itself is to be reused). + + SYNOPSIS + hash_reset() + hash the hash to delete elements of +*/ + +void hash_reset(HASH *hash) +{ + DBUG_ENTER("hash_reset"); + if (hash->free) + { + HASH_LINK *link= dynamic_element(&hash->array, 0, HASH_LINK*); + HASH_LINK *end= link + hash->records; + for (; link < end; ++link) + (*hash->free)(link->data); + } + reset_dynamic(&hash->array); + hash->records= 0; + hash->blength= 1; + hash->current_record= NO_RECORD; + DBUG_VOID_RETURN; +} + /* some helper functions */ /* diff --git a/sql-common/client.c b/sql-common/client.c index 930388d2459..43e116eb0b6 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -2239,6 +2239,32 @@ static void mysql_close_free(MYSQL *mysql) } +/* + Clear connection pointer of every statement: this is necessary + to give error on attempt to use a prepared statement of closed + connection. + + SYNOPSYS + mysql_detach_stmt_list() + stmt_list pointer to mysql->stmts +*/ + +void mysql_detach_stmt_list(LIST **stmt_list) +{ +#ifdef MYSQL_CLIENT + /* Reset connection handle in all prepared statements. */ + LIST *element= *stmt_list; + for (; element; element= element->next) + { + MYSQL_STMT *stmt= (MYSQL_STMT *) element->data; + stmt->mysql= 0; + /* No need to call list_delete for statement here */ + } + *stmt_list= 0; +#endif /* MYSQL_CLIENT */ +} + + void STDCALL mysql_close(MYSQL *mysql) { DBUG_ENTER("mysql_close"); @@ -2255,20 +2281,7 @@ void STDCALL mysql_close(MYSQL *mysql) } mysql_close_free_options(mysql); mysql_close_free(mysql); -#ifdef MYSQL_CLIENT - if (mysql->stmts) - { - /* Reset connection handle in all prepared statements. */ - LIST *element; - for (element= mysql->stmts; element; element= element->next) - { - MYSQL_STMT *stmt= (MYSQL_STMT *) element->data; - stmt->mysql= 0; - /* No need to call list_delete for statement here */ - } - mysql->stmts= 0; - } -#endif /*MYSQL_CLIENT*/ + mysql_detach_stmt_list(&mysql->stmts); #ifndef TO_BE_DELETED /* free/close slave list */ if (mysql->rpl_pivot) diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 16c0c206df3..bc8820a3a44 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -328,6 +328,7 @@ void THD::change_user(void) cleanup(); cleanup_done= 0; init(); + stmt_map.reset(); hash_init(&user_vars, &my_charset_bin, USER_VARS_HASH_SIZE, 0, 0, (hash_get_key) get_var_key, (hash_free_key) free_user_var, 0); diff --git a/sql/sql_class.h b/sql/sql_class.h index 387bba43cad..09b44568f90 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -566,7 +566,7 @@ public: assignment in Statement::Statement) Non-empty statement names are unique too: attempt to insert a new statement with duplicate name causes older statement to be deleted - + Statements are auto-deleted when they are removed from the map and when the map is deleted. */ @@ -575,7 +575,7 @@ class Statement_map { public: Statement_map(); - + int insert(Statement *statement); Statement *find_by_name(LEX_STRING *name) @@ -608,11 +608,18 @@ public: } hash_delete(&st_hash, (byte *) statement); } + /* Erase all statements (calls Statement destructor) */ + void reset() + { + hash_reset(&names_hash); + hash_reset(&st_hash); + last_found_statement= 0; + } ~Statement_map() { - hash_free(&st_hash); hash_free(&names_hash); + hash_free(&st_hash); } private: HASH st_hash; diff --git a/tests/client_test.c b/tests/client_test.c index 0a4d635984d..accefb668a8 100644 --- a/tests/client_test.c +++ b/tests/client_test.c @@ -10391,6 +10391,34 @@ static void test_bug5194() } +static void test_bug5315() +{ + MYSQL_STMT *stmt; + const char *stmt_text; + int rc; + + myheader("test_bug5315"); + + stmt_text= "SELECT 1"; + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text)); + DBUG_ASSERT(rc == 0); + mysql_change_user(mysql, opt_user, opt_password, current_db); + rc= mysql_stmt_execute(stmt); + DBUG_ASSERT(rc != 0); + if (rc) + printf("Got error (as expected):\n%s", mysql_stmt_error(stmt)); + /* check that connection is OK */ + mysql_stmt_close(stmt); + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text)); + DBUG_ASSERT(rc == 0); + rc= mysql_stmt_execute(stmt); + DBUG_ASSERT(rc == 0); + mysql_stmt_close(stmt); +} + + /* Read and parse arguments and MySQL options from my.cnf */ @@ -10694,6 +10722,8 @@ int main(int argc, char **argv) test_bug5399(); /* check that statement id uniquely identifies statement */ test_bug5194(); /* bulk inserts in prepared mode */ + test_bug5315(); /* check that mysql_change_user closes all + prepared statements */ /* XXX: PLEASE RUN THIS PROGRAM UNDER VALGRIND AND VERIFY THAT YOUR TEST DOESN'T CONTAIN WARNINGS/ERRORS BEFORE YOU PUSH. |