activerecord-jdbc-adapter 1.3.11 → 1.3.12

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,11 +6,6 @@ module ActiveRecord
6
6
  module OID
7
7
  class Type
8
8
  def type; end
9
- def simplified_type(sql_type); type end
10
-
11
- def infinity(options = {})
12
- ::Float::INFINITY * (options[:negative] ? -1 : 1)
13
- end
14
9
  end
15
10
 
16
11
  class Identity < Type
@@ -19,46 +14,9 @@ module ActiveRecord
19
14
  end
20
15
  end
21
16
 
22
- if ActiveRecord::VERSION::MINOR >= 2 # >= 4.2
23
-
24
- class String < Type
25
- def type; :string end
26
-
27
- def type_cast(value)
28
- return if value.nil?
29
-
30
- value.to_s
31
- end
32
- end
33
-
34
- else
35
-
36
- # String, Text types do not exist in AR 4.0/4.1
37
- # AR reports Identity for them - make it similar
38
-
39
- class String < Identity
40
- def type; :string end
41
- end
42
-
43
- end
44
-
45
- class SpecializedString < OID::String
46
- def type; @type end
47
-
48
- def initialize(type)
49
- @type = type
50
- end
51
- end
52
-
53
- class Text < OID::String
54
- def type; :text end
55
- end
56
-
57
17
  class Bit < Type
58
- def type; :string end
59
-
60
18
  def type_cast(value)
61
- if ::String === value
19
+ if String === value
62
20
  ConnectionAdapters::PostgreSQLColumn.string_to_bit value
63
21
  else
64
22
  value
@@ -67,8 +25,6 @@ module ActiveRecord
67
25
  end
68
26
 
69
27
  class Bytea < Type
70
- def type; :binary end
71
-
72
28
  def type_cast(value)
73
29
  return if value.nil?
74
30
  PGconn.unescape_bytea value
@@ -76,11 +32,9 @@ module ActiveRecord
76
32
  end
77
33
 
78
34
  class Money < Type
79
- def type; :decimal end
80
-
81
35
  def type_cast(value)
82
36
  return if value.nil?
83
- return value unless ::String === value
37
+ return value unless String === value
84
38
 
85
39
  # Because money output is formatted according to the locale, there are two
86
40
  # cases to consider (note the decimal separators):
@@ -122,8 +76,6 @@ module ActiveRecord
122
76
  end
123
77
 
124
78
  class Point < Type
125
- def type; :string end
126
-
127
79
  def type_cast(value)
128
80
  if ::String === value
129
81
  ConnectionAdapters::PostgreSQLColumn.string_to_point value
@@ -134,8 +86,6 @@ module ActiveRecord
134
86
  end
135
87
 
136
88
  class Array < Type
137
- def type; @subtype.type end
138
-
139
89
  attr_reader :subtype
140
90
  def initialize(subtype)
141
91
  @subtype = subtype
@@ -152,8 +102,6 @@ module ActiveRecord
152
102
 
153
103
  class Range < Type
154
104
  attr_reader :subtype
155
- def simplified_type(sql_type); sql_type.to_sym end
156
-
157
105
  def initialize(subtype)
158
106
  @subtype = subtype
159
107
  end
@@ -161,19 +109,23 @@ module ActiveRecord
161
109
  def extract_bounds(value)
162
110
  from, to = value[1..-2].split(',')
163
111
  {
164
- from: (value[1] == ',' || from == '-infinity') ? @subtype.infinity(negative: true) : from,
165
- to: (value[-2] == ',' || to == 'infinity') ? @subtype.infinity : to,
112
+ from: (value[1] == ',' || from == '-infinity') ? infinity(:negative => true) : from,
113
+ to: (value[-2] == ',' || to == 'infinity') ? infinity : to,
166
114
  exclude_start: (value[0] == '('),
167
115
  exclude_end: (value[-1] == ')')
168
116
  }
169
117
  end
170
118
 
119
+ def infinity(options = {})
120
+ ::Float::INFINITY * (options[:negative] ? -1 : 1)
121
+ end
122
+
171
123
  def infinity?(value)
172
124
  value.respond_to?(:infinite?) && value.infinite?
173
125
  end
174
126
 
175
- def type_cast_single(value)
176
- infinity?(value) ? value : @subtype.type_cast(value)
127
+ def to_integer(value)
128
+ infinity?(value) ? value : value.to_i
177
129
  end
178
130
 
179
131
  def type_cast(value)
@@ -181,27 +133,32 @@ module ActiveRecord
181
133
  return value if value.is_a?(::Range)
182
134
 
183
135
  extracted = extract_bounds(value)
184
- from = type_cast_single extracted[:from]
185
- to = type_cast_single extracted[:to]
186
-
187
- if !infinity?(from) && extracted[:exclude_start]
188
- if from.respond_to?(:succ)
189
- from = from.succ
190
- ActiveSupport::Deprecation.warn <<-MESSAGE
191
- Excluding the beginning of a Range is only partialy supported through `#succ`.
192
- This is not reliable and will be removed in the future.
193
- MESSAGE
194
- else
195
- raise ArgumentError, "The Ruby Range object does not support excluding the beginning of a Range. (unsupported value: '#{value}')"
196
- end
136
+
137
+ case @subtype
138
+ when :date
139
+ from = ConnectionAdapters::Column.value_to_date(extracted[:from])
140
+ from += 1.day if extracted[:exclude_start]
141
+ to = ConnectionAdapters::Column.value_to_date(extracted[:to])
142
+ when :decimal
143
+ from = BigDecimal.new(extracted[:from].to_s)
144
+ # FIXME: add exclude start for ::Range, same for timestamp ranges
145
+ to = BigDecimal.new(extracted[:to].to_s)
146
+ when :time
147
+ from = ConnectionAdapters::Column.string_to_time(extracted[:from])
148
+ to = ConnectionAdapters::Column.string_to_time(extracted[:to])
149
+ when :integer
150
+ from = to_integer(extracted[:from]) rescue value ? 1 : 0
151
+ from += 1 if extracted[:exclude_start]
152
+ to = to_integer(extracted[:to]) rescue value ? 1 : 0
153
+ else
154
+ return value
197
155
  end
156
+
198
157
  ::Range.new(from, to, extracted[:exclude_end])
199
158
  end
200
159
  end
201
160
 
202
161
  class Integer < Type
203
- def type; :integer end
204
-
205
162
  def type_cast(value)
206
163
  return if value.nil?
207
164
 
@@ -210,8 +167,6 @@ This is not reliable and will be removed in the future.
210
167
  end
211
168
 
212
169
  class Boolean < Type
213
- def type; :boolean end
214
-
215
170
  def type_cast(value)
216
171
  return if value.nil?
217
172
 
@@ -221,9 +176,6 @@ This is not reliable and will be removed in the future.
221
176
 
222
177
  class Timestamp < Type
223
178
  def type; :timestamp; end
224
- def simplified_type(sql_type)
225
- :datetime
226
- end
227
179
 
228
180
  def type_cast(value)
229
181
  return if value.nil?
@@ -235,7 +187,7 @@ This is not reliable and will be removed in the future.
235
187
  end
236
188
 
237
189
  class Date < Type
238
- def type; :date; end
190
+ def type; :datetime; end
239
191
 
240
192
  def type_cast(value)
241
193
  return if value.nil?
@@ -247,8 +199,6 @@ This is not reliable and will be removed in the future.
247
199
  end
248
200
 
249
201
  class Time < Type
250
- def type; :time end
251
-
252
202
  def type_cast(value)
253
203
  return if value.nil?
254
204
 
@@ -259,8 +209,6 @@ This is not reliable and will be removed in the future.
259
209
  end
260
210
 
261
211
  class Float < Type
262
- def type; :float end
263
-
264
212
  def type_cast(value)
265
213
  case value
266
214
  when nil; nil
@@ -274,32 +222,19 @@ This is not reliable and will be removed in the future.
274
222
  end
275
223
 
276
224
  class Decimal < Type
277
- def type; :decimal end
278
-
279
225
  def type_cast(value)
280
226
  return if value.nil?
281
227
 
282
228
  ConnectionAdapters::Column.value_to_decimal value
283
229
  end
284
-
285
- def infinity(options = {})
286
- BigDecimal.new("Infinity") * (options[:negative] ? -1 : 1)
287
- end
288
- end
289
-
290
- class Enum < Type
291
- def type; :enum end
292
-
293
- def type_cast(value)
294
- value.to_s
295
- end
296
230
  end
297
231
 
298
232
  class Hstore < Type
299
- def type; :hstore end
300
-
301
233
  def type_cast_for_write(value)
302
- ConnectionAdapters::PostgreSQLColumn.hstore_to_string value
234
+ # roundtrip to ensure uniform uniform types
235
+ # TODO: This is not an efficient solution.
236
+ stringified = ConnectionAdapters::PostgreSQLColumn.hstore_to_string(value)
237
+ type_cast(stringified)
303
238
  end
304
239
 
305
240
  def type_cast(value)
@@ -314,22 +249,19 @@ This is not reliable and will be removed in the future.
314
249
  end
315
250
 
316
251
  class Cidr < Type
317
- def type; :cidr end
318
252
  def type_cast(value)
319
253
  return if value.nil?
320
254
 
321
255
  ConnectionAdapters::PostgreSQLColumn.string_to_cidr value
322
256
  end
323
257
  end
324
- class Inet < Cidr
325
- def type; :inet end
326
- end
327
258
 
328
259
  class Json < Type
329
- def type; :json end
330
-
331
260
  def type_cast_for_write(value)
332
- ConnectionAdapters::PostgreSQLColumn.json_to_string value
261
+ # roundtrip to ensure uniform uniform types
262
+ # TODO: This is not an efficient solution.
263
+ stringified = ConnectionAdapters::PostgreSQLColumn.json_to_string(value)
264
+ type_cast(stringified)
333
265
  end
334
266
 
335
267
  def type_cast(value)
@@ -343,13 +275,6 @@ This is not reliable and will be removed in the future.
343
275
  end
344
276
  end
345
277
 
346
- class Uuid < Type
347
- def type; :uuid end
348
- def type_cast(value)
349
- value.presence
350
- end
351
- end
352
-
353
278
  class TypeMap
354
279
  def initialize
355
280
  @mapping = {}
@@ -396,7 +321,7 @@ This is not reliable and will be removed in the future.
396
321
  }
397
322
 
398
323
  # Register an OID type named +name+ with a typecasting object in
399
- # +type+. +name+ should correspond to the `typname` column in
324
+ # +type+. +name+ should correspond to the `typname` column in
400
325
  # the `pg_type` table.
401
326
  def self.register_type(name, type)
402
327
  NAMES[name] = type
@@ -413,48 +338,55 @@ This is not reliable and will be removed in the future.
413
338
  end
414
339
 
415
340
  register_type 'int2', OID::Integer.new
416
- alias_type 'int4', 'int2'
417
- alias_type 'int8', 'int2'
418
- alias_type 'oid', 'int2'
341
+ alias_type 'int4', 'int2'
342
+ alias_type 'int8', 'int2'
343
+ alias_type 'oid', 'int2'
344
+
345
+ register_type 'daterange', OID::Range.new(:date)
346
+ register_type 'numrange', OID::Range.new(:decimal)
347
+ register_type 'tsrange', OID::Range.new(:time)
348
+ register_type 'int4range', OID::Range.new(:integer)
349
+ alias_type 'tstzrange', 'tsrange'
350
+ alias_type 'int8range', 'int4range'
351
+
419
352
  register_type 'numeric', OID::Decimal.new
420
- register_type 'float4', OID::Float.new
421
- alias_type 'float8', 'float4'
422
- register_type 'text', OID::Text.new
423
- register_type 'varchar', OID::String.new
424
- alias_type 'char', 'varchar'
425
- alias_type 'name', 'varchar'
426
- alias_type 'bpchar', 'varchar'
353
+ register_type 'text', OID::Identity.new
354
+ alias_type 'varchar', 'text'
355
+ alias_type 'char', 'text'
356
+ alias_type 'bpchar', 'text'
357
+ alias_type 'xml', 'text'
358
+
359
+ # FIXME: why are we keeping these types as strings?
360
+ alias_type 'tsvector', 'text'
361
+ alias_type 'interval', 'text'
362
+ alias_type 'macaddr', 'text'
363
+ alias_type 'uuid', 'text'
364
+
365
+ register_type 'money', OID::Money.new
366
+ register_type 'bytea', OID::Bytea.new
427
367
  register_type 'bool', OID::Boolean.new
428
368
  register_type 'bit', OID::Bit.new
429
- alias_type 'varbit', 'bit'
369
+ register_type 'varbit', OID::Bit.new
370
+
371
+ register_type 'float4', OID::Float.new
372
+ alias_type 'float8', 'float4'
373
+
430
374
  register_type 'timestamp', OID::Timestamp.new
431
- alias_type 'timestamptz', 'timestamp'
375
+ register_type 'timestamptz', OID::Timestamp.new
432
376
  register_type 'date', OID::Date.new
433
377
  register_type 'time', OID::Time.new
434
378
 
435
- register_type 'money', OID::Money.new
436
- register_type 'bytea', OID::Bytea.new
379
+ register_type 'path', OID::Identity.new
437
380
  register_type 'point', OID::Point.new
381
+ register_type 'polygon', OID::Identity.new
382
+ register_type 'circle', OID::Identity.new
438
383
  register_type 'hstore', OID::Hstore.new
439
384
  register_type 'json', OID::Json.new
440
- register_type 'cidr', OID::Cidr.new
441
- register_type 'inet', OID::Inet.new
442
- register_type 'uuid', OID::Uuid.new
443
- register_type 'xml', SpecializedString.new(:xml)
444
- register_type 'tsvector', SpecializedString.new(:tsvector)
445
- register_type 'macaddr', SpecializedString.new(:macaddr)
446
- register_type 'citext', SpecializedString.new(:citext)
447
- register_type 'ltree', SpecializedString.new(:ltree)
385
+ register_type 'ltree', OID::Identity.new
448
386
 
449
- # FIXME: why are we keeping these types as strings?
450
- alias_type 'interval', 'varchar'
451
- alias_type 'path', 'varchar'
452
- alias_type 'line', 'varchar'
453
- alias_type 'polygon', 'varchar'
454
- alias_type 'circle', 'varchar'
455
- alias_type 'lseg', 'varchar'
456
- alias_type 'box', 'varchar'
387
+ register_type 'cidr', OID::Cidr.new
388
+ alias_type 'inet', 'cidr'
457
389
  end
458
390
  end
459
391
  end
460
- end
392
+ end
@@ -1,19 +1,20 @@
1
- require 'arjdbc/postgresql/base/oid' # 'active_record/connection_adapters/postgresql/oid'
2
1
  require 'thread'
3
2
 
4
3
  module ArJdbc
5
4
  module PostgreSQL
5
+
6
+ if AR42_COMPAT
7
+ require 'active_record/connection_adapters/postgresql/oid'
8
+ else
9
+ require 'arjdbc/postgresql/base/oid'
10
+ end
11
+
6
12
  # @private
7
13
  module OIDTypes
8
14
 
9
15
  OID = ActiveRecord::ConnectionAdapters::PostgreSQL::OID
10
16
 
11
- def get_oid_type(oid, fmod, column_name)
12
- type_map.fetch(oid, fmod) {
13
- warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
14
- type_map[oid] = OID::Identity.new
15
- }
16
- end
17
+ Type = ActiveRecord::Type if AR42_COMPAT
17
18
 
18
19
  # @override
19
20
  def enable_extension(name)
@@ -36,13 +37,40 @@ module ArJdbc
36
37
  @extensions ||= super
37
38
  end
38
39
 
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
+
39
60
  private
40
61
 
41
62
  @@type_map_cache = {}
42
63
  @@type_map_cache_lock = Mutex.new
43
64
 
65
+ if AR42_COMPAT
66
+ TypeMap = ActiveRecord::Type::HashLookupTypeMap
67
+ else
68
+ TypeMap = OID::TypeMap
69
+ end
70
+
71
+ # @see #type_map
44
72
  # @private
45
- class OID::TypeMap
73
+ TypeMap.class_eval do
46
74
  def dup
47
75
  dup = super # make sure @mapping is not shared
48
76
  dup.instance_variable_set(:@mapping, @mapping.dup)
@@ -56,7 +84,7 @@ module ArJdbc
56
84
  if type_map = @@type_map_cache[ type_cache_key ]
57
85
  type_map.dup
58
86
  else
59
- type_map = OID::TypeMap.new
87
+ type_map = TypeMap.new
60
88
  initialize_type_map(type_map)
61
89
  cache_type_map(type_map)
62
90
  type_map
@@ -120,7 +148,101 @@ module ArJdbc
120
148
  array = OID::Array.new type_map[ row['typelem'].to_i ]
121
149
  type_map[ row['oid'].to_i ] = array
122
150
  end
123
- 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
124
246
 
125
247
  end
126
248
  end