PostgreSQLアダプタでのタイムゾーン型マップ更新タイミングの最適化
PostgreSQLアダプタにおけるタイムゾーン型マップの更新処理が、クエリ実行時から型キャスト時に移動されました。この変更により、バインドパラメータの型変換が行われる正確なタイミングで型マップが更新されるようになり、今後の改善に向けた明確な基盤が確立されます。
背景
update_typemap_for_default_timezone メソッドは、ActiveRecordのデフォルトタイムゾーン設定に応じてPostgreSQLの型マップを更新する役割を持ちます。従来この処理は perform_query メソッド内で実行されていましたが、実際に型変換が必要になるのはバインドパラメータをキャストする段階です。型マップの更新タイミングとその利用タイミングが分離していたことで、処理フローの理解が難しくなっていました。
技術的な変更
型マップの更新呼び出しが database_statements.rb の perform_query から quoting.rb の type_casted_binds に移動されました。
変更前:
def perform_query(raw_connection, intent)
update_typemap_for_default_timezone
result = if intent.prepare
# ...
end
end
変更後:
def type_casted_binds(binds) # :nodoc:
update_typemap_for_default_timezone
super
end
type_casted_binds メソッドは、バインドパラメータを実際にデータベース用の値に変換する直前に呼び出されます。型マップの更新をこのタイミングで行うことで、変換処理が常に正しいタイムゾーン設定を参照することが保証されます。
テストケースの追加により、find_by_sql のような低レベルAPIでもタイムゾーン切り替えが正しく動作することが確認されています:
def test_switching_default_time_zone_with_find_by_sql
with_timezone_config default: :utc do
default = Default.create!
assert_equal Time.utc(2004, 1, 1, 0, 0, 0, 0), default.fixed_time
ActiveRecord.default_timezone = :local
time = Default.find_by_sql("SELECT * FROM defaults WHERE id = #{default.id}").first.fixed_time
assert_equal Time.local(2004, 1, 1, 0, 0, 0, 0), time
end
end
設計判断
型変換の直前での型マップ更新 という方針が採用されました。
PR本文では「This doesn't change any existing behaviour」と明記されており、既存の動作を変えずに処理フローを整理することが優先されています。perform_query は複数の目的を持つメソッドですが、type_casted_binds は型変換という単一の責務を持つため、型マップの更新場所としてより適切です。この変更により、型マップの更新と利用が同じコンテキスト内に配置され、将来的な変更時の影響範囲が明確になります。
コメントで「it just sets up a clearer path ahead of other changes I'm planning to make here」と述べられているように、この変更は今後の改善に向けた準備段階として位置づけられています。処理の責務分離を進めることで、タイムゾーン処理の最適化や拡張が容易になります。
まとめ
本PRは、PostgreSQLアダプタの型マップ更新処理を、その利用タイミングである型キャスト時に移動させた変更です。既存の動作を維持しながら処理フローの責務を明確化することで、今後のタイムゾーン処理の改善に向けた基盤が整えられました。