character_set_results=sjisを発行した上でcp932なテーブルへアクセス

Connector/NETのデフォルトの状態,つまり"character_set_results=NULL"でcp932なテーブルへアクセスするとサーバから返ってくるパケットの解析時にcp932を処理できなくてエラーが起きると以前のエントリで書いたと思う.

そこでテーブルはcp932で作りつつ,character_set_results=sjisを指定してMySQLサーバ側に結果データを"cp932→sjis"にコンバートさせてsjisとしてパケットを返すようにしたらどうなるかを実験.まあ理屈では上手くいくはず.

ソースはこちら

using System;
using MySql.Data.MySqlClient;
namespace CNTNS {

    class ConnectorNETTest {
        
        public static void Main() {
            Console.WriteLine("Hello World!");
                
            string myConnectionString = "server=127.0.0.1;uid=root;" +
                "pwd=;database=test;charset=sjis";

            MySqlConnection conn = new MySqlConnection();
            conn.ConnectionString = myConnectionString;
            conn.Open();
            
            MySqlDataReader reader = null;
            MySqlCommand cmd = null;
            
            cmd = new MySqlCommand("set character_set_results=sjis", conn);
            reader = cmd.ExecuteReader();
            reader.Close();
            
            cmd = new MySqlCommand("DROP TABLE IF EXISTS t1", conn);
            reader = cmd.ExecuteReader();
            reader.Close();
            
            cmd = new MySqlCommand("CREATE TABLE t1 (c1 INT, c2 CHAR(1)) DEFAULT CHARSET=cp932", conn);
            reader = cmd.ExecuteReader();
            reader.Close();
            
            cmd = new MySqlCommand("INSERT INTO t1 VALUES (1, '1')", conn);
            reader = cmd.ExecuteReader();
            reader.Close();
            
            cmd = new MySqlCommand("INSERT INTO t1 VALUES (2, 'ァ')", conn);
            reader = cmd.ExecuteReader();
            reader.Close();
            
            cmd = new MySqlCommand("INSERT INTO t1 VALUES (3, 'あ')", conn);
            reader = cmd.ExecuteReader();
            reader.Close();
            
            cmd = new MySqlCommand("INSERT INTO t1 VALUES (4, '㈱')", conn);
            reader = cmd.ExecuteReader();
            reader.Close();
            
            cmd = new MySqlCommand("SELECT * FROM t1", conn);
            reader = cmd.ExecuteReader();           
            while (reader.Read()) {
                    Console.WriteLine(reader.GetString(0) + "=" + reader.GetString(1));
            }
            reader.Close();

            conn.Close();
            
            Console.WriteLine("Hello MySQL!");  
        }
    }
}

実行結果

D:\share\workspace\current\CSharp>ConnectorNETTest.exe
Hello World!
1=1
2=ァ
3=あ
4=?
Hello MySQL!

そのときのGeneral Log

1 Connect     root@localhost on test
1 Query       SHOW VARIABLES
1 Query       SHOW COLLATION
1 Query       SET NAMES sjis;SET character_set_results=NULL
1 Init DB     test
1 Query       set character_set_results=sjis
1 Query       DROP TABLE IF EXISTS t1
1 Query       CREATE TABLE t1 (c1 INT, c2 CHAR(1)) DEFAULT CHARSET=cp932
1 Query       INSERT INTO t1 VALUES (1, '1')
1 Query       INSERT INTO t1 VALUES (2, 'ァ')
1 Query       INSERT INTO t1 VALUES (3, 'あ')
1 Query       INSERT INTO t1 VALUES (4, '㈱')
1 Query       SHOW WARNINGS
1 Query       SELECT * FROM t1

ふむ.実行結果では文字化けしている.General Logでは文字化けしていないのでとりあえずMySQLServerへは無事届けられたようだ.

General Logを良く見てみると"SHOW WARNING"の文が入っている.MySQLでは文字が変換中に破壊(Truncate)されるとWARNINGがでる.C/NETはそれを拾うためにこの"SHOW WARNIG"を発行しているものと思われる.

ということでサーバ側を覗いてみる.

mysql> set names cp932;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from t1;
+------+------+
| c1   | c2   |
+------+------+
|    1 | 1    |
|    2 | ァ   |
|    3 | あ   |
|    4 | ?    |
+------+------+
4 rows in set (0.00 sec)

mysql> select hex(c2) from t1;
+---------+
| hex(c2) |
+---------+
| 31      |
| 8340    |
| 82A0    |
| 3F      |
+---------+
4 rows in set (0.00 sec)

hexで"3F"となっているものは半角はてなマーク"?"であり,INSERT時に文字が壊れた場合にこれが現れる.MySQLsjisからcp932へ括弧株を変換できなかったために起きたものだ.

となるとこれはどういう意味になるだろうか.

  • Connector/NETはsjisを使うことで括弧株などの文字もちゃんと扱えるものの
  • Connector/NETから既存のcp932テーブルは十分には扱えない
  • 別のJavaなサブシステムがcp932テーブルを使っている場合,そのテーブルを.NETなサブシステムと共有できないことになる

とりあえず思いつくのはこういう問題.

さてここらでもう一度自分的にsjisとcp932を整理する必要がありそうだ.