はじめに
Railsアプリケーション作成中、MySQLで以下エラーが発生したため解決した方法を残します。
エラー内容
rails db:migrate
を実行したときに発生。
Mysql2::Error: Specified key was too long; max key length is 767 bytes: <キー名>
key長の制限が最大767バイトになっているのに、それを超えてしまったことで発生したようです。
解決法は大きく2つ
- 767バイトを超えないように何かを削る
- 767バイトを超えてもいいようにする
今回は**1.**で対応してみます。
環境
OS: macOS Catalina 10.15.3
Ruby: 2.6.5
Rails: 6.0.2.1
MySQL: 5.7
結論:解決法
以下のようなmysql.rb
をconfig/initializer
配下に新規作成します。
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
なぜ解決するのか?
解決はしたのですが、理由を理解しておきたかったので深堀ります。
前提:文字コードutf8
とutf8mb4
の違い
こちらの記事で詳しく解説されていました。
RailsやCakePHPのデフォルトで生成される255文字のVARCHARでは、3バイトのutf8では255×3=765でほぼぴったり収まります。一方、utf8mb4ではオーバーしてしまうので、エラーとなりました。
今回自分の環境でもutf8mb4
を使用していました。
utf8mb4
では1文字4バイト、
そのため、255 × 4 = 1020バイトとなり、冒頭の767バイトを超えてエラーが発生したということですね。
その他、utf8
とutf8mb4
の違いについては以下記事もわかりやすく解説をされていました。
MySQL で utf8 と utf8mb4 の混在で起きること - @tmtms のメモ
では、これを踏まえて今回のmysql.rb
でなぜ解決したのかを確認します。
今回作成したファイルで変わる設定
今回のmysql.rb
はActiveRecord内の
NATIVE_DATABASE_TYPES
という設定を上書きするファイルになっています。
該当のソースはこちら
ソースから当記事に関連する部分だけ抜き出すと、
module ActiveRecord
module ConnectionAdapters
class AbstractMysqlAdapter < AbstractAdapter
#...略
NATIVE_DATABASE_TYPES = {
primary_key: "bigint auto_increment PRIMARY KEY",
string: { name: "varchar", limit: 255 }, #ここ
text: { name: "text" },
integer: { name: "int", limit: 4 },
float: { name: "float", limit: 24 },
decimal: { name: "decimal" },
datetime: { name: "datetime" },
timestamp: { name: "timestamp" },
time: { name: "time" },
date: { name: "date" },
binary: { name: "blob" },
blob: { name: "blob" },
boolean: { name: "tinyint", limit: 1 },
json: { name: "json" },
}
#...略
#その他、change_columnメソッドのソース(どんなSQLを発行するかの定義)などが記載
end
end
end
先程の、mysql.rb
ではここの一行を上書きしていることになります。
string: { name: "varchar", limit: 255 }
つまり、Railsで
$ rails g model User name:string
などでString型を設定したらMySQLでは自動的に**VARCHAR(255)**で設定されるということです。
これをmysql.rb
を追加することで、**VARCHAR(191)**に変更が出来ます。
つまり、767バイト ÷ 4バイト = 191.75文字を超えないので、エラー解決となります。
では、設定変更が出来ているかを確認します。
確認
rails console
にて確認します。
[1] pry(main)> ActiveRecord::Base.connection.native_database_types
=> {:primary_key=>"bigint auto_increment PRIMARY KEY",
:string=>{:name=>"varchar", :limit=>255}, #limitが255になっている
#...以下略
↓
[2] pry(main)> ActiveRecord::Base.connection.native_database_types
=> {:primary_key=>"bigint auto_increment PRIMARY KEY",
:string=>{:name=>"varchar", :limit=>191}, #ここがmysql.rbで設定した内容に変わる
#...以下略
mysql.rb
によって設定が変更されていることが確認出来ました!
以上です!
おわりに
最後まで読んで頂きありがとうございました
どなたかの参考になれば幸いです