Skip to content

Commit 91949e4

Browse files
committed
deprecate support for pg ranges with excluding beginnings.
The Ruby Range object does not support excluding beginnings. We currently support excluding beginnings for some subtypes using manually by incrementing them (now using the `#succ` method). This is approach is flawed as it's not equal to an excluding beginning. This commit deprecates the current support for excluding beginnings. It also raises an `ArgumentError` for subtypes that do not implement the `succ` method. This is a temporary solution to get rid of the broken state. We might still add complete support for excluding beginnings afterwards. (Probably with a new `PGRange` object, which acts like a `Range` but has excluding beginnings.
1 parent 4cb4716 commit 91949e4

3 files changed

Lines changed: 78 additions & 23 deletions

File tree

activerecord/CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
* Deprecate half-baked support for PostgreSQL range values with excluding beginnings.
2+
We currently map PostgreSQL ranges to Ruby ranges. This conversion is not fully
3+
possible because the Ruby range does not support excluded beginnings.
4+
5+
The current solution of incrementing the beginning is not correct and is now
6+
deprecated. For subtypes where we don't know how to increment (e.g. `#succ`
7+
is not defined) it will raise an ArgumentException for ranges with excluding
8+
beginnings.
9+
10+
*Yves Senn*
11+
112
* Support for user created range types in PostgreSQL.
213

314
*Yves Senn*

activerecord/lib/active_record/connection_adapters/postgresql/oid.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,18 @@ def type_cast(value)
135135
extracted = extract_bounds(value)
136136
from = type_cast_single extracted[:from]
137137
to = type_cast_single extracted[:to]
138+
139+
if !infinity?(from) && extracted[:exclude_start]
140+
if from.respond_to?(:succ)
141+
from = from.succ
142+
ActiveSupport::Deprecation.warn <<-MESSAGE
143+
Excluding the beginning of a Range is only partialy supported through `#succ`.
144+
This is not reliable and will be removed in the future.
145+
MESSAGE
146+
else
147+
raise ArgumentError, "The Ruby Range object does not support excluding the beginning of a Range. (unsupported value: '#{value}')"
148+
end
149+
end
138150
::Range.new(from, to, extracted[:exclude_end])
139151
end
140152
end

activerecord/test/cases/adapters/postgresql/range_test.rb

Lines changed: 55 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -53,21 +53,21 @@ def setup
5353
float_range: "[0.5, 0.7]")
5454

5555
insert_range(id: 102,
56-
date_range: "(''2012-01-02'', ''2012-01-04'')",
57-
num_range: "(0.1, 0.2)",
58-
ts_range: "(''2010-01-01 14:30'', ''2011-01-01 14:30'')",
59-
tstz_range: "(''2010-01-01 14:30:00+05'', ''2011-01-01 14:30:00-03'')",
60-
int4_range: "(1, 10)",
61-
int8_range: "(10, 100)",
62-
float_range: "(0.5, 0.7)")
56+
date_range: "[''2012-01-02'', ''2012-01-04'')",
57+
num_range: "[0.1, 0.2)",
58+
ts_range: "[''2010-01-01 14:30'', ''2011-01-01 14:30'')",
59+
tstz_range: "[''2010-01-01 14:30:00+05'', ''2011-01-01 14:30:00-03'')",
60+
int4_range: "[1, 10)",
61+
int8_range: "[10, 100)",
62+
float_range: "[0.5, 0.7)")
6363

6464
insert_range(id: 103,
65-
date_range: "(''2012-01-02'',]",
65+
date_range: "[''2012-01-02'',]",
6666
num_range: "[0.1,]",
6767
ts_range: "[''2010-01-01 14:30'',]",
6868
tstz_range: "[''2010-01-01 14:30:00+05'',]",
69-
int4_range: "(1,]",
70-
int8_range: "(10,]",
69+
int4_range: "[1,]",
70+
int8_range: "[10,]",
7171
float_range: "[0.5,]")
7272

7373
insert_range(id: 104,
@@ -80,13 +80,13 @@ def setup
8080
float_range: "[,]")
8181

8282
insert_range(id: 105,
83-
date_range: "(''2012-01-02'', ''2012-01-02'')",
84-
num_range: "(0.1, 0.1)",
85-
ts_range: "(''2010-01-01 14:30'', ''2010-01-01 14:30'')",
86-
tstz_range: "(''2010-01-01 14:30:00+05'', ''2010-01-01 06:30:00-03'')",
87-
int4_range: "(1, 1)",
88-
int8_range: "(10, 10)",
89-
float_range: "(0.5, 0.5)")
83+
date_range: "[''2012-01-02'', ''2012-01-02'')",
84+
num_range: "[0.1, 0.1)",
85+
ts_range: "[''2010-01-01 14:30'', ''2010-01-01 14:30'')",
86+
tstz_range: "[''2010-01-01 14:30:00+05'', ''2010-01-01 06:30:00-03'')",
87+
int4_range: "[1, 1)",
88+
int8_range: "[10, 10)",
89+
float_range: "[0.5, 0.5)")
9090

9191
@new_range = PostgresqlRange.new
9292
@first_range = PostgresqlRange.find(101)
@@ -107,24 +107,24 @@ def test_data_type_of_range_types
107107

108108
def test_int4range_values
109109
assert_equal 1...11, @first_range.int4_range
110-
assert_equal 2...10, @second_range.int4_range
111-
assert_equal 2...Float::INFINITY, @third_range.int4_range
110+
assert_equal 1...10, @second_range.int4_range
111+
assert_equal 1...Float::INFINITY, @third_range.int4_range
112112
assert_equal(-Float::INFINITY...Float::INFINITY, @fourth_range.int4_range)
113113
assert_nil @empty_range.int4_range
114114
end
115115

116116
def test_int8range_values
117117
assert_equal 10...101, @first_range.int8_range
118-
assert_equal 11...100, @second_range.int8_range
119-
assert_equal 11...Float::INFINITY, @third_range.int8_range
118+
assert_equal 10...100, @second_range.int8_range
119+
assert_equal 10...Float::INFINITY, @third_range.int8_range
120120
assert_equal(-Float::INFINITY...Float::INFINITY, @fourth_range.int8_range)
121121
assert_nil @empty_range.int8_range
122122
end
123123

124124
def test_daterange_values
125125
assert_equal Date.new(2012, 1, 2)...Date.new(2012, 1, 5), @first_range.date_range
126-
assert_equal Date.new(2012, 1, 3)...Date.new(2012, 1, 4), @second_range.date_range
127-
assert_equal Date.new(2012, 1, 3)...Float::INFINITY, @third_range.date_range
126+
assert_equal Date.new(2012, 1, 2)...Date.new(2012, 1, 4), @second_range.date_range
127+
assert_equal Date.new(2012, 1, 2)...Float::INFINITY, @third_range.date_range
128128
assert_equal(-Float::INFINITY...Float::INFINITY, @fourth_range.date_range)
129129
assert_nil @empty_range.date_range
130130
end
@@ -230,6 +230,38 @@ def test_update_int8range
230230
assert_nil_round_trip(@first_range, :int8_range, 39999...39999)
231231
end
232232

233+
def test_exclude_beginning_for_subtypes_with_succ_method_is_deprecated
234+
tz = ::ActiveRecord::Base.default_timezone
235+
236+
silence_warnings {
237+
assert_deprecated {
238+
range = PostgresqlRange.create!(date_range: "(''2012-01-02'', ''2012-01-04'']")
239+
assert_equal Date.new(2012, 1, 3)..Date.new(2012, 1, 4), range.date_range
240+
}
241+
assert_deprecated {
242+
range = PostgresqlRange.create!(ts_range: "(''2010-01-01 14:30'', ''2011-01-01 14:30'']")
243+
assert_equal Time.send(tz, 2010, 1, 1, 14, 30, 1)..Time.send(tz, 2011, 1, 1, 14, 30, 0), range.ts_range
244+
}
245+
assert_deprecated {
246+
range = PostgresqlRange.create!(tstz_range: "(''2010-01-01 14:30:00+05'', ''2011-01-01 14:30:00-03'']")
247+
assert_equal Time.parse('2010-01-01 09:30:01 UTC')..Time.parse('2011-01-01 17:30:00 UTC'), range.tstz_range
248+
}
249+
assert_deprecated {
250+
range = PostgresqlRange.create!(int4_range: "(1, 10]")
251+
assert_equal 2..10, range.int4_range
252+
}
253+
assert_deprecated {
254+
range = PostgresqlRange.create!(int8_range: "(10, 100]")
255+
assert_equal 11..100, range.int8_range
256+
}
257+
}
258+
end
259+
260+
def test_exclude_beginning_for_subtypes_without_succ_method_is_not_supported
261+
assert_raises(ArgumentError) { PostgresqlRange.create!(num_range: "(0.1, 0.2]") }
262+
assert_raises(ArgumentError) { PostgresqlRange.create!(float_range: "(0.5, 0.7]") }
263+
end
264+
233265
private
234266
def assert_equal_round_trip(range, attribute, value)
235267
round_trip(range, attribute, value)

0 commit comments

Comments
 (0)