timestampの挙動が変わっていてどハマリしたので共有。
今回使うテーブル定義
CREATE TABLE `foobar` (
(省略)
`start_at` timestamp NOT NULL COMMENT '開始日時',
`end_at` timestamp COMMENT '終了日時',
(省略)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
事件の発端
end_at に値してないのに現在時刻が入っているよ?
という思ってたんと違う挙動をしてたのでいろいろ調べた。
ちなみにこれ自体は勘違いで、NULLのつもりだったけど
実際はNULLもNOT NULLも指定していないので、結果的にNOT NULLになるので正しい挙動。
調査した
show create table
でとってみたら下記のような状態になっていた。
CREATE TABLE `foobar` (
(省略)
`start_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '開始日時',
`end_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '終了日時',
(省略)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
start_at:
指定してないのに DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
が勝手に追加されていた。
プログラム上、値が絶対に入ることになっていたからどっちでもよかったけど、
想定してたものと挙動が変わるのは怖い。
しかもほかで DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
使ってたので影響あり。
end_at:
NOT NULL DEFAULT '0000-00-00 00:00:00'
が勝手に追加されてた。
NOT NULL
が設定されたのはNULL
, NOT NULL
の指定をしてなかったから。
DEFAULT '0000-00-00 00:00:00'
は上と同様の挙動で追加されたけど、
DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
をもうセットしてたから
DEFAULT '0000-00-00 00:00:00'
になったと思われる。
検証1 マイグレーションツールを使ってたせい
仮説
手動でクエリ叩いたわけじゃなくて、マイグレーション使ってたせいでは?
結果
手動でも変わらない
検証2 カラム名のせい
仮説
◯◯atって名前が原因では?
Railsとかで create_at とかよくあるし
結果
カラム名を変えて書き換えてみたが変わらなかった
ALTER TABLE request CHANGE start_at start_time timestamp NOT NULL COMMENT '開始日時';
ALTER TABLE request CHANGE end_at end_time timestamp COMMENT '終了日時';
検証3 デフォルト値が必須になった
仮説
timestampはデフォルト値を入れないと勝手に設定されるようになったのでは?
結果
デフォルト値を設定したらうまくいった
ALTER TABLE request CHANGE start_at start_at timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '開始日時';
ALTER TABLE request CHANGE end_at end_at timestamp NULL DEFAULT NULL COMMENT '終了日時';
ググった
MySQL 5.6からtimestamp 型の暗黙的なデフォルト値は非推奨とのことで、
5.6.6からの警告も出るようになったらしい
MySQL :: MySQL 5.6 Reference Manual :: 5.1.4 Server System Variables
参照元:
http://www.mk-mode.com/octopress/2013/05/31/mysql-5-6-timestamp-default-warning/
(#゚Д゚)ゴルァ!! しらんがな!!
教訓
- MySQLがマイナーバージョンアップしたらドキュメントはとりあえず隅々まで読んでおく!!
- ユニットテストでDBにデータがきちんとInsertされたらOKにしない。Insertされた中身もチェックする (DBの仕様変更で影響が出る場合あり)