activerecord-jdbc-alt-adapter 50.7.0-java → 51.3.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -3
  3. data/.travis.yml +24 -28
  4. data/Gemfile +5 -2
  5. data/README.md +26 -20
  6. data/Rakefile +4 -30
  7. data/activerecord-jdbc-adapter.gemspec +2 -2
  8. data/activerecord-jdbc-alt-adapter.gemspec +4 -3
  9. data/lib/arel/visitors/sqlserver.rb +17 -3
  10. data/lib/arjdbc/abstract/core.rb +4 -2
  11. data/lib/arjdbc/abstract/database_statements.rb +2 -8
  12. data/lib/arjdbc/abstract/transaction_support.rb +2 -9
  13. data/lib/arjdbc/db2/adapter.rb +2 -52
  14. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  15. data/lib/arjdbc/jdbc/column.rb +11 -5
  16. data/lib/arjdbc/jdbc/error.rb +1 -1
  17. data/lib/arjdbc/jdbc.rb +4 -0
  18. data/lib/arjdbc/mssql/adapter.rb +33 -39
  19. data/lib/arjdbc/mssql/connection_methods.rb +0 -5
  20. data/lib/arjdbc/mssql/database_statements.rb +1 -1
  21. data/lib/arjdbc/mssql/explain_support.rb +1 -1
  22. data/lib/arjdbc/mssql/{extensions.rb → extensions/attribute_methods.rb} +0 -0
  23. data/lib/arjdbc/mssql/extensions/calculations.rb +29 -0
  24. data/lib/arjdbc/mssql/schema_creation.rb +12 -0
  25. data/lib/arjdbc/mssql/schema_definitions.rb +17 -0
  26. data/lib/arjdbc/mssql/schema_dumper.rb +37 -0
  27. data/lib/arjdbc/mssql/schema_statements.rb +73 -44
  28. data/lib/arjdbc/mssql/types/date_and_time_types.rb +9 -9
  29. data/lib/arjdbc/mssql/utils.rb +1 -0
  30. data/lib/arjdbc/mssql.rb +1 -1
  31. data/lib/arjdbc/mysql/adapter.rb +12 -1
  32. data/lib/arjdbc/mysql/connection_methods.rb +7 -13
  33. data/lib/arjdbc/postgresql/adapter.rb +29 -12
  34. data/lib/arjdbc/postgresql/column.rb +3 -6
  35. data/lib/arjdbc/postgresql/connection_methods.rb +1 -3
  36. data/lib/arjdbc/postgresql/oid_types.rb +7 -12
  37. data/lib/arjdbc/sqlite3/adapter.rb +169 -139
  38. data/lib/arjdbc/sqlite3/connection_methods.rb +1 -2
  39. data/lib/arjdbc/version.rb +1 -1
  40. data/rakelib/01-tomcat.rake +2 -2
  41. data/rakelib/02-test.rake +2 -0
  42. data/rakelib/rails.rake +1 -1
  43. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +17 -68
  44. data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +38 -282
  45. data/src/java/arjdbc/postgresql/ByteaUtils.java +1 -0
  46. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +3 -3
  47. data/src/java/arjdbc/util/DateTimeUtils.java +5 -22
  48. metadata +20 -12
  49. data/lib/activerecord-jdbc-alt-adapter.rb +0 -1
@@ -7,6 +7,9 @@ require 'arel/visitors/bind_visitor'
7
7
  require 'arel/visitors/sqlserver'
8
8
  require 'active_record/connection_adapters/abstract_adapter'
9
9
 
10
+ require 'arjdbc/mssql/extensions/attribute_methods'
11
+ require 'arjdbc/mssql/extensions/calculations'
12
+
10
13
  require 'arjdbc/abstract/core'
11
14
  require 'arjdbc/abstract/connection_management'
12
15
  require 'arjdbc/abstract/database_statements'
@@ -18,9 +21,9 @@ require 'arjdbc/mssql/types'
18
21
  require 'arjdbc/mssql/quoting'
19
22
  require 'arjdbc/mssql/schema_definitions'
20
23
  require 'arjdbc/mssql/schema_statements'
24
+ require 'arjdbc/mssql/schema_dumper'
21
25
  require 'arjdbc/mssql/database_statements'
22
26
  require 'arjdbc/mssql/explain_support'
23
- require 'arjdbc/mssql/extensions'
24
27
  require 'arjdbc/mssql/transaction'
25
28
  require 'arjdbc/mssql/errors'
26
29
  require 'arjdbc/mssql/schema_creation'
@@ -51,6 +54,7 @@ module ActiveRecord
51
54
 
52
55
  include MSSQL::Quoting
53
56
  include MSSQL::SchemaStatements
57
+ include MSSQL::ColumnDumper
54
58
  include MSSQL::DatabaseStatements
55
59
  include MSSQL::ExplainSupport
56
60
 
@@ -81,38 +85,11 @@ module ActiveRecord
81
85
  ::ActiveRecord::ConnectionAdapters::MSSQLColumn
82
86
  end
83
87
 
84
- # Does this adapter support DDL rollbacks in transactions? That is, would
85
- # CREATE TABLE or ALTER TABLE get rolled back by a transaction?
86
- def supports_ddl_transactions?
87
- true
88
- end
89
-
90
- # Can this adapter determine the primary key for tables not attached
91
- # to an Active Record class, such as join tables?
92
- def supports_primary_key?
93
- true
94
- end
95
-
96
88
  # Does this adapter support creating foreign key constraints?
97
89
  def supports_foreign_keys?
98
90
  true
99
91
  end
100
92
 
101
- # Does this adapter support index sort order?
102
- def supports_index_sort_order?
103
- true
104
- end
105
-
106
- # Also known as filtered index
107
- def supports_partial_index?
108
- true
109
- end
110
-
111
- # Does this adapter support migrations?
112
- def supports_migrations?
113
- true
114
- end
115
-
116
93
  # Does this adapter support setting the isolation level for a transaction?
117
94
  def supports_transaction_isolation?
118
95
  true
@@ -123,6 +100,11 @@ module ActiveRecord
123
100
  true
124
101
  end
125
102
 
103
+ # Does this adapter support views?
104
+ def supports_views?
105
+ true
106
+ end
107
+
126
108
  # Overrides abstract method which always returns false
127
109
  def valid_type?(type)
128
110
  !native_database_types[type].nil?
@@ -134,13 +116,6 @@ module ActiveRecord
134
116
  super
135
117
  end
136
118
 
137
- def reset!
138
- # execute 'IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION'
139
- # NOTE: it seems the above line interferes with the jdbc driver
140
- # and ending up in connection closed, issue seen in rails 5.2 and 6.0
141
- reconnect!
142
- end
143
-
144
119
  def disable_referential_integrity
145
120
  tables = tables_with_referential_integrity
146
121
 
@@ -211,22 +186,37 @@ module ActiveRecord
211
186
  alias_method :current_schema=, :default_schema=
212
187
 
213
188
  # Overrides method in abstract adapter
189
+ # FIXME: This needs to be fixed the we find a way how to
190
+ # get the collation per column basis. At the moment we only use
191
+ # the global database collation
214
192
  def case_sensitive_comparison(table, attribute, column, value)
215
193
  if value.nil?
216
194
  table[attribute].eq(value)
217
- elsif value.acts_like?(:string)
195
+ elsif [:string, :text].include?(column.type) && collation && !collation.match(/_CS/)
218
196
  table[attribute].eq(Arel::Nodes::Bin.new(Arel::Nodes::BindParam.new))
197
+ # elsif value.acts_like?(:string)
198
+ # table[attribute].eq(Arel::Nodes::Bin.new(Arel::Nodes::BindParam.new))
219
199
  else
220
200
  table[attribute].eq(Arel::Nodes::BindParam.new)
221
201
  end
222
202
  end
223
203
 
224
204
  def configure_connection
225
- # Here goes initial settings per connection
205
+ execute("SET LOCK_TIMEOUT #{lock_timeout}")
226
206
 
227
207
  set_session_transaction_isolation
228
208
  end
229
209
 
210
+ def lock_timeout
211
+ timeout = config[:lock_timeout].to_i
212
+
213
+ if timeout.positive?
214
+ timeout
215
+ else
216
+ 5_000
217
+ end
218
+ end
219
+
230
220
  def set_session_transaction_isolation
231
221
  isolation_level = config[:transaction_isolation]
232
222
 
@@ -263,9 +253,9 @@ module ActiveRecord
263
253
  end
264
254
  end
265
255
 
266
- protected
256
+ private
267
257
 
268
- def translate_exception(e, message)
258
+ def translate_exception(exception, message)
269
259
  case message
270
260
  when /(cannot insert duplicate key .* with unique index) | (violation of unique key constraint)/i
271
261
  RecordNotUnique.new(message)
@@ -275,6 +265,10 @@ module ActiveRecord
275
265
  ActiveRecord::InvalidForeignKey.new(message)
276
266
  when /(String or binary data would be truncated)/i
277
267
  ActiveRecord::ValueTooLong.new(message)
268
+ when /Cannot insert the value NULL into column .* does not allow nulls/
269
+ ActiveRecord::NotNullViolation.new(message)
270
+ when /Arithmetic overflow error converting expression/
271
+ ActiveRecord::RangeError.new(message)
278
272
  else
279
273
  super
280
274
  end
@@ -52,8 +52,6 @@ ArJdbc::ConnectionMethods.module_eval do
52
52
 
53
53
  # @note Assumes SQLServer SQL-JDBC driver on the class-path.
54
54
  def sqlserver_connection(config)
55
- config = config.deep_dup
56
-
57
55
  config[:adapter_spec] ||= ::ArJdbc::MSSQL
58
56
  config[:adapter_class] = ActiveRecord::ConnectionAdapters::MSSQLAdapter unless config.key?(:adapter_class)
59
57
 
@@ -62,15 +60,12 @@ ArJdbc::ConnectionMethods.module_eval do
62
60
  config[:host] ||= 'localhost'
63
61
  config[:driver] ||= 'com.microsoft.sqlserver.jdbc.SQLServerDriver'
64
62
  config[:connection_alive_sql] ||= 'SELECT 1'
65
- config[:lock_timeout] ||= 5000
66
63
 
67
64
  config[:url] ||= begin
68
65
  url = "jdbc:sqlserver://#{config[:host]}"
69
66
  url << ( config[:port] ? ":#{config[:port]};" : ';' )
70
67
  url << "databaseName=#{config[:database]};" if config[:database]
71
68
  url << "instanceName=#{config[:instance]};" if config[:instance]
72
- url << "loginTimeout=#{config[:login_timeout].to_i};" if config[:login_timeout]
73
- url << "lockTimeout=#{config[:lock_timeout].to_i};"
74
69
  app = config[:appname] || config[:application]
75
70
  url << "applicationName=#{app};" if app
76
71
  isc = config[:integrated_security] # Win only - needs sqljdbc_auth.dll
@@ -87,7 +87,7 @@ module ActiveRecord
87
87
  end
88
88
 
89
89
  def identity_column_name(table_name)
90
- for column in schema_cache.columns(table_name)
90
+ for column in columns(table_name)
91
91
  return column.name if column.identity?
92
92
  end
93
93
  nil
@@ -41,7 +41,7 @@ module ActiveRecord
41
41
 
42
42
  binds.each do |bind|
43
43
  value = quote(bind.value_for_database)
44
- sql.sub!('?', value)
44
+ sql = sql.sub('?', value)
45
45
  end
46
46
 
47
47
  sql
@@ -0,0 +1,29 @@
1
+ require 'active_record/relation'
2
+ require 'active_record/version'
3
+
4
+ # NOTE: some improvements in active record broke sql calculations and habtm
5
+ # associations for this adapter and the supporting arel visitor.
6
+ # this extension fixes this issue in rails 5.1.4 onwards
7
+ #
8
+ # https://github.com/rails/rails/pull/29848
9
+ # https://github.com/rails/rails/pull/30686
10
+
11
+ module ActiveRecord
12
+ module ConnectionAdapters
13
+ module MSSQL
14
+ module Calculations
15
+ private
16
+
17
+ def build_count_subquery(relation, column_name, distinct)
18
+ super(relation.unscope(:order), column_name, distinct)
19
+ end
20
+
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ ActiveSupport.on_load(:active_record) do
27
+ mod = ActiveRecord::ConnectionAdapters::MSSQL::Calculations
28
+ ActiveRecord::Relation.prepend(mod)
29
+ end
@@ -15,6 +15,18 @@ module ActiveRecord
15
15
  end
16
16
  end
17
17
 
18
+ def add_column_options!(sql, options)
19
+ sql << " DEFAULT #{quote_default_expression(options[:default], options[:column])}" if options_include_default?(options)
20
+
21
+ sql << ' NOT NULL' if options[:null] == false
22
+
23
+ sql << ' IDENTITY(1,1)' if options[:is_identity] == true
24
+
25
+ sql << ' PRIMARY KEY' if options[:primary_key] == true
26
+
27
+ sql
28
+ end
29
+
18
30
  # There is no RESTRICT in MSSQL but it has NO ACTION which behave
19
31
  # same as RESTRICT, added this behave according rails api.
20
32
  def action_sql(action, dependency)
@@ -2,6 +2,14 @@ module ActiveRecord
2
2
  module ConnectionAdapters
3
3
  module MSSQL
4
4
  module ColumnMethods
5
+ def primary_key(name, type = :primary_key, **options)
6
+ if [:integer, :bigint].include?(type)
7
+ options[:is_identity] = true unless options.key?(:default)
8
+ end
9
+
10
+ super
11
+ end
12
+
5
13
  # datetime with seconds always zero (:00) and without fractional seconds
6
14
  def smalldatetime(*args, **options)
7
15
  args.each { |name| column(name, :smalldatetime, options) }
@@ -64,6 +72,15 @@ module ActiveRecord
64
72
 
65
73
  class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
66
74
  include ColumnMethods
75
+
76
+ def new_column_definition(name, type, **options)
77
+ case type
78
+ when :primary_key
79
+ options[:is_identity] = true
80
+ end
81
+
82
+ super
83
+ end
67
84
  end
68
85
 
69
86
  class Table < ActiveRecord::ConnectionAdapters::Table
@@ -0,0 +1,37 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module MSSQL
4
+ module ColumnDumper # :nodoc:
5
+ MSSQL_NO_LIMIT_TYPES = [
6
+ 'text',
7
+ 'ntext',
8
+ 'varchar(max)',
9
+ 'nvarchar(max)',
10
+ 'varbinary(max)'
11
+ ].freeze
12
+
13
+ private
14
+
15
+ def schema_limit(column)
16
+ return if MSSQL_NO_LIMIT_TYPES.include?(column.sql_type)
17
+
18
+ super
19
+ end
20
+
21
+ def explicit_primary_key_default?(column)
22
+ !column.identity?
23
+ end
24
+
25
+ def default_primary_key?(column)
26
+ super && column.identity?
27
+ end
28
+
29
+ # def schema_collation(column)
30
+ # return unless column.collation
31
+ # column.collation if column.collation != collation
32
+ # end
33
+
34
+ end
35
+ end
36
+ end
37
+ end
@@ -5,7 +5,7 @@ module ActiveRecord
5
5
 
6
6
  NATIVE_DATABASE_TYPES = {
7
7
  # Logical Rails types to SQL Server types
8
- primary_key: 'int NOT NULL IDENTITY(1,1) PRIMARY KEY',
8
+ primary_key: 'bigint NOT NULL IDENTITY(1,1) PRIMARY KEY',
9
9
  integer: { name: 'int', limit: 4 },
10
10
  boolean: { name: 'bit' },
11
11
  decimal: { name: 'decimal' },
@@ -41,43 +41,16 @@ module ActiveRecord
41
41
  NATIVE_DATABASE_TYPES
42
42
  end
43
43
 
44
- # Returns an array of table names defined in the database.
45
- def tables(name = nil)
46
- if name
47
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
48
- Passing arguments to #tables is deprecated without replacement.
49
- MSG
50
- end
51
-
52
- @connection.tables(nil, name)
53
- end
54
-
55
44
  # Returns an array of Column objects for the table specified by +table_name+.
56
45
  # See the concrete implementation for details on the expected parameter values.
57
46
  # NOTE: This is ready, all implemented in the java part of adapter,
58
47
  # it uses MSSQLColumn, SqlTypeMetadata, etc.
59
48
  def columns(table_name)
60
- log('JDBC: GETCOLUMNS', 'SCHEMA') { @connection.columns(table_name) }
49
+ @connection.columns(table_name)
61
50
  rescue => e
62
51
  raise translate_exception_class(e, nil)
63
52
  end
64
53
 
65
- # Returns an array of view names defined in the database.
66
- # (to be implemented)
67
- def views
68
- []
69
- end
70
-
71
- def table_exists?(table_name)
72
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
73
- #table_exists? currently checks both tables and views.
74
- This behavior is deprecated and will be changed with Rails 5.1 to only check tables.
75
- Use #data_source_exists? instead.
76
- MSG
77
-
78
- tables.include?(table_name.to_s)
79
- end
80
-
81
54
  # Returns an array of indexes for the given table.
82
55
  def indexes(table_name, name = nil)
83
56
  @connection.indexes(table_name, name)
@@ -96,7 +69,7 @@ module ActiveRecord
96
69
  end
97
70
 
98
71
  def collation
99
- select_value "SELECT Collation = CAST(SERVERPROPERTY('Collation') AS NVARCHAR(128))"
72
+ @collation ||= select_value("SELECT Collation = CAST(SERVERPROPERTY('Collation') AS NVARCHAR(128))")
100
73
  end
101
74
 
102
75
  def current_database
@@ -178,10 +151,10 @@ module ActiveRecord
178
151
  end
179
152
 
180
153
  # @private these cannot specify a limit
181
- NO_LIMIT_TYPES = %w(text binary boolean date)
154
+ NO_LIMIT_TYPES = %i[text binary boolean date].freeze
182
155
 
183
- def type_to_sql(type, limit = nil, precision = nil, scale = nil)
184
- type_s = type.to_s
156
+ # Maps logical Rails types to MSSQL-specific data types.
157
+ def type_to_sql(type, limit: nil, precision: nil, scale: nil, **) # :nodoc:
185
158
  # MSSQL's NVARCHAR(n | max) column supports either a number between 1 and
186
159
  # 4000, or the word "MAX", which corresponds to 2**30-1 UCS-2 characters.
187
160
  #
@@ -190,11 +163,14 @@ module ActiveRecord
190
163
  #
191
164
  # See: http://msdn.microsoft.com/en-us/library/ms186939.aspx
192
165
  #
193
- if type_s == 'string' && limit == 1073741823
194
- 'NVARCHAR(MAX)'
195
- elsif NO_LIMIT_TYPES.include?(type_s)
166
+ type = type.to_sym if type
167
+ native = native_database_types[type]
168
+
169
+ if type == :string && limit == 1_073_741_823
170
+ 'nvarchar(max)'
171
+ elsif NO_LIMIT_TYPES.include?(type)
196
172
  super(type)
197
- elsif type_s == 'integer' || type_s == 'int'
173
+ elsif %i[int integer].include?(type)
198
174
  if limit.nil? || limit == 4
199
175
  'int'
200
176
  elsif limit == 2
@@ -204,8 +180,22 @@ module ActiveRecord
204
180
  else
205
181
  'bigint'
206
182
  end
207
- elsif type_s == 'uniqueidentifier'
208
- type_s
183
+ elsif type == :uniqueidentifier
184
+ 'uniqueidentifier'
185
+ elsif %i[datetime time].include?(type)
186
+ precision ||= 7
187
+ column_type_sql = (native.is_a?(Hash) ? native[:name] : native).dup
188
+ if (0..7).include?(precision)
189
+ column_type_sql << "(#{precision})"
190
+ else
191
+ raise(
192
+ ActiveRecordError,
193
+ "No #{native[:name]} type has precision of #{precision}. The " \
194
+ 'allowed range of precision is from 0 to 7, even though the ' \
195
+ 'sql type precision is 7 this adapter will persist up to 6 ' \
196
+ 'precision only.'
197
+ )
198
+ end
209
199
  else
210
200
  super
211
201
  end
@@ -241,7 +231,11 @@ module ActiveRecord
241
231
  default = extract_new_default_value(default_or_changes)
242
232
  unless default.nil?
243
233
  column = columns(table_name).find { |c| c.name.to_s == column_name.to_s }
244
- result = execute "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT DF_#{table_name}_#{column_name} DEFAULT #{quote_default_expression(default, column)} FOR #{quote_column_name(column_name)}"
234
+ result = execute(
235
+ "ALTER TABLE #{quote_table_name(table_name)} " \
236
+ "ADD CONSTRAINT DF_#{table_name}_#{column_name} " \
237
+ "DEFAULT #{quote_default_expression(default, column)} FOR #{quote_column_name(column_name)}"
238
+ )
245
239
  result
246
240
  end
247
241
  end
@@ -257,11 +251,21 @@ module ActiveRecord
257
251
  end
258
252
 
259
253
  if !options[:null].nil? && options[:null] == false && !options[:default].nil?
260
- execute "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_expression(options[:default], column)} WHERE #{quote_column_name(column_name)} IS NULL"
254
+ execute(
255
+ "UPDATE #{quote_table_name(table_name)} SET " \
256
+ "#{quote_column_name(column_name)}=#{quote_default_expression(options[:default], column)} " \
257
+ "WHERE #{quote_column_name(column_name)} IS NULL"
258
+ )
261
259
  end
262
260
 
263
261
  change_column_type(table_name, column_name, type, options)
264
- change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
262
+
263
+ if options_include_default?(options)
264
+ change_column_default(table_name, column_name, options[:default])
265
+ elsif options.key?(:default) && options[:null] == false
266
+ # Drop default constraint when null option is false
267
+ remove_default_constraint(table_name, column_name)
268
+ end
265
269
 
266
270
  # add any removed indexes back
267
271
  indexes.each do |index|
@@ -280,7 +284,7 @@ module ActiveRecord
280
284
  end
281
285
  sql_alter = [
282
286
  "ALTER TABLE #{quoted_table}",
283
- "ALTER COLUMN #{quoted_column} #{type_to_sql column.type, column.limit, column.precision, column.scale}",
287
+ "ALTER COLUMN #{quoted_column} #{type_to_sql(column.type, limit: column.limit, precision: column.precision, scale: column.scale)}",
284
288
  (' NOT NULL' unless null)
285
289
  ]
286
290
 
@@ -289,8 +293,33 @@ module ActiveRecord
289
293
 
290
294
  private
291
295
 
296
+ def data_source_sql(name = nil, type: nil)
297
+ scope = quoted_scope(name, type: type)
298
+ table_name = 'TABLE_NAME'
299
+
300
+ sql = "SELECT #{table_name}"
301
+ sql << ' FROM INFORMATION_SCHEMA.TABLES'
302
+ sql << ' WHERE TABLE_CATALOG = DB_NAME()'
303
+ sql << " AND TABLE_SCHEMA = #{quote(scope[:schema])}"
304
+ sql << " AND TABLE_NAME = #{quote(scope[:name])}" if scope[:name]
305
+ sql << " AND TABLE_TYPE = #{quote(scope[:type])}" if scope[:type]
306
+ sql << " ORDER BY #{table_name}"
307
+ sql
308
+ end
309
+
310
+ def quoted_scope(raw_name = nil, type: nil)
311
+ schema = ArJdbc::MSSQL::Utils.unqualify_table_schema(raw_name)
312
+ name = ArJdbc::MSSQL::Utils.unqualify_table_name(raw_name)
313
+
314
+ scope = {}
315
+ scope[:schema] = schema || 'dbo'
316
+ scope[:name] = name if name
317
+ scope[:type] = type if type
318
+ scope
319
+ end
320
+
292
321
  def change_column_type(table_name, column_name, type, options = {})
293
- sql = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
322
+ sql = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, limit: options[:limit], precision: options[:precision], scale: options[:scale])}"
294
323
  sql << (options[:null] ? " NULL" : " NOT NULL") if options.has_key?(:null)
295
324
  result = execute(sql)
296
325
  result
@@ -8,12 +8,12 @@ module ActiveRecord
8
8
 
9
9
  class DateTime2 < ActiveRecord::Type::DateTime
10
10
  def type_cast_for_schema(value)
11
- return "'#{value}'" if value.acts_like?(:string)
11
+ return %("#{value}") if value.acts_like?(:string)
12
12
 
13
13
  if value.usec > 0
14
- "'#{value.to_s(:db)}.#{value.usec.to_s.remove(/0+$/)}'"
14
+ %("#{value.to_s(:db)}.#{value.usec.to_s.remove(/0+$/)}")
15
15
  else
16
- "'#{value.to_s(:db)}'"
16
+ %("#{value.to_s(:db)}")
17
17
  end
18
18
  end
19
19
 
@@ -49,12 +49,12 @@ module ActiveRecord
49
49
  end
50
50
 
51
51
  def type_cast_for_schema(value)
52
- return "'#{value}'" if value.acts_like?(:string)
52
+ return %("#{value}") if value.acts_like?(:string)
53
53
 
54
54
  if value.usec > 0
55
- "'#{value.to_s(:db)}.#{value.usec.to_s.remove(/0+$/)}'"
55
+ %("#{value.to_s(:db)}.#{value.usec.to_s.remove(/0+$/)}")
56
56
  else
57
- "'#{value.to_s(:db)}'"
57
+ %("#{value.to_s(:db)}")
58
58
  end
59
59
  end
60
60
 
@@ -95,12 +95,12 @@ module ActiveRecord
95
95
 
96
96
  class Time < ActiveRecord::Type::Time
97
97
  def type_cast_for_schema(value)
98
- return "'#{value}'" if value.acts_like?(:string)
98
+ return %("#{value}") if value.acts_like?(:string)
99
99
 
100
100
  if value.usec > 0
101
- "'#{value.to_s(:db)}.#{value.usec.to_s.remove(/0+$/)}'"
101
+ %("#{value.to_s(:db)}.#{value.usec.to_s.remove(/0+$/)}")
102
102
  else
103
- "'#{value.to_s(:db)}'"
103
+ %("#{value.to_s(:db)}")
104
104
  end
105
105
  end
106
106
 
@@ -41,6 +41,7 @@ module ArJdbc
41
41
  end
42
42
 
43
43
  def unqualify_table_name(table_name)
44
+ return if table_name.blank?
44
45
  remove_identifier_delimiters(table_name.to_s.split('.').last)
45
46
  end
46
47
 
data/lib/arjdbc/mssql.rb CHANGED
@@ -4,4 +4,4 @@ require 'arjdbc/mssql/connection_methods'
4
4
  module ArJdbc
5
5
  MsSQL = MSSQL # compatibility with 1.2
6
6
  end
7
- ArJdbc.warn_unsupported_adapter 'mssql', [5, 0] # warns on AR >= 4.2
7
+ ArJdbc.warn_unsupported_adapter 'mssql', [5, 1] # warns on AR >= 4.2
@@ -61,6 +61,10 @@ module ActiveRecord
61
61
  true
62
62
  end
63
63
 
64
+ def supports_transaction_isolation?
65
+ true
66
+ end
67
+
64
68
  # HELPER METHODS ===========================================
65
69
 
66
70
  # Reloading the type map in abstract/statement_cache.rb blows up postgres
@@ -92,7 +96,14 @@ module ActiveRecord
92
96
 
93
97
  #--
94
98
  # QUOTING ==================================================
95
- #++
99
+ #+
100
+
101
+ # FIXME: 5.1 crashes without this. I think this is Arel hitting a fallback path in to_sql.rb.
102
+ # So maybe an untested code path in their source. Still means we are doing something wrong to
103
+ # even hit it.
104
+ def quote(value, comment=nil)
105
+ super(value)
106
+ end
96
107
 
97
108
  # NOTE: quote_string(string) provided by ArJdbc::MySQL (native code),
98
109
  # this piece is also native (mysql2) under MRI: `@connection.escape(string)`
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  ArJdbc::ConnectionMethods.module_eval do
3
3
  def mysql_connection(config)
4
- config = config.deep_dup
5
4
  # NOTE: this isn't "really" necessary but Rails (in tests) assumes being able to :
6
5
  # ActiveRecord::Base.mysql2_connection ActiveRecord::Base.configurations['arunit'].merge(database: ...)
7
6
  config = symbolize_keys_if_necessary(config)
@@ -11,9 +10,11 @@ ArJdbc::ConnectionMethods.module_eval do
11
10
 
12
11
  return jndi_connection(config) if jndi_config?(config)
13
12
 
14
- driver = config[:driver]
15
- mysql_driver = driver.nil? || driver.to_s.start_with?('com.mysql.')
16
- mariadb_driver = ! mysql_driver && driver.to_s.start_with?('org.mariadb.')
13
+ driver = config[:driver] ||=
14
+ defined?(::Jdbc::MySQL.driver_name) ? ::Jdbc::MySQL.driver_name : 'com.mysql.jdbc.Driver'
15
+
16
+ mysql_driver = driver.start_with?('com.mysql.')
17
+ mariadb_driver = ! mysql_driver && driver.start_with?('org.mariadb.')
17
18
 
18
19
  begin
19
20
  require 'jdbc/mysql'
@@ -21,11 +22,6 @@ ArJdbc::ConnectionMethods.module_eval do
21
22
  rescue LoadError # assuming driver.jar is on the class-path
22
23
  end if mysql_driver
23
24
 
24
- if driver.nil?
25
- config[:driver] ||=
26
- defined?(::Jdbc::MySQL.driver_name) ? ::Jdbc::MySQL.driver_name : 'com.mysql.jdbc.Driver'
27
- end
28
-
29
25
  config[:username] = 'root' unless config.key?(:username)
30
26
  # jdbc:mysql://[host][,failoverhost...][:port]/[database]
31
27
  # - if the host name is not specified, it defaults to 127.0.0.1
@@ -40,8 +36,7 @@ ArJdbc::ConnectionMethods.module_eval do
40
36
 
41
37
  properties = ( config[:properties] ||= {} )
42
38
  if mysql_driver
43
- properties['zeroDateTimeBehavior'] ||=
44
- config[:driver].to_s.start_with?('com.mysql.cj.') ? 'CONVERT_TO_NULL' : 'convertToNull'
39
+ properties['zeroDateTimeBehavior'] ||= 'convertToNull'
45
40
  properties['jdbcCompliantTruncation'] ||= false
46
41
  # NOTE: this is "better" than passing what users are used to set on MRI
47
42
  # e.g. 'utf8mb4' will fail cause the driver will check for a Java charset
@@ -113,8 +108,7 @@ ArJdbc::ConnectionMethods.module_eval do
113
108
  rescue LoadError # assuming driver.jar is on the class-path
114
109
  end
115
110
 
116
- config[:driver] ||=
117
- defined?(::Jdbc::MariaDB.driver_name) ? ::Jdbc::MariaDB.driver_name : 'org.mariadb.jdbc.Driver'
111
+ config[:driver] ||= 'org.mariadb.jdbc.Driver'
118
112
 
119
113
  mysql_connection(config)
120
114
  end