diff options
author | Yves Senn <yves.senn@gmail.com> | 2014-04-01 19:42:22 +0200 |
---|---|---|
committer | Yves Senn <yves.senn@gmail.com> | 2014-04-01 19:52:34 +0200 |
commit | f7a6b115fea9f675190a79b701c7034214678f19 (patch) | |
tree | e185ba5ab64b915a6d5c630fe35fd40e681ffe24 | |
parent | fc0b98da0b83329474922a920a6070d370091228 (diff) | |
download | rails-f7a6b115fea9f675190a79b701c7034214678f19.tar.gz rails-f7a6b115fea9f675190a79b701c7034214678f19.tar.bz2 rails-f7a6b115fea9f675190a79b701c7034214678f19.zip |
PostgreSQL, register custom domains. Closes #14305.
This patch registers custom domains in our OID-type_map.
They will behave exactly as the type specified by `pg_type.typbasetype`.
/cc @matthewd
-rw-r--r-- | activerecord/CHANGELOG.md | 4 | ||||
-rw-r--r-- | activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb | 12 | ||||
-rw-r--r-- | activerecord/test/cases/adapters/postgresql/domain_test.rb | 53 |
3 files changed, 68 insertions, 1 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 0235e652d3..1aeba4f856 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,7 @@ +* The PostgreSQL adapter supports custom domains. Fixes #14305. + + *Yves Senn* + * 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`. diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index c31fe96842..e8719d5269 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -582,7 +582,7 @@ module ActiveRecord def initialize_type_map(type_map) if supports_ranges? result = execute(<<-SQL, 'SCHEMA') - SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype + SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype FROM pg_type as t LEFT JOIN pg_range as r ON oid = rngtypid SQL @@ -593,6 +593,7 @@ module ActiveRecord SQL end ranges, nodes = result.partition { |row| row['typinput'] == 'range_in' } + domains, nodes = nodes.partition { |row| row['typtype'] == 'd' } leaves, nodes = nodes.partition { |row| row['typelem'] == '0' } arrays, nodes = nodes.partition { |row| row['typinput'] == 'array_in' } @@ -626,6 +627,15 @@ module ActiveRecord range = OID::Range.new subtype type_map[row['oid'].to_i] = range end + + # populate domain types + domains.each do |row| + if base_type = type_map[row["typbasetype"].to_i] + type_map[row['oid'].to_i] = base_type + else + warn "unknown base type (OID: #{row["typbasetype"].to_i}) for domain #{row["typname"]}." + end + end end FEATURE_NOT_SUPPORTED = "0A000" #:nodoc: diff --git a/activerecord/test/cases/adapters/postgresql/domain_test.rb b/activerecord/test/cases/adapters/postgresql/domain_test.rb new file mode 100644 index 0000000000..fdf5ce974d --- /dev/null +++ b/activerecord/test/cases/adapters/postgresql/domain_test.rb @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +require "cases/helper" +require 'active_record/base' +require 'active_record/connection_adapters/postgresql_adapter' + +class PostgresqlDomainTest < ActiveRecord::TestCase + class PostgresqlDomain < ActiveRecord::Base + self.table_name = "postgresql_domains" + end + + def setup + # reset connection to bust all cached statement plans + connection_spec = ActiveRecord::Base.remove_connection + ActiveRecord::Base.establish_connection(connection_spec) + + @connection = ActiveRecord::Base.connection + @connection.transaction do + @connection.execute "CREATE DOMAIN custom_money as numeric(8,2)" + @connection.create_table('postgresql_domains') do |t| + t.column :price, :custom_money + end + end + + # reload type map after creating the enum type + @connection.send(:reload_type_map) + end + + teardown do + @connection.execute 'DROP TABLE IF EXISTS postgresql_domains' + @connection.execute 'DROP DOMAIN IF EXISTS custom_money' + end + + def test_column + column = PostgresqlDomain.columns_hash["price"] + assert_equal :decimal, column.type + assert_equal "custom_money", column.sql_type + assert column.number? + assert_not column.text? + assert_not column.binary? + assert_not column.array + end + + def test_domain_acts_like_basetype + PostgresqlDomain.create price: "" + record = PostgresqlDomain.first + assert_nil record.price + + record.price = "34.15" + record.save! + + assert_equal BigDecimal.new("34.15"), record.reload.price + end +end |