activerecord-jdbc-adapter 1.3.7 → 1.3.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +33 -3
  3. data/Appraisals +11 -5
  4. data/Gemfile +21 -15
  5. data/History.md +31 -1
  6. data/lib/active_record/connection_adapters/mariadb_adapter.rb +1 -0
  7. data/lib/arel/visitors/firebird.rb +7 -10
  8. data/lib/arel/visitors/h2.rb +9 -0
  9. data/lib/arel/visitors/sql_server.rb +21 -2
  10. data/lib/arjdbc/h2/adapter.rb +31 -2
  11. data/lib/arjdbc/h2/connection_methods.rb +1 -1
  12. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  13. data/lib/arjdbc/jdbc/column.rb +2 -1
  14. data/lib/arjdbc/mssql/adapter.rb +40 -23
  15. data/lib/arjdbc/mssql/column.rb +4 -4
  16. data/lib/arjdbc/mysql/adapter.rb +36 -10
  17. data/lib/arjdbc/mysql/column.rb +12 -7
  18. data/lib/arjdbc/mysql/connection_methods.rb +53 -21
  19. data/lib/arjdbc/oracle/adapter.rb +22 -5
  20. data/lib/arjdbc/postgresql/adapter.rb +54 -18
  21. data/lib/arjdbc/postgresql/base/array_parser.rb +95 -0
  22. data/lib/arjdbc/postgresql/base/oid.rb +460 -0
  23. data/lib/arjdbc/postgresql/column.rb +50 -15
  24. data/lib/arjdbc/postgresql/oid_types.rb +126 -0
  25. data/lib/arjdbc/tasks/h2_database_tasks.rb +4 -2
  26. data/lib/arjdbc/version.rb +1 -1
  27. data/rakelib/02-test.rake +3 -30
  28. data/src/java/arjdbc/derby/DerbyModule.java +0 -8
  29. data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +1 -0
  30. data/src/java/arjdbc/h2/H2RubyJdbcConnection.java +2 -0
  31. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +8 -8
  32. data/src/java/arjdbc/mssql/MSSQLModule.java +50 -19
  33. data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +1 -0
  34. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +6 -6
  35. data/src/java/arjdbc/oracle/OracleModule.java +1 -1
  36. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +66 -2
  37. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +23 -10
  38. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +1 -0
  39. data/src/java/arjdbc/util/CallResultSet.java +826 -0
  40. data/src/java/arjdbc/util/QuotingUtils.java +14 -7
  41. metadata +8 -3
  42. data/lib/arjdbc/postgresql/array_parser.rb +0 -89
@@ -0,0 +1,95 @@
1
+ # based on active_record/connection_adapters/postgresql/array_parser.rb
2
+ # until it's some day shareable with Rails ... this is not public API !
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module PostgreSQL
6
+ module ArrayParser
7
+
8
+ DOUBLE_QUOTE = '"'
9
+ BACKSLASH = "\\"
10
+ COMMA = ','
11
+ BRACKET_OPEN = '{'
12
+ BRACKET_CLOSE = '}'
13
+
14
+ def parse_pg_array(string)
15
+ local_index = 0
16
+ array = []
17
+ while(local_index < string.length)
18
+ case string[local_index]
19
+ when BRACKET_OPEN
20
+ local_index,array = parse_array_contents(array, string, local_index + 1)
21
+ when BRACKET_CLOSE
22
+ return array
23
+ end
24
+ local_index += 1
25
+ end
26
+
27
+ array
28
+ end
29
+
30
+ private
31
+
32
+ def parse_array_contents(array, string, index)
33
+ is_escaping = false
34
+ is_quoted = false
35
+ was_quoted = false
36
+ current_item = ''
37
+
38
+ local_index = index
39
+ while local_index
40
+ token = string[local_index]
41
+ if is_escaping
42
+ current_item << token
43
+ is_escaping = false
44
+ else
45
+ if is_quoted
46
+ case token
47
+ when DOUBLE_QUOTE
48
+ is_quoted = false
49
+ was_quoted = true
50
+ when BACKSLASH
51
+ is_escaping = true
52
+ else
53
+ current_item << token
54
+ end
55
+ else
56
+ case token
57
+ when BACKSLASH
58
+ is_escaping = true
59
+ when COMMA
60
+ add_item_to_array(array, current_item, was_quoted)
61
+ current_item = ''
62
+ was_quoted = false
63
+ when DOUBLE_QUOTE
64
+ is_quoted = true
65
+ when BRACKET_OPEN
66
+ internal_items = []
67
+ local_index,internal_items = parse_array_contents(internal_items, string, local_index + 1)
68
+ array.push(internal_items)
69
+ when BRACKET_CLOSE
70
+ add_item_to_array(array, current_item, was_quoted)
71
+ return local_index,array
72
+ else
73
+ current_item << token
74
+ end
75
+ end
76
+ end
77
+
78
+ local_index += 1
79
+ end
80
+ return local_index,array
81
+ end
82
+
83
+ def add_item_to_array(array, current_item, quoted)
84
+ return if !quoted && current_item.length == 0
85
+
86
+ if !quoted && current_item == 'NULL'
87
+ array.push nil
88
+ else
89
+ array.push current_item
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,460 @@
1
+ # copied from active_record/connection_adapters/postgresql/oid.rb
2
+ # until it's some day shareable with Rails ... this is not public API !
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module PostgreSQL
6
+ module OID
7
+ class Type
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
+ end
15
+
16
+ class Identity < Type
17
+ def type_cast(value)
18
+ value
19
+ end
20
+ end
21
+
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
+ class Bit < Type
58
+ def type; :string end
59
+
60
+ def type_cast(value)
61
+ if ::String === value
62
+ ConnectionAdapters::PostgreSQLColumn.string_to_bit value
63
+ else
64
+ value
65
+ end
66
+ end
67
+ end
68
+
69
+ class Bytea < Type
70
+ def type; :binary end
71
+
72
+ def type_cast(value)
73
+ return if value.nil?
74
+ PGconn.unescape_bytea value
75
+ end
76
+ end
77
+
78
+ class Money < Type
79
+ def type; :decimal end
80
+
81
+ def type_cast(value)
82
+ return if value.nil?
83
+ return value unless ::String === value
84
+
85
+ # Because money output is formatted according to the locale, there are two
86
+ # cases to consider (note the decimal separators):
87
+ # (1) $12,345,678.12
88
+ # (2) $12.345.678,12
89
+ # Negative values are represented as follows:
90
+ # (3) -$2.55
91
+ # (4) ($2.55)
92
+
93
+ value.sub!(/^\((.+)\)$/, '-\1') # (4)
94
+ case value
95
+ when /^-?\D+[\d,]+\.\d{2}$/ # (1)
96
+ value.gsub!(/[^-\d.]/, '')
97
+ when /^-?\D+[\d.]+,\d{2}$/ # (2)
98
+ value.gsub!(/[^-\d,]/, '').sub!(/,/, '.')
99
+ end
100
+
101
+ ConnectionAdapters::Column.value_to_decimal value
102
+ end
103
+ end
104
+
105
+ class Vector < Type
106
+ attr_reader :delim, :subtype
107
+
108
+ # +delim+ corresponds to the `typdelim` column in the pg_types
109
+ # table. +subtype+ is derived from the `typelem` column in the
110
+ # pg_types table.
111
+ def initialize(delim, subtype)
112
+ @delim = delim
113
+ @subtype = subtype
114
+ end
115
+
116
+ # FIXME: this should probably split on +delim+ and use +subtype+
117
+ # to cast the values. Unfortunately, the current Rails behavior
118
+ # is to just return the string.
119
+ def type_cast(value)
120
+ value
121
+ end
122
+ end
123
+
124
+ class Point < Type
125
+ def type; :string end
126
+
127
+ def type_cast(value)
128
+ if ::String === value
129
+ ConnectionAdapters::PostgreSQLColumn.string_to_point value
130
+ else
131
+ value
132
+ end
133
+ end
134
+ end
135
+
136
+ class Array < Type
137
+ def type; @subtype.type end
138
+
139
+ attr_reader :subtype
140
+ def initialize(subtype)
141
+ @subtype = subtype
142
+ end
143
+
144
+ def type_cast(value)
145
+ if ::String === value
146
+ ConnectionAdapters::PostgreSQLColumn.string_to_array value, @subtype
147
+ else
148
+ value
149
+ end
150
+ end
151
+ end
152
+
153
+ class Range < Type
154
+ attr_reader :subtype
155
+ def simplified_type(sql_type); sql_type.to_sym end
156
+
157
+ def initialize(subtype)
158
+ @subtype = subtype
159
+ end
160
+
161
+ def extract_bounds(value)
162
+ from, to = value[1..-2].split(',')
163
+ {
164
+ from: (value[1] == ',' || from == '-infinity') ? @subtype.infinity(negative: true) : from,
165
+ to: (value[-2] == ',' || to == 'infinity') ? @subtype.infinity : to,
166
+ exclude_start: (value[0] == '('),
167
+ exclude_end: (value[-1] == ')')
168
+ }
169
+ end
170
+
171
+ def infinity?(value)
172
+ value.respond_to?(:infinite?) && value.infinite?
173
+ end
174
+
175
+ def type_cast_single(value)
176
+ infinity?(value) ? value : @subtype.type_cast(value)
177
+ end
178
+
179
+ def type_cast(value)
180
+ return if value.nil? || value == 'empty'
181
+ return value if value.is_a?(::Range)
182
+
183
+ 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
197
+ end
198
+ ::Range.new(from, to, extracted[:exclude_end])
199
+ end
200
+ end
201
+
202
+ class Integer < Type
203
+ def type; :integer end
204
+
205
+ def type_cast(value)
206
+ return if value.nil?
207
+
208
+ ConnectionAdapters::Column.value_to_integer value
209
+ end
210
+ end
211
+
212
+ class Boolean < Type
213
+ def type; :boolean end
214
+
215
+ def type_cast(value)
216
+ return if value.nil?
217
+
218
+ ConnectionAdapters::Column.value_to_boolean value
219
+ end
220
+ end
221
+
222
+ class Timestamp < Type
223
+ def type; :timestamp; end
224
+ def simplified_type(sql_type)
225
+ :datetime
226
+ end
227
+
228
+ def type_cast(value)
229
+ return if value.nil?
230
+
231
+ # FIXME: probably we can improve this since we know it is PG
232
+ # specific
233
+ ConnectionAdapters::PostgreSQLColumn.string_to_time value
234
+ end
235
+ end
236
+
237
+ class Date < Type
238
+ def type; :date; end
239
+
240
+ def type_cast(value)
241
+ return if value.nil?
242
+
243
+ # FIXME: probably we can improve this since we know it is PG
244
+ # specific
245
+ ConnectionAdapters::Column.value_to_date value
246
+ end
247
+ end
248
+
249
+ class Time < Type
250
+ def type; :time end
251
+
252
+ def type_cast(value)
253
+ return if value.nil?
254
+
255
+ # FIXME: probably we can improve this since we know it is PG
256
+ # specific
257
+ ConnectionAdapters::Column.string_to_dummy_time value
258
+ end
259
+ end
260
+
261
+ class Float < Type
262
+ def type; :float end
263
+
264
+ def type_cast(value)
265
+ case value
266
+ when nil; nil
267
+ when 'Infinity'; ::Float::INFINITY
268
+ when '-Infinity'; -::Float::INFINITY
269
+ when 'NaN'; ::Float::NAN
270
+ else
271
+ value.to_f
272
+ end
273
+ end
274
+ end
275
+
276
+ class Decimal < Type
277
+ def type; :decimal end
278
+
279
+ def type_cast(value)
280
+ return if value.nil?
281
+
282
+ ConnectionAdapters::Column.value_to_decimal value
283
+ 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
+ end
297
+
298
+ class Hstore < Type
299
+ def type; :hstore end
300
+
301
+ def type_cast_for_write(value)
302
+ ConnectionAdapters::PostgreSQLColumn.hstore_to_string value
303
+ end
304
+
305
+ def type_cast(value)
306
+ return if value.nil?
307
+
308
+ ConnectionAdapters::PostgreSQLColumn.string_to_hstore value
309
+ end
310
+
311
+ def accessor
312
+ ActiveRecord::Store::StringKeyedHashAccessor
313
+ end
314
+ end
315
+
316
+ class Cidr < Type
317
+ def type; :cidr end
318
+ def type_cast(value)
319
+ return if value.nil?
320
+
321
+ ConnectionAdapters::PostgreSQLColumn.string_to_cidr value
322
+ end
323
+ end
324
+ class Inet < Cidr
325
+ def type; :inet end
326
+ end
327
+
328
+ class Json < Type
329
+ def type; :json end
330
+
331
+ def type_cast_for_write(value)
332
+ ConnectionAdapters::PostgreSQLColumn.json_to_string value
333
+ end
334
+
335
+ def type_cast(value)
336
+ return if value.nil?
337
+
338
+ ConnectionAdapters::PostgreSQLColumn.string_to_json value
339
+ end
340
+
341
+ def accessor
342
+ ActiveRecord::Store::StringKeyedHashAccessor
343
+ end
344
+ end
345
+
346
+ class Uuid < Type
347
+ def type; :uuid end
348
+ def type_cast(value)
349
+ value.presence
350
+ end
351
+ end
352
+
353
+ class TypeMap
354
+ def initialize
355
+ @mapping = {}
356
+ end
357
+
358
+ def []=(oid, type)
359
+ @mapping[oid] = type
360
+ end
361
+
362
+ def [](oid)
363
+ @mapping[oid]
364
+ end
365
+
366
+ def clear
367
+ @mapping.clear
368
+ end
369
+
370
+ def key?(oid)
371
+ @mapping.key? oid
372
+ end
373
+
374
+ def fetch(ftype, fmod)
375
+ # The type for the numeric depends on the width of the field,
376
+ # so we'll do something special here.
377
+ #
378
+ # When dealing with decimal columns:
379
+ #
380
+ # places after decimal = fmod - 4 & 0xffff
381
+ # places before decimal = (fmod - 4) >> 16 & 0xffff
382
+ if ftype == 1700 && (fmod - 4 & 0xffff).zero?
383
+ ftype = 23
384
+ end
385
+
386
+ @mapping.fetch(ftype) { |oid| yield oid, fmod }
387
+ end
388
+ end
389
+
390
+ # When the PG adapter connects, the pg_type table is queried. The
391
+ # key of this hash maps to the `typname` column from the table.
392
+ # type_map is then dynamically built with oids as the key and type
393
+ # objects as values.
394
+ NAMES = Hash.new { |h,k| # :nodoc:
395
+ h[k] = OID::Identity.new
396
+ }
397
+
398
+ # Register an OID type named +name+ with a typecasting object in
399
+ # +type+. +name+ should correspond to the `typname` column in
400
+ # the `pg_type` table.
401
+ def self.register_type(name, type)
402
+ NAMES[name] = type
403
+ end
404
+
405
+ # Alias the +old+ type to the +new+ type.
406
+ def self.alias_type(new, old)
407
+ NAMES[new] = NAMES[old]
408
+ end
409
+
410
+ # Is +name+ a registered type?
411
+ def self.registered_type?(name)
412
+ NAMES.key? name
413
+ end
414
+
415
+ register_type 'int2', OID::Integer.new
416
+ alias_type 'int4', 'int2'
417
+ alias_type 'int8', 'int2'
418
+ alias_type 'oid', 'int2'
419
+ 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'
427
+ register_type 'bool', OID::Boolean.new
428
+ register_type 'bit', OID::Bit.new
429
+ alias_type 'varbit', 'bit'
430
+ register_type 'timestamp', OID::Timestamp.new
431
+ alias_type 'timestamptz', 'timestamp'
432
+ register_type 'date', OID::Date.new
433
+ register_type 'time', OID::Time.new
434
+
435
+ register_type 'money', OID::Money.new
436
+ register_type 'bytea', OID::Bytea.new
437
+ register_type 'point', OID::Point.new
438
+ register_type 'hstore', OID::Hstore.new
439
+ 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)
448
+
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'
457
+ end
458
+ end
459
+ end
460
+ end