Patch for BUG#30472: libmysql doesn't reset charset,
insert_id after succ. mysql_change_user() call. See also WL 4066. This bug reveals two problems: - the problem on the client side which was described originally; - the problem in protocol / the server side: connection context on client and server should be like after mysql_real_connect() and be consistent. The server however just resets character set variables to the global defaults. The fix seems to be as follows: - extend the protocol so that the client be able to send character set information in COM_CHANGE_USER command; - change the server so that it understands client character set in the command; - change the client: - reset character set to the default value (which has been read from the configuration); - send character set in COM_CHANGE_USER command.
This commit is contained in:
parent
e452c06438
commit
8051b7568d
@ -82,3 +82,7 @@ enum options_client
|
||||
OPT_DEBUG_INFO, OPT_DEBUG_CHECK, OPT_COLUMN_TYPES, OPT_ERROR_LOG_FILE,
|
||||
OPT_WRITE_BINLOG, OPT_MAX_CLIENT_OPTION
|
||||
};
|
||||
|
||||
C_MODE_START
|
||||
extern int mysql_init_character_set(MYSQL *mysql);
|
||||
C_MODE_END
|
||||
|
@ -685,14 +685,25 @@ int cli_read_change_user_result(MYSQL *mysql, char *buff, const char *passwd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user,
|
||||
const char *passwd, const char *db)
|
||||
{
|
||||
char buff[512],*end=buff;
|
||||
int rc;
|
||||
CHARSET_INFO *saved_cs= mysql->charset;
|
||||
|
||||
DBUG_ENTER("mysql_change_user");
|
||||
|
||||
/* Get the connection-default character set. */
|
||||
|
||||
if (mysql_init_character_set(mysql))
|
||||
{
|
||||
mysql->charset= saved_cs;
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
|
||||
/* Use an empty string instead of NULL. */
|
||||
|
||||
if (!user)
|
||||
user="";
|
||||
if (!passwd)
|
||||
@ -721,6 +732,14 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user,
|
||||
/* Add database if needed */
|
||||
end= strmov(end, db ? db : "") + 1;
|
||||
|
||||
/* Add character set number. */
|
||||
|
||||
if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
|
||||
{
|
||||
*end= (uchar) mysql->charset->number;
|
||||
++end;
|
||||
}
|
||||
|
||||
/* Write authentication package */
|
||||
simple_command(mysql,COM_CHANGE_USER, (uchar*) buff, (ulong) (end-buff), 1);
|
||||
|
||||
@ -743,6 +762,11 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user,
|
||||
mysql->passwd=my_strdup(passwd,MYF(MY_WME));
|
||||
mysql->db= db ? my_strdup(db,MYF(MY_WME)) : 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
mysql->charset= saved_cs;
|
||||
}
|
||||
|
||||
DBUG_RETURN(rc);
|
||||
}
|
||||
|
||||
|
@ -8,8 +8,8 @@
|
||||
# server or run mysql-test-run --debug mysql_client_test and check
|
||||
# var/log/mysql_client_test.trace
|
||||
|
||||
--exec echo "$MYSQL_CLIENT_TEST" > $MYSQLTEST_VARDIR/log/mysql_client_test.log 2>&1
|
||||
--exec $MYSQL_CLIENT_TEST --getopt-ll-test=25600M >> $MYSQLTEST_VARDIR/log/mysql_client_test.log 2>&1
|
||||
--exec echo "$MYSQL_CLIENT_TEST" > $MYSQLTEST_VARDIR/log/mysql_client_test.out.log 2>&1
|
||||
--exec $MYSQL_CLIENT_TEST --getopt-ll-test=25600M >> $MYSQLTEST_VARDIR/log/mysql_client_test.out.log 2>&1
|
||||
|
||||
# End of 4.1 tests
|
||||
echo ok;
|
||||
|
@ -876,7 +876,13 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
|
||||
my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
|
||||
break;
|
||||
}
|
||||
db_length= packet_end - db - 1; /* do not count the trailing '\0' */
|
||||
db_length= strlen(db);
|
||||
|
||||
uint cs_number= 0;
|
||||
|
||||
if (db + db_length < packet_end)
|
||||
cs_number= (uchar) *(db + db_length + 1);
|
||||
|
||||
/* Convert database name to utf8 */
|
||||
db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1,
|
||||
system_charset_info, db, db_length,
|
||||
@ -921,6 +927,12 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
|
||||
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
|
||||
x_free((uchar*) save_db);
|
||||
x_free((uchar*) save_security_ctx.user);
|
||||
|
||||
if (cs_number)
|
||||
{
|
||||
thd_init_client_charset(thd, cs_number);
|
||||
thd->update_charset();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -119,6 +119,8 @@ static void client_disconnect(void);
|
||||
|
||||
#define DIE_UNLESS(expr) \
|
||||
((void) ((expr) ? 0 : (die(__FILE__, __LINE__, #expr), 0)))
|
||||
#define DIE_IF(expr) \
|
||||
((void) ((expr) ? (die(__FILE__, __LINE__, #expr), 0) : 0))
|
||||
#define DIE(expr) \
|
||||
die(__FILE__, __LINE__, #expr)
|
||||
|
||||
@ -177,8 +179,8 @@ if (stmt == 0) \
|
||||
DIE_UNLESS(stmt == 0);\
|
||||
}
|
||||
|
||||
#define mytest(x) if (!x) {myerror(NULL);DIE_UNLESS(FALSE);}
|
||||
#define mytest_r(x) if (x) {myerror(NULL);DIE_UNLESS(FALSE);}
|
||||
#define mytest(x) if (!(x)) {myerror(NULL);DIE_UNLESS(FALSE);}
|
||||
#define mytest_r(x) if ((x)) {myerror(NULL);DIE_UNLESS(FALSE);}
|
||||
|
||||
|
||||
/* A workaround for Sun Forte 5.6 on Solaris x86 */
|
||||
@ -16649,6 +16651,175 @@ static void test_bug29306()
|
||||
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
/*
|
||||
Bug#30472: libmysql doesn't reset charset, insert_id after succ.
|
||||
mysql_change_user() call row insertions.
|
||||
*/
|
||||
|
||||
static void bug30472_retrieve_charset_info(MYSQL *con,
|
||||
char *character_set_name,
|
||||
char *character_set_client,
|
||||
char *character_set_results,
|
||||
char *collation_connection)
|
||||
{
|
||||
MYSQL_RES *rs;
|
||||
MYSQL_ROW row;
|
||||
|
||||
/* Get the cached client character set name. */
|
||||
|
||||
strcpy(character_set_name, mysql_character_set_name(con));
|
||||
|
||||
/* Retrieve server character set information. */
|
||||
|
||||
DIE_IF(mysql_query(con, "SHOW VARIABLES LIKE 'character_set_client'"));
|
||||
DIE_UNLESS(rs= mysql_store_result(con));
|
||||
DIE_UNLESS(row= mysql_fetch_row(rs));
|
||||
strcpy(character_set_client, row[1]);
|
||||
mysql_free_result(rs);
|
||||
|
||||
DIE_IF(mysql_query(con, "SHOW VARIABLES LIKE 'character_set_results'"));
|
||||
DIE_UNLESS(rs= mysql_store_result(con));
|
||||
DIE_UNLESS(row= mysql_fetch_row(rs));
|
||||
strcpy(character_set_results, row[1]);
|
||||
mysql_free_result(rs);
|
||||
|
||||
DIE_IF(mysql_query(con, "SHOW VARIABLES LIKE 'collation_connection'"));
|
||||
DIE_UNLESS(rs= mysql_store_result(con));
|
||||
DIE_UNLESS(row= mysql_fetch_row(rs));
|
||||
strcpy(collation_connection, row[1]);
|
||||
mysql_free_result(rs);
|
||||
}
|
||||
|
||||
static void test_bug30472()
|
||||
{
|
||||
MYSQL con;
|
||||
|
||||
char character_set_name_1[MY_CS_NAME_SIZE];
|
||||
char character_set_client_1[MY_CS_NAME_SIZE];
|
||||
char character_set_results_1[MY_CS_NAME_SIZE];
|
||||
char collation_connnection_1[MY_CS_NAME_SIZE];
|
||||
|
||||
char character_set_name_2[MY_CS_NAME_SIZE];
|
||||
char character_set_client_2[MY_CS_NAME_SIZE];
|
||||
char character_set_results_2[MY_CS_NAME_SIZE];
|
||||
char collation_connnection_2[MY_CS_NAME_SIZE];
|
||||
|
||||
char character_set_name_3[MY_CS_NAME_SIZE];
|
||||
char character_set_client_3[MY_CS_NAME_SIZE];
|
||||
char character_set_results_3[MY_CS_NAME_SIZE];
|
||||
char collation_connnection_3[MY_CS_NAME_SIZE];
|
||||
|
||||
char character_set_name_4[MY_CS_NAME_SIZE];
|
||||
char character_set_client_4[MY_CS_NAME_SIZE];
|
||||
char character_set_results_4[MY_CS_NAME_SIZE];
|
||||
char collation_connnection_4[MY_CS_NAME_SIZE];
|
||||
|
||||
/* Create a new connection. */
|
||||
|
||||
DIE_UNLESS(mysql_init(&con));
|
||||
|
||||
DIE_UNLESS(mysql_real_connect(&con,
|
||||
opt_host,
|
||||
opt_user,
|
||||
opt_password,
|
||||
opt_db ? opt_db : "test",
|
||||
opt_port,
|
||||
opt_unix_socket,
|
||||
CLIENT_FOUND_ROWS));
|
||||
|
||||
/* Retrieve character set information. */
|
||||
|
||||
bug30472_retrieve_charset_info(&con,
|
||||
character_set_name_1,
|
||||
character_set_client_1,
|
||||
character_set_results_1,
|
||||
collation_connnection_1);
|
||||
|
||||
/* Switch client character set. */
|
||||
|
||||
DIE_IF(mysql_set_character_set(&con, "utf8"));
|
||||
|
||||
/* Retrieve character set information. */
|
||||
|
||||
bug30472_retrieve_charset_info(&con,
|
||||
character_set_name_2,
|
||||
character_set_client_2,
|
||||
character_set_results_2,
|
||||
collation_connnection_2);
|
||||
|
||||
/*
|
||||
Check that
|
||||
1) character set has been switched and
|
||||
2) new character set is different from the original one.
|
||||
*/
|
||||
|
||||
DIE_UNLESS(strcmp(character_set_name_2, "utf8") == 0);
|
||||
DIE_UNLESS(strcmp(character_set_client_2, "utf8") == 0);
|
||||
DIE_UNLESS(strcmp(character_set_results_2, "utf8") == 0);
|
||||
DIE_UNLESS(strcmp(collation_connnection_2, "utf8_general_ci") == 0);
|
||||
|
||||
DIE_UNLESS(strcmp(character_set_name_1, character_set_name_2) != 0);
|
||||
DIE_UNLESS(strcmp(character_set_client_1, character_set_client_2) != 0);
|
||||
DIE_UNLESS(strcmp(character_set_results_1, character_set_results_2) != 0);
|
||||
DIE_UNLESS(strcmp(collation_connnection_1, collation_connnection_2) != 0);
|
||||
|
||||
/* Call mysql_change_user() with the same username, password, database. */
|
||||
|
||||
DIE_IF(mysql_change_user(&con,
|
||||
opt_user,
|
||||
opt_password,
|
||||
opt_db ? opt_db : "test"));
|
||||
|
||||
/* Retrieve character set information. */
|
||||
|
||||
bug30472_retrieve_charset_info(&con,
|
||||
character_set_name_3,
|
||||
character_set_client_3,
|
||||
character_set_results_3,
|
||||
collation_connnection_3);
|
||||
|
||||
/* Check that character set information has been reset. */
|
||||
|
||||
DIE_UNLESS(strcmp(character_set_name_1, character_set_name_3) == 0);
|
||||
DIE_UNLESS(strcmp(character_set_client_1, character_set_client_3) == 0);
|
||||
DIE_UNLESS(strcmp(character_set_results_1, character_set_results_3) == 0);
|
||||
DIE_UNLESS(strcmp(collation_connnection_1, collation_connnection_3) == 0);
|
||||
|
||||
/* Change connection-default character set in the client. */
|
||||
|
||||
con.options.charset_name= my_strdup("utf8", MYF(MY_FAE));
|
||||
|
||||
/*
|
||||
Call mysql_change_user() in order to check that new connection will
|
||||
have UTF8 character set on the client and on the server.
|
||||
*/
|
||||
|
||||
DIE_IF(mysql_change_user(&con,
|
||||
opt_user,
|
||||
opt_password,
|
||||
opt_db ? opt_db : "test"));
|
||||
|
||||
/* Retrieve character set information. */
|
||||
|
||||
bug30472_retrieve_charset_info(&con,
|
||||
character_set_name_4,
|
||||
character_set_client_4,
|
||||
character_set_results_4,
|
||||
collation_connnection_4);
|
||||
|
||||
/* Check that we have UTF8 on the server and on the client. */
|
||||
|
||||
DIE_UNLESS(strcmp(character_set_name_4, "utf8") == 0);
|
||||
DIE_UNLESS(strcmp(character_set_client_4, "utf8") == 0);
|
||||
DIE_UNLESS(strcmp(character_set_results_4, "utf8") == 0);
|
||||
DIE_UNLESS(strcmp(collation_connnection_4, "utf8_general_ci") == 0);
|
||||
|
||||
/* That's it. Cleanup. */
|
||||
|
||||
mysql_close(&con);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Read and parse arguments and MySQL options from my.cnf
|
||||
*/
|
||||
@ -16943,6 +17114,7 @@ static struct my_tests_st my_tests[]= {
|
||||
{ "test_bug29692", test_bug29692 },
|
||||
{ "test_bug29306", test_bug29306 },
|
||||
{ "test_change_user", test_change_user },
|
||||
{ "test_bug30472", test_bug30472 },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user