activerecord-jdbc-adapter 1.3.17 → 1.3.18

Sign up to get free protection for your applications and to get access to all the features.
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