Skip to content

Commit d9418ea

Browse files
committed
[postgres] OIDTypes helper that as compatible with AR 4.2
1 parent 0f16478 commit d9418ea

File tree

1 file changed

+125
-9
lines changed

1 file changed

+125
-9
lines changed

lib/arjdbc/postgresql/oid_types.rb

Lines changed: 125 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,7 @@ module OIDTypes
1414

1515
OID = ActiveRecord::ConnectionAdapters::PostgreSQL::OID
1616

17-
def get_oid_type(oid, fmod, column_name)
18-
type_map.fetch(oid, fmod) {
19-
warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
20-
type_map[oid] = OID::Identity.new
21-
}
22-
end
17+
Type = ActiveRecord::Type if AR42_COMPAT
2318

2419
# @override
2520
def enable_extension(name)
@@ -42,13 +37,40 @@ def extensions
4237
@extensions ||= super
4338
end
4439

40+
def get_oid_type(oid, fmod, column_name)
41+
type_map.fetch(oid, fmod) {
42+
warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
43+
type_map[oid] = OID::Identity.new
44+
}
45+
end unless AR42_COMPAT
46+
47+
def get_oid_type(oid, fmod, column_name, sql_type = '') # :nodoc:
48+
if !type_map.key?(oid)
49+
load_additional_types(type_map, [oid])
50+
end
51+
52+
type_map.fetch(oid, fmod, sql_type) {
53+
warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
54+
Type::Value.new.tap do |cast_type|
55+
type_map.register_type(oid, cast_type)
56+
end
57+
}
58+
end if AR42_COMPAT
59+
4560
private
4661

4762
@@type_map_cache = {}
4863
@@type_map_cache_lock = Mutex.new
4964

65+
if AR42_COMPAT
66+
TypeMap = ActiveRecord::Type::HashLookupTypeMap
67+
else
68+
TypeMap = OID::TypeMap
69+
end
70+
71+
# @see #type_map
5072
# @private
51-
class OID::TypeMap
73+
TypeMap.class_eval do
5274
def dup
5375
dup = super # make sure @mapping is not shared
5476
dup.instance_variable_set(:@mapping, @mapping.dup)
@@ -62,7 +84,7 @@ def type_map
6284
if type_map = @@type_map_cache[ type_cache_key ]
6385
type_map.dup
6486
else
65-
type_map = OID::TypeMap.new
87+
type_map = TypeMap.new
6688
initialize_type_map(type_map)
6789
cache_type_map(type_map)
6890
type_map
@@ -126,7 +148,101 @@ def initialize_type_map(type_map)
126148
array = OID::Array.new type_map[ row['typelem'].to_i ]
127149
type_map[ row['oid'].to_i ] = array
128150
end
129-
end
151+
end unless AR42_COMPAT
152+
153+
def initialize_type_map(m) # :nodoc:
154+
register_class_with_limit m, 'int2', OID::Integer
155+
m.alias_type 'int4', 'int2'
156+
m.alias_type 'int8', 'int2'
157+
m.alias_type 'oid', 'int2'
158+
m.register_type 'float4', OID::Float.new
159+
m.alias_type 'float8', 'float4'
160+
m.register_type 'text', Type::Text.new
161+
register_class_with_limit m, 'varchar', Type::String
162+
m.alias_type 'char', 'varchar'
163+
m.alias_type 'name', 'varchar'
164+
m.alias_type 'bpchar', 'varchar'
165+
m.register_type 'bool', Type::Boolean.new
166+
register_class_with_limit m, 'bit', OID::Bit
167+
register_class_with_limit m, 'varbit', OID::BitVarying
168+
m.alias_type 'timestamptz', 'timestamp'
169+
m.register_type 'date', OID::Date.new
170+
m.register_type 'time', OID::Time.new
171+
172+
m.register_type 'money', OID::Money.new
173+
m.register_type 'bytea', OID::Bytea.new
174+
m.register_type 'point', OID::Point.new
175+
m.register_type 'hstore', OID::Hstore.new
176+
m.register_type 'json', OID::Json.new
177+
m.register_type 'jsonb', OID::Jsonb.new
178+
m.register_type 'cidr', OID::Cidr.new
179+
m.register_type 'inet', OID::Inet.new
180+
m.register_type 'uuid', OID::Uuid.new
181+
m.register_type 'xml', OID::Xml.new
182+
m.register_type 'tsvector', OID::SpecializedString.new(:tsvector)
183+
m.register_type 'macaddr', OID::SpecializedString.new(:macaddr)
184+
m.register_type 'citext', OID::SpecializedString.new(:citext)
185+
m.register_type 'ltree', OID::SpecializedString.new(:ltree)
186+
187+
# FIXME: why are we keeping these types as strings?
188+
m.alias_type 'interval', 'varchar'
189+
m.alias_type 'path', 'varchar'
190+
m.alias_type 'line', 'varchar'
191+
m.alias_type 'polygon', 'varchar'
192+
m.alias_type 'circle', 'varchar'
193+
m.alias_type 'lseg', 'varchar'
194+
m.alias_type 'box', 'varchar'
195+
196+
m.register_type 'timestamp' do |_, _, sql_type|
197+
precision = extract_precision(sql_type)
198+
OID::DateTime.new(precision: precision)
199+
end
200+
201+
m.register_type 'numeric' do |_, fmod, sql_type|
202+
precision = extract_precision(sql_type)
203+
scale = extract_scale(sql_type)
204+
205+
# The type for the numeric depends on the width of the field,
206+
# so we'll do something special here.
207+
#
208+
# When dealing with decimal columns:
209+
#
210+
# places after decimal = fmod - 4 & 0xffff
211+
# places before decimal = (fmod - 4) >> 16 & 0xffff
212+
if fmod && (fmod - 4 & 0xffff).zero?
213+
# FIXME: Remove this class, and the second argument to
214+
# lookups on PG
215+
Type::DecimalWithoutScale.new(precision: precision)
216+
else
217+
OID::Decimal.new(precision: precision, scale: scale)
218+
end
219+
end
220+
221+
load_additional_types(m)
222+
end if AR42_COMPAT
223+
224+
def load_additional_types(type_map, oids = nil) # :nodoc:
225+
if supports_ranges?
226+
query = <<-SQL
227+
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
228+
FROM pg_type as t
229+
LEFT JOIN pg_range as r ON oid = rngtypid
230+
SQL
231+
else
232+
query = <<-SQL
233+
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, t.typtype, t.typbasetype
234+
FROM pg_type as t
235+
SQL
236+
end
237+
238+
if oids
239+
query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
240+
end
241+
242+
initializer = OID::TypeMapInitializer.new(type_map)
243+
records = execute(query, 'SCHEMA')
244+
initializer.run(records)
245+
end if AR42_COMPAT
130246

131247
end
132248
end

0 commit comments

Comments
 (0)