Commit 20b08f47 authored by unknown's avatar unknown

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.


client/client_priv.h:
  Declare a function, used in libmysql.c and client.c.
libmysql/libmysql.c:
  1. Reset character set on the client in mysql_change_user().
  2. Send character set to the server in COM_CHANGE_USER command.
mysql-test/t/mysql_client_test.test:
  mysql_client_test.log is used by the test suite.
  
  Use mysql_client_test.out.log to collect mysql_client_test
  real output.
sql/sql_parse.cc:
  Switch character set in COM_CHANGE_USER.
tests/mysql_client_test.c:
  Test case for BUG#30472.
parent fa48986a
......@@ -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 }
};
......
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