Skip to content

Commit 8d825b5

Browse files
committed
Make we always type cast TimeWithZone objects before passing to mysql2
mysql2 knows how to handle Time and Date objects but doesn't know about TimeWithZone. This was causing failures when prepared statements were enabled since we were passing TimeWithZone objects that mysql2 didn't know how to deal with. An new environment variable to enable prepared statements were added to config.example.yml, so we can test in our CI and prevent regressions. Fixes #41368.
1 parent 4bd8621 commit 8d825b5

File tree

3 files changed

+36
-2
lines changed

3 files changed

+36
-2
lines changed

activerecord/lib/active_record/connection_adapters/mysql/quoting.rb

+17-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# frozen_string_literal: true
22

3+
require "active_support/time_with_zone"
4+
35
module ActiveRecord
46
module ConnectionAdapters
57
module MySQL
@@ -69,10 +71,23 @@ def column_name_with_order_matcher
6971
private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
7072

7173
private
74+
# Override +_type_cast+ we pass to mysql2 Date and Time objects instead
75+
# of Strings since mysql2 is able to handle those classes more efficiently.
7276
def _type_cast(value)
7377
case value
74-
when Date, Time then value
75-
else super
78+
when ActiveSupport::TimeWithZone
79+
# We need to check explicitly for ActiveSupport::TimeWithZone because
80+
# we need to transform it to Time objects but we don't want to
81+
# transform Time objects to themselves.
82+
if ActiveRecord::Base.default_timezone == :utc
83+
value.getutc
84+
else
85+
value.getlocal
86+
end
87+
when Date, Time
88+
value
89+
else
90+
super
7691
end
7792
end
7893
end

activerecord/test/cases/base_test.rb

+13
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
require "models/bird"
2727
require "models/car"
2828
require "models/bulb"
29+
require "models/pet"
2930
require "concurrent/atomic/count_down_latch"
3031
require "active_support/core_ext/enumerable"
3132

@@ -312,6 +313,18 @@ def test_preserving_time_objects_with_time_with_zone_conversion_to_default_timez
312313
end
313314
end
314315

316+
def test_time_zone_aware_attribute_with_default_timezone_utc_on_utc_can_be_created
317+
with_env_tz eastern_time_zone do
318+
with_timezone_config aware_attributes: true, default: :utc, zone: "UTC" do
319+
pet = Pet.create(name: 'Bidu')
320+
assert_predicate pet, :persisted?
321+
saved_pet = Pet.find(pet.id)
322+
assert_not_nil saved_pet.created_at
323+
assert_not_nil saved_pet.updated_at
324+
end
325+
end
326+
end
327+
315328
def eastern_time_zone
316329
if Gem.win_platform?
317330
"EST5EDT"

activerecord/test/config.example.yml

+6
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,19 @@ connections:
4040
username: rails
4141
encoding: utf8mb4
4242
collation: utf8mb4_unicode_ci
43+
<% if ENV['MYSQL_PREPARED_STATEMENTS'] %>
44+
prepared_statements: true
45+
<% end %>
4346
<% if ENV['MYSQL_HOST'] %>
4447
host: <%= ENV['MYSQL_HOST'] %>
4548
<% end %>
4649
arunit2:
4750
username: rails
4851
encoding: utf8mb4
4952
collation: utf8mb4_general_ci
53+
<% if ENV['MYSQL_PREPARED_STATEMENTS'] %>
54+
prepared_statements: true
55+
<% end %>
5056
<% if ENV['MYSQL_HOST'] %>
5157
host: <%= ENV['MYSQL_HOST'] %>
5258
<% end %>

0 commit comments

Comments
 (0)