activerecord-jdbc-adapter 1.3.17 → 1.3.18

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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +24 -5
  3. data/History.md +54 -0
  4. data/lib/arel/visitors/compat.rb +30 -2
  5. data/lib/arel/visitors/db2.rb +118 -29
  6. data/lib/arel/visitors/derby.rb +84 -29
  7. data/lib/arel/visitors/firebird.rb +66 -9
  8. data/lib/arel/visitors/h2.rb +16 -0
  9. data/lib/arel/visitors/hsqldb.rb +6 -3
  10. data/lib/arel/visitors/postgresql_jdbc.rb +6 -0
  11. data/lib/arel/visitors/sql_server.rb +121 -40
  12. data/lib/arel/visitors/sql_server/ng42.rb +293 -0
  13. data/lib/arjdbc.rb +1 -7
  14. data/lib/arjdbc/db2.rb +1 -0
  15. data/lib/arjdbc/db2/adapter.rb +118 -18
  16. data/lib/arjdbc/derby/adapter.rb +29 -8
  17. data/lib/arjdbc/firebird.rb +1 -0
  18. data/lib/arjdbc/firebird/adapter.rb +126 -11
  19. data/lib/arjdbc/hsqldb/adapter.rb +3 -0
  20. data/lib/arjdbc/informix.rb +1 -0
  21. data/lib/arjdbc/jdbc.rb +17 -0
  22. data/lib/arjdbc/jdbc/adapter.rb +28 -3
  23. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  24. data/lib/arjdbc/jdbc/column.rb +7 -3
  25. data/lib/arjdbc/jdbc/type_cast.rb +2 -0
  26. data/lib/arjdbc/jdbc/type_converter.rb +28 -15
  27. data/lib/arjdbc/mimer.rb +1 -0
  28. data/lib/arjdbc/mssql.rb +2 -1
  29. data/lib/arjdbc/mssql/adapter.rb +105 -30
  30. data/lib/arjdbc/mssql/column.rb +30 -7
  31. data/lib/arjdbc/mssql/limit_helpers.rb +22 -9
  32. data/lib/arjdbc/mssql/types.rb +343 -0
  33. data/lib/arjdbc/mssql/utils.rb +25 -2
  34. data/lib/arjdbc/mysql/adapter.rb +22 -21
  35. data/lib/arjdbc/oracle.rb +1 -0
  36. data/lib/arjdbc/oracle/adapter.rb +291 -19
  37. data/lib/arjdbc/oracle/column.rb +9 -5
  38. data/lib/arjdbc/oracle/connection_methods.rb +4 -1
  39. data/lib/arjdbc/postgresql/_bc_time_cast_patch.rb +21 -0
  40. data/lib/arjdbc/postgresql/adapter.rb +7 -1
  41. data/lib/arjdbc/postgresql/oid/bytea.rb +3 -0
  42. data/lib/arjdbc/postgresql/oid_types.rb +2 -1
  43. data/lib/arjdbc/tasks/database_tasks.rb +3 -0
  44. data/lib/arjdbc/util/quoted_cache.rb +2 -2
  45. data/lib/arjdbc/util/serialized_attributes.rb +11 -0
  46. data/lib/arjdbc/version.rb +1 -1
  47. data/rakelib/02-test.rake +1 -1
  48. data/rakelib/db.rake +3 -1
  49. data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +190 -0
  50. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +259 -61
  51. data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +13 -2
  52. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +192 -15
  53. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +10 -2
  54. metadata +9 -4
@@ -14,11 +14,7 @@ module ArJdbc
14
14
  class << base; include Cast; end
15
15
  end
16
16
 
17
- include LockMethods
18
-
19
- attr_accessor :identity, :special
20
- # @deprecated
21
- alias_method :is_special, :special
17
+ include LockMethods unless AR42
22
18
 
23
19
  # @override
24
20
  def simplified_type(field_type)
@@ -42,7 +38,7 @@ module ArJdbc
42
38
 
43
39
  # @override
44
40
  def default_value(value)
45
- return $1 if value =~ /^\(N?'(.*)'\)$/
41
+ return $1 if value =~ /^\(N?'(.*)'\)$/ || value =~ /^\(\(?(.*?)\)?\)$/
46
42
  value
47
43
  end
48
44
 
@@ -81,12 +77,39 @@ module ArJdbc
81
77
  end
82
78
  end
83
79
 
84
- private
80
+ # #primary replacement that works on 4.2 as well
81
+ # #columns will set @primary even when on AR 4.2
82
+ def primary?; @primary end
83
+ alias_method :is_primary, :primary?
84
+
85
+ def identity?
86
+ !! sql_type.downcase.index('identity')
87
+ end
88
+ # @deprecated
89
+ alias_method :identity, :identity?
90
+ alias_method :is_identity, :identity?
91
+
92
+ # NOTE: these do not handle = equality as expected
93
+ # see {#repair_special_columns}
94
+ # (TEXT, NTEXT, and IMAGE data types are deprecated)
95
+ # @private
96
+ def special?
97
+ unless defined? @special # /text|ntext|image|xml/i
98
+ sql_type = @sql_type.downcase
99
+ @special = !! ( sql_type.index('text') || sql_type.index('image') || sql_type.index('xml') )
100
+ end
101
+ @special
102
+ end
103
+ # @deprecated
104
+ alias_method :special, :special?
105
+ alias_method :is_special, :special?
85
106
 
86
107
  def is_utf8?
87
108
  !!( sql_type =~ /nvarchar|ntext|nchar/i )
88
109
  end
89
110
 
111
+ private
112
+
90
113
  def unquote(value)
91
114
  value.to_s.sub(/\A\([\(\']?/, "").sub(/[\'\)]?\)\Z/, "")
92
115
  end
@@ -7,8 +7,12 @@ module ArJdbc
7
7
  # @private
8
8
  FIND_AGGREGATE_FUNCTION = /(AVG|COUNT|COUNT_BIG|MAX|MIN|SUM|STDDEV|STDEVP|VAR|VARP)\(/i
9
9
 
10
+ # @private
10
11
  module SqlServerReplaceLimitOffset
11
12
 
13
+ GROUP_BY = 'GROUP BY'
14
+ ORDER_BY = 'ORDER BY'
15
+
12
16
  module_function
13
17
 
14
18
  def replace_limit_offset!(sql, limit, offset, order)
@@ -27,19 +31,27 @@ module ArJdbc
27
31
  # Ensure correct queries if the rest_of_query contains a 'GROUP BY'. Otherwise the following error occurs:
28
32
  # ActiveRecord::StatementInvalid: ActiveRecord::JDBCError: Column 'users.id' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
29
33
  # SELECT t.* FROM ( SELECT ROW_NUMBER() OVER(ORDER BY users.id) AS _row_num, [users].[lft], COUNT([users].[lft]) FROM [users] GROUP BY [users].[lft] HAVING COUNT([users].[lft]) > 1 ) AS t WHERE t._row_num BETWEEN 1 AND 1
30
- if rest_of_query.downcase.include?('group by')
31
- order_start = order.strip[0, 8]; order_start.upcase!
32
- if order_start == 'ORDER BY' && order.match(FIND_AGGREGATE_FUNCTION)
33
- # do nothing
34
- elsif order.count(',') == 0
35
- order.gsub!(/ORDER +BY +([^\s]+)(\s+ASC|\s+DESC)?/i, 'ORDER BY MIN(\1)\2')
36
- else
37
- raise('Only one order condition allowed.')
34
+ if i = ( rest_of_query.rindex(GROUP_BY) || rest_of_query.rindex('group by') )
35
+ # Do not catch 'GROUP BY' statements from sub-selects, indicated
36
+ # by more closing than opening brackets after the last group by.
37
+ rest_after_last_group_by = rest_of_query[i..-1]
38
+ opening_brackets_count = rest_after_last_group_by.count('(')
39
+ closing_brackets_count = rest_after_last_group_by.count(')')
40
+
41
+ if opening_brackets_count == closing_brackets_count
42
+ order_start = order.strip[0, 8]; order_start.upcase!
43
+ if order_start == ORDER_BY && order.match(FIND_AGGREGATE_FUNCTION)
44
+ # do nothing
45
+ elsif order.count(',') == 0
46
+ order.gsub!(/ORDER +BY +([^\s]+)(\s+ASC|\s+DESC)?/i, 'ORDER BY MIN(\1)\2')
47
+ else
48
+ raise("can not handle multiple order conditions (#{order.inspect}) in #{sql.inspect}")
49
+ end
38
50
  end
39
51
  end
40
52
 
41
53
  if distinct # select =~ /DISTINCT/i
42
- order = order.gsub(/([a-z0-9_])+\./, 't.')
54
+ order = order.gsub(/(\[[a-z0-9_]+\]|[a-z0-9_]+)\./, 't.')
43
55
  new_sql = "SELECT t.* FROM "
44
56
  new_sql << "( SELECT ROW_NUMBER() OVER(#{order}) AS _row_num, t.* FROM (#{select} #{rest_of_query}) AS t ) AS t"
45
57
  append_limit_row_num_clause(new_sql, limit, offset)
@@ -73,6 +85,7 @@ module ArJdbc
73
85
 
74
86
  end
75
87
 
88
+ # @private
76
89
  module SqlServer2000ReplaceLimitOffset
77
90
 
78
91
  module_function
@@ -0,0 +1,343 @@
1
+ module ArJdbc
2
+ module MSSQL
3
+
4
+ def initialize_type_map(m)
5
+ #m.register_type %r{.*}, UnicodeStringType.new
6
+ # Exact Numerics
7
+ register_class_with_limit m, /^bigint./, BigIntegerType
8
+ m.alias_type 'bigint', 'bigint(8)'
9
+ register_class_with_limit m, /^int\(|\s/, ActiveRecord::Type::Integer
10
+ m.alias_type /^integer/, 'int(4)'
11
+ m.alias_type 'int', 'int(4)'
12
+ register_class_with_limit m, /^smallint./, SmallIntegerType
13
+ m.alias_type 'smallint', 'smallint(2)'
14
+ register_class_with_limit m, /^tinyint./, TinyIntegerType
15
+ m.alias_type 'tinyint', 'tinyint(1)'
16
+ m.register_type /^bit/, ActiveRecord::Type::Boolean.new
17
+ m.register_type %r{\Adecimal} do |sql_type|
18
+ scale = extract_scale(sql_type)
19
+ precision = extract_precision(sql_type)
20
+ DecimalType.new :precision => precision, :scale => scale
21
+ #if scale == 0
22
+ # ActiveRecord::Type::Integer.new(:precision => precision)
23
+ #else
24
+ # DecimalType.new(:precision => precision, :scale => scale)
25
+ #end
26
+ end
27
+ m.alias_type %r{\Anumeric}, 'decimal'
28
+ m.register_type /^money/, MoneyType.new
29
+ m.register_type /^smallmoney/, SmallMoneyType.new
30
+ # Approximate Numerics
31
+ m.register_type /^float/, ActiveRecord::Type::Float.new
32
+ m.register_type /^real/, RealType.new
33
+ # Date and Time
34
+ m.register_type /^date\(?/, ActiveRecord::Type::Date.new
35
+ m.register_type /^datetime\(?/, DateTimeType.new
36
+ m.register_type /smalldatetime/, SmallDateTimeType.new
37
+ m.register_type %r{\Atime} do |sql_type|
38
+ TimeType.new :precision => extract_precision(sql_type)
39
+ end
40
+ # Character Strings
41
+ register_class_with_limit m, %r{\Achar}i, CharType
42
+ #register_class_with_limit m, %r{\Avarchar}i, VarcharType
43
+ m.register_type %r{\Anvarchar}i do |sql_type|
44
+ limit = extract_limit(sql_type)
45
+ if limit == 2_147_483_647 # varchar(max)
46
+ VarcharMaxType.new
47
+ else
48
+ VarcharType.new :limit => limit
49
+ end
50
+ end
51
+ #m.register_type 'varchar(max)', VarcharMaxType.new
52
+ m.register_type /^text/, TextType.new
53
+ # Unicode Character Strings
54
+ register_class_with_limit m, %r{\Anchar}i, UnicodeCharType
55
+ #register_class_with_limit m, %r{\Anvarchar}i, UnicodeVarcharType
56
+ m.register_type %r{\Anvarchar}i do |sql_type|
57
+ limit = extract_limit(sql_type)
58
+ if limit == 1_073_741_823 # nvarchar(max)
59
+ UnicodeVarcharMaxType.new
60
+ else
61
+ UnicodeVarcharType.new :limit => limit
62
+ end
63
+ end
64
+ #m.register_type 'nvarchar(max)', UnicodeVarcharMaxType.new
65
+ m.alias_type 'string', 'nvarchar(4000)'
66
+ m.register_type /^ntext/, UnicodeTextType.new
67
+ # Binary Strings
68
+ register_class_with_limit m, %r{\Aimage}i, ImageType
69
+ register_class_with_limit m, %r{\Abinary}i, BinaryType
70
+ register_class_with_limit m, %r{\Avarbinary}i, VarbinaryType
71
+ #m.register_type 'varbinary(max)', VarbinaryMaxType.new
72
+ # Other Data Types
73
+ m.register_type 'uniqueidentifier', UUIDType.new
74
+ # TODO
75
+ #m.register_type 'timestamp', SQLServer::Type::Timestamp.new
76
+ m.register_type 'xml', XmlType.new
77
+ end
78
+
79
+ def clear_cache!
80
+ super
81
+ reload_type_map
82
+ end
83
+
84
+ # @private
85
+ class BigIntegerType < ActiveRecord::Type::BigInteger
86
+ def type; :bigint end
87
+ end
88
+
89
+ # @private
90
+ SmallIntegerType = ActiveRecord::Type::Integer
91
+
92
+ # @private
93
+ class TinyIntegerType < ActiveRecord::Type::Integer
94
+ def max_value; 256 end
95
+ def min_value; 0 end
96
+ end
97
+
98
+ # @private
99
+ class RealType < ActiveRecord::Type::Float
100
+ def type; :real end
101
+ end
102
+
103
+ # @private
104
+ class DecimalType < ActiveRecord::Type::Decimal
105
+
106
+ private
107
+
108
+ def cast_value(value)
109
+ return 0 if value.equal? false
110
+ return 1 if value.equal? true
111
+
112
+ if @scale == 0 # act-like an integer
113
+ return value.to_i rescue nil
114
+ end
115
+
116
+ case value
117
+ when ::Float
118
+ if precision
119
+ BigDecimal(value, float_precision)
120
+ else
121
+ value.to_d
122
+ end
123
+ when ::Numeric, ::String
124
+ BigDecimal(value, precision.to_i)
125
+ else
126
+ if value.respond_to?(:to_d)
127
+ value.to_d
128
+ else
129
+ BigDecimal(value.to_s, precision.to_i)
130
+ end
131
+ end
132
+ end
133
+
134
+ def float_precision
135
+ if precision.to_i > ::Float::DIG + 1
136
+ ::Float::DIG + 1
137
+ else
138
+ precision.to_i
139
+ end
140
+ end
141
+
142
+ #def max_value; ::Float::INFINITY end
143
+
144
+ end
145
+
146
+ # @private
147
+ class MoneyType < DecimalType
148
+ def type; :money end
149
+ def initialize(options = {})
150
+ super; @precision = 19; @scale = 4
151
+ end
152
+ end
153
+
154
+ # @private
155
+ class SmallMoneyType < MoneyType
156
+ def type; :smallmoney end
157
+ def initialize(options = {})
158
+ super; @precision = 10; @scale = 4
159
+ end
160
+ end
161
+
162
+ # @private
163
+ class DateTimeType < ActiveRecord::Type::DateTime
164
+
165
+ def type_cast_for_schema(value)
166
+ value.acts_like?(:string) ? "'#{value}'" : "'#{value.to_s(:db)}'"
167
+ end
168
+
169
+ private
170
+
171
+ def cast_value(value)
172
+ value = value.respond_to?(:usec) ? value : super
173
+ return unless value
174
+ value.change usec: cast_usec(value)
175
+ end
176
+
177
+ def cast_usec(value)
178
+ return 0 if !value.respond_to?(:usec) || value.usec.zero?
179
+ seconds = value.usec.to_f / 1_000_000.0
180
+ second_precision = 0.00333
181
+ ss_seconds = ((seconds * (1 / second_precision)).round / (1 / second_precision)).round(3)
182
+ (ss_seconds * 1_000_000).to_i
183
+ end
184
+
185
+ end
186
+
187
+ # @private
188
+ class SmallDateTimeType < DateTimeType
189
+ def cast_usec(value); 0 end
190
+ def cast_usec_for_database(value); '.000' end
191
+ end
192
+
193
+ # @private
194
+ class TimeType < ActiveRecord::Type::Time
195
+
196
+ def initialize(options = {})
197
+ super; @precision = nil if @precision == 7
198
+ end
199
+
200
+ def type_cast_for_schema(value)
201
+ value.acts_like?(:string) ? "'#{value}'" : super
202
+ end
203
+
204
+ private
205
+
206
+ def cast_value(value)
207
+ value = value.respond_to?(:usec) ?
208
+ value.change(year: 2000, month: 01, day: 01) :
209
+ cast_value_like_super(value)
210
+
211
+ return if value.blank?
212
+ value.change usec: cast_usec(value)
213
+ end
214
+
215
+ def cast_value_like_super(value)
216
+ return value unless value.is_a?(::String)
217
+ return if value.empty?
218
+
219
+ dummy_value = "2000-01-01 #{value}"
220
+
221
+ fast_string_to_time(dummy_value) || DateTime.parse(dummy_value).to_time # rescue nil
222
+ end
223
+
224
+ def cast_usec(value)
225
+ (usec_to_seconds_frction(value) * 1_000_000).to_i
226
+ end
227
+
228
+ def usec_to_seconds_frction(value)
229
+ (value.usec.to_f / 1_000_000.0).round(precision || 7)
230
+ end
231
+
232
+ #def quote_usec(value)
233
+ # usec_to_seconds_frction(value).to_s.split('.').last
234
+ #end
235
+
236
+ end
237
+
238
+ # @private
239
+ StringType = ActiveRecord::Type::String
240
+
241
+ # @private
242
+ class CharType < StringType
243
+ def type; :char end
244
+ end
245
+
246
+ # @private
247
+ class VarcharType < StringType
248
+ def type; :varchar end
249
+ def initialize(options = {})
250
+ super; @limit = 8000 if @limit.to_i == 0
251
+ end
252
+ end
253
+
254
+ # @private
255
+ class TextType < StringType
256
+ def type; :text_basic end
257
+ end
258
+
259
+ # @private
260
+ class VarcharMaxType < TextType
261
+ def type; :varchar_max end
262
+ def limit; @limit ||= 2_147_483_647 end
263
+ end
264
+
265
+ # @private
266
+ class UnicodeCharType < StringType
267
+ def type; :nchar end
268
+ end
269
+
270
+ # @private
271
+ class UnicodeVarcharType < StringType
272
+ def type; :string end
273
+ def initialize(options = {})
274
+ super; @limit = 4000 if @limit.to_i == 0
275
+ end
276
+ end
277
+
278
+ # @private
279
+ class UnicodeVarcharMaxType < TextType
280
+ def type; :text end # :nvarchar_max end
281
+ def limit; @limit ||= 2_147_483_647 end
282
+ end
283
+
284
+ # @private
285
+ class UnicodeTextType < TextType
286
+ def type; :ntext end
287
+ def limit; @limit ||= 2_147_483_647 end
288
+ end
289
+
290
+ # @private
291
+ class BinaryType < ActiveRecord::Type::Binary
292
+ def type; :binary_basic end
293
+ end
294
+
295
+ # @private
296
+ class ImageType < BinaryType # ActiveRecord::Type::Binary
297
+ def type; :binary end
298
+ def limit; @limit ||= 2_147_483_647 end
299
+ end
300
+
301
+ # @private
302
+ class VarbinaryType < BinaryType # ActiveRecord::Type::Binary
303
+ def type; :varbinary end
304
+ def initialize(options = {})
305
+ super; @limit = 8000 if @limit.to_i == 0
306
+ end
307
+ end
308
+
309
+ # @private
310
+ class VarbinaryMaxType < BinaryType # ActiveRecord::Type::Binary
311
+ def type; :varbinary_max end
312
+ def limit; @limit ||= 2_147_483_647 end
313
+ end
314
+
315
+ # @private
316
+ class UUIDType < ActiveRecord::Type::String
317
+ def type; :uuid end
318
+
319
+ alias_method :type_cast_for_database, :type_cast_from_database
320
+
321
+ ACCEPTABLE_UUID = %r{\A\{?([a-fA-F0-9]{4}-?){8}\}?\z}x
322
+ def type_cast(value); value.to_s[ACCEPTABLE_UUID, 0] end
323
+ end
324
+
325
+ # @private
326
+ class XmlType < ActiveRecord::Type::String
327
+ def type; :xml end
328
+
329
+ def type_cast_for_database(value)
330
+ return unless value
331
+ Data.new(super)
332
+ end
333
+
334
+ class Data
335
+ def initialize(value)
336
+ @value = value
337
+ end
338
+ def to_s; @value end
339
+ end
340
+ end
341
+
342
+ end
343
+ end