chcp 65001 すると MySQLが日本語を受け付けてくれない


環境-1つめ:10.1.32-MariaDB , XAMPP v3.2.2 , Windows10 Enterprise
環境-2つめ:10.4.17-MariaDB , XAMPP v3.2.4 , Windows10 PRO

結論から書くと私の場合は chcp 65001 を使ってWindowsコマンドプロンプト上から utf-8 の入力をするのは避けたほうがいい気がする。となりました。

起こったこと

  • Windowsコマンドプロンプトから MySQL を操作する時、chcp 65001 でコマンドプロンプトの文字コードを utf-8 にすると MySQL が日本語(3バイトuft-8)を受け付けてくれない
  • 読み出し時の文字化けはない。
  • また chcp 932 のまま、MySQL内で set names cp932 をすればなにも問題は起きない。
  • ふたつの環境で再現する。
# chcp 65001
///// ウィンドウが変わる
Active code page: 65001

# mysql -u root -p

MariaDB [(none)]> USE db1
Database changed

///// insert を試みる
MariaDB [db1]> INSERT INTO tb10(num,name) VALUES(1,'佐藤');
    '>
    '>
    '>     ///// ← 処理が完了していない
    '> ;    ///// ← セミコロン入れても完了しない
    '>
    '> '    ///// ← よく見るとシングルクオーテーション抜けだと言われてるので入力する
    ->
    -> ;    ///// ← セミコロンを入れて完了しエラー文が出る
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '' at line 1

この挙動は insert だけではなく as句 による名付けや、他の構文内で日本語を使った場合でもほぼ変わらない。
(as句だとシングルクオーテーションのありなしで少し変わる)

MariaDB [db1]> SELECT name AS '名前' FROM tb10;
    '> '
    -> ;
ERROR 1054 (42S22): Unknown column 'name' in 'field list'

MariaDB [db1]> SELECT name AS 名前 FROM tb10;
    -> ;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '' at line 1

MariaDB [db1]> SELECT ROD('あ');
    '> '
    -> ;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '' at line 1

直接文字コードを書くことによる insert はできる

utf-8 を CHAR()関数によって変換することはできる。
また直接文字コードを書くことによる insert もできる。

MariaDB [db1]> SELECT CHAR(0xe38182 using utf8);
+---------------------------+
| CHAR(0xe38182 using utf8) |
+---------------------------+
| あ                        |
+---------------------------+
1 row in set (0.000 sec)

MariaDB [db1]> INSERT INTO tb10(num,name) VALUES(1,0xe38182e38184);
Query OK, 1 row affected (0.00 sec)

MariaDB [db1]> SELECT * FROM tb10;
+------+--------+
| num  | name   |
+------+--------+
|    1 | あい   |
+------+--------+
1 row in set (0.000 sec)
接続が utf8 でカラムが utf8 のとき、4bit文字が現れるとそこで文字が切れる現象を思い出す

参考:https://tmtms.hatenablog.com/entry/2016/09/06/mysql-utf8

日本語の後ろが読まれてないのでは? というわけで日本語の末尾から行を分けてinsertしてみる。

MariaDB [db1]> insert into tb10(num,name) values(3,'佐藤
    '> ');                 ///// 『 '); 』を改行してから入力する
Query OK, 1 row affected (0.011 sec)

MariaDB [db1]> select * from tb10;
+------+--------+
| num  | name   |
+------+--------+
|    1 | あい   |
|    2 | 鈴木   |
|    3 |
      |
+------+--------+
3 rows in set (0.000 sec)

受け付けてくれた。
……なんかおかしいけど。
ただinsertできたらこっちのもので出力させます。

MariaDB [db1]> SELECT * FROM tb10 INTO OUTFILE 'a.dmp';
Query OK, 3 rows affected, 1 warning (0.001 sec)

これをテキストと16進数とでそれぞれ見て……

>type a.dmp
1       あい
2       鈴木
3       \


>certutil -f -encodehex a.dmp a_16.txt 4    ///// 16進数への変換
Input Length = 12
Output Length = 38
CertUtil: -encodehex command completed successfully.

>type a_16.txt
31 09 e3 81 82 e3 81 84  0a 32 09 e9 88 b4 e6 9c
a8 0a 33 09 5c 0a 0a

うん…………?

ちょっとわかりにくいので、新しく tb11 を作り2回insertします。

MariaDB [db1]> CREATE TABLE tb11(text VARCHAR(100));   ///// テーブルの作成から
Query OK, 0 rows affected (0.007 sec)

MariaDB [db1]> INSERT INTO tb11(text) VALUES('あい
    '> ');
Query OK, 1 row affected (0.003 sec)

MariaDB [db1]> INSERT INTO tb11(text) VALUES('高橋
    '> ');
Query OK, 1 row affected (0.001 sec)

MariaDB [db1]> SELECT * FROM tb11;
+------+
| text |
+------+
|
    |
|
    |
+------+
2 rows in set (0.000 sec)

MariaDB [db1]> SELECT * FROM tb11 INTO OUTFILE 'b.dmp';   ///// 出力する
Query OK, 2 rows affected, 1 warning (0.002 sec)

それをまたテキストファイルと16進数とで見て

>type b.dmp
\

\


>type b_16.txt
5c 0a 0a 5c 0a 0a

うん…………。
0a=<LF> の改行がひとつ多く入ってるのはシングルクォーテーションの間で一度改行してるのを正しく反映していると言えます。
その前、どんな文字を入れても 5c になってるのは、ちょっとわからない。
(BOM も疑ったけどBOMは EF BB BF だからいまいちそれと確信持てない……)

と、ここでお手上げになりました。

以下はMySQLの設定の確認

MySQL側の文字コードは utf-8 で統一されている(はず)

character_set の確認
MariaDB [(none)]> show global variables like 'char%';
+--------------------------+----------------------------------------------------------------+
| Variable_name            | Value                                                          |
+--------------------------+----------------------------------------------------------------+
| character_set_client     | utf8mb4                                                        |
| character_set_connection | utf8mb4                                                        |
| character_set_database   | utf8mb4                                                        |
| character_set_filesystem | binary                                                         |
| character_set_results    | utf8mb4                                                        |
| character_set_server     | utf8mb4                                                        |
| character_set_system     | utf8                                                           |
| character_sets_dir       | C:\ <--中略--> mysql\share\charsets\ |
+--------------------------+----------------------------------------------------------------+
8 rows in set (0.001 sec)

MariaDB [(none)]> show session variables like 'char%';
+--------------------------+----------------------------------------------------------------+
| Variable_name            | Value                                                          |
+--------------------------+----------------------------------------------------------------+
| character_set_client     | utf8mb4                                                        |
| character_set_connection | utf8mb4                                                        |
| character_set_database   | utf8mb4                                                        |
| character_set_filesystem | binary                                                         |
| character_set_results    | utf8mb4                                                        |
| character_set_server     | utf8mb4                                                        |
| character_set_system     | utf8                                                           |
| character_sets_dir       | C:\ <--中略--> mysql\share\charsets\ |
+--------------------------+----------------------------------------------------------------+
8 rows in set (0.000 sec)
その上で作られた db1
MariaDB [(none)]> select * from INFORMATION_SCHEMA.SCHEMATA where SCHEMA_NAME='db1';
+--------------+-------------+----------------------------+------------------------+----------+
| CATALOG_NAME | SCHEMA_NAME | DEFAULT_CHARACTER_SET_NAME | DEFAULT_COLLATION_NAME | SQL_PATH |
+--------------+-------------+----------------------------+------------------------+----------+
| def          | db1         | utf8mb4                    | utf8mb4_unicode_ci     | NULL     |
+--------------+-------------+----------------------------+------------------------+----------+
1 row in set (0.002 sec)
その上で作られた tb10
MariaDB [db1]> show create table tb10;
+-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                                                                         |
+-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| tb10  | CREATE TABLE `tb10` (
  `num` int(11) DEFAULT NULL,
  `name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci |
+-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.000 sec)
一応 my.ini の記述も置いておきます
[mysqld]
## UTF 8 Settings
init-connect='SET NAMES utf8mb4'
collation_server=utf8mb4_unicode_ci
character_set_server=utf8mb4
skip-character-set-client-handshake
character_sets-dir="C:/ <--中略--> mysql/share/charsets"
sql_mode=NO_ZERO_IN_DATE,NO_ZERO_DATE,NO_ENGINE_SUBSTITUTION
log_bin_trust_function_creators = 1

[mysqldump]
default-character-set   = utf8mb4

[mysql]
default-character-set   = utf8mb4

Shift_JIS のまま set names すれば問題は起きない

# chcp
現在のコード ページ: 932

# mysql -u root -p

MariaDB [(none)]> SET NAMES cp932;
Query OK, 0 rows affected (0.000 sec)

MariaDB [(none)]> USE db1
Database changed

MariaDB [db1]> INSERT INTO tb10(num,name) VALUES(2,'鈴木');
Query OK, 1 row affected (0.003 sec)

MariaDB [db1]> SELECT * from tb10;
+------+------+
| num  | name |
+------+------+
|    1 | あい     |
|    2 | 鈴木     |
+------+------+
2 rows in set (0.003 sec)

怪しいのは chcp 65001 かな…………

コマンドラインから MySQL に渡している文字列を16進数で抜き出す(どこかに出力させる)ことができればもう少しわかりそうなのですが。