MySQLのデータベースをutf8で作成していたけれども、どうもiPhoneやAndroidから絵文字を送るとエラーになってしまっている。
それでちょっと調べてみたところ、どうやら絵文字は4バイトUTF-8で送られてくるみたい。
MySQL 5.5.3以降はその4バイトUTF-8に対応しているようなので、カラムの文字コードを4バイトUTF-8(utf8mb4)にしてやる必要がある。
データベースを最初から作る場合
最初から作れる場合、または作り直せる場合は、文字コードを「utf8」ではなく「utf8mb4」で定義してやればいい。
すでにutf8で作ってしまっている場合
その場合は、絵文字を格納したいカラムに対して、文字コードを変更します。
例えば、データベースdb1のテーブルtable1にあるcomment (text)に対して格納したい場合、
alter table table1 modify comment text character set utf8mb4;
としてやる。
最終的に4バイト文字が格納されるところがutf8mb4になっていればいいみたいなので、他はutf8のままでも大丈夫そう。
テーブルのデフォルト文字コードを変更する場合は、
alter table table1 default character set utf8mb4;
とします。
これで今後追加するカラムはutf8mb4になる。
データベースごとやる場合はこう。
alter database db1 default character set utf8mb4
utf8で動いていたものをutf8mb4にして影響ないかというところについては、とりあえず大丈夫そうだと思います。4バイト文字も格納できるようになったという、いわば拡張ですし。
(稼働中のシステムに適用するなら、一応バックアップを取るとか、別の環境にデータだけ持ってきて試すとかしてみるといいかとは思いますが)
接続クライアントの文字コード設定
データベース側はこれでいいのですが、接続するクライアント側の文字コードもutf8mb4にしてやらないといけません。
そうしないと送信時に弾いてくれているみたい。
設定についてはそれぞれの言語、クライアントのものを参照してもらってという感じですが、一例としてPHPのPDOでは、接続時のオプション「PDO::MYSQL_ATTR_INIT_COMMAND」に、
SET NAMES utf8mb4
として指定してやっています。
…SET NAMESがうちの環境ではとりあえず上手くいったんですよねー…まあ、送信側のコードも変えるという話で。
横着な話
よく文字コードがらみで忌み嫌われている「latin1」ですが、クライアントもデータベースもこれにすると文字コード変換が発生せず、すべてのバイトをノーチェックノー変換で格納できたからSJIS/EUC時代の絵文字はわざとこれにしてそのままたたき込んでいたこともありました。
いつごろからか文字コードの範囲内かをチェックして弾いてくれるようになってて、それはいいんだけどケータイの絵文字とか範囲外にばんばん入ってるもんだから結果として絵文字が格納できないみたいな事態になってたんですよねえ。確か。
文字コードは結局のところ、入力と出力のつじつまが最終的に合っていればいい。変換したときに情報が落ちなければ(Javaとかみたいに????に置き換えられたりしなければ)一時的に読めないバイトになっても化けない。みたいなふうに調整して。
まあバッドノウハウなのですが、絵文字がらみでそういう手があったのも思い出した。