Docker環境のMySQLで日本語が打てない！

# 問題提起
DockerでMySQLを使おうと思ってコンテナを起動した。
```sh
docker run --name mysql-sandbox -e MYSQL_ROOT_PASSWORD=secret -d mysql:9.0.1-oraclelinux9
docker exec -it mysql-sandbox bash
```
```sh:bash
mysql -p
```
すると...
```sql:mysql
SELECT '日本語';
```
ここで躓いた。ローマ字入力で「にほんご」と打ち、変換してEnterキーを押すと「日本語」が画面に表示されなかった。

# 原因
ロケールの設定ができていなかった。
```sh:bash
locale
```
を実行した結果が以下の通り。
```
LANG=
LC_CTYPE="POSIX"
LC_NUMERIC="POSIX"
LC_TIME="POSIX"
LC_COLLATE="POSIX"
LC_MONETARY="POSIX"
LC_MESSAGES="POSIX"
LC_PAPER="POSIX"
LC_NAME="POSIX"
LC_ADDRESS="POSIX"
LC_TELEPHONE="POSIX"
LC_MEASUREMENT="POSIX"
LC_IDENTIFICATION="POSIX"
LC_ALL=
```
どうやら`LC_CTYPE`の値が`POSIX`だと日本語のようなマルチバイト文字は認識できないらしい。
# 解決法
`LC_CTYPE`の値を変更する。現時点で使用できるロケールは
```sh:bash
locale -a
```
で確認できる。このコマンドの結果は
```
C
C.utf8
POSIX
```
だった。そこで
```sh:bash
export LC_CTYPE=C.utf8
```
によって環境変数を変更する。すると`locale`コマンドの実行結果が
```
LANG=
LC_CTYPE=C.utf8
LC_NUMERIC="POSIX"
LC_TIME="POSIX"
LC_COLLATE="POSIX"
LC_MONETARY="POSIX"
LC_MESSAGES="POSIX"
LC_PAPER="POSIX"
LC_NAME="POSIX"
LC_ADDRESS="POSIX"
LC_TELEPHONE="POSIX"
LC_MEASUREMENT="POSIX"
LC_IDENTIFICATION="POSIX"
LC_ALL=
```
に変わった。その後MySQLクライアント内で`SELECT '日本語';`が入力できるようになり、実行すると
```
+-----------+
| 日本語    |
+-----------+
| 日本語    |
+-----------+
```
が表示された。

# MySQLのシステム変数も勝手に切り替わる
`LC_CTYPE`を変更する前は、MySQLサーバーのシステム変数は
```sql:mysql
SHOW VARIABLES LIKE '%character%';
```
```
+--------------------------+--------------------------------+
| Variable_name            | Value                          |
+--------------------------+--------------------------------+
| character_set_client     | latin1                         |
| character_set_connection | latin1                         |
| character_set_database   | utf8mb4                        |
| character_set_filesystem | binary                         |
| character_set_results    | latin1                         |
| character_set_server     | utf8mb4                        |
| character_set_system     | utf8mb3                        |
| character_sets_dir       | /usr/share/mysql-9.0/charsets/ |
+--------------------------+--------------------------------+
```
```sql:mysql
SHOW VARIABLES LIKE '%collation%';
```
```
+-------------------------------+--------------------+
| Variable_name                 | Value              |
+-------------------------------+--------------------+
| collation_connection          | latin1_swedish_ci  |
| collation_database            | utf8mb4_0900_ai_ci |
| collation_server              | utf8mb4_0900_ai_ci |
| default_collation_for_utf8mb4 | utf8mb4_0900_ai_ci |
+-------------------------------+--------------------+
```
となっていた。またステータスは
```sql:mysql
STATUS;
```
```
--------------
mysql  Ver 9.0.1 for Linux on aarch64 (MySQL Community Server - GPL)

Connection id:		8
Current database:
Current user:		root@localhost
SSL:			Not in use
Current pager:		stdout
Using outfile:		''
Using delimiter:	;
Server version:		9.0.1 MySQL Community Server - GPL
Protocol version:	10
Connection:		Localhost via UNIX socket
Server characterset:	utf8mb4
Db     characterset:	utf8mb4
Client characterset:	latin1
Conn.  characterset:	latin1
UNIX socket:		/var/run/mysqld/mysqld.sock
Binary data as:		Hexadecimal
Uptime:			9 sec

Threads: 2  Questions: 6  Slow queries: 0  Opens: 119  Flush tables: 3  Open tables: 38  Queries per second avg: 0.666
--------------
```
であった。一方`LC_CTYPE`変更後は、順に
```
+--------------------------+--------------------------------+
| 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     | utf8mb3                        |
| character_sets_dir       | /usr/share/mysql-9.0/charsets/ |
+--------------------------+--------------------------------+
```
```
+-------------------------------+--------------------+
| Variable_name                 | Value              |
+-------------------------------+--------------------+
| collation_connection          | utf8mb4_0900_ai_ci |
| collation_database            | utf8mb4_0900_ai_ci |
| collation_server              | utf8mb4_0900_ai_ci |
| default_collation_for_utf8mb4 | utf8mb4_0900_ai_ci |
+-------------------------------+--------------------+
```
```
--------------
mysql  Ver 9.0.1 for Linux on aarch64 (MySQL Community Server - GPL)

Connection id:		9
Current database:
Current user:		root@localhost
SSL:			Not in use
Current pager:		stdout
Using outfile:		''
Using delimiter:	;
Server version:		9.0.1 MySQL Community Server - GPL
Protocol version:	10
Connection:		Localhost via UNIX socket
Server characterset:	utf8mb4
Db     characterset:	utf8mb4
Client characterset:	utf8mb4
Conn.  characterset:	utf8mb4
UNIX socket:		/var/run/mysqld/mysqld.sock
Binary data as:		Hexadecimal
Uptime:			32 sec

Threads: 2  Questions: 12  Slow queries: 0  Opens: 119  Flush tables: 3  Open tables: 38  Queries per second avg: 0.375
--------------
```
となっていた。各変数の意味については以下から確認できる。

https://dev.mysql.com/doc/refman/9.0/en/server-system-variables.html

utf8mb3についてはutf8mb4を使った方がよいとのこと。

https://dev.mysql.com/doc/refman/9.0/en/charset-unicode-utf8mb3.html


# `LC_CTYPE`を変更する別の方法
`export LC_CTYPE=C.utf8`の代わりに
```sh:bash
export LANG=C.utf8
```
を実行すると、`locale`コマンドの結果は
```
LANG=C.utf8
LC_CTYPE="C.utf8"
LC_NUMERIC="C.utf8"
LC_TIME="C.utf8"
LC_COLLATE="C.utf8"
LC_MONETARY="C.utf8"
LC_MESSAGES="C.utf8"
LC_PAPER="C.utf8"
LC_NAME="C.utf8"
LC_ADDRESS="C.utf8"
LC_TELEPHONE="C.utf8"
LC_MEASUREMENT="C.utf8"
LC_IDENTIFICATION="C.utf8"
LC_ALL=
```
となる。また、
```sh:bash
export LC_ALL=C.utf8
```
を実行した場合は
```
LANG=
LC_CTYPE="C.utf8"
LC_NUMERIC="C.utf8"
LC_TIME="C.utf8"
LC_COLLATE="C.utf8"
LC_MONETARY="C.utf8"
LC_MESSAGES="C.utf8"
LC_PAPER="C.utf8"
LC_NAME="C.utf8"
LC_ADDRESS="C.utf8"
LC_TELEPHONE="C.utf8"
LC_MEASUREMENT="C.utf8"
LC_IDENTIFICATION="C.utf8"
LC_ALL=C.utf8
```
となる。

# 日本語ロケールを設定する方法
https://www.oracle.com/linux/what-is-linux/#why

Oracle LinuxはRed Hat Enterprise Linuxと互換があり、パッケージマネージャーとして`dnf`を使用している。ただ今回使用しているMySQLイメージのベースイメージはOracle Linuxのslimイメージで、パッケージマネージャーは`microdnf`である。

https://github.com/oracle/container-images?tab=readme-ov-file#differences-between-oraclelinux8-and-oraclelinux8-slim

下のissueを参考にして、`microdnf`を使用して日本語ロケールをインストールする手順を示す。

https://github.com/oracle/container-images/issues/20#issuecomment-592722337

まず以下を実行する。
```sh:bash
microdnf update
microdnf install glibc-langpack-ja
```
すると`locale -a`で表示される、利用可能なロケールのリストに日本語ロケールが追加される。
```
C
C.utf8
POSIX
ja_JP.eucjp
ja_JP.utf8
```
続いて環境変数を変更する。
```sh:bash
export LANG=ja_JP.utf8
```
するとロケールが変更される。
```
LANG=ja_JP.utf8
LC_CTYPE="ja_JP.utf8"
LC_NUMERIC="ja_JP.utf8"
LC_TIME="ja_JP.utf8"
LC_COLLATE="ja_JP.utf8"
LC_MONETARY="ja_JP.utf8"
LC_MESSAGES="ja_JP.utf8"
LC_PAPER="ja_JP.utf8"
LC_NAME="ja_JP.utf8"
LC_ADDRESS="ja_JP.utf8"
LC_TELEPHONE="ja_JP.utf8"
LC_MEASUREMENT="ja_JP.utf8"
LC_IDENTIFICATION="ja_JP.utf8"
LC_ALL=
```
ついでにタイムゾーンも変更する。コンテナを起動する際に環境変数を追加する。
```sh
docker run --name mysql-sandbox -e MYSQL_ROOT_PASSWORD=secret -e TZ=Asia/Tokyo -d mysql:9.0.1-oraclelinux9
```
するとMySQLのシステム変数も変更される。
```sql:mysql
SHOW VARIABLES LIKE '%time_zone%';
```
```
+------------------+--------+
| Variable_name    | Value  |
+------------------+--------+
| system_time_zone | JST    |
| time_zone        | SYSTEM |
+------------------+--------+
```
また`date`コマンドの結果が`2024年  8月 11日 日曜日 14:37:57 JST`のように日本にローカライズされる。

# `localectl`は使えない？
Oracle Linuxのロケールを設定するには、

https://docs.oracle.com/en/learn/oracle-linux-localization/index.html#update-the-system-locale-and-language

によれば`localectl`コマンドを使用するようだが、command not foundと言われてしまった。
```sh:bash
microdnf update
microdnf install dnf
dnf search locale
```
で検索してみてもヒットしない。

詳しいことはよくわからないが、そもそもDocker環境内では`systemctl`同様`localectl`は使用できないらしい（[参考](https://note.shiftinc.jp/n/n9a2ca483148d)）。