summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorunknown <anozdrin/alik@station.>2007-09-28 23:30:54 +0400
committerunknown <anozdrin/alik@station.>2007-09-28 23:30:54 +0400
commit20b08f4705a1f526b0e746ec5dcd065364971c03 (patch)
tree605786f152b4a3a692332f47c2dc95125435e7d5
parentfa48986a0d1b8214a118b29b95a3e8fdb0fd9eb4 (diff)
downloadmariadb-git-20b08f4705a1f526b0e746ec5dcd065364971c03.tar.gz
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.
-rw-r--r--client/client_priv.h4
-rw-r--r--libmysql/libmysql.c26
-rw-r--r--mysql-test/t/mysql_client_test.test4
-rw-r--r--sql/sql_parse.cc14
-rw-r--r--tests/mysql_client_test.c176
5 files changed, 218 insertions, 6 deletions
diff --git a/client/client_priv.h b/client/client_priv.h
index a2f61b9e9ca..4bc275e6630 100644
--- a/client/client_priv.h
+++ b/client/client_priv.h
@@ -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
diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c
index c8e2d48873f..e2e481fdd21 100644
--- a/libmysql/libmysql.c
+++ b/libmysql/libmysql.c
@@ -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);
}
diff --git a/mysql-test/t/mysql_client_test.test b/mysql-test/t/mysql_client_test.test
index 66a27abd61a..7667522feaf 100644
--- a/mysql-test/t/mysql_client_test.test
+++ b/mysql-test/t/mysql_client_test.test
@@ -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;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 3dbcfe6d2bc..923fd1b2bfe 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -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;
}
diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c
index 93a85f44919..eff8df8109a 100644
--- a/tests/mysql_client_test.c
+++ b/tests/mysql_client_test.c
@@ -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 */
@@ -16650,6 +16652,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 }
};