mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Merge pull request #30725 from cheerful/record-milliseconds-in-postgres-timestamp-ranges
Use `ActiveRecord::ConnectionAdapters::Quoting.quote` to quote bounds for a PostgreSQL range
This commit is contained in:
commit
48ef5e0ad5
5 changed files with 81 additions and 2 deletions
|
@ -1,3 +1,23 @@
|
|||
* PostgreSQL `tsrange` now preserves subsecond precision
|
||||
|
||||
PostgreSQL 9.1+ introduced range types, and Rails added support for using
|
||||
this datatype in ActiveRecord. However, the serialization of
|
||||
PostgreSQL::OID::Range was incomplete, because it did not properly
|
||||
cast the bounds that make up the range. This led to subseconds being
|
||||
dropped in SQL commands:
|
||||
|
||||
(byebug) from = type_cast_single_for_database(range.first)
|
||||
2010-01-01 13:30:00 UTC
|
||||
|
||||
(byebug) to = type_cast_single_for_database(range.last)
|
||||
2011-02-02 19:30:00 UTC
|
||||
|
||||
(byebug) "[#{from},#{to}#{value.exclude_end? ? ')' : ']'}"
|
||||
"[2010-01-01 13:30:00 UTC,2011-02-02 19:30:00 UTC)"
|
||||
|
||||
(byebug) "[#{type_cast(from)},#{type_cast(to)}#{value.exclude_end? ? ')' : ']'}"
|
||||
"['2010-01-01 13:30:00.670277','2011-02-02 19:30:00.745125')"
|
||||
|
||||
* Passing a `Set` to `Relation#where` now behaves the same as passing an
|
||||
array.
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ module ActiveRecord
|
|||
if value.is_a?(::Range)
|
||||
from = type_cast_single_for_database(value.begin)
|
||||
to = type_cast_single_for_database(value.end)
|
||||
"[#{from},#{to}#{value.exclude_end? ? ')' : ']'}"
|
||||
::Range.new(from, to, value.exclude_end?)
|
||||
else
|
||||
super
|
||||
end
|
||||
|
|
|
@ -101,6 +101,8 @@ module ActiveRecord
|
|||
end
|
||||
when OID::Array::Data
|
||||
_quote(encode_array(value))
|
||||
when Range
|
||||
_quote(encode_range(value))
|
||||
else
|
||||
super
|
||||
end
|
||||
|
@ -117,6 +119,8 @@ module ActiveRecord
|
|||
value.to_s
|
||||
when OID::Array::Data
|
||||
encode_array(value)
|
||||
when Range
|
||||
encode_range(value)
|
||||
else
|
||||
super
|
||||
end
|
||||
|
@ -133,6 +137,10 @@ module ActiveRecord
|
|||
result
|
||||
end
|
||||
|
||||
def encode_range(range)
|
||||
"[#{type_cast(range.first)},#{type_cast(range.last)}#{range.exclude_end? ? ')' : ']'}"
|
||||
end
|
||||
|
||||
def determine_encoding_of_strings_in_array(value)
|
||||
case value
|
||||
when ::Array then determine_encoding_of_strings_in_array(value.first)
|
||||
|
|
|
@ -232,6 +232,57 @@ _SQL
|
|||
end
|
||||
end
|
||||
|
||||
def test_create_tstzrange_preserve_usec
|
||||
tstzrange = Time.parse("2010-01-01 14:30:00.670277 +0100")...Time.parse("2011-02-02 14:30:00.745125 CDT")
|
||||
round_trip(@new_range, :tstz_range, tstzrange)
|
||||
assert_equal @new_range.tstz_range, tstzrange
|
||||
assert_equal @new_range.tstz_range, Time.parse("2010-01-01 13:30:00.670277 UTC")...Time.parse("2011-02-02 19:30:00.745125 UTC")
|
||||
end
|
||||
|
||||
def test_update_tstzrange_preserve_usec
|
||||
assert_equal_round_trip(@first_range, :tstz_range,
|
||||
Time.parse("2010-01-01 14:30:00.245124 CDT")...Time.parse("2011-02-02 14:30:00.451274 CET"))
|
||||
assert_nil_round_trip(@first_range, :tstz_range,
|
||||
Time.parse("2010-01-01 14:30:00.245124 +0100")...Time.parse("2010-01-01 13:30:00.245124 +0000"))
|
||||
end
|
||||
|
||||
def test_create_tsrange_preseve_usec
|
||||
tz = ::ActiveRecord::Base.default_timezone
|
||||
assert_equal_round_trip(@new_range, :ts_range,
|
||||
Time.send(tz, 2010, 1, 1, 14, 30, 0, 125435)...Time.send(tz, 2011, 2, 2, 14, 30, 0, 225435))
|
||||
end
|
||||
|
||||
def test_update_tsrange_preserve_usec
|
||||
tz = ::ActiveRecord::Base.default_timezone
|
||||
assert_equal_round_trip(@first_range, :ts_range,
|
||||
Time.send(tz, 2010, 1, 1, 14, 30, 0, 142432)...Time.send(tz, 2011, 2, 2, 14, 30, 0, 224242))
|
||||
assert_nil_round_trip(@first_range, :ts_range,
|
||||
Time.send(tz, 2010, 1, 1, 14, 30, 0, 142432)...Time.send(tz, 2010, 1, 1, 14, 30, 0, 142432))
|
||||
end
|
||||
|
||||
def test_timezone_awareness_tsrange_preserve_usec
|
||||
tz = "Pacific Time (US & Canada)"
|
||||
|
||||
in_time_zone tz do
|
||||
PostgresqlRange.reset_column_information
|
||||
time_string = "2017-09-26 07:30:59.132451 -0700"
|
||||
time = Time.zone.parse(time_string)
|
||||
assert time.usec > 0
|
||||
|
||||
record = PostgresqlRange.new(ts_range: time_string..time_string)
|
||||
assert_equal time..time, record.ts_range
|
||||
assert_equal ActiveSupport::TimeZone[tz], record.ts_range.begin.time_zone
|
||||
assert_equal time.usec, record.ts_range.begin.usec
|
||||
|
||||
record.save!
|
||||
record.reload
|
||||
|
||||
assert_equal time..time, record.ts_range
|
||||
assert_equal ActiveSupport::TimeZone[tz], record.ts_range.begin.time_zone
|
||||
assert_equal time.usec, record.ts_range.begin.usec
|
||||
end
|
||||
end
|
||||
|
||||
def test_create_numrange
|
||||
assert_equal_round_trip(@new_range, :num_range,
|
||||
BigDecimal.new("0.5")...BigDecimal.new("1"))
|
||||
|
|
|
@ -30,6 +30,6 @@ class PostgresqlTypeLookupTest < ActiveRecord::PostgreSQLTestCase
|
|||
big_range = 0..123456789123456789
|
||||
|
||||
assert_raises(ActiveModel::RangeError) { int_range.serialize(big_range) }
|
||||
assert_equal "[0,123456789123456789]", bigint_range.serialize(big_range)
|
||||
assert_equal "[0,123456789123456789]", @connection.type_cast(bigint_range.serialize(big_range))
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue