activerecord-jdbc-adapter 1.3.11 → 1.3.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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