Skip to content
Browse files

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
  • Loading branch information...
1 parent fc0b98d commit f7a6b115fea9f675190a79b701c7034214678f19 @senny senny committed Apr 1, 2014
View
4 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`.
View
12 activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -582,7 +582,7 @@ def add_oid(row, records_by_oid, type_map)
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 @@ def initialize_type_map(type_map)
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 @@ def initialize_type_map(type_map)
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:
View
53 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

0 comments on commit f7a6b11

Please sign in to comment.
Something went wrong with that request. Please try again.