aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/connection_adapters
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record/connection_adapters')
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb3
-rw-r--r--activerecord/lib/active_record/connection_adapters/column.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/cast.rb36
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid.rb76
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb19
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb8
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb50
8 files changed, 123 insertions, 72 deletions
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index 3675184193..847d9da6e6 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -237,7 +237,7 @@ module ActiveRecord
@spec = spec
@checkout_timeout = spec.config[:checkout_timeout] || 5
- @dead_connection_timeout = spec.config[:dead_connection_timeout]
+ @dead_connection_timeout = spec.config[:dead_connection_timeout] || 5
@reaper = Reaper.new self, spec.config[:reaping_frequency]
@reaper.run
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
index 9d6111b51e..fd5eaab9c9 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
@@ -47,6 +47,9 @@ module ActiveRecord
value.to_s
when Date, DateTime, Time
"'#{value.to_s(:db)}'"
+ when Range
+ # infinity dumps as Infinity, which causes uninitialized constant error
+ value.inspect.gsub('Infinity', '::Float::INFINITY')
else
value.inspect
end
diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb
index fb28ecb6cf..747331f3a1 100644
--- a/activerecord/lib/active_record/connection_adapters/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/column.rb
@@ -126,7 +126,6 @@ module ActiveRecord
when :hstore then "#{klass}.string_to_hstore(#{var_name})"
when :inet, :cidr then "#{klass}.string_to_cidr(#{var_name})"
when :json then "#{klass}.string_to_json(#{var_name})"
- when :intrange then "#{klass}.string_to_intrange(#{var_name})"
else var_name
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb b/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
index f7d734a2f1..3d8f0b575c 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
@@ -62,6 +62,12 @@ module ActiveRecord
"{#{casted_values.join(',')}}"
end
+ def range_to_string(object)
+ from = object.begin.respond_to?(:infinite?) && object.begin.infinite? ? '' : object.begin
+ to = object.end.respond_to?(:infinite?) && object.end.infinite? ? '' : object.end
+ "[#{from},#{to}#{object.exclude_end? ? ')' : ']'}"
+ end
+
def string_to_json(string)
if String === string
ActiveSupport::JSON.decode(string)
@@ -92,36 +98,6 @@ module ActiveRecord
parse_pg_array(string).map{|val| oid.type_cast val}
end
- def string_to_intrange(string)
- if string.nil?
- nil
- elsif "empty" == string
- (nil..nil)
- elsif String === string && (matches = /^(\(|\[)([0-9]+),(\s?)([0-9]+)(\)|\])$/i.match(string))
- lower_bound = ("(" == matches[1] ? (matches[2].to_i + 1) : matches[2].to_i)
- upper_bound = (")" == matches[5] ? (matches[4].to_i - 1) : matches[4].to_i)
- (lower_bound..upper_bound)
- else
- string
- end
- end
-
- def intrange_to_string(object)
- if object.nil?
- nil
- elsif Range === object
- if [object.first, object.last].all? { |el| Integer === el }
- "[#{object.first.to_i},#{object.exclude_end? ? object.last.to_i : object.last.to_i + 1})"
- elsif [object.first, object.last].all? { |el| NilClass === el }
- "empty"
- else
- nil
- end
- else
- object
- end
- end
-
private
HstorePair = begin
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
index 02c295983f..d90b9283ef 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
@@ -78,6 +78,64 @@ module ActiveRecord
end
end
+ class Range < Type
+ attr_reader :subtype
+ def initialize(subtype)
+ @subtype = subtype
+ end
+
+ def exctract_bounds(value)
+ from, to = value[1..-2].split(',')
+ {
+ from: (value[1] == ',' || from == '-infinity') ? infinity(:negative => true) : from,
+ to: (value[-2] == ',' || to == 'infinity') ? infinity : to,
+ exclude_start: (value[0] == '('),
+ exclude_end: (value[-1] == ')')
+ }
+ end
+
+ def infinity(options = {})
+ ::Float::INFINITY * (options[:negative] ? -1 : 1)
+ end
+
+ def infinity?(value)
+ value.respond_to?(:infinite?) && value.infinite?
+ end
+
+ def to_integer(value)
+ infinity?(value) ? value : value.to_i
+ end
+
+ def type_cast(value)
+ return if value.nil? || value == 'empty'
+ return value if value.is_a?(::Range)
+
+ extracted = exctract_bounds(value)
+
+ case @subtype
+ when :date
+ from = ConnectionAdapters::Column.value_to_date(extracted[:from])
+ from -= 1.day if extracted[:exclude_start]
+ to = ConnectionAdapters::Column.value_to_date(extracted[:to])
+ when :decimal
+ from = BigDecimal.new(extracted[:from].to_s)
+ # FIXME: add exclude start for ::Range, same for timestamp ranges
+ to = BigDecimal.new(extracted[:to].to_s)
+ when :time
+ from = ConnectionAdapters::Column.string_to_time(extracted[:from])
+ to = ConnectionAdapters::Column.string_to_time(extracted[:to])
+ when :integer
+ from = to_integer(extracted[:from]) rescue value ? 1 : 0
+ from -= 1 if extracted[:exclude_start]
+ to = to_integer(extracted[:to]) rescue value ? 1 : 0
+ else
+ return value
+ end
+
+ ::Range.new(from, to, extracted[:exclude_end])
+ end
+ end
+
class Integer < Type
def type_cast(value)
return if value.nil?
@@ -168,14 +226,6 @@ module ActiveRecord
end
end
- class IntRange < Type
- def type_cast(value)
- return if value.nil?
-
- ConnectionAdapters::PostgreSQLColumn.string_to_intrange value
- end
- end
-
class TypeMap
def initialize
@mapping = {}
@@ -241,6 +291,13 @@ module ActiveRecord
alias_type 'int8', 'int2'
alias_type 'oid', 'int2'
+ register_type 'daterange', OID::Range.new(:date)
+ register_type 'numrange', OID::Range.new(:decimal)
+ register_type 'tsrange', OID::Range.new(:time)
+ register_type 'int4range', OID::Range.new(:integer)
+ alias_type 'tstzrange', 'tsrange'
+ alias_type 'int8range', 'int4range'
+
register_type 'numeric', OID::Decimal.new
register_type 'text', OID::Identity.new
alias_type 'varchar', 'text'
@@ -278,9 +335,6 @@ module ActiveRecord
register_type 'json', OID::Json.new
register_type 'ltree', OID::Identity.new
- register_type 'int4range', OID::IntRange.new
- alias_type 'int8range', 'int4range'
-
register_type 'cidr', OID::Cidr.new
alias_type 'inet', 'cidr'
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
index c2fcef94da..791b032023 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
@@ -19,6 +19,12 @@ module ActiveRecord
return super unless column
case value
+ when Range
+ if /range$/ =~ column.sql_type
+ "'#{PostgreSQLColumn.range_to_string(value)}'::#{column.sql_type}"
+ else
+ super
+ end
when Array
if column.array
"'#{PostgreSQLColumn.array_to_string(value, column, self)}'"
@@ -31,11 +37,6 @@ module ActiveRecord
when 'json' then super(PostgreSQLColumn.json_to_string(value), column)
else super
end
- when Range
- case column.sql_type
- when 'int4range', 'int8range' then super(PostgreSQLColumn.intrange_to_string(value), column)
- else super
- end
when IPAddr
case column.sql_type
when 'inet', 'cidr' then super(PostgreSQLColumn.cidr_to_string(value), column)
@@ -74,6 +75,9 @@ module ActiveRecord
return super(value, column) unless column
case value
+ when Range
+ return super(value, column) unless /range$/ =~ column.sql_type
+ PostgreSQLColumn.range_to_string(value)
when NilClass
if column.array && array_member
'NULL'
@@ -94,11 +98,6 @@ module ActiveRecord
when 'json' then PostgreSQLColumn.json_to_string(value)
else super(value, column)
end
- when Range
- case column.sql_type
- when 'int4range', 'int8range' then PostgreSQLColumn.intrange_to_string(value)
- else super(value, column)
- end
when IPAddr
return super(value, column) unless ['inet','cidr'].include? column.sql_type
PostgreSQLColumn.cidr_to_string(value)
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
index 8c68576bdc..73ca2c8e61 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -417,14 +417,6 @@ module ActiveRecord
when 0..6; "timestamp(#{precision})"
else raise(ActiveRecordError, "No timestamp type has precision of #{precision}. The allowed range of precision is from 0 to 6")
end
- when 'intrange'
- return 'int4range' unless limit
-
- case limit
- when 1..4; 'int4range'
- when 5..8; 'int8range'
- else raise(ActiveRecordError, "No range type has byte size #{limit}. Use a numeric with precision 0 instead.")
- end
else
super
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index b1b0467379..209553b26e 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -77,6 +77,8 @@ module ActiveRecord
return default unless default
case default
+ when /\A'(.*)'::(num|date|tstz|ts|int4|int8)range\z/m
+ $1
# Numeric types
when /\A\(?(-?\d+(\.\d*)?\)?)\z/
$1
@@ -117,9 +119,6 @@ module ActiveRecord
# JSON
when /\A'(.*)'::json\z/
$1
- # int4range, int8range
- when /\A'(.*)'::int(4|8)range\z/
- $1
# Object identifier types
when /\A-?\d+\z/
$1
@@ -220,12 +219,11 @@ module ActiveRecord
# JSON type
when 'json'
:json
- # int4range, int8range types
- when 'int4range', 'int8range'
- :intrange
# Small and big integer types
when /^(?:small|big)int$/
:integer
+ when /(num|date|tstz|ts|int4|int8)range$/
+ field_type.to_sym
# Pass through all types that are not specific to PostgreSQL.
else
super
@@ -276,6 +274,30 @@ module ActiveRecord
column(args[0], 'tsvector', options)
end
+ def int4range(name, options = {})
+ column(name, 'int4range', options)
+ end
+
+ def int8range(name, options = {})
+ column(name, 'int8range', options)
+ end
+
+ def tsrange(name, options = {})
+ column(name, 'tsrange', options)
+ end
+
+ def tstzrange(name, options = {})
+ column(name, 'tstzrange', options)
+ end
+
+ def numrange(name, options = {})
+ column(name, 'numrange', options)
+ end
+
+ def daterange(name, options = {})
+ column(name, 'daterange', options)
+ end
+
def hstore(name, options = {})
column(name, 'hstore', options)
end
@@ -304,10 +326,6 @@ module ActiveRecord
column(name, 'json', options)
end
- def intrange(name, options = {})
- column(name, 'intrange', options)
- end
-
def column(name, type = nil, options = {})
super
column = self[name]
@@ -339,6 +357,12 @@ module ActiveRecord
timestamp: { name: "timestamp" },
time: { name: "time" },
date: { name: "date" },
+ daterange: { name: "daterange" },
+ numrange: { name: "numrange" },
+ tsrange: { name: "tsrange" },
+ tstzrange: { name: "tstzrange" },
+ int4range: { name: "int4range" },
+ int8range: { name: "int8range" },
binary: { name: "bytea" },
boolean: { name: "boolean" },
xml: { name: "xml" },
@@ -349,7 +373,6 @@ module ActiveRecord
macaddr: { name: "macaddr" },
uuid: { name: "uuid" },
json: { name: "json" },
- intrange: { name: "int4range" },
ltree: { name: "ltree" }
}
@@ -552,6 +575,11 @@ module ActiveRecord
true
end
+ # Range datatypes weren't introduced until PostgreSQL 9.2
+ def supports_ranges?
+ postgresql_version >= 90200
+ end
+
# Returns the configured supported identifier length supported by PostgreSQL
def table_alias_length
@table_alias_length ||= query('SHOW max_identifier_length', 'SCHEMA')[0][0].to_i