Railsで4バイトUnicode絵文字を扱えるようにする

大量のテキストをDBに突っ込んでたら4バイト絵文字を混ぜてくるの試されてるとしか思えない。

ActiveRecord::StatementInvalid: Mysql2::Error: Incorrect string value: '\xF0\x9F\x92\x85 f...' for column 'message' at row 1

えーっと、このコードはどうやらハサミの絵文字のようです。

Unicode絵文字扱うにはutf8じゃなくてutf8mb4というのを使わないといけないっぽいので文字コード変更したメモ。
環境はRails4.2.0でMySQL5.6.23です。

まず文字コード変更してDBごと作り直しする。テーブル単位でも指定できるので、DBごと作りなおす必要はないのだけど、個人的に複数の文字コードが混ざってるの気持ち悪いのでDBごと作りなおす。必須ではないです。

mysql> DROP DATABASE db_development;
mysql> DROP DATABASE db_test;
mysql> DROP DATABASE db_production;
mysql> CREATE DATABASE db_development DEFAULT CHARACTER SET utf8mb4;
mysql> CREATE DATABASE db_test DEFAULT CHARACTER SET utf8mb4;
mysql> CREATE DATABASE db_production DEFAULT CHARACTER SET utf8mb4;

次にconfig/database.ymlでRails側の文字コード指定する。

default: &default
  adapter: mysql2
  charset: utf8mb4
  encoding: utf8mb4
  collation: utf8mb4_general_ci

あとこのままだと、単純にstringのバイト数が増えてMySQLデフォルトのインデックス対象カラムバイト数767を超えてエラーになるらしい。

Mysql2::Error: Specified key was too long; max key length is 767 bytes: CREATE UNIQUE INDEX

utf8だとvarchar(255)*3 = 765だけど、utf8mb4だとvarchar(255)*4 = 1020で767制限を超える。
MySQL側の設定で上限を上げることもできるようなのだけど、今回は特に文字列長下げても困らないのでstringのデフォルトの文字数を191に変更する。
具体的には、config/initializers/mysqlpls.rbというファイルを作って、以下のコードを書く。

require 'active_record/connection_adapters/abstract_mysql_adapter'

module ActiveRecord
  module ConnectionAdapters
    class AbstractMysqlAdapter
      NATIVE_DATABASE_TYPES[:string] = { :name => "varchar", :limit => 191 }
    end
  end
end

準備できたらマイグレーションする。

$ bundle exec rake db:migrate

これで4バイトUnicode絵文字がDBに格納できるようなった。

ちなみに絵文字が表示できるかはブラウザ依存でスマホとかならたぶん表示できるとは思うんだけど、PCブラウザはChrome41からやっと見えるぐらいで、基本的にブラウザに拡張入れるか、ブラウザ依存させないにはサーバ側で画像に置き換えないと見えないらしいです。今回用途は絵文字を表示したいわけではなく、テキスト処理データに絵文字が混ざってて困ってただけなので、DBに突っ込めればよいです。

参考