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:
parent
856ffbe705
commit
0c9bbc6326
8 changed files with 57 additions and 16 deletions
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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' }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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|
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue