Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 367 lines (299 sloc) 10.619 kb
9fd3bde @tenderlove many of the OIDs mapped and implemented
tenderlove authored
1 require 'active_record/connection_adapters/abstract_adapter'
2
3 module ActiveRecord
4 module ConnectionAdapters
5 class PostgreSQLAdapter < AbstractAdapter
6 module OID
5396fca @tenderlove give each PG type a `type` method and decortate tz attributes
tenderlove authored
7 class Type
8 def type; end
2927007 @tenderlove only unescape bytea after it has been read from the database
tenderlove authored
9
10 def type_cast_for_write(value)
11 value
12 end
5396fca @tenderlove give each PG type a `type` method and decortate tz attributes
tenderlove authored
13 end
14
15 class Identity < Type
9fd3bde @tenderlove many of the OIDs mapped and implemented
tenderlove authored
16 def type_cast(value)
17 value
18 end
19 end
20
9339005 @kennyj Add OID::Bit for supporting bit string.
kennyj authored
21 class Bit < Type
22 def type_cast(value)
23 if String === value
24 ConnectionAdapters::PostgreSQLColumn.string_to_bit value
25 else
26 value
27 end
28 end
29 end
30
5396fca @tenderlove give each PG type a `type` method and decortate tz attributes
tenderlove authored
31 class Bytea < Type
9fd3bde @tenderlove many of the OIDs mapped and implemented
tenderlove authored
32 def type_cast(value)
280c5ed @mattetti fix for the bytea/binary nil value bug
mattetti authored
33 return if value.nil?
c50cb4a @tenderlove PG column consults oid types when typecasting
tenderlove authored
34 PGconn.unescape_bytea value
9fd3bde @tenderlove many of the OIDs mapped and implemented
tenderlove authored
35 end
36 end
37
5396fca @tenderlove give each PG type a `type` method and decortate tz attributes
tenderlove authored
38 class Money < Type
9fd3bde @tenderlove many of the OIDs mapped and implemented
tenderlove authored
39 def type_cast(value)
20440fd @tenderlove return early from typecasting if the value is nil
tenderlove authored
40 return if value.nil?
41
9fd3bde @tenderlove many of the OIDs mapped and implemented
tenderlove authored
42 # Because money output is formatted according to the locale, there are two
43 # cases to consider (note the decimal separators):
44 # (1) $12,345,678.12
45 # (2) $12.345.678,12
46
47 case value
48 when /^-?\D+[\d,]+\.\d{2}$/ # (1)
49 value.gsub!(/[^-\d.]/, '')
50 when /^-?\D+[\d.]+,\d{2}$/ # (2)
51 value.gsub!(/[^-\d,]/, '').sub!(/,/, '.')
52 end
53
54 ConnectionAdapters::Column.value_to_decimal value
55 end
56 end
57
5396fca @tenderlove give each PG type a `type` method and decortate tz attributes
tenderlove authored
58 class Vector < Type
9fd3bde @tenderlove many of the OIDs mapped and implemented
tenderlove authored
59 attr_reader :delim, :subtype
60
61 # +delim+ corresponds to the `typdelim` column in the pg_types
62 # table. +subtype+ is derived from the `typelem` column in the
63 # pg_types table.
64 def initialize(delim, subtype)
65 @delim = delim
66 @subtype = subtype
67 end
68
69 # FIXME: this should probably split on +delim+ and use +subtype+
70 # to cast the values. Unfortunately, the current Rails behavior
71 # is to just return the string.
72 def type_cast(value)
73 value
74 end
75 end
76
336b376 @MSch Make Postgres point type correspond to ruby array with two floats inside
MSch authored
77 class Point < Type
78 def type_cast(value)
79 if String === value
80 ConnectionAdapters::PostgreSQLColumn.string_to_point value
81 else
82 value
83 end
84 end
85 end
86
4544d2b @danmcclain Moves column dump specific code to a module included in AbstractAdapter
danmcclain authored
87 class Array < Type
88 attr_reader :subtype
89 def initialize(subtype)
90 @subtype = subtype
91 end
92
93 def type_cast(value)
94 if String === value
95 ConnectionAdapters::PostgreSQLColumn.string_to_array value, @subtype
96 else
97 value
98 end
99 end
100 end
101
af1ef85 @slbug Add postgresql range types support
slbug authored
102 class Range < Type
103 attr_reader :subtype
104 def initialize(subtype)
105 @subtype = subtype
106 end
107
3bdfece @rafaelfranca Fix typo
rafaelfranca authored
108 def extract_bounds(value)
af1ef85 @slbug Add postgresql range types support
slbug authored
109 from, to = value[1..-2].split(',')
110 {
111 from: (value[1] == ',' || from == '-infinity') ? infinity(:negative => true) : from,
112 to: (value[-2] == ',' || to == 'infinity') ? infinity : to,
113 exclude_start: (value[0] == '('),
114 exclude_end: (value[-1] == ')')
115 }
116 end
117
118 def infinity(options = {})
119 ::Float::INFINITY * (options[:negative] ? -1 : 1)
120 end
121
122 def infinity?(value)
123 value.respond_to?(:infinite?) && value.infinite?
124 end
125
126 def to_integer(value)
127 infinity?(value) ? value : value.to_i
128 end
129
130 def type_cast(value)
131 return if value.nil? || value == 'empty'
132 return value if value.is_a?(::Range)
133
3bdfece @rafaelfranca Fix typo
rafaelfranca authored
134 extracted = extract_bounds(value)
af1ef85 @slbug Add postgresql range types support
slbug authored
135
136 case @subtype
137 when :date
138 from = ConnectionAdapters::Column.value_to_date(extracted[:from])
139 from -= 1.day if extracted[:exclude_start]
140 to = ConnectionAdapters::Column.value_to_date(extracted[:to])
141 when :decimal
142 from = BigDecimal.new(extracted[:from].to_s)
143 # FIXME: add exclude start for ::Range, same for timestamp ranges
144 to = BigDecimal.new(extracted[:to].to_s)
145 when :time
146 from = ConnectionAdapters::Column.string_to_time(extracted[:from])
147 to = ConnectionAdapters::Column.string_to_time(extracted[:to])
148 when :integer
149 from = to_integer(extracted[:from]) rescue value ? 1 : 0
150 from -= 1 if extracted[:exclude_start]
151 to = to_integer(extracted[:to]) rescue value ? 1 : 0
152 else
153 return value
154 end
155
156 ::Range.new(from, to, extracted[:exclude_end])
157 end
158 end
159
5396fca @tenderlove give each PG type a `type` method and decortate tz attributes
tenderlove authored
160 class Integer < Type
9fd3bde @tenderlove many of the OIDs mapped and implemented
tenderlove authored
161 def type_cast(value)
20440fd @tenderlove return early from typecasting if the value is nil
tenderlove authored
162 return if value.nil?
163
d64c7c7 @rafaelfranca Reuse the Column integer converter
rafaelfranca authored
164 ConnectionAdapters::Column.value_to_integer value
9fd3bde @tenderlove many of the OIDs mapped and implemented
tenderlove authored
165 end
166 end
167
5396fca @tenderlove give each PG type a `type` method and decortate tz attributes
tenderlove authored
168 class Boolean < Type
9fd3bde @tenderlove many of the OIDs mapped and implemented
tenderlove authored
169 def type_cast(value)
20440fd @tenderlove return early from typecasting if the value is nil
tenderlove authored
170 return if value.nil?
171
9454076 @tenderlove mapping more oids
tenderlove authored
172 ConnectionAdapters::Column.value_to_boolean value
9fd3bde @tenderlove many of the OIDs mapped and implemented
tenderlove authored
173 end
174 end
175
5396fca @tenderlove give each PG type a `type` method and decortate tz attributes
tenderlove authored
176 class Timestamp < Type
177 def type; :timestamp; end
178
9fd3bde @tenderlove many of the OIDs mapped and implemented
tenderlove authored
179 def type_cast(value)
20440fd @tenderlove return early from typecasting if the value is nil
tenderlove authored
180 return if value.nil?
181
9fd3bde @tenderlove many of the OIDs mapped and implemented
tenderlove authored
182 # FIXME: probably we can improve this since we know it is PG
183 # specific
e54d5cc @tenderlove use the pg column to cast values
tenderlove authored
184 ConnectionAdapters::PostgreSQLColumn.string_to_time value
9fd3bde @tenderlove many of the OIDs mapped and implemented
tenderlove authored
185 end
186 end
187
5396fca @tenderlove give each PG type a `type` method and decortate tz attributes
tenderlove authored
188 class Date < Type
189 def type; :datetime; end
190
9fd3bde @tenderlove many of the OIDs mapped and implemented
tenderlove authored
191 def type_cast(value)
20440fd @tenderlove return early from typecasting if the value is nil
tenderlove authored
192 return if value.nil?
193
9fd3bde @tenderlove many of the OIDs mapped and implemented
tenderlove authored
194 # FIXME: probably we can improve this since we know it is PG
195 # specific
196 ConnectionAdapters::Column.value_to_date value
197 end
198 end
199
5396fca @tenderlove give each PG type a `type` method and decortate tz attributes
tenderlove authored
200 class Time < Type
9fd3bde @tenderlove many of the OIDs mapped and implemented
tenderlove authored
201 def type_cast(value)
20440fd @tenderlove return early from typecasting if the value is nil
tenderlove authored
202 return if value.nil?
203
9fd3bde @tenderlove many of the OIDs mapped and implemented
tenderlove authored
204 # FIXME: probably we can improve this since we know it is PG
205 # specific
206 ConnectionAdapters::Column.string_to_dummy_time value
207 end
208 end
209
5396fca @tenderlove give each PG type a `type` method and decortate tz attributes
tenderlove authored
210 class Float < Type
9fd3bde @tenderlove many of the OIDs mapped and implemented
tenderlove authored
211 def type_cast(value)
20440fd @tenderlove return early from typecasting if the value is nil
tenderlove authored
212 return if value.nil?
213
9fd3bde @tenderlove many of the OIDs mapped and implemented
tenderlove authored
214 value.to_f
215 end
216 end
217
5396fca @tenderlove give each PG type a `type` method and decortate tz attributes
tenderlove authored
218 class Decimal < Type
3efbd7f @tenderlove taking column width in to account when fetching decimal fields
tenderlove authored
219 def type_cast(value)
220 return if value.nil?
221
222 ConnectionAdapters::Column.value_to_decimal value
223 end
224 end
225
5396fca @tenderlove give each PG type a `type` method and decortate tz attributes
tenderlove authored
226 class Hstore < Type
9821175 @tenderlove hstores can be typecast
tenderlove authored
227 def type_cast(value)
228 return if value.nil?
229
f7b915b @tenderlove Merge branch 'joelhoffman-postgres_schema_builder' into instance_reader
tenderlove authored
230 ConnectionAdapters::PostgreSQLColumn.string_to_hstore value
9821175 @tenderlove hstores can be typecast
tenderlove authored
231 end
232 end
233
c334175 @danmcclain Converts inet and cidr columns to NetAddr::CIDR
danmcclain authored
234 class Cidr < Type
235 def type_cast(value)
236 return if value.nil?
237
238 ConnectionAdapters::PostgreSQLColumn.string_to_cidr value
239 end
240 end
241
3b516b5 @guedes ActiveRecord support to PostgreSQL 9.2 JSON type
guedes authored
242 class Json < Type
243 def type_cast(value)
244 return if value.nil?
245
246 ConnectionAdapters::PostgreSQLColumn.string_to_json value
247 end
248 end
249
3efbd7f @tenderlove taking column width in to account when fetching decimal fields
tenderlove authored
250 class TypeMap
251 def initialize
252 @mapping = {}
253 end
254
255 def []=(oid, type)
256 @mapping[oid] = type
257 end
258
259 def [](oid)
260 @mapping[oid]
261 end
262
efd2be3 @tenderlove reloading type map on extension changing
tenderlove authored
263 def clear
264 @mapping.clear
265 end
266
fa6cda5 @tenderlove dynamically populate casting objects via the pg_type table
tenderlove authored
267 def key?(oid)
268 @mapping.key? oid
269 end
270
3efbd7f @tenderlove taking column width in to account when fetching decimal fields
tenderlove authored
271 def fetch(ftype, fmod)
272 # The type for the numeric depends on the width of the field,
273 # so we'll do something special here.
274 #
275 # When dealing with decimal columns:
276 #
277 # places after decimal = fmod - 4 & 0xffff
278 # places before decimal = (fmod - 4) >> 16 & 0xffff
279 if ftype == 1700 && (fmod - 4 & 0xffff).zero?
280 ftype = 23
281 end
282
283 @mapping.fetch(ftype) { |oid| yield oid, fmod }
284 end
285 end
286
287 TYPE_MAP = TypeMap.new # :nodoc:
9fd3bde @tenderlove many of the OIDs mapped and implemented
tenderlove authored
288
fa6cda5 @tenderlove dynamically populate casting objects via the pg_type table
tenderlove authored
289 # When the PG adapter connects, the pg_type table is queried. The
290 # key of this hash maps to the `typname` column from the table.
291 # TYPE_MAP is then dynamically built with oids as the key and type
292 # objects as values.
293 NAMES = Hash.new { |h,k| # :nodoc:
294 h[k] = OID::Identity.new
295 }
296
297 # Register an OID type named +name+ with a typcasting object in
298 # +type+. +name+ should correspond to the `typname` column in
299 # the `pg_type` table.
300 def self.register_type(name, type)
301 NAMES[name] = type
302 end
3efbd7f @tenderlove taking column width in to account when fetching decimal fields
tenderlove authored
303
fa6cda5 @tenderlove dynamically populate casting objects via the pg_type table
tenderlove authored
304 # Alias the +old+ type to the +new+ type.
305 def self.alias_type(new, old)
306 NAMES[new] = NAMES[old]
307 end
308
309 # Is +name+ a registered type?
310 def self.registered_type?(name)
311 NAMES.key? name
312 end
313
314 register_type 'int2', OID::Integer.new
315 alias_type 'int4', 'int2'
316 alias_type 'int8', 'int2'
317 alias_type 'oid', 'int2'
318
af1ef85 @slbug Add postgresql range types support
slbug authored
319 register_type 'daterange', OID::Range.new(:date)
320 register_type 'numrange', OID::Range.new(:decimal)
321 register_type 'tsrange', OID::Range.new(:time)
322 register_type 'int4range', OID::Range.new(:integer)
323 alias_type 'tstzrange', 'tsrange'
324 alias_type 'int8range', 'int4range'
325
fa6cda5 @tenderlove dynamically populate casting objects via the pg_type table
tenderlove authored
326 register_type 'numeric', OID::Decimal.new
327 register_type 'text', OID::Identity.new
328 alias_type 'varchar', 'text'
329 alias_type 'char', 'text'
c50cb4a @tenderlove PG column consults oid types when typecasting
tenderlove authored
330 alias_type 'bpchar', 'text'
331 alias_type 'xml', 'text'
9fd3bde @tenderlove many of the OIDs mapped and implemented
tenderlove authored
332
333 # FIXME: why are we keeping these types as strings?
fa6cda5 @tenderlove dynamically populate casting objects via the pg_type table
tenderlove authored
334 alias_type 'tsvector', 'text'
335 alias_type 'interval', 'text'
c334175 @danmcclain Converts inet and cidr columns to NetAddr::CIDR
danmcclain authored
336 alias_type 'macaddr', 'text'
12e9a75 @etehtsea Add uuid type support to PostgreSQL adapter
etehtsea authored
337 alias_type 'uuid', 'text'
9fd3bde @tenderlove many of the OIDs mapped and implemented
tenderlove authored
338
fa6cda5 @tenderlove dynamically populate casting objects via the pg_type table
tenderlove authored
339 register_type 'money', OID::Money.new
340 register_type 'bytea', OID::Bytea.new
341 register_type 'bool', OID::Boolean.new
9339005 @kennyj Add OID::Bit for supporting bit string.
kennyj authored
342 register_type 'bit', OID::Bit.new
343 register_type 'varbit', OID::Bit.new
9fd3bde @tenderlove many of the OIDs mapped and implemented
tenderlove authored
344
fa6cda5 @tenderlove dynamically populate casting objects via the pg_type table
tenderlove authored
345 register_type 'float4', OID::Float.new
346 alias_type 'float8', 'float4'
9fd3bde @tenderlove many of the OIDs mapped and implemented
tenderlove authored
347
fa6cda5 @tenderlove dynamically populate casting objects via the pg_type table
tenderlove authored
348 register_type 'timestamp', OID::Timestamp.new
2cc0944 @troyk Fix PostgreSQL TIMESTAMP WITH TIME ZONE to return ActiveSupport::Time
troyk authored
349 register_type 'timestamptz', OID::Timestamp.new
fa6cda5 @tenderlove dynamically populate casting objects via the pg_type table
tenderlove authored
350 register_type 'date', OID::Date.new
351 register_type 'time', OID::Time.new
9fd3bde @tenderlove many of the OIDs mapped and implemented
tenderlove authored
352
fa6cda5 @tenderlove dynamically populate casting objects via the pg_type table
tenderlove authored
353 register_type 'path', OID::Identity.new
336b376 @MSch Make Postgres point type correspond to ruby array with two floats inside
MSch authored
354 register_type 'point', OID::Point.new
fa6cda5 @tenderlove dynamically populate casting objects via the pg_type table
tenderlove authored
355 register_type 'polygon', OID::Identity.new
356 register_type 'circle', OID::Identity.new
357 register_type 'hstore', OID::Hstore.new
3b516b5 @guedes ActiveRecord support to PostgreSQL 9.2 JSON type
guedes authored
358 register_type 'json', OID::Json.new
e209107 @robworley Support for PostgreSQL's ltree data type.
robworley authored
359 register_type 'ltree', OID::Identity.new
c334175 @danmcclain Converts inet and cidr columns to NetAddr::CIDR
danmcclain authored
360
361 register_type 'cidr', OID::Cidr.new
362 alias_type 'inet', 'cidr'
9fd3bde @tenderlove many of the OIDs mapped and implemented
tenderlove authored
363 end
364 end
365 end
366 end
Something went wrong with that request. Please try again.