最初にざっくりまとめ
-
Rails7.0.8
でsqlite
のchange_column
に対して変更が入った - その影響で以前とは振る舞いが変わっている
-
change_column
で指定した要素以外の設定がデフォルトに戻ってしまう
-
-
mysql
では起きない(検証してないけど多分sqlite
以外は大丈夫)
つまり、 Rails7.0.8
でsqlite
に対してchange_column
するときは必要な全ての要素を指定しよう
調べたこと
Rails7.0.8
のリリースノート
色々と機能がリリースされていますが、今回の事象に関わるのは以下。この修正でsqlite
のchange_column
に対して実装が追加されています。
Fix change_column not setting precision: 6 on datetime columns when using 7.0+ Migrations and SQLite.
Hartley McGuire
Rails7.0.7.2
とRails7.0.8
で挙動比較
検証バージョン
- Ruby: 3.2.2
- sqlite3: 3.39.2
マイグレーションファイルを2つ用意します。一つはテーブル作成で、その際にdefault
とnull
を指定しておきます。もう一つはカラム変更で、属性としてコメントだけを指定します。命名が適当なのは許して🙏
class CreateTestTable < ActiveRecord::Migration[7.0]
def change
create_table :test_tables do |t|
t.string :name, null: false, default: 'name'
t.timestamps
end
end
end
class ChangeTestTableColumn < ActiveRecord::Migration[7.0]
def change
change_column :test_tables, :name, :string, comment: 'changed'
end
end
この二つのマイグレーションを実行すると、sqlite
のテーブル定義は以下のようになりました。
CREATE TABLE IF NOT EXISTS "test_tables" (
"id" integer PRIMARY KEY AUTOINCREMENT NOT NULL,
"name" varchar DEFAULT 'name' NOT NULL,
"created_at" datetime(6) NOT NULL,
"updated_at" datetime(6) NOT NULL
);
CREATE TABLE IF NOT EXISTS "test_tables" (
"id" integer NOT NULL PRIMARY KEY,
"name" varchar, -- DEFAULT 'name' NOT NULL が消えてしまった!
"created_at" datetime(6) NOT NULL,
"updated_at" datetime(6) NOT NULL
);
Rails7.0.7.2
以前は、change_column
で指定した要素以外は、それまでの設定を引き継いでくれていました。
Rails7.0.8
では、change_column
で指定した要素以外の設定はデフォルトに戻るようになりました。
ちなみに、mysql
を使った場合はどちらのバージョンでもRails7.0.7.2
xsqlite
の挙動と同じでした。
解決策
change_column
を使うときは、サボらず必要な要素を全部書きましょう。
class ChangeTestTableColumn < ActiveRecord::Migration[7.0]
def change
change_column :test_tables, :name, :string, null: false, default: 'name', comment: 'changed'
end
end
終わりに
今回はテストが通らなくなったことでこの事象に気づくことができました。テストって大事👏
本番環境でsqlite
を使っているプロダクトは少ないと思うので、そこまで影響はないと思いますが、振る舞いが変わるのは怖いですね。バージョンアップする際は動作確認しっかりやりましょう💪