diff options
Diffstat (limited to 'activerecord')
8 files changed, 92 insertions, 96 deletions
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb index e54c092bf6..2494e19f84 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb @@ -27,73 +27,6 @@ module ActiveRecord module ConnectionAdapters module PostgreSQL module OID # :nodoc: - # When the PG adapter connects, the pg_type table is queried. The - # key of this hash maps to the `typname` column from the table. - # type_map is then dynamically built with oids as the key and type - # objects as values. - NAMES = Hash.new { |h,k| # :nodoc: - h[k] = Type::Value.new - } - - # Register an OID type named +name+ with a typecasting object in - # +type+. +name+ should correspond to the `typname` column in - # the `pg_type` table. - def self.register_type(name, type) - NAMES[name] = type - end - - # Alias the +old+ type to the +new+ type. - def self.alias_type(new, old) - NAMES[new] = NAMES[old] - end - - # Is +name+ a registered type? - def self.registered_type?(name) - NAMES.key? name - end - - register_type 'int2', OID::Integer.new - alias_type 'int4', 'int2' - alias_type 'int8', 'int2' - alias_type 'oid', 'int2' - register_type 'numeric', OID::Decimal.new - register_type 'float4', OID::Float.new - alias_type 'float8', 'float4' - register_type 'text', Type::Text.new - register_type 'varchar', Type::String.new - alias_type 'char', 'varchar' - alias_type 'name', 'varchar' - alias_type 'bpchar', 'varchar' - register_type 'bool', Type::Boolean.new - register_type 'bit', OID::Bit.new - alias_type 'varbit', 'bit' - register_type 'timestamp', OID::DateTime.new - alias_type 'timestamptz', 'timestamp' - register_type 'date', OID::Date.new - register_type 'time', OID::Time.new - - register_type 'money', OID::Money.new - register_type 'bytea', OID::Bytea.new - register_type 'point', OID::Point.new - register_type 'hstore', OID::Hstore.new - register_type 'json', OID::Json.new - register_type 'cidr', OID::Cidr.new - register_type 'inet', OID::Inet.new - register_type 'uuid', OID::Uuid.new - register_type 'xml', SpecializedString.new(:xml) - register_type 'tsvector', SpecializedString.new(:tsvector) - register_type 'macaddr', SpecializedString.new(:macaddr) - 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 diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb index 27829ae1a3..28f7a4eafb 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb @@ -13,7 +13,8 @@ module ActiveRecord end def run(records) - mapped, nodes = records.partition { |row| OID.registered_type? row['typname'] } + nodes = records.reject { |row| @store.key? row['oid'].to_i } + mapped, nodes = nodes.partition { |row| @store.key? row['typname'] } ranges, nodes = nodes.partition { |row| row['typtype'] == 'r' } enums, nodes = nodes.partition { |row| row['typtype'] == 'e' } domains, nodes = nodes.partition { |row| row['typtype'] == 'd' } @@ -30,7 +31,7 @@ module ActiveRecord private def register_mapped_type(row) - register row['oid'], OID::NAMES[row['typname']] + alias_type row['oid'], row['typname'] end def register_enum_type(row) @@ -64,12 +65,18 @@ module ActiveRecord end def register(oid, oid_type) - oid = oid.to_i + oid = assert_valid_registration(oid, oid_type) + @store.register_type(oid, oid_type) + end - raise ArgumentError, "can't register nil type for OID #{oid}" if oid_type.nil? - return if @store.key?(oid) + def alias_type(oid, target) + oid = assert_valid_registration(oid, target) + @store.alias_type(oid, target) + end - @store.register_type(oid, oid_type) + def assert_valid_registration(oid, oid_type) + raise ArgumentError, "can't register nil type for OID #{oid}" if oid_type.nil? + oid.to_i end end end 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 539ba38c4a..c04a1d7178 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb @@ -178,7 +178,7 @@ module ActiveRecord def columns(table_name) # Limit, precision, and scale are all handled by the superclass. column_definitions(table_name).map do |column_name, type, default, notnull, oid, fmod| - oid = get_oid_type(oid.to_i, fmod.to_i, column_name) + oid = get_oid_type(oid.to_i, fmod.to_i, column_name, type) PostgreSQLColumn.new(column_name, default, oid, type, notnull == 'f') end end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index ed3e884455..7d1fa0dd08 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -538,12 +538,12 @@ module ActiveRecord private - def get_oid_type(oid, fmod, column_name) + def get_oid_type(oid, fmod, column_name, sql_type = '') if !type_map.key?(oid) - initialize_type_map(type_map, [oid]) + load_additional_types(type_map, [oid]) end - type_map.fetch(normalize_oid_type(oid, fmod)) { + type_map.fetch(normalize_oid_type(oid, fmod), sql_type) { warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String." Type::Value.new.tap do |cast_type| type_map.register_type(oid, cast_type) @@ -566,7 +566,54 @@ module ActiveRecord end end - def initialize_type_map(type_map, oids = nil) + def initialize_type_map(m) + m.register_type 'int2', OID::Integer.new + m.alias_type 'int4', 'int2' + m.alias_type 'int8', 'int2' + m.alias_type 'oid', 'int2' + m.register_type 'numeric', OID::Decimal.new + m.register_type 'float4', OID::Float.new + m.alias_type 'float8', 'float4' + m.register_type 'text', Type::Text.new + m.register_type 'varchar', Type::String.new + m.alias_type 'char', 'varchar' + m.alias_type 'name', 'varchar' + m.alias_type 'bpchar', 'varchar' + m.register_type 'bool', Type::Boolean.new + m.register_type 'bit', OID::Bit.new + m.alias_type 'varbit', 'bit' + m.register_type 'timestamp', OID::DateTime.new + m.alias_type 'timestamptz', 'timestamp' + m.register_type 'date', OID::Date.new + m.register_type 'time', OID::Time.new + + m.register_type 'money', OID::Money.new + m.register_type 'bytea', OID::Bytea.new + m.register_type 'point', OID::Point.new + m.register_type 'hstore', OID::Hstore.new + m.register_type 'json', OID::Json.new + m.register_type 'cidr', OID::Cidr.new + m.register_type 'inet', OID::Inet.new + m.register_type 'uuid', OID::Uuid.new + m.register_type 'xml', OID::SpecializedString.new(:xml) + m.register_type 'tsvector', OID::SpecializedString.new(:tsvector) + m.register_type 'macaddr', OID::SpecializedString.new(:macaddr) + m.register_type 'citext', OID::SpecializedString.new(:citext) + m.register_type 'ltree', OID::SpecializedString.new(:ltree) + + # FIXME: why are we keeping these types as strings? + m.alias_type 'interval', 'varchar' + m.alias_type 'path', 'varchar' + m.alias_type 'line', 'varchar' + m.alias_type 'polygon', 'varchar' + m.alias_type 'circle', 'varchar' + m.alias_type 'lseg', 'varchar' + m.alias_type 'box', 'varchar' + + load_additional_types(m) + end + + def load_additional_types(type_map, oids = nil) if supports_ranges? query = <<-SQL SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype diff --git a/activerecord/lib/active_record/connection_adapters/type/hash_lookup_type_map.rb b/activerecord/lib/active_record/connection_adapters/type/hash_lookup_type_map.rb index 8503d3ea1b..bb1abc77ff 100644 --- a/activerecord/lib/active_record/connection_adapters/type/hash_lookup_type_map.rb +++ b/activerecord/lib/active_record/connection_adapters/type/hash_lookup_type_map.rb @@ -4,16 +4,16 @@ module ActiveRecord class HashLookupTypeMap < TypeMap # :nodoc: delegate :key?, to: :@mapping - def lookup(type) - @mapping.fetch(type, proc { default_value }).call(type) + def lookup(type, *args) + @mapping.fetch(type, proc { default_value }).call(type, *args) end - def fetch(type, &block) - @mapping.fetch(type, block).call(type) + def fetch(type, *args, &block) + @mapping.fetch(type, block).call(type, *args) end def alias_type(type, alias_type) - register_type(type) { lookup(alias_type) } + register_type(type) { |_, *args| lookup(alias_type, *args) } end end end diff --git a/activerecord/lib/active_record/connection_adapters/type/type_map.rb b/activerecord/lib/active_record/connection_adapters/type/type_map.rb index d89171a820..48b8b51417 100644 --- a/activerecord/lib/active_record/connection_adapters/type/type_map.rb +++ b/activerecord/lib/active_record/connection_adapters/type/type_map.rb @@ -6,13 +6,13 @@ module ActiveRecord @mapping = {} end - def lookup(lookup_key) + def lookup(lookup_key, *args) matching_pair = @mapping.reverse_each.detect do |key, _| key === lookup_key end if matching_pair - matching_pair.last.call(lookup_key) + matching_pair.last.call(lookup_key, *args) else default_value end @@ -29,9 +29,9 @@ module ActiveRecord end def alias_type(key, target_key) - register_type(key) do |sql_type| + register_type(key) do |sql_type, *args| metadata = sql_type[/\(.*\)/, 0] - lookup("#{target_key}#{metadata}") + lookup("#{target_key}#{metadata}", *args) end end diff --git a/activerecord/test/cases/adapters/postgresql/composite_test.rb b/activerecord/test/cases/adapters/postgresql/composite_test.rb index f01717d1a7..1f55cce352 100644 --- a/activerecord/test/cases/adapters/postgresql/composite_test.rb +++ b/activerecord/test/cases/adapters/postgresql/composite_test.rb @@ -102,15 +102,7 @@ class PostgresqlCompositeWithCustomOIDTest < ActiveRecord::TestCase def setup super - @registration = ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::OID - @registration.register_type "full_address", FullAddressType.new - end - - def teardown - super - - # there is currently no clean way to unregister a OID::Type - @registration::NAMES.delete("full_address") + @connection.type_map.register_type "full_address", FullAddressType.new end def test_column diff --git a/activerecord/test/cases/connection_adapters/type/type_map_test.rb b/activerecord/test/cases/connection_adapters/type/type_map_test.rb index 4b4d9f6b0f..3abd7a276e 100644 --- a/activerecord/test/cases/connection_adapters/type/type_map_test.rb +++ b/activerecord/test/cases/connection_adapters/type/type_map_test.rb @@ -88,6 +88,23 @@ module ActiveRecord assert_equal mapping.lookup('varchar'), binary end + def test_additional_lookup_args + mapping = TypeMap.new + + mapping.register_type(/varchar/i) do |type, limit| + if limit > 255 + 'text' + else + 'string' + end + end + mapping.alias_type(/string/i, 'varchar') + + assert_equal mapping.lookup('varchar', 200), 'string' + assert_equal mapping.lookup('varchar', 400), 'text' + assert_equal mapping.lookup('string', 400), 'text' + end + def test_requires_value_or_block mapping = TypeMap.new |