aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG.md4
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb12
-rw-r--r--activerecord/test/cases/adapters/postgresql/domain_test.rb53
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