​ ​

Postgresqlの日付として無限大(Infinity)/無限小(-Infinity)を利用して,RubyOnRails5.0からどう見えるのか調べました

以前「Postgresqlの日付として無限大(Infinity)/無限小(-Infinity)を利用して,RubyOnRails4.2.xからどう見えるのか調べました」という記事を書きました. Rails5 がリリースされましたので,Rails5 ではどうなっているか確認しましょう.

ところでこの記事が公開される日(2016-07-20)には,J2で現在首位にいる北海道コンサドーレ札幌とJ2で現在2位につける松本山雅FCの,1位2位直接対決の試合が19:00から札幌ドームで行われます. ワクワクドキドキしますね.私は仕事が終わり次第シュッとかけつける予定です.勝つぞ!おー!

Rails5系に対象のコミットが含まれているかを確認する

前回の記事では 5ff08055194963394c24742ae89f69e4e43567a4 というコミットで Infinity を扱えるようになったようだと書きました. それではそのコミットが rails のどのタグに含まれているかを調べましょう.

/Users/d-kitamura/src/rails% git tag --list --contains 5ff08055194963394c24742ae89f69e4e43567a4
v5.0.0
v5.0.0.beta1
v5.0.0.beta1.1
v5.0.0.beta2
v5.0.0.beta3
v5.0.0.beta4
v5.0.0.rc1
v5.0.0.rc2

おお v5.0.0!.使えそうですね.

Rails5で試す

比較が容易なように,前回と同じ手順で進めます.

Railsプロジェクトを準備

まずは Rails プロジェクトを準備します.

/tmp% psql --version
psql (PostgreSQL) 9.5.3
/tmp% rails -v
Rails 5.0.0
/tmp% rails new infplayground --database postgresql
/tmp% cd infplayground
/tmp% bin/rake db:create
/tmp% bin/rails g model Event date:date
/tmp% bin/rake db:migrate

これで Rails には Event というモデル,DB には events というテーブルができたはずです.

データを登録

データは psql 経由で登録します.今回も date の内容が

  • 2015-05-18
  • 2015-05-19
  • infinity
  • -infinity
  • null

となるような 5 つのレコードを用意しました.

/tmp% psql infplayground_development
psql (9.5.3)
Type "help" for help.

infplayground_development=# INSERT INTO events(date, created_at, updated_at) VALUES('2015-05-18', now(), now());
infplayground_development=# INSERT INTO events(date, created_at, updated_at) VALUES('2015-05-19', now(), now());
infplayground_development=# INSERT INTO events(date, created_at, updated_at) VALUES('infinity', now(), now());
infplayground_development=# INSERT INTO events(date, created_at, updated_at) VALUES('-infinity', now(), now());
infplayground_development=# INSERT INTO events(date, created_at, updated_at) VALUES(null, now(), now());
infplayground_development=# SELECT * FROM events;
 id |    date    |         created_at         |         updated_at
----+------------+----------------------------+----------------------------
  1 | 2015-05-18 | 2016-07-19 17:06:46.848518 | 2016-07-19 17:06:46.848518
  2 | 2015-05-19 | 2016-07-19 17:06:54.083392 | 2016-07-19 17:06:54.083392
  3 | infinity   | 2016-07-19 17:07:01.604299 | 2016-07-19 17:07:01.604299
  4 | -infinity  | 2016-07-19 17:07:07.978353 | 2016-07-19 17:07:07.978353
  5 |            | 2016-07-19 17:07:14.213247 | 2016-07-19 17:07:14.213247
(5 rows)

よさそうですね.

Rails5から試す

それでは Rails5 でどのように取得できるか試してみましょう. bin/rails console で console を立ち上げます.

/tmp/infplayground% bin/rails console
Running via Spring preloader in process 29080
Loading development environment (Rails 5.0.0)
irb(main):001:0> Event.pluck(:id, :date)
   (0.3ms)  SELECT "events"."id", "events"."date" FROM "events"
=> [[1, Mon, 18 May 2015], [2, Tue, 19 May 2015], [3, nil], [4, nil], [5, nil]]

おやっ...... datenil になってしまっていますね. Infinity を扱えるようになったコミットが含まれているので,id が 3 や 4 のものは Infinity が返ってくることを期待していたのですが.

調査

調査しましょう.railsのActiveRecordへのコミットの中で,infinityという単語が増減するタイミングを探します. 余談ですが git log -S ... はソースコードの歴史を調査するときに便利なのでみなさんぜひお試しください.

/Users/d-kitamura/src/rails% git log -S infinity -p -- activerecord/test
(略)
commit 42418cfc94d1356d35d28d786f63e7fab9406ad6
Author: Sean Griffin <sean@thoughtbot.com>
Date:   Mon Dec 8 14:49:46 2014 -0700

    Allow custom handling of non-standard types in `time_zone_conversion`

    PostgreSQL for example, allows infinity as a valid value for date time
    columns. The PG type has explicit handling for that case. However, time
    zone conversion will end up trampling that handling. Unfortunately, we
    can't call super and then convert time zones.

    However, if we get back nil from `.in_time_zone`, it's something we
    didn't expect so we can let the superclass handle it.

    Fixes #17971

diff --git a/activerecord/test/cases/adapters/postgresql/infinity_test.rb b/activerecord/test/cases/adapters/postgresql/infinity_test.rb
index 22e8873..30404cd 100644
--- a/activerecord/test/cases/adapters/postgresql/infinity_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/infinity_test.rb
@@ -41,4 +41,10 @@ class PostgresqlInfinity < ActiveRecord::Base
     record.reload
     assert_equal Float::INFINITY, record.datetime
   end
+
+  test "assigning 'infinity' on a datetime column" do
+    record = PostgresqlInfinity.create!(datetime: "infinity")
+    assert_equal Float::INFINITY, record.datetime
+    assert_equal record.datetime, record.reload.datetime
+  end
 end

ほほう.infinityのテストを見つけました.良くみると date 型ではなく datetime 型に対して INFINITY をテストしていますね...... 私は今まで date 型で試しておりましたが,それだとうまく動かず, INFINITYdatetime 型でなければいけないようです.

前回の記事では date 型を使って誤った調査を行っていたようですね.申し訳ありません.

Rails5から(datetime型で)試す

今までは date 型で試していたのでうまく動かなかったようです. 新しくわかったことを元に, datetime 型で試します.

Railsプロジェクトを準備

もう一度Railsプロジェクトから作りなおします. Event のカラム名と型を date から datetime へと変更しました.

/tmp% rails new infplayground_datetime --database postgresql
/tmp% cd infplayground_datetime
/tmp% bin/rake db:create
/tmp% bin/rails g model Event datetime:datetime
/tmp% bin/rake db:migrate

データを登録

データを psql 経由で登録します. 先ほどと登録する内容は同じですが datetime 型として登録しています.

/tmp% psql infplayground_datetime_development
psql (9.5.3)
Type "help" for help.

infplayground_datetime_development=# INSERT INTO events(datetime, created_at, updated_at) VALUES('2015-05-18', now(), now());
infplayground_datetime_development=# INSERT INTO events(datetime, created_at, updated_at) VALUES('2015-05-19', now(), now());
infplayground_datetime_development=# INSERT INTO events(datetime, created_at, updated_at) VALUES('infinity', now(), now());
infplayground_datetime_development=# INSERT INTO events(datetime, created_at, updated_at) VALUES('-infinity', now(), now());
infplayground_datetime_development=# INSERT INTO events(datetime, created_at, updated_at) VALUES(null, now(), now());
infplayground_datetime_development=# SELECT * FROM events;
 id |      datetime       |         created_at         |         updated_at
----+---------------------+----------------------------+----------------------------
  1 | 2015-05-18 00:00:00 | 2016-07-19 18:00:03.65277  | 2016-07-19 18:00:03.65277
  2 | 2015-05-19 00:00:00 | 2016-07-19 18:00:13.261477 | 2016-07-19 18:00:13.261477
  3 | infinity            | 2016-07-19 18:00:22.599552 | 2016-07-19 18:00:22.599552
  4 | -infinity           | 2016-07-19 18:00:31.588861 | 2016-07-19 18:00:31.588861
  5 |                     | 2016-07-19 18:00:43.510486 | 2016-07-19 18:00:43.510486
(5 rows)

Rails5から試す

それでは Rails5 でどのように取得できるか試してみましょう. bin/rails console で console を立ち上げます.

/tmp/infplayground_datetime% bin/rails console
Running via Spring preloader in process 29080
Loading development environment (Rails 5.0.0)
irb(main):001:0> Event.pluck(:id, :datetime)
   (0.5ms)  SELECT "events"."id", "events"."datetime" FROM "events"
=> [[1, Mon, 18 May 2015 00:00:00 UTC +00:00], [2, Tue, 19 May 2015 00:00:00 UTC +00:00], [3, Infinity], [4, -Infinity], [5, nil]]

おおっ id が 3 と 4 のところに Infinity-Infinity が出ました! Rails5 で Postgresql へ接続して,カラムが datetime 型に Infinity が含まれている場合は,うまく Rails のオブジェクトへと変換してくれるようです.

まとめ

  • Rails4系ではカラムの値として Infinity-Infinity をうまく扱うことが できない
  • Rails5系で date 型を利用しているときは,カラムの値として Infinity-Infinity をうまく扱うことが できない
  • Rails5系で datetime 型を利用しているときは,カラムの値として Infinity-Infinity をうまく扱うことができる

ファームノートでは顧客の要望にできるだけ早く応えるため, 一般的にアプリケーションより寿命が長いと言われているデータの保全,難しいと言われているRDBのリファクタリングを狙い続けるエンジニアや, データと最新のアプリケーションの組み合わせで,今あるプロダクションコードのさらなる削減を狙い続けるエンジニアを募集しております.

私のインタビューもございます.ご笑覧下さい.

このエントリーをはてなブックマークに追加