mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
PostgreSQL determine Column#type
through corresponding OID. #7814
I ran the whole test suite and compared the old to the new types. Following is the list of types that did change with this patch: ``` DIFFERENT TYPE FOR mood: NEW: enum, BEFORE: DIFFERENT TYPE FOR floatrange: NEW: floatrange, BEFORE: float ``` The `floatrange` is a custom type. The old type `float` was simply a coincidence form the name `floatrange` and our type-guessing.
This commit is contained in:
parent
5eb13fcaac
commit
4d344bb4fd
6 changed files with 105 additions and 108 deletions
|
@ -1,3 +1,11 @@
|
|||
* PostgreSQL `Column#type` is now determined through the corresponding OID.
|
||||
The column types stay the same except for enum columns. They no longer have
|
||||
`nil` as type but `enum`.
|
||||
|
||||
See #7814.
|
||||
|
||||
*Yves Senn*
|
||||
|
||||
* Fixed error when specifying a non-empty default value on a PostgreSQL array column.
|
||||
|
||||
Fixes #10613.
|
||||
|
|
|
@ -151,70 +151,7 @@ module ActiveRecord
|
|||
|
||||
# Maps PostgreSQL-specific data types to logical Rails types.
|
||||
def simplified_type(field_type)
|
||||
case field_type
|
||||
# Numeric and monetary types
|
||||
when /^(?:real|double precision)$/
|
||||
:float
|
||||
# Monetary types
|
||||
when 'money'
|
||||
:decimal
|
||||
when 'hstore'
|
||||
:hstore
|
||||
when 'ltree'
|
||||
:ltree
|
||||
# Network address types
|
||||
when 'inet'
|
||||
:inet
|
||||
when 'cidr'
|
||||
:cidr
|
||||
when 'macaddr'
|
||||
:macaddr
|
||||
# Character types
|
||||
when /^(?:character varying|bpchar)(?:\(\d+\))?$/
|
||||
:string
|
||||
when /^citext(?:\(\d+\))?$/
|
||||
:citext
|
||||
# Binary data types
|
||||
when 'bytea'
|
||||
:binary
|
||||
# Date/time types
|
||||
when /^timestamp with(?:out)? time zone$/
|
||||
:datetime
|
||||
when /^interval(?:|\(\d+\))$/
|
||||
:string
|
||||
# Geometric types
|
||||
when /^(?:point|line|lseg|box|"?path"?|polygon|circle)$/
|
||||
:string
|
||||
# Bit strings
|
||||
when /^bit(?: varying)?(?:\(\d+\))?$/
|
||||
:string
|
||||
# XML type
|
||||
when 'xml'
|
||||
:xml
|
||||
# tsvector type
|
||||
when 'tsvector'
|
||||
:tsvector
|
||||
# Arrays
|
||||
when /^\D+\[\]$/
|
||||
:string
|
||||
# Object identifier types
|
||||
when 'oid'
|
||||
:integer
|
||||
# UUID type
|
||||
when 'uuid'
|
||||
:uuid
|
||||
# JSON type
|
||||
when 'json'
|
||||
:json
|
||||
# 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
|
||||
end
|
||||
@oid_type.simplified_type(field_type) || super
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,6 +6,7 @@ module ActiveRecord
|
|||
module OID
|
||||
class Type
|
||||
def type; end
|
||||
def simplified_type(sql_type); type end
|
||||
|
||||
def infinity(options = {})
|
||||
::Float::INFINITY * (options[:negative] ? -1 : 1)
|
||||
|
@ -18,7 +19,9 @@ module ActiveRecord
|
|||
end
|
||||
end
|
||||
|
||||
class Text < Type
|
||||
class String < Type
|
||||
def type; :string end
|
||||
|
||||
def type_cast(value)
|
||||
return if value.nil?
|
||||
|
||||
|
@ -26,9 +29,23 @@ module ActiveRecord
|
|||
end
|
||||
end
|
||||
|
||||
class SpecializedString < OID::String
|
||||
def type; @type end
|
||||
|
||||
def initialize(type)
|
||||
@type = type
|
||||
end
|
||||
end
|
||||
|
||||
class Text < OID::String
|
||||
def type; :text end
|
||||
end
|
||||
|
||||
class Bit < Type
|
||||
def type; :string end
|
||||
|
||||
def type_cast(value)
|
||||
if String === value
|
||||
if ::String === value
|
||||
ConnectionAdapters::PostgreSQLColumn.string_to_bit value
|
||||
else
|
||||
value
|
||||
|
@ -37,6 +54,8 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
class Bytea < Type
|
||||
def type; :binary end
|
||||
|
||||
def type_cast(value)
|
||||
return if value.nil?
|
||||
PGconn.unescape_bytea value
|
||||
|
@ -44,9 +63,11 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
class Money < Type
|
||||
def type; :decimal end
|
||||
|
||||
def type_cast(value)
|
||||
return if value.nil?
|
||||
return value unless String === value
|
||||
return value unless ::String === value
|
||||
|
||||
# Because money output is formatted according to the locale, there are two
|
||||
# cases to consider (note the decimal separators):
|
||||
|
@ -88,8 +109,10 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
class Point < Type
|
||||
def type; :string end
|
||||
|
||||
def type_cast(value)
|
||||
if String === value
|
||||
if ::String === value
|
||||
ConnectionAdapters::PostgreSQLColumn.string_to_point value
|
||||
else
|
||||
value
|
||||
|
@ -98,13 +121,15 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
class Array < Type
|
||||
def type; @subtype.type end
|
||||
|
||||
attr_reader :subtype
|
||||
def initialize(subtype)
|
||||
@subtype = subtype
|
||||
end
|
||||
|
||||
def type_cast(value)
|
||||
if String === value
|
||||
if ::String === value
|
||||
ConnectionAdapters::PostgreSQLColumn.string_to_array value, @subtype
|
||||
else
|
||||
value
|
||||
|
@ -114,6 +139,8 @@ module ActiveRecord
|
|||
|
||||
class Range < Type
|
||||
attr_reader :subtype
|
||||
def simplified_type(sql_type); sql_type.to_sym end
|
||||
|
||||
def initialize(subtype)
|
||||
@subtype = subtype
|
||||
end
|
||||
|
@ -160,6 +187,8 @@ This is not reliable and will be removed in the future.
|
|||
end
|
||||
|
||||
class Integer < Type
|
||||
def type; :integer end
|
||||
|
||||
def type_cast(value)
|
||||
return if value.nil?
|
||||
|
||||
|
@ -168,6 +197,8 @@ This is not reliable and will be removed in the future.
|
|||
end
|
||||
|
||||
class Boolean < Type
|
||||
def type; :boolean end
|
||||
|
||||
def type_cast(value)
|
||||
return if value.nil?
|
||||
|
||||
|
@ -177,6 +208,14 @@ This is not reliable and will be removed in the future.
|
|||
|
||||
class Timestamp < Type
|
||||
def type; :timestamp; end
|
||||
def simplified_type(sql_type)
|
||||
case sql_type
|
||||
when /^timestamp with(?:out)? time zone$/
|
||||
:datetime
|
||||
else
|
||||
:timestamp
|
||||
end
|
||||
end
|
||||
|
||||
def type_cast(value)
|
||||
return if value.nil?
|
||||
|
@ -188,7 +227,7 @@ This is not reliable and will be removed in the future.
|
|||
end
|
||||
|
||||
class Date < Type
|
||||
def type; :datetime; end
|
||||
def type; :date; end
|
||||
|
||||
def type_cast(value)
|
||||
return if value.nil?
|
||||
|
@ -200,6 +239,8 @@ This is not reliable and will be removed in the future.
|
|||
end
|
||||
|
||||
class Time < Type
|
||||
def type; :time end
|
||||
|
||||
def type_cast(value)
|
||||
return if value.nil?
|
||||
|
||||
|
@ -210,6 +251,8 @@ This is not reliable and will be removed in the future.
|
|||
end
|
||||
|
||||
class Float < Type
|
||||
def type; :float end
|
||||
|
||||
def type_cast(value)
|
||||
return if value.nil?
|
||||
|
||||
|
@ -218,6 +261,8 @@ This is not reliable and will be removed in the future.
|
|||
end
|
||||
|
||||
class Decimal < Type
|
||||
def type; :decimal end
|
||||
|
||||
def type_cast(value)
|
||||
return if value.nil?
|
||||
|
||||
|
@ -230,12 +275,16 @@ This is not reliable and will be removed in the future.
|
|||
end
|
||||
|
||||
class Enum < Type
|
||||
def type; :enum end
|
||||
|
||||
def type_cast(value)
|
||||
value.to_s
|
||||
end
|
||||
end
|
||||
|
||||
class Hstore < Type
|
||||
def type; :hstore end
|
||||
|
||||
def type_cast_for_write(value)
|
||||
ConnectionAdapters::PostgreSQLColumn.hstore_to_string value
|
||||
end
|
||||
|
@ -252,14 +301,20 @@ This is not reliable and will be removed in the future.
|
|||
end
|
||||
|
||||
class Cidr < Type
|
||||
def type; :cidr end
|
||||
def type_cast(value)
|
||||
return if value.nil?
|
||||
|
||||
ConnectionAdapters::PostgreSQLColumn.string_to_cidr value
|
||||
end
|
||||
end
|
||||
class Inet < Cidr
|
||||
def type; :inet end
|
||||
end
|
||||
|
||||
class Json < Type
|
||||
def type; :json end
|
||||
|
||||
def type_cast_for_write(value)
|
||||
ConnectionAdapters::PostgreSQLColumn.json_to_string value
|
||||
end
|
||||
|
@ -321,7 +376,7 @@ This is not reliable and will be removed in the future.
|
|||
}
|
||||
|
||||
# Register an OID type named +name+ with a typecasting object in
|
||||
# +type+. +name+ should correspond to the `typname` column in
|
||||
# +type+. +name+ should correspond to the `typname` column in
|
||||
# the `pg_type` table.
|
||||
def self.register_type(name, type)
|
||||
NAMES[name] = type
|
||||
|
@ -338,48 +393,46 @@ This is not reliable and will be removed in the future.
|
|||
end
|
||||
|
||||
register_type 'int2', OID::Integer.new
|
||||
alias_type 'int4', 'int2'
|
||||
alias_type 'int8', 'int2'
|
||||
alias_type 'oid', 'int2'
|
||||
|
||||
alias_type 'int4', 'int2'
|
||||
alias_type 'int8', 'int2'
|
||||
alias_type 'oid', 'int2'
|
||||
register_type 'numeric', OID::Decimal.new
|
||||
register_type 'text', OID::Text.new
|
||||
alias_type 'varchar', 'text'
|
||||
alias_type 'char', 'text'
|
||||
alias_type 'bpchar', 'text'
|
||||
alias_type 'xml', 'text'
|
||||
|
||||
# FIXME: why are we keeping these types as strings?
|
||||
alias_type 'tsvector', 'text'
|
||||
alias_type 'interval', 'text'
|
||||
alias_type 'macaddr', 'text'
|
||||
alias_type 'uuid', 'text'
|
||||
|
||||
register_type 'money', OID::Money.new
|
||||
register_type 'bytea', OID::Bytea.new
|
||||
register_type 'bool', OID::Boolean.new
|
||||
register_type 'bit', OID::Bit.new
|
||||
register_type 'varbit', OID::Bit.new
|
||||
|
||||
register_type 'float4', OID::Float.new
|
||||
alias_type 'float8', 'float4'
|
||||
|
||||
register_type 'text', OID::Text.new
|
||||
register_type 'varchar', OID::String.new
|
||||
alias_type 'char', 'varchar'
|
||||
alias_type 'bpchar', 'varchar'
|
||||
register_type 'bool', OID::Boolean.new
|
||||
register_type 'bit', OID::Bit.new
|
||||
alias_type 'varbit', 'bit'
|
||||
register_type 'timestamp', OID::Timestamp.new
|
||||
register_type 'timestamptz', OID::Timestamp.new
|
||||
alias_type 'timestamptz', 'timestamp'
|
||||
register_type 'date', OID::Date.new
|
||||
register_type 'time', OID::Time.new
|
||||
|
||||
register_type 'path', OID::Text.new
|
||||
register_type 'money', OID::Money.new
|
||||
register_type 'bytea', OID::Bytea.new
|
||||
register_type 'point', OID::Point.new
|
||||
register_type 'polygon', OID::Text.new
|
||||
register_type 'circle', OID::Text.new
|
||||
register_type 'hstore', OID::Hstore.new
|
||||
register_type 'json', OID::Json.new
|
||||
register_type 'citext', OID::Text.new
|
||||
register_type 'ltree', OID::Text.new
|
||||
|
||||
register_type 'cidr', OID::Cidr.new
|
||||
alias_type 'inet', 'cidr'
|
||||
register_type 'inet', OID::Inet.new
|
||||
register_type 'xml', SpecializedString.new(:xml)
|
||||
register_type 'tsvector', SpecializedString.new(:tsvector)
|
||||
register_type 'macaddr', SpecializedString.new(:macaddr)
|
||||
register_type 'uuid', SpecializedString.new(:uuid)
|
||||
register_type 'citext', SpecializedString.new(:citext)
|
||||
register_type 'ltree', SpecializedString.new(:ltree)
|
||||
|
||||
# FIXME: why are we keeping these types as strings?
|
||||
alias_type 'interval', 'varchar'
|
||||
alias_type 'path', 'varchar'
|
||||
alias_type 'line', 'varchar'
|
||||
alias_type 'polygon', 'varchar'
|
||||
alias_type 'circle', 'varchar'
|
||||
alias_type 'lseg', 'varchar'
|
||||
alias_type 'box', 'varchar'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -29,8 +29,7 @@ class PostgresqlEnumTest < ActiveRecord::TestCase
|
|||
|
||||
def test_column
|
||||
column = PostgresqlEnum.columns_hash["current_mood"]
|
||||
# TODO: enum columns should be of type enum or string, not nil.
|
||||
assert_nil column.type
|
||||
assert_equal :enum, column.type
|
||||
assert_equal "mood", column.sql_type
|
||||
assert_not column.number?
|
||||
assert_not column.text?
|
||||
|
|
|
@ -47,9 +47,9 @@ module ActiveRecord
|
|||
|
||||
def test_quote_cast_numeric
|
||||
fixnum = 666
|
||||
c = PostgreSQLColumn.new(nil, nil, OID::Decimal.new, 'varchar')
|
||||
c = PostgreSQLColumn.new(nil, nil, OID::String.new, 'varchar')
|
||||
assert_equal "'666'", @conn.quote(fixnum, c)
|
||||
c = PostgreSQLColumn.new(nil, nil, OID::Decimal.new, 'text')
|
||||
c = PostgreSQLColumn.new(nil, nil, OID::Text.new, 'text')
|
||||
assert_equal "'666'", @conn.quote(fixnum, c)
|
||||
end
|
||||
|
||||
|
|
|
@ -127,13 +127,13 @@ module ActiveRecord
|
|||
|
||||
if current_adapter?(:PostgreSQLAdapter)
|
||||
def test_bigint_column_should_map_to_integer
|
||||
oid = PostgreSQLAdapter::OID::Identity.new
|
||||
oid = PostgreSQLAdapter::OID::Integer.new
|
||||
bigint_column = PostgreSQLColumn.new('number', nil, oid, "bigint")
|
||||
assert_equal :integer, bigint_column.type
|
||||
end
|
||||
|
||||
def test_smallint_column_should_map_to_integer
|
||||
oid = PostgreSQLAdapter::OID::Identity.new
|
||||
oid = PostgreSQLAdapter::OID::Integer.new
|
||||
smallint_column = PostgreSQLColumn.new('number', nil, oid, "smallint")
|
||||
assert_equal :integer, smallint_column.type
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue