Help us understand the problem. What is going on with this article?

Rails 5 と MySQL 5.6 環境における utf8mb4 を扱う設定について

More than 1 year has passed since last update.

すでに Rails 環境における utf8mb4 の扱いに関しては多くの記事が出ているため新しいことは何も書いてありません。
Rails 5 と現状 MySQL 5.6 compatible な Aurora の環境での考察です。

環境

  • Rails 5.0.1
  • MySQL 5.6.22

なぜ 5.6 か :question:

Amazon Aurora を利用しており、まだ MySQL 5.7 compatible になっていないため 5.6 系である必要がありました。

今回利用する Charset と Collation

Charset は utf8mb4 に、Collation は utf8mb4_bin にすることとします。

Charset や Collation に関しては、直近で以下の素晴らしい資料があがっておりました。すごく詳しくてありがたい。
MySQLの文字コード事情 2017版 - SlideShare

ただ、今回はサーバー側も utf8mb4 で揃えるということはできないのですが。

Rails の設定

今回私が気にした点は以下の 2 点のみです。

  • database.yml の設定
  • character を入れるカラムのインデックスに関して

database.yml の設定

database.yml
development:
  adapter: mysql2
  pool: 5
  username: root
  password:
  database: utf8mb4_sample_development
  charset: utf8mb4
  collation: utf8mb4_bin
  encoding: utf8mb4

以降では、ここで設定した charset, collation, encoding がどういう役割を担っていくかの一部を見ていきます。

charset と collation 設定

上記の database.yml に設定した状態で、bin/rails db:create を行うと以下のようになっています。

character_set_database 以外はサーバー自体の設定が utf8 なので気にしないでください。 私が利用している Aurora の設定に合わせてあるだけです。

mysql> use utf8mb4_sample_development;
mysql> show variables like "chara%";
+--------------------------+------------------------------------------------------+
| Variable_name            | Value                                                |
+--------------------------+------------------------------------------------------+
| character_set_client     | utf8                                                 |
| character_set_connection | utf8                                                 |
| character_set_database   | utf8mb4                                              |
| character_set_filesystem | binary                                               |
| character_set_results    | utf8                                                 |
| character_set_server     | utf8                                                 |
| character_set_system     | utf8                                                 |
| character_sets_dir       | /usr/local/Cellar/mysql/5.6.22/share/mysql/charsets/ |
+--------------------------+------------------------------------------------------+
8 rows in set (0.00 sec)

mysql> SHOW CREATE DATABASE utf8mb4_sample_development;
+----------------------------+------------------------------------------------------------------------------------------------------------+
| Database                   | Create Database                                                                                            |
+----------------------------+------------------------------------------------------------------------------------------------------------+
| utf8mb4_sample_development | CREATE DATABASE `utf8mb4_sample_development` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin */ |
+----------------------------+------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

デフォルトデータベースの character_set_databaseutf8mb4 になっており、 Database としてもデフォルトの charset と collate が狙い通りになっているのが分かります。

ではこの状態でテーブルを作ってみます。

migration/create_book.rb
class CreateBookTable < ActiveRecord::Migration[5.0]
  def change
    create_table :books do |t|
      t.string :title

      t.timestamps
    end
  end
end
% bin/rails db:migrate
config/schema.rb
  create_table "book_tables", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin" do |t|
    t.string   "title"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end
mysql> SHOW CREATE TABLE books;
+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                                                                                                                                                                 |
+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| books | CREATE TABLE `books` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin |
+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> show full columns from books;
+------------+--------------+-------------+------+-----+---------+----------------+---------------------------------+---------+
| Field      | Type         | Collation   | Null | Key | Default | Extra          | Privileges                      | Comment |
+------------+--------------+-------------+------+-----+---------+----------------+---------------------------------+---------+
| id         | int(11)      | NULL        | NO   | PRI | NULL    | auto_increment | select,insert,update,references |         |
| title      | varchar(255) | utf8mb4_bin | YES  |     | NULL    |                | select,insert,update,references |         |
| created_at | datetime     | NULL        | NO   |     | NULL    |                | select,insert,update,references |         |
| updated_at | datetime     | NULL        | NO   |     | NULL    |                | select,insert,update,references |         |
+------------+--------------+-------------+------+-----+---------+----------------+---------------------------------+---------+
4 rows in set (0.01 sec)

テーブル、カラムの設定も狙い通りの charset, collate になっていることが確認できています。

では試しに 4 byte 文字を入れてみます。

mysql> INSERT INTO books (title, created_at, updated_at) VALUES ('😇', NOW(), NOW());
Query OK, 1 row affected, 1 warning (0.01 sec)

mysql> SHOW warnings;
+---------+------+------------------------------------------------------------------------+
| Level   | Code | Message                                                                |
+---------+------+------------------------------------------------------------------------+
| Warning | 1366 | Incorrect string value: '\xF0\x9F\x98\x87' for column 'title' at row 1 |
+---------+------+------------------------------------------------------------------------+
1 row in set (0.00 sec)

お、Warning が出ていますね。 Incorrect となっています。中を見てみても以下のように正しく登録できていません。

mysql> SELECT * FROM books;
+----+-------+---------------------+---------------------+
| id | title | created_at          | updated_at          |
+----+-------+---------------------+---------------------+
|  1 | ????  | 2017-02-02 17:00:39 | 2017-02-02 17:00:39 |
+----+-------+---------------------+---------------------+

最初に紹介したスライドにもあるようにクライアント側が utf8 になっているためです。(かつ strict にはしていないので登録もされています)

なので、utf8mb4 にします。

mysql> SET NAMES 'utf8mb4';
Query OK, 0 rows affected (0.00 sec)

mysql> show variables like "chara%";
+--------------------------+------------------------------------------------------+
| Variable_name            | Value                                                |
+--------------------------+------------------------------------------------------+
| character_set_client     | utf8mb4                                              |
| character_set_connection | utf8mb4                                              |
| character_set_database   | utf8mb4                                              |
| character_set_filesystem | binary                                               |
| character_set_results    | utf8mb4                                              |
| character_set_server     | utf8                                                 |
| character_set_system     | utf8                                                 |
| character_sets_dir       | /usr/local/Cellar/mysql/5.6.22/share/mysql/charsets/ |
+--------------------------+------------------------------------------------------+
8 rows in set (0.00 sec)
mysql> SELECT * FROM books;
+----+-------+---------------------+---------------------+
| id | title | created_at          | updated_at          |
+----+-------+---------------------+---------------------+
|  1 | ????  | 2017-02-02 17:00:39 | 2017-02-02 17:00:39 |
|  2 | 😇      | 2017-02-02 17:02:43 | 2017-02-02 17:02:43 |
+----+-------+---------------------+---------------------+
2 rows in set (0.00 sec)

無事登録できていますね :tada:

ついでにクライアントが utf8 の場合には登録できた 4 byte 文字を正しく読めないことも確認しておきます。

mysql> SET NAMES 'utf8';
Query OK, 0 rows affected (0.00 sec)

mysql> show variables like "chara%";
+--------------------------+------------------------------------------------------+
| Variable_name            | Value                                                |
+--------------------------+------------------------------------------------------+
| character_set_client     | utf8                                                 |
| character_set_connection | utf8                                                 |
| character_set_database   | utf8mb4                                              |
| character_set_filesystem | binary                                               |
| character_set_results    | utf8                                                 |
| character_set_server     | utf8                                                 |
| character_set_system     | utf8                                                 |
| character_sets_dir       | /usr/local/Cellar/mysql/5.6.22/share/mysql/charsets/ |
+--------------------------+------------------------------------------------------+
8 rows in set (0.00 sec)
mysql> SELECT * FROM books;
+----+-------+---------------------+---------------------+
| id | title | created_at          | updated_at          |
+----+-------+---------------------+---------------------+
|  1 | ????  | 2017-02-02 17:00:39 | 2017-02-02 17:00:39 |
|  2 | ?     | 2017-02-02 17:02:43 | 2017-02-02 17:02:43 |
+----+-------+---------------------+---------------------+
2 rows in set (0.00 sec)

読めていませんね。期待通りに動作しました。

SET NAMES がやっていること

先ほど SET NAMES utf8mb4 を実行しましたが、挙動を見て分かる通り以下のような事をやっています。

https://dev.mysql.com/doc/refman/5.6/ja/charset-connection.html

SET NAMES 'charset_name' [COLLATE 'collation_name']

SET NAMES は、クライアントからサーバーへの SQL ステートメントの送信に使用される文字セットを示します。したがって、SET NAMES 'cp1251' は、「このクライアントから今後受信するメッセージが文字セット cp1251 で送信される」ことを、サーバーに知らせます。また、クライアントに結果を返信するときにサーバーが使用する文字セットも指定します。(たとえば、SELECT ステートメントを使用する場合に、カラム値に使用する文字セットを指定します。)

SET NAMES 'charset_name' ステートメントは次の 3 つのステートメントと同等です。

SET character_set_client = charset_name;
SET character_set_results = charset_name;
SET character_set_connection = charset_name;

SET NAMES で変更される 3 つの変数は クライアント側の文字セット (character set) に関するものであり、以下の説明がされています。

https://dev.mysql.com/doc/refman/5.6/ja/charset-connection.html

  • クライアントから送信されるときに、ステートメントはどの文字セットで送信されますか。

サーバーは、character_set_client システム変数値を、クライアントが送信するステートメントの文字セットにします。

  • ステートメントを受信したあとで、サーバーはこれをどの文字セットに変換しますか。

これには、サーバーは character_set_connection および collation_connection システム変数値を使用します。クライアントから送信されたステートメントは、character_set_client から character_set_connection に変換されます (latin1 や _utf8 などのイントロデューサがある文字列リテラルを除きます)。collation_connection はリテラル文字列の比較で重要です。カラム値のある文字列の比較には、collationconnection は重要視されません。なぜなら、カラムには独自の照合順序があり、この照合順序が優先されるからです。

  • 結果セットまたはエラーメッセージをクライアントに返送する前に、サーバーはこれらをどの文字セットに変換しますか。

character_set_results システム変数値は、サーバーがクライアントにクエリー結果を返信するときに使用する文字セットを示します。これには、カラム値などの結果データと、カラム名やエラーメッセージなどの結果メタデータが含まれます。

SET NAMES と encoding 設定

これは Rails では encoding の設定をすることによって有効にできます。

http://edgeapi.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/MysqlAdapter.html

:encoding - (Optional) Sets the client encoding by executing “SET NAMES ” after connection.

実際のコードを見ると以下のようになっています。collation に関しても設定されていることがわかりますね。

https://github.com/rails/rails/blob/master/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb#L887-L907

rails/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
module ActiveRecord
  module ConnectionAdapters
    class AbstractMysqlAdapter < AbstractAdapter
        def configure_connection
          # ....

          # NAMES does not have an equals sign, see
          # http://dev.mysql.com/doc/refman/5.7/en/set-statement.html#id944430
          # (trailing comma because variable_assignments will always have content)
          if @config[:encoding]
            encoding = "NAMES #{@config[:encoding]}"
            encoding << " COLLATE #{@config[:collation]}" if @config[:collation]
            encoding << ", "
          end

          # Gather up all of the SET variables...
          variable_assignments = variables.map do |k, v|
            if defaults.include?(v)
              "@@SESSION.#{k} = DEFAULT" # Sets the value to the global or compile default
            elsif !v.nil?
              "@@SESSION.#{k} = #{quote(v)}"
            end
            # or else nil; compact to clear nils out
          end.compact.join(", ")

          # ...and send them all in one query
          execute "SET #{encoding} #{sql_mode_assignment} #{variable_assignments}"
        end

よって、 encoding の設定が正しくしてあれば Rails から実行する場合には問題は起きません。

Loading development environment (Rails 5.0.1)
irb(main):001:0> Book.create(title: '😖')
   (0.2ms)  BEGIN
  SQL (3.8ms)  INSERT INTO `books` (`title`, `created_at`, `updated_at`) VALUES ('😖', '2017-02-02 08:22:47', '2017-02-02 08:22:47')
   (2.4ms)  COMMIT
=> #<Book id: 3, title: "😖", created_at: "2017-02-02 08:22:47", updated_at: "2017-02-02 08:22:47">
irb(main):004:0> b= Book.last
  Book Load (13.8ms)  SELECT  `books`.* FROM `books` ORDER BY `books`.`id` DESC LIMIT 1
=> #<Book id: 3, title: "😖", created_at: "2017-02-02 08:22:47", updated_at: "2017-02-02 08:22:47">
irb(main):005:0> b.title
=> "😖"

database.yml の設定まとめ

ここまでで database.yml に指定した charset, collation, encoding がどのような役割をしたかを確認しました。

ひとまずはこの設定があれば utf8mb4 対応はできたことになります。

次に、だいたいぶつかるインデックスの課題について見ていきます。

character を入れるカラムのインデックスに関して

MySQL(InnoDB) で "Index column size too large. The maximum column size is 767 bytes." いわれるときの対策 - かみぽわーる

上記の記事から引用しますと、

ActiveRecordでstring型のカラムを定義すると、MySQLだとvarchar(255)になるので、utf8mb4だとインデックス張ると767バイトを超えてしまう。

という自体が発生します。

これに関しては回避方法は多くあると思いますが、ここでは以下の 3 つに絞って考察します。

  1. キープレフィックスの制限を拡張
  2. カラムの文字数を制限
  3. カラムの charset を変更

:one: キープレフィックスの制限を拡張

これに関しては、上記の記事でも解決法としてあげられています。

では Aurora 環境ではどうすればよいかですが、Aurora でも同様の事が実現可能と思われます。(というのも、実際やってはいない。。)
パラメータ的には可能だろうというものです。

  • innodb_file_format
    • パラメータグループからオンラインで変更可能
  • innodb_large_prefix
    • パラメータグループからオンラインで変更可能
  • innodb_file_per_table
    • パラメータグループには無いが、設定を見ると元から 1 になっている
  • ROW_FORMAT
    • Rails の create_table オプションに渡す (5.6 系なので)
db/migrate/create_book.rb
create_table :books, options: 'ROW_FORMAT=DYNAMIC' do |t|
db/schema.rb
  create_table "books", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC" do |t|

file_format に関しては以下の記事が勉強になりました。
InnoDBの制限とファイルフォーマットAntelopeとBarracudaの違い - かみぽわーる

Rails での ROW_FORMAT に関しては以下が参考になります。

:two: カラムの文字数を制限

インデックスに使うカラムを 191 文字以下に制限するという方法です。

一時期は schema_migrations テーブルの version カラムに関してもこの対応が取られていたのでしょうか。
https://github.com/rails/rails/pull/17601

最も単純にやるなら以下の方法で良さそうです。

db/migrate/create_book.rb
class CreateBook < ActiveRecord::Migration[5.0]
  def change
    create_table :books do |t|
      t.string :title, limit: 191, index: true

      t.timestamps
    end
  end
end
db/schema.rb
  create_table "books", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin" do |t|
    t.string   "title",      limit: 191
    t.datetime "created_at",             null: false
    t.datetime "updated_at",             null: false
    t.index ["title"], name: "index_books_on_title", using: :btree
  end

:three: カラムの charset を変更

4byte の charset でなければ良いので例えばそのカラムが utf8 でも問題無いのなら、カラムの charset を変更すれば文字数の最大値を減らさなくてもすみます。

:bulb: 当然ではありますが、charset を変えるということはインデックスを付けるカラムには 4byte 文字は入らなくなるのでその点は注意してください。

方法に関してですが以下のように charset を直接指定することもできます。

class CreateBook < ActiveRecord::Migration[5.0]
  def change
    create_table :books do |t|
      t.string :title, index: true, charset: :utf8

      t.timestamps
    end
  end
end

これで charset 自体は確かに問題なくインデックスも作成されるのですが、以下のように collate が意図しないものに変わります。

mysql> show full columns from books;
+------------+--------------+-----------------+------+-----+---------+----------------+---------------------------------+---------+
| Field      | Type         | Collation       | Null | Key | Default | Extra          | Privileges                      | Comment |
+------------+--------------+-----------------+------+-----+---------+----------------+---------------------------------+---------+
| id         | int(11)      | NULL            | NO   | PRI | NULL    | auto_increment | select,insert,update,references |         |
| title      | varchar(255) | utf8_general_ci | YES  | MUL | NULL    |                | select,insert,update,references |         |
| created_at | datetime     | NULL            | NO   |     | NULL    |                | select,insert,update,references |         |
| updated_at | datetime     | NULL            | NO   |     | NULL    |                | select,insert,update,references |         |
+------------+--------------+-----------------+------+-----+---------+----------------+---------------------------------+---------+
4 rows in set (0.00 sec)

ここに関しては Rails が schema_migrations に対して現在取っているアプローチが良さそうです。

https://github.com/rails/rails/blob/65bf1c60053e727835e06392d27a2fb49665484c/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb#L75-L81
(PR はこれかな? https://github.com/rails/rails/pull/23168)

rails/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
module ActiveRecord
  module ConnectionAdapters
    class AbstractMysqlAdapter < AbstractAdapter
      #...

      CHARSETS_OF_4BYTES_MAXLEN = ["utf8mb4", "utf16", "utf16le", "utf32"]

      def internal_string_options_for_primary_key # :nodoc:
        super.tap { |options|
          options[:collation] = collation.sub(/\A[^_]+/, "utf8") if CHARSETS_OF_4BYTES_MAXLEN.include?(charset)
        }
      end

これは collate の方を直しています。例えば utf8mb4_bin であったのなら utf8_bin になります。

この方法を用いると以下のように書けます。

class CreateBook < ActiveRecord::Migration[5.0]
  def change
    create_table :books do |t|
      t.string :title, index: true, collation: :utf8_bin

      t.timestamps
    end
  end
end

これを実行すると以下のような結果になります。

db/schema.rb
  create_table "books", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin" do |t|
    t.string   "title",                   collation: "utf8_bin"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["title"], name: "index_books_on_title", using: :btree
  end
mysql> show full columns from books;
+------------+--------------+-----------+------+-----+---------+----------------+---------------------------------+---------+
| Field      | Type         | Collation | Null | Key | Default | Extra          | Privileges                      | Comment |
+------------+--------------+-----------+------+-----+---------+----------------+---------------------------------+---------+
| id         | int(11)      | NULL      | NO   | PRI | NULL    | auto_increment | select,insert,update,references |         |
| title      | varchar(255) | utf8_bin  | YES  | MUL | NULL    |                | select,insert,update,references |         |
| created_at | datetime     | NULL      | NO   |     | NULL    |                | select,insert,update,references |         |
| updated_at | datetime     | NULL      | NO   |     | NULL    |                | select,insert,update,references |         |
+------------+--------------+-----------+------+-----+---------+----------------+---------------------------------+---------+
4 rows in set (0.01 sec)

mysql> SHOW CREATE TABLE books;
+-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                                                                                                                                                                                                                         |
+-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| books | CREATE TABLE `books` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  PRIMARY KEY (`id`),
  KEY `index_books_on_title` (`title`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin |
+-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

charset が utf8 になり collate も意図通りになりました。

character を入れるカラムのインデックスに関してのまとめ

4byte の character を扱うカラムのインデックスに関して、回避方法 3 つを調査しました。

1 で対応できるならば 1 が最もコードへの影響はなく良さそうに見えます。
2, 3 に関しては、最大文字長・charset の制限に対して要件として問題無いのであれば、選択しても良さそうです。

私個人としては DBA とも相談し、要件を鑑みた上で 2 で十分という事になり 2 を採用しました。

余談

ActiveRecord::ValueTooLong に関して

Rails5から使えるActiveRecord便利機能 - Qiita の記事で知ったのですが、 ActiveRecord::ValueTooLong が制限値を超えているケースで raise されるようになったようです。

同僚が昔に MySQL で制限値を超えた時に文字を切られて保存されたという事象にあたったと聞いたので、今回 limit を付けることもあり調査していました。

https://github.com/rails/rails/blob/fdef4b7baa7a5374058e3b97697d9531b57265c1/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb#L733-L769

module ActiveRecord
  module ConnectionAdapters
    class AbstractMysqlAdapter < AbstractAdapter
      # ...

      private

        # See https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html
        ER_DATA_TOO_LONG        = 1406

        def translate_exception(exception, message)
          case error_number(exception)
          when ER_DUP_ENTRY
            RecordNotUnique.new(message)
          # 略
          when ER_DATA_TOO_LONG
            ValueTooLong.new(message)
          when ER_OUT_OF_RANGE
            RangeError.new(message)
          # 略
          else
            super
          end
        end

上記の通り、この例外は MySQL の ER_DATA_TOO_LONG というエラーコードが投げられた時に変換されるもののようです。

B.3 Server Error Codes and Messages - MySQL

Error: 1406 SQLSTATE: 22001 (ER_DATA_TOO_LONG)

Message: Data too long for column '%s' at row %ld

このエラーは sql_mode が strict になっていなければ発行されません。 (STRICT_TRANS_TABLES, STRICT_ALL_TABLES, TRADITIONAL)
5.1.7 サーバー SQL モード - MySQL

ここまで見てきたように私が検証していた DB は strict ではありません。(Warning を出して不正な文字が登録されていた)

しかし Rails console から文字数制限を超える処理を実行すると ActiveRecord::ValueTooLong は発行されてきます。

strict が未指定の場合は true

以下は encoding の時も出てきた #configure_connection です。

https://github.com/rails/rails/blob/65bf1c60053e727835e06392d27a2fb49665484c/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb#L878-L893

rails/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
module ActiveRecord
  module ConnectionAdapters
    class AbstractMysqlAdapter < AbstractAdapter

      def strict_mode?
        self.class.type_cast_config_to_boolean(@config.fetch(:strict, true))
      end

      private

        def configure_connection
          # ....

          # Make MySQL reject illegal values rather than truncating or blanking them, see
          # http://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sqlmode_strict_all_tables
          # If the user has provided another value for sql_mode, don't replace it.
          if sql_mode = variables.delete("sql_mode")
            sql_mode = quote(sql_mode)
          elsif !defaults.include?(strict_mode?)
            if strict_mode?
              sql_mode = "CONCAT(@@sql_mode, ',STRICT_ALL_TABLES')"
            else
              sql_mode = "REPLACE(@@sql_mode, 'STRICT_TRANS_TABLES', '')"
              sql_mode = "REPLACE(#{sql_mode}, 'STRICT_ALL_TABLES', '')"
              sql_mode = "REPLACE(#{sql_mode}, 'TRADITIONAL', '')"
            end
            sql_mode = "CONCAT(#{sql_mode}, ',NO_AUTO_VALUE_ON_ZERO')"
          end
          sql_mode_assignment = "@@SESSION.sql_mode = #{sql_mode}, " if sql_mode

上記を見て分かる通り、 strict_mode?true の場合には STRICT_ALL_TABLESsql_mode に指定されることがわかります。

そして #strict_mode? を見ると、 @config 今回だと database.yml の設定から strict を fetch する際にデフォルトを true にしています。

実際に以下のように true が返ってくることがわかります。

irb(main):007:0> Book.connection.strict_mode?
=> true

よって、明示的に strictsql_mode を異なるものに指定しない限りは STRICT_ALL_TABLES が適用されているため、データベースにその設定をしていなくても Rails での実行時には ActiveRecord::ValueTooLongを受け取れるということになります。

dany1468
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした