PostgreSQL のデフォルト timestamp/time 精度を 6 として報告

rails/rails

PostgreSQL が内部的に持つマイクロ秒精度(typmod が -1 の場合)を、Active Record の型メタデータでも 6 桁として表現するように変更しました。これにより、Rails が取得するカラム情報と実際に DB が永続化するデータの精度が一致し、型キャストの挙動が一貫します。

背景

PostgreSQL では timestamptime カラムを型修飾子なしで作成すると、内部的にはマイクロ秒(6 桁)で保存されますが、従来の Active Record メタデータは precisionnil として扱っていました。その結果、Rails が生成する SQL と DB が保持する実際の精度に差異が生じ、型キャスト時に余計な丸めが発生していました。[#57845] ではこの不整合を解消する目的が示されています。

この不一致は、schema.rb の出力やマイグレーション生成時にも影響し、PostgreSQL 固有のデフォルト動作を正しく表現できないという設計上の課題となっていました。そこで、PostgreSQL の typmod 値を直接利用し、precision が未指定の場合は 6 を返すロジックが導入されました。

技術的な変更

register_class_with_precision メソッドPostgreSQLAdapter に追加され、型登録時に typmod (fmod) を受け取り、-1 の場合は 6、そうでなければ typmod の値をそのまま precision として設定します。

def register_class_with_precision(mapping, key, klass, **kwargs) # :nodoc:
  mapping.register_type(key) do |_, fmod, _sql_type|
    precision = fmod == -1 ? 6 : fmod
    klass.new(precision: precision, **kwargs).freeze
  end
end

このメソッドは initialize_type_map 内で time, timetz, timestamp, timestamptz の各型に対して呼び出され、precision が常に具体的な整数になるよう統一しています。

self.class.register_class_with_precision m, "time", Type::Time, timezone: @default_timezone
self.class.register_class_with_precision m, "timetz", Type::Time, timezone: @default_timezone
self.class.register_class_with_precision m, "timestamp", OID::Timestamp, timezone: @default_timezone
self.class.register_class_with_precision m, "timestamptz", OID::TimestampWithTimeZone

テストスイートでは bare_timestampbare_time などのカラムが precision 6 と報告されること、さらに型キャスト時にナノ秒がマイクロ秒単位に切り捨てられることが検証されています。これにより、Active Record が DB から取得した値と永続化時の精度が完全に一致します。

設計判断

既存の型登録ロジックを拡張し、precision を明示的に扱う方針が採用されました。register_class_with_precision は既存の register_type 呼び出しに最小限の差分で追加でき、後方互換性を保ちながら PostgreSQL 固有の挙動を反映しています。

この実装は PostgreSQL 専用 であり、他のアダプター(MySQL、SQLite 等)には影響を与えません。migration/compatibility_test.rb では PostgreSQL の場合に暗黙的なデフォルト精度が 6 になることを明示的にテストし、他の DB では従来通り precision: nil が使用されることを保証しています。

また、schema_dumper_test.rbdate_time_precision_test.rb でも precision: 6 が PostgreSQL のスキーマダンプに正しく出力されるよう調整され、Rails が生成するスキーマ定義と実際の DB 動作が同一になるよう設計されています。

まとめ

この PR は PostgreSQL のデフォルト timestamp/time 精度(6 桁)を Active Record のメタデータに反映させ、型キャストとスキーマダンプの一貫性を確保しました。register_class_with_precision による小規模な拡張で、既存コードへの影術的影響を抑えつつ、データ永続化の正確性を向上させています。

記事メタデータ

Generated by:
gpt-oss-120b for DiffDaily
LLM Trace:
28e6ff10

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
gpt-oss-120b for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

リード文、背景、技術詳細、設計判断(任意)、まとめの3部構成が明確に分かれており、各セクションに適切な要素が揃っている。

カスタムMarkdown構文 ⚠ WARNING

シンタックスハイライト・GitHubリンク記法の正確性

コードブロックは正しいファイル名付きシンタックスハイライト形式で記述されているが、本文中の [#57845] がリンク化されておらず、GitHubリンク記法が一部欠如している。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

Rails/ActiveRecord に詳しいエンジニア向けの内容で、初心者向けの過度な説明はなく適切。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションは総論→各論→結論の流れで構成され、段落はトピックセンテンスで始まり、1段落1トピック・長さも適切に区切られている。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事中のコードブロックは提供されたDiffと完全に一致し、ファイル名・内容とも合致している。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

typmod、precision、microsecond などの用語が正確に使用され、PR記述と一致している。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

技術的な主張(デフォルト精度が6になること、テストでの検証等)はDiffおよびPR説明で裏付けられている。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

PRに記載された事実のみを述べており、根拠のない数値や外部知識の付加はない。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号#57845や数値の6など、すべて正確に記載されている。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事タイトルはPRタイトル「Report PostgreSQL default timestamp/time precision as 6」の内容を正確に日本語化している。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

LTSやリリース日程等、PR外の情報は一切含まれていない。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

時間表現の歪曲はなく、PRの記述と一致している。