1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

PostgreSQL, adapter automatically reloads it's type map. Closes #14678.

[Yves Senn & Matthew Draper]
This commit is contained in:
Yves Senn 2014-04-10 20:25:40 +02:00
parent 856ffbe705
commit 0c9bbc6326
8 changed files with 57 additions and 16 deletions

View file

@ -1,3 +1,10 @@
* PostgreSQL adapter automatically reloads it's type map when encountering
unknown OIDs.
Fixes #14678.
*Matthew Draper*, *Yves Senn*
* Fix insertion of records via `has_many :through` association with scope.
Fixes #3548.

View file

@ -142,10 +142,7 @@ module ActiveRecord
fields.each_with_index do |fname, i|
ftype = result.ftype i
fmod = result.fmod i
types[fname] = type_map.fetch(ftype, fmod) { |oid, mod|
warn "unknown OID: #{fname}(#{oid}) (#{sql})"
OID::Identity.new
}
types[fname] = get_oid_type(ftype, fmod, fname)
end
ret = ActiveRecord::Result.new(fields, result.values, types)

View file

@ -182,9 +182,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 = type_map.fetch(oid.to_i, fmod.to_i) {
OID::Identity.new
}
oid = get_oid_type(oid.to_i, fmod.to_i, column_name)
PostgreSQLColumn.new(column_name, default, oid, type, notnull == 'f')
end
end

View file

@ -559,6 +559,17 @@ module ActiveRecord
@type_map
end
def get_oid_type(oid, fmod, column_name)
if !type_map.key?(oid)
initialize_type_map(type_map, [oid])
end
type_map.fetch(oid, fmod) {
warn "unknown OID #{oid}: failed to recognize type of #{column_name}"
OID::Identity.new
}
end
def reload_type_map
type_map.clear
initialize_type_map(type_map)
@ -583,19 +594,25 @@ module ActiveRecord
type_map
end
def initialize_type_map(type_map)
def initialize_type_map(type_map, oids = nil)
if supports_ranges?
result = execute(<<-SQL, 'SCHEMA')
query = <<-SQL
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
else
result = execute(<<-SQL, 'SCHEMA')
query = <<-SQL
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, t.typtype, t.typbasetype
FROM pg_type as t
SQL
end
if oids
query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
end
result = execute(query, 'SCHEMA')
ranges, nodes = result.partition { |row| row['typtype'] == 'r' }
enums, nodes = nodes.partition { |row| row['typtype'] == 'e' }
domains, nodes = nodes.partition { |row| row['typtype'] == 'd' }

View file

@ -19,9 +19,6 @@ class PostgresqlDomainTest < ActiveRecord::TestCase
t.column :price, :custom_money
end
end
# reload type map after creating the enum type
@connection.send(:reload_type_map)
end
teardown do

View file

@ -21,8 +21,6 @@ class PostgresqlEnumTest < ActiveRecord::TestCase
t.column :current_mood, :mood
end
end
# reload type map after creating the enum type
@connection.send(:reload_type_map)
end
teardown do

View file

@ -1,11 +1,13 @@
# encoding: utf-8
require "cases/helper"
require 'support/ddl_helper'
require 'support/connection_helper'
module ActiveRecord
module ConnectionAdapters
class PostgreSQLAdapterTest < ActiveRecord::TestCase
include DdlHelper
include ConnectionHelper
def setup
@connection = ActiveRecord::Base.connection
@ -357,6 +359,32 @@ module ActiveRecord
end
end
def test_reload_type_map_for_newly_defined_types
@connection.execute "CREATE TYPE feeling AS ENUM ('good', 'bad')"
result = @connection.select_all "SELECT 'good'::feeling"
assert_instance_of(PostgreSQLAdapter::OID::Enum,
result.column_types["feeling"])
ensure
@connection.execute "DROP TYPE IF EXISTS feeling"
reset_connection
end
def test_only_reload_type_map_once_for_every_unknown_type
silence_warnings do
assert_queries 2, ignore_none: true do
@connection.select_all "SELECT NULL::anyelement"
end
assert_queries 1, ignore_none: true do
@connection.select_all "SELECT NULL::anyelement"
end
assert_queries 2, ignore_none: true do
@connection.select_all "SELECT NULL::anyarray"
end
end
ensure
reset_connection
end
private
def insert(ctx, data)
binds = data.map { |name, value|

View file

@ -34,7 +34,6 @@ _SQL
@connection.add_column 'postgresql_ranges', 'float_range', 'floatrange'
end
@connection.send :reload_type_map
PostgresqlRange.reset_column_information
rescue ActiveRecord::StatementInvalid
skip "do not test on PG without range"