1
0
Fork 0
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:
Yves Senn 2014-03-26 17:14:05 +01:00
parent 5eb13fcaac
commit 4d344bb4fd
6 changed files with 105 additions and 108 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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