MySQL 8.0.24 の文字コードまわり

2021/4/20 にリリースされた MySQL 8.0.24 について私が気になったものについて。 まあ文字コードまわりだけなんだけど。

utf8utf8mb3 として出力する

Client applications and test suite plugins now report utf8mb3 rather than utf8 when writing character set names. (Bug #32164079, Bug #32164125)

Important Note: When a utf8mb3 collation was specified in a CREATE TABLE statement, SHOW CREATE TABLE, DEFAULT CHARSET, the values of system variables containing character set names, and the binary log all subsequently displayed the character set as utf8 which is becoming a synonym for utf8mb4. Now in such cases, utf8mb3 is shown instead, and CREATE TABLE raises the warning 'collation_name' is a collation of the deprecated character set UTF8MB3. Please consider using UTF8MB4 with an appropriate collation instead. (Bug #27225287, Bug #32085357, Bug #32122844)

tmtms.hatenablog.com にも書いたんだけど(う、もう3年も前なのか…)、

なお、utf8mb3 を使えという割には、参照時には utf8 と表示されるのがイマイチ。show create table の結果をそのまま使うと warning が出るという…。

ということだったんだけど、やっと SHOW CREATE TBALE でも utf8mb3 と出るようになった。

8.0.23

mysql> create table t (i int) charset utf8mb3;
Query OK, 0 rows affected, 1 warning (0.14 sec)

mysql> show create table t\G
*************************** 1. row ***************************
       Table: t
Create Table: CREATE TABLE `t` (
  `i` int DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.02 sec)

8.0.24

mysql> create table t (i int) charset utf8mb3;
Query OK, 0 rows affected, 1 warning (0.11 sec)

mysql> show create table t\G
*************************** 1. row ***************************
       Table: t
Create Table: CREATE TABLE `t` (
  `i` int DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3
1 row in set (0.03 sec)

そのうち(8.1 とか 9.0 とか?)で utf8utf8mb4 の別名になるんでその準備っぽい(さすがに 8.0.x でそうなることはないと思いたい)。 もっと早くやっておいても良かったと思うが…。

8.0.24 より前のバージョンをマスター、utf8utf8mb4 の別名になった後のバージョンをスレーブとしてレプリケーション組むと、マスターでは utf8mb3 でスレーブは utf8mb4 になっちゃうんで、気をつけたほうが良さそう。

mysql --default-character-set=utf8mb4 で日本語が入力できなかったのが直った

For builds compiled using the libedit library, if the mysql client was invoked with the --default-character-set=utf8 option, libedit rejected input of multibyte characters. (Bug #32329078, Bug #32583436, Bug #102806)

tmtms.hatenablog.com に似たようなことを書いたんだけど、この記事で書いたのはロケールが設定されてないと日本語入力できないってものだったのでちょっと違う。

こっちのツイートの方

8.0.23

% LC_ALL=C.UTF-8 mysql --default-character-set=utf8mb4
mysql> (←日本語が入力できない)

8.0.24

% LC_ALL=C.UTF-8 mysql --default-character-set=utf8mb4
mysql> select 'あ';
+-----+
| あ  |
+-----+
| あ  |
+-----+
1 row in set (0.00 sec)

ascii charset で 8bit 文字が不正扱いになった

It was possible to insert illegal ASCII values (outside 7-bit range) into character columns that used the ascii character set. This is now prohibited. (Bug #24847620)

ASCII は 7bit の文字コードなんだけど、MySQL の ascii charset は 8bit 文字も受け付けていた。 前に「大文字小文字を区別したいんだけど 8bit データも入れたい」という用途で時々使ってたんだけど、とうとう不正文字扱いになってしまった。

8.0.23 は warnings が出つつもデータは格納できていた。

mysql> create table t (c varchar(10)) charset ascii;
Query OK, 0 rows affected (0.15 sec)

mysql> insert into t value (0x414243ff303132);
Query OK, 1 row affected, 1 warning (0.03 sec)

mysql> show warnings;
+---------+------+------------------------------------------------+
| Level   | Code | Message                                        |
+---------+------+------------------------------------------------+
| Warning | 1300 | Invalid ascii character string: 'ABC\xFF01...' |
+---------+------+------------------------------------------------+
1 row in set (0.00 sec)

mysql> select * from t;
+---------+
| c       |
+---------+
| ABC?012 |
+---------+
1 row in set (0.00 sec)

mysql> select c,hex(c) from t;
+---------+----------------+
| c       | hex(c)         |
+---------+----------------+
| ABC?012 | 414243FF303132 |
+---------+----------------+
1 row in set (0.00 sec)

8.0.24 はエラーになる。sql_mode を変更すると warnings が出て格納できる…ように見えるんだけど、8bit データ以降はちょん切られちゃってる。

mysql> create table t (c varchar(10)) charset ascii;
Query OK, 0 rows affected (0.15 sec)

mysql> insert into t value (0x414243ff303132);
ERROR 1366 (HY000): Incorrect string value: '\xFF012' for column 'c' at row 1

mysql> set sql_mode='';
Query OK, 0 rows affected (0.00 sec)

mysql> insert into t value (0x414243ff303132);
Query OK, 1 row affected, 1 warning (0.03 sec)

mysql> show warnings;
+---------+------+-----------------------------------------------------------+
| Level   | Code | Message                                                   |
+---------+------+-----------------------------------------------------------+
| Warning | 1366 | Incorrect string value: '\xFF012' for column 'c' at row 1 |
+---------+------+-----------------------------------------------------------+
1 row in set (0.00 sec)

mysql> select c,hex(c) from t;
+------+--------+
| c    | hex(c) |
+------+--------+
| ABC  | 414243 |
+------+--------+
1 row in set (0.00 sec)

これバグ修正扱いでいいのかなぁ。非互換な気がするんだけど。まあ変な使い方してる方が悪いか。