Commit 36548b10 authored by konstantin@mysql.com's avatar konstantin@mysql.com

A fix and test case for Bug#5315 "mysql_change_user() doesn't free

prepared statements."
parent e7a70ed1
...@@ -47,6 +47,7 @@ my_bool _hash_init(HASH *hash, CHARSET_INFO *charset, ...@@ -47,6 +47,7 @@ my_bool _hash_init(HASH *hash, CHARSET_INFO *charset,
uint key_length, hash_get_key get_key, uint key_length, hash_get_key get_key,
void (*free_element)(void*), uint flags CALLER_INFO_PROTO); void (*free_element)(void*), uint flags CALLER_INFO_PROTO);
void hash_free(HASH *tree); void hash_free(HASH *tree);
void hash_reset(HASH *hash);
byte *hash_element(HASH *hash,uint idx); byte *hash_element(HASH *hash,uint idx);
gptr hash_search(HASH *info,const byte *key,uint length); gptr hash_search(HASH *info,const byte *key,uint length);
gptr hash_next(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); ...@@ -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); void hash_replace(HASH *hash, uint idx, byte *new_row);
my_bool hash_check(HASH *hash); /* Only in debug library */ 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) #define hash_inited(H) ((H)->array.buffer != 0)
#ifdef __cplusplus #ifdef __cplusplus
......
...@@ -42,6 +42,7 @@ my_bool handle_local_infile(MYSQL *mysql, const char *net_filename); ...@@ -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, void mysql_read_default_options(struct st_mysql_options *options,
const char *filename,const char *group); const char *filename,const char *group);
void mysql_detach_stmt_list(LIST **stmt_list);
MYSQL * STDCALL MYSQL * STDCALL
cli_mysql_real_connect(MYSQL *mysql,const char *host, const char *user, cli_mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
const char *passwd, const char *db, const char *passwd, const char *db,
......
...@@ -662,6 +662,7 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user, ...@@ -662,6 +662,7 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user,
const char *passwd, const char *db) const char *passwd, const char *db)
{ {
char buff[512],*end=buff; char buff[512],*end=buff;
int rc;
DBUG_ENTER("mysql_change_user"); DBUG_ENTER("mysql_change_user");
if (!user) if (!user)
...@@ -695,8 +696,15 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user, ...@@ -695,8 +696,15 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user,
/* Write authentication package */ /* Write authentication package */
simple_command(mysql,COM_CHANGE_USER, buff,(ulong) (end-buff),1); simple_command(mysql,COM_CHANGE_USER, buff,(ulong) (end-buff),1);
if ((*mysql->methods->read_change_user_result)(mysql, buff, passwd)) rc= (*mysql->methods->read_change_user_result)(mysql, buff, passwd);
DBUG_RETURN(1);
/*
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 */ /* Free old connect information */
my_free(mysql->user,MYF(MY_ALLOW_ZERO_PTR)); my_free(mysql->user,MYF(MY_ALLOW_ZERO_PTR));
my_free(mysql->passwd,MYF(MY_ALLOW_ZERO_PTR)); my_free(mysql->passwd,MYF(MY_ALLOW_ZERO_PTR));
...@@ -706,7 +714,8 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user, ...@@ -706,7 +714,8 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user,
mysql->user= my_strdup(user,MYF(MY_WME)); mysql->user= my_strdup(user,MYF(MY_WME));
mysql->passwd=my_strdup(passwd,MYF(MY_WME)); mysql->passwd=my_strdup(passwd,MYF(MY_WME));
mysql->db= db ? my_strdup(db,MYF(MY_WME)) : 0; mysql->db= db ? my_strdup(db,MYF(MY_WME)) : 0;
DBUG_RETURN(0); }
DBUG_RETURN(rc);
} }
#if defined(HAVE_GETPWUID) && defined(NO_GETPWUID_DECL) #if defined(HAVE_GETPWUID) && defined(NO_GETPWUID_DECL)
......
...@@ -88,6 +88,32 @@ void hash_free(HASH *hash) ...@@ -88,6 +88,32 @@ void hash_free(HASH *hash)
DBUG_VOID_RETURN; 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 */ /* some helper functions */
/* /*
......
...@@ -2239,6 +2239,32 @@ static void mysql_close_free(MYSQL *mysql) ...@@ -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) void STDCALL mysql_close(MYSQL *mysql)
{ {
DBUG_ENTER("mysql_close"); DBUG_ENTER("mysql_close");
...@@ -2255,20 +2281,7 @@ void STDCALL mysql_close(MYSQL *mysql) ...@@ -2255,20 +2281,7 @@ void STDCALL mysql_close(MYSQL *mysql)
} }
mysql_close_free_options(mysql); mysql_close_free_options(mysql);
mysql_close_free(mysql); mysql_close_free(mysql);
#ifdef MYSQL_CLIENT mysql_detach_stmt_list(&mysql->stmts);
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*/
#ifndef TO_BE_DELETED #ifndef TO_BE_DELETED
/* free/close slave list */ /* free/close slave list */
if (mysql->rpl_pivot) if (mysql->rpl_pivot)
......
...@@ -328,6 +328,7 @@ void THD::change_user(void) ...@@ -328,6 +328,7 @@ void THD::change_user(void)
cleanup(); cleanup();
cleanup_done= 0; cleanup_done= 0;
init(); init();
stmt_map.reset();
hash_init(&user_vars, &my_charset_bin, USER_VARS_HASH_SIZE, 0, 0, hash_init(&user_vars, &my_charset_bin, USER_VARS_HASH_SIZE, 0, 0,
(hash_get_key) get_var_key, (hash_get_key) get_var_key,
(hash_free_key) free_user_var, 0); (hash_free_key) free_user_var, 0);
......
...@@ -608,11 +608,18 @@ public: ...@@ -608,11 +608,18 @@ public:
} }
hash_delete(&st_hash, (byte *) statement); 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() ~Statement_map()
{ {
hash_free(&st_hash);
hash_free(&names_hash); hash_free(&names_hash);
hash_free(&st_hash);
} }
private: private:
HASH st_hash; HASH st_hash;
......
...@@ -10391,6 +10391,34 @@ static void test_bug5194() ...@@ -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 Read and parse arguments and MySQL options from my.cnf
*/ */
...@@ -10694,6 +10722,8 @@ int main(int argc, char **argv) ...@@ -10694,6 +10722,8 @@ int main(int argc, char **argv)
test_bug5399(); /* check that statement id uniquely identifies test_bug5399(); /* check that statement id uniquely identifies
statement */ statement */
test_bug5194(); /* bulk inserts in prepared mode */ 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 XXX: PLEASE RUN THIS PROGRAM UNDER VALGRIND AND VERIFY THAT YOUR TEST
DOESN'T CONTAIN WARNINGS/ERRORS BEFORE YOU PUSH. DOESN'T CONTAIN WARNINGS/ERRORS BEFORE YOU PUSH.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment