MySQLに独自charsetを追加する

MySQL に独自 charset を追加できる…という話はずっと前に聞いたことあったけど、やったことなかったんでやってみた。

詳しくは MySQL :: MySQL 8.0 リファレンスマニュアル :: 10.13 文字セットの追加 を。

マルチバイト charset は C でプログラムを書いてコンパイルする必要があるけど、1バイト charset はファイルを置くだけで追加できる。

1バイトの charset と言えばみんなご存知の JIS X 0201 ですよね。ということで、jisx0201 という charset を作ってみる。

JIS X 0201 のコードはこんな感じ:

x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF
0x NUL SOH STX ETX EOT ENQ ACK BEL BS HT LF VT FF CR SO SI
1x DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC FS GS RS US
2x SP ! " # $ % & ' ( ) * + , - . /
3x 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
4x @ A B C D E F G H I J K L M N O
5x P Q R S T U V W X Y Z [ ¥ ] ^ _
6x ` a b c d e f g h i j k l m n o
7x p q r s t u v w x y z { | } DEL
8x
9x
Ax
Bx ソ
Cx
Dx
Ex
Fx

0x00〜0x7F は ASCII とほぼ同じだけど、違いは 0x5C が「\」ではなくて「¥」、0x7E が「~」ではなくて「」なこと。 0xA1〜0xDF に半角カナが割り当てられてる。

/usr/local/mysql/share/charsets/Index.xml に次を追記:

<charset name="jisx0201">
  <description>JIS X 0201</description>
  <collation name="jisx0201_general_ci" id="1234">
    <flag>primary</flag>
  </collation>
  <collation name="jisx0201_bin" id="1235">
    <flag>binary</flag>
  </collation>
</charset>

collation は jisx0201_general_cijisx0201_bin の2つ。 collation の id は 1024〜2047 がユーザー定義用のものなので適当に。

同じディレクトリに jisx0201.xml を追加:

<?xml version='1.0' encoding="utf-8"?>

<charsets>

<charset name="jisx0201">

<ctype>
<map>
 00
 20 20 20 20 20 20 20 20 20 28 28 28 28 28 20 20
 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
 48 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
 84 84 84 84 84 84 84 84 84 84 10 10 10 10 10 10
 10 81 81 81 81 81 81 01 01 01 01 01 01 01 01 01
 01 01 01 01 01 01 01 01 01 01 01 10 10 10 10 10
 10 82 82 82 82 82 82 02 02 02 02 02 02 02 02 02
 02 02 02 02 02 02 02 02 02 02 02 10 10 10 10 20
 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 00 10 10 10 10 10 01 02 02 02 02 02 02 02 02 02
 10 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
 01 01 01 01 01 01 01 01 01 01 01 01 01 01 10 10
 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
</map>
</ctype>

<lower>
<map>
 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
 40 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
 70 71 72 73 74 75 76 77 78 79 7A 5B 5C 5D 5E 5F
 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F
 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
 A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
 B0 A7 A8 A9 AA AB B6 B7 B8 B9 BA BB BC BD BE BF
 C0 C1 AF C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
 D0 D1 D2 D3 AC AD AE D7 D8 D9 DA DB DC DD DE DF
 E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
</map>
</lower>

<upper>
<map>
 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
 60 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F
 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
 A0 A1 A2 A3 A4 A5 A6 B1 B2 B3 B4 B5 D4 D5 D6 C2
 B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
 C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
 D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
 E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
</map>
</upper>


<unicode>
<map>
0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000A 000B 000C 000D 000E 000F
0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001A 001B 001C 001D 001E 001F
0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002A 002B 002C 002D 002E 002F
0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003A 003B 003C 003D 003E 003F
0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004A 004B 004C 004D 004E 004F
0050 0051 0052 0053 0054 0055 0056 0057 0058 0059 005A 005B 00A5 005D 005E 005F
0060 0061 0062 0063 0064 0065 0066 0067 0068 0069 006A 006B 006C 006D 006E 006F
0070 0071 0072 0073 0074 0075 0076 0077 0078 0079 007A 007B 007C 007D 203E 007F
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0000 FF61 FF62 FF63 FF64 FF65 FF66 FF67 FF68 FF69 FF6A FF6B FF6C FF6D FF6E FF6F
FF70 FF71 FF72 FF73 FF74 FF75 FF76 FF77 FF78 FF79 FF7A FF7B FF7C FF7D FF7E FF7F
FF80 FF81 FF82 FF83 FF84 FF85 FF86 FF87 FF88 FF89 FF8A FF8B FF8C FF8D FF8E FF8F
FF90 FF91 FF92 FF93 FF94 FF95 FF96 FF97 FF98 FF99 FF9A FF9B FF9C FF9D FF9E FF9F
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
</map>
</unicode>


<collation name="jisx0201_general_ci">
<map>
 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
 60 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F
 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
 A0 A1 A2 A3 A4 A5 A6 B1 B2 B3 B4 B5 D4 D5 D6 C2
 B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
 C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
 D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
 E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
</map>
</collation>

<collation name="jisx0201_bin" flag="binary"/>

</charset>

</charsets>

<ctype> は各文字のタイプ。次のビットを論理和で指定。

ビット 意味
01 大文字
02 小文字
04 数字
08 空白(TABや改行コード等)
10 記号
20 制御文字
40 空白(SPACE)
80 16進数文字

ア〜ン は大文字として 01ァ ィ ゥ ェ ォ ャ ュ ョ ッ は小文字として 02。 「 」 、 ・ ー ゙ ゚ は記号として 10 を指定してる。

<lower>, <upper> は小文字化、大文字化したときのコード。 たとえば <lower> の「」(0xB1) の位置に「」(0xA7) を書いて、<upper> の「」(0xA7) の位置には「」(0xB1) を書いてある。

<unicode> は各文字に対応するユニコードのコードポイントを書く。 ちゃんと 0x5C は「¥」(U+00A5)、0x7E は「」(U+203E)にしてる。

mysqld を再起動するとこのファイルが読み込まれ、charset と collation に反映される。

mysql> SHOW CHARSET LIKE 'jis%';
+----------+-------------+---------------------+--------+
| Charset  | Description | Default collation   | Maxlen |
+----------+-------------+---------------------+--------+
| jisx0201 | JIS X 0201  | jisx0201_general_ci |      1 |
+----------+-------------+---------------------+--------+
1 row in set (0.00 sec)

mysql> SHOW COLLATION LIKE 'jis%';
+---------------------+----------+------+---------+----------+---------+---------------+
| Collation           | Charset  | Id   | Default | Compiled | Sortlen | Pad_attribute |
+---------------------+----------+------+---------+----------+---------+---------------+
| jisx0201_bin        | jisx0201 | 1235 |         |          |       0 | PAD SPACE     |
| jisx0201_general_ci | jisx0201 | 1234 | Yes     |          |       0 | PAD SPACE     |
+---------------------+----------+------+---------+----------+---------+---------------+
2 rows in set (0.00 sec)

mysql> CREATE TABLE x (c varchar(255)) CHARSET jisx0201;
Query OK, 0 rows affected (0.08 sec)

数字や英字や半角カタカナは保存できるけど:

mysql> INSERT INTO x VALUES ('012'),('ABC'),('アイウ');
Query OK, 3 rows affected (0.02 sec)
Records: 3  Duplicates: 0  Warnings: 0

バックスラッシュやチルダは保存できない:

mysql> INSERT INTO x VALUES ('\\');
ERROR 1366 (HY000): Incorrect string value: '\' for column 'c' at row 1

mysql> INSERT INTO x VALUES ('~');
ERROR 1366 (HY000): Incorrect string value: '~' for column 'c' at row 1

円マークやオーバーラインは保存できる:

mysql> INSERT INTO x VALUES ('¥‾');
Query OK, 1 row affected (0.02 sec)

ちゃんと JIS X 0201 のコードとして保存されてる:

mysql> SELECT c,HEX(c) FROM x;
+-----------+--------+
| c         | HEX(c) |
+-----------+--------+
| 012       | 303132 |
| ABC       | 414243 |
| アイウ       | B1B2B3 |
| ¥‾        | 5C7E   |
+-----------+--------+
4 rows in set (0.00 sec)

対応している文字は LOWER() で小文字化できる:

mysql> SELECT LOWER(c) FROM x;
+-----------+
| LOWER(c)  |
+-----------+
| 012       |
| abc       |
| ァィゥ       |
| ¥‾        |
+-----------+
4 rows in set (0.00 sec)

「ア」と「ァ」は同じ:

mysql> SELECT 'ア'='ァ';
+-------------+
| 'ア'='ァ'     |
+-------------+
|           1 |
+-------------+
1 row in set (0.00 sec)

ちゃんと動いてそう! お手軽ですね!