Rails + MySQL(InnoDB)で絵文字を使う

## なぜ絵文字をデフォルトで使えないか
- utf8は3バイト文字までしか対応していない
- 絵文字は4バイトなので不正な文字として扱われる

## 絵文字をMysqlで扱うには
- 文字コードをutf8mb4にする
	- 参考（MySQLのutf8mb4 文字セットについて）：https://dev.mysql.com/doc/refman/5.6/ja/charset-unicode-utf8mb4.html


Rails + MySQLでのutf8mb4の設定フローは下記のとおり。

## 1. my.cnfを設定

``` my.cnf
innodb_large_prefix = ON
innodb_file_format = Barracuda
```

### innodb_large_prefix
- のちに ```ERROR 1071 (42000): Specified key was too long; max key length is 767 bytes``` とエラーが出ることがある。
- これは767byte問題ともいわれる。InnoDBテーブルのキープリフィックスは最高で767バイトの長さまで可能であり、たとえばutf8でvarchar(255)を使うことはよくあるが、255 * 3 = 755byteで許容範囲だったものが、uth8mb4に変更すると255 * 4 > 767byteとなり、キープレフィックスの許容範囲を超え上記エラーが出る。
- つまり最大長は、UTF8の場合：767 ÷ 3 ≒ 255となり、UTF8MB4なら、767 ÷ 4 ≒ 191である。
- innodb_large_prefixはインデックスの最大長を3072byteまで引き上げるので、上記エラーを防ぐために設定する
- innodb_large_prefixのドキュメント：http://dev.mysql.com/doc/refman/5.6/en/innodb-restrictions.html

### innodb_file_format = Barracuda
- InnoDBのfile formatは、昔からあるAntelopeと新しいBarracudaがある。Barracudaには新機能が追加おり、その一つにインデックスサイズ制限の拡張がある。なので、innodb_large_prefixを使うには必須の設定事項となる
- また、Barracudaはデータベースのバイナリーファイルを圧縮する機能をもつ。（たとえば、よく利用するデータは圧縮せずに展開したままにして、メモリー上に一時的に展開する）

### AWS上（RDS）の設定を変更する場合
- RDSのパラメータグループより上記設定が可能

## 2. mysqld restart
- 設定を読み直す

## 3. Railsでmigrate時にrow_formatを指定する

``` config/initializers/ar_innodb_row_format.rb
ActiveSupport.on_load :active_record do
  module ActiveRecord::ConnectionAdapters

    class AbstractMysqlAdapter
      def create_table_with_innodb_row_format(table_name, options = {})
        table_options = options.merge(:options => 'ENGINE=InnoDB ROW_FORMAT=DYNAMIC')
        create_table_without_innodb_row_format(table_name, table_options) do |td|
          yield td if block_given?
        end
      end
      alias_method_chain :create_table, :innodb_row_format
    end

  end
end
```

### row_format
- [公式](http://dev.mysql.com/doc/refman/5.6/en/innodb-row-format-dynamic.html)より引用： *When a table is created with ROW_FORMAT=DYNAMIC or ROW_FORMAT=COMPRESSED, long column values are stored fully off-page, and the clustered index record contains only a 20-byte pointer to the overflow page.*
- つまり可変長カラムを20byteに圧縮するので、InnoDB 8KBの壁（InnoDBの行の最大長は約8KBで、これを超えようとするとエラーが発生する）を回避することができる

## 4. データベースの文字コードを修正する
``` 
alter database <DBNAME> character set utf8mb4; 
```

## 5. 既存のrow_formatデータを修正する
```
alter table <TABLENAME> ROW_FORMAT=DYNAMIC;
```

## 6. 既存のテーブルの文字コードをutf8mb4に修正する
```
alter table <TABLENAME> convert to character set utf8mb4;
```

## 7. Circle Ciへ変更を反映
もしCIツールとしてCircle Ciを使っているのであれば、

``` circle.yml
database:
  override:
    - mysql -u root -e 'set global innodb_file_format = Barracuda'
    - mysql -u root -e 'set global innodb_large_prefix = 1'
    - export RAILS_ENV=test; export RACK_ENV=test; bundle exec rake db:migrate:reset --trace
```
上記のようにoverrideする必要がある


## Tips
### ワンライナー
```
(mysql -uroot -p <DATABASE_NAME> -e "show tables" --batch --skip-column-names | xargs -I{} echo 'alter table `'{}'` convert to character set utf8mb4;') > /tmp/alters.sql
```
上記のようにxargsを用いてmysqlコマンドファイルを作成し、

```
cat /tmp/alters.sql | mysql -uroot -p ...
```
でテーブル全てに反映する

### 参考
- http://nippondanji.blogspot.jp/2009/01/innodb_16.html
- http://nippondanji.blogspot.jp/2010/03/innodb-plugin.html
- http://qiita.com/kenjiszk/items/56f0fa58fba84b686401
- http://kirikiriyamama.hatenablog.com/entry/2015/05/14/175653