1
0
Fork 0
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:
Sean Griffin 2017-09-26 20:08:20 -04:00 committed by GitHub
commit 48ef5e0ad5
5 changed files with 81 additions and 2 deletions

View file

@ -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.

View file

@ -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

View file

@ -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)

View file

@ -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"))

View file

@ -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