activerecord-jdbc-alt-adapter 70.2.0-java → 71.0.0.alpha1-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/main.yml +135 -21
  3. data/.github/workflows/ruby.yml +10 -10
  4. data/.gitignore +1 -0
  5. data/.solargraph.yml +15 -0
  6. data/Gemfile +17 -4
  7. data/README.md +7 -3
  8. data/RUNNING_TESTS.md +36 -0
  9. data/activerecord-jdbc-adapter.gemspec +2 -2
  10. data/activerecord-jdbc-alt-adapter.gemspec +1 -1
  11. data/lib/arjdbc/abstract/connection_management.rb +23 -10
  12. data/lib/arjdbc/abstract/core.rb +5 -6
  13. data/lib/arjdbc/abstract/database_statements.rb +35 -25
  14. data/lib/arjdbc/abstract/statement_cache.rb +1 -6
  15. data/lib/arjdbc/abstract/transaction_support.rb +37 -9
  16. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  17. data/lib/arjdbc/jdbc/column.rb +0 -34
  18. data/lib/arjdbc/jdbc/connection_methods.rb +1 -1
  19. data/lib/arjdbc/mssql/adapter.rb +93 -80
  20. data/lib/arjdbc/mssql/column.rb +1 -0
  21. data/lib/arjdbc/mssql/connection_methods.rb +7 -55
  22. data/lib/arjdbc/mssql/database_statements.rb +182 -71
  23. data/lib/arjdbc/mssql/explain_support.rb +8 -5
  24. data/lib/arjdbc/mssql/schema_creation.rb +1 -1
  25. data/lib/arjdbc/mssql/schema_definitions.rb +10 -0
  26. data/lib/arjdbc/mssql/schema_statements.rb +19 -11
  27. data/lib/arjdbc/mssql/server_version.rb +56 -0
  28. data/lib/arjdbc/mssql/utils.rb +23 -9
  29. data/lib/arjdbc/mysql/adapter.rb +64 -22
  30. data/lib/arjdbc/sqlite3/adapter.rb +218 -135
  31. data/lib/arjdbc/sqlite3/column.rb +103 -0
  32. data/lib/arjdbc/sqlite3/connection_methods.rb +7 -2
  33. data/lib/arjdbc/tasks/mssql_database_tasks.rb +9 -5
  34. data/lib/arjdbc/version.rb +1 -1
  35. data/rakelib/02-test.rake +1 -1
  36. data/rakelib/rails.rake +2 -0
  37. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +4 -2
  38. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +2 -1
  39. metadata +9 -12
  40. data/lib/arel/visitors/sql_server/ng42.rb +0 -294
  41. data/lib/arel/visitors/sql_server.rb +0 -124
  42. data/lib/arjdbc/mssql/limit_helpers.rb +0 -231
  43. data/lib/arjdbc/mssql/lock_methods.rb +0 -77
  44. data/lib/arjdbc/mssql/old_adapter.rb +0 -804
  45. data/lib/arjdbc/mssql/old_column.rb +0 -200
@@ -9,83 +9,85 @@ module ArJdbc
9
9
 
10
10
  NO_BINDS = [].freeze
11
11
 
12
- def exec_insert(sql, name = nil, binds = NO_BINDS, pk = nil, sequence_name = nil)
12
+ def exec_insert(sql, name = nil, binds = NO_BINDS, pk = nil, sequence_name = nil, returning: nil)
13
13
  sql = transform_query(sql)
14
14
 
15
15
  if preventing_writes?
16
16
  raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
17
17
  end
18
18
 
19
- materialize_transactions
20
19
  mark_transaction_written_if_write(sql)
21
20
 
22
21
  binds = convert_legacy_binds_to_attributes(binds) if binds.first.is_a?(Array)
23
22
 
24
- if without_prepared_statement?(binds)
25
- log(sql, name) { @connection.execute_insert_pk(sql, pk) }
26
- else
27
- log(sql, name, binds) do
28
- @connection.execute_insert_pk(sql, binds, pk)
23
+ with_raw_connection do |conn|
24
+ if without_prepared_statement?(binds)
25
+ log(sql, name) { conn.execute_insert_pk(sql, pk) }
26
+ else
27
+ log(sql, name, binds) do
28
+ conn.execute_insert_pk(sql, binds, pk)
29
+ end
29
30
  end
30
31
  end
31
32
  end
32
33
 
33
34
  # It appears that at this point (AR 5.0) "prepare" should only ever be true
34
35
  # if prepared statements are enabled
35
- def exec_query(sql, name = nil, binds = NO_BINDS, prepare: false, async: false)
36
+ def internal_exec_query(sql, name = nil, binds = NO_BINDS, prepare: false, async: false)
36
37
  sql = transform_query(sql)
37
38
 
38
39
  if preventing_writes? && write_query?(sql)
39
40
  raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
40
41
  end
41
42
 
42
- materialize_transactions
43
43
  mark_transaction_written_if_write(sql)
44
44
 
45
45
  binds = convert_legacy_binds_to_attributes(binds) if binds.first.is_a?(Array)
46
46
 
47
- if without_prepared_statement?(binds)
48
- log(sql, name) { @connection.execute_query(sql) }
49
- else
50
- log(sql, name, binds) do
51
- # this is different from normal AR that always caches
52
- cached_statement = fetch_cached_statement(sql) if prepare && @jdbc_statement_cache_enabled
53
- @connection.execute_prepared_query(sql, binds, cached_statement)
47
+ with_raw_connection do |conn|
48
+ if without_prepared_statement?(binds)
49
+ log(sql, name, async: async) { conn.execute_query(sql) }
50
+ else
51
+ log(sql, name, binds, async: async) do
52
+ # this is different from normal AR that always caches
53
+ cached_statement = fetch_cached_statement(sql) if prepare && @jdbc_statement_cache_enabled
54
+ conn.execute_prepared_query(sql, binds, cached_statement)
55
+ end
54
56
  end
55
57
  end
56
58
  end
57
59
 
58
- def exec_update(sql, name = nil, binds = NO_BINDS)
60
+ def exec_update(sql, name = 'SQL', binds = NO_BINDS)
59
61
  sql = transform_query(sql)
60
62
 
61
63
  if preventing_writes?
62
64
  raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
63
65
  end
64
66
 
65
- materialize_transactions
66
67
  mark_transaction_written_if_write(sql)
67
68
 
68
69
  binds = convert_legacy_binds_to_attributes(binds) if binds.first.is_a?(Array)
69
70
 
70
- if without_prepared_statement?(binds)
71
- log(sql, name) { @connection.execute_update(sql) }
72
- else
73
- log(sql, name, binds) { @connection.execute_prepared_update(sql, binds) }
71
+ with_raw_connection do |conn|
72
+ if without_prepared_statement?(binds)
73
+ log(sql, name) { conn.execute_update(sql) }
74
+ else
75
+ log(sql, name, binds) { conn.execute_prepared_update(sql, binds) }
76
+ end
74
77
  end
75
78
  end
76
79
  alias :exec_delete :exec_update
77
80
 
78
- def execute(sql, name = nil, async: false)
81
+ def execute(sql, name = nil, async: false, allow_retry: false, materialize_transactions: true)
79
82
  sql = transform_query(sql)
80
83
 
81
84
  if preventing_writes? && write_query?(sql)
82
85
  raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
83
86
  end
84
87
 
85
- materialize_transactions
86
88
  mark_transaction_written_if_write(sql)
87
89
 
88
- log(sql, name, async: async) { @connection.execute(sql) }
90
+ raw_execute(sql, name, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions)
89
91
  end
90
92
 
91
93
  # overridden to support legacy binds
@@ -102,6 +104,14 @@ module ArJdbc
102
104
  end
103
105
  end
104
106
 
107
+ def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: false)
108
+ log(sql, name, async: async) do
109
+ with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
110
+ conn.execute(sql)
111
+ end
112
+ end
113
+ end
114
+
105
115
  end
106
116
  end
107
117
  end
@@ -30,17 +30,12 @@ module ArJdbc
30
30
  @statements = StatementPool.new(statement_limit) # AR (5.0) expects this to be stored as @statements
31
31
  end
32
32
 
33
- # Clears the prepared statements cache.
34
- def clear_cache!
35
- @statements.clear
36
- end
37
-
38
33
  def delete_cached_statement(sql)
39
34
  @statements.delete(sql_key(sql))
40
35
  end
41
36
 
42
37
  def fetch_cached_statement(sql)
43
- @statements[sql_key(sql)] ||= @connection.prepare_statement(sql)
38
+ @statements[sql_key(sql)] ||= @raw_connection.prepare_statement(sql)
44
39
  end
45
40
 
46
41
  private
@@ -12,11 +12,11 @@ module ArJdbc
12
12
  # @since 1.3.0
13
13
  # @override
14
14
  def supports_savepoints?
15
- @connection.supports_savepoints?
15
+ @raw_connection.supports_savepoints?
16
16
  end
17
17
 
18
18
  def supports_transaction_isolation?
19
- @connection.supports_transaction_isolation?
19
+ @raw_connection.supports_transaction_isolation?
20
20
  end
21
21
 
22
22
  ########################## Transaction Interface ##########################
@@ -24,26 +24,42 @@ module ArJdbc
24
24
  # Starts a database transaction.
25
25
  # @override
26
26
  def begin_db_transaction
27
- log('BEGIN', 'TRANSACTION') { @connection.begin }
27
+ log('BEGIN', 'TRANSACTION') do
28
+ with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
29
+ conn.begin
30
+ end
31
+ end
28
32
  end
29
33
 
30
34
  # Starts a database transaction.
31
35
  # @param isolation the transaction isolation to use
32
36
  def begin_isolated_db_transaction(isolation)
33
- log("BEGIN ISOLATED - #{isolation}", 'TRANSACTION') { @connection.begin(isolation) }
37
+ log("BEGIN ISOLATED - #{isolation}", 'TRANSACTION') do
38
+ with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
39
+ conn.begin(isolation)
40
+ end
41
+ end
34
42
  end
35
43
 
36
44
  # Commits the current database transaction.
37
45
  # @override
38
46
  def commit_db_transaction
39
- log('COMMIT', 'TRANSACTION') { @connection.commit }
47
+ log('COMMIT', 'TRANSACTION') do
48
+ with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
49
+ conn.commit
50
+ end
51
+ end
40
52
  end
41
53
 
42
54
  # Rolls back the current database transaction.
43
55
  # Called from 'rollback_db_transaction' in the AbstractAdapter
44
56
  # @override
45
57
  def exec_rollback_db_transaction
46
- log('ROLLBACK', 'TRANSACTION') { @connection.rollback }
58
+ log('ROLLBACK', 'TRANSACTION') do
59
+ with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
60
+ conn.rollback
61
+ end
62
+ end
47
63
  end
48
64
 
49
65
  ########################## Savepoint Interface ############################
@@ -55,7 +71,11 @@ module ArJdbc
55
71
  # @since 1.3.0
56
72
  # @extension added optional name parameter
57
73
  def create_savepoint(name = current_savepoint_name)
58
- log("SAVEPOINT #{name}", 'TRANSACTION') { @connection.create_savepoint(name) }
74
+ log("SAVEPOINT #{name}", 'TRANSACTION') do
75
+ with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
76
+ conn.create_savepoint(name)
77
+ end
78
+ end
59
79
  end
60
80
 
61
81
  # Transaction rollback to a given (previously created) save-point.
@@ -64,7 +84,11 @@ module ArJdbc
64
84
  # @param name the save-point name
65
85
  # @extension added optional name parameter
66
86
  def exec_rollback_to_savepoint(name = current_savepoint_name)
67
- log("ROLLBACK TO SAVEPOINT #{name}", 'TRANSACTION') { @connection.rollback_savepoint(name) }
87
+ log("ROLLBACK TO SAVEPOINT #{name}", 'TRANSACTION') do
88
+ with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
89
+ conn.rollback_savepoint(name)
90
+ end
91
+ end
68
92
  end
69
93
 
70
94
  # Release a previously created save-point.
@@ -73,7 +97,11 @@ module ArJdbc
73
97
  # @param name the save-point name
74
98
  # @extension added optional name parameter
75
99
  def release_savepoint(name = current_savepoint_name)
76
- log("RELEASE SAVEPOINT #{name}", 'TRANSACTION') { @connection.release_savepoint(name) }
100
+ log("RELEASE SAVEPOINT #{name}", 'TRANSACTION') do
101
+ with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
102
+ conn.release_savepoint(name)
103
+ end
104
+ end
77
105
  end
78
106
 
79
107
  end
Binary file
@@ -12,45 +12,11 @@ module ActiveRecord
12
12
  # specific type.
13
13
  # @see JdbcAdapter#jdbc_column_class
14
14
  class JdbcColumn < Column
15
- # @deprecated attribute writers will be removed in 1.4
16
- attr_writer :limit, :precision # unless ArJdbc::AR42
17
-
18
- def initialize(config, name, *args)
19
- if self.class == JdbcColumn
20
- # NOTE: extending classes do not want this if they do they shall call
21
- call_discovered_column_callbacks(config) if config
22
- default = args.shift
23
- else # for extending classes allow ignoring first argument :
24
- if ! config.nil? && ! config.is_a?(Hash)
25
- default = name; name = config # initialize(name, default, *args)
26
- else
27
- default = args.shift
28
- end
29
- end
30
-
31
- super(name, default, *args)
32
- init_column(name, default, *args)
33
- end
34
-
35
- # Additional column initialization for sub-classes.
36
- def init_column(*args); end
37
15
 
38
16
  # Similar to `ActiveRecord`'s `extract_value_from_default(default)`.
39
17
  # @return default value for a column (possibly extracted from driver value)
40
18
  def default_value(value); value; end
41
19
 
42
- protected
43
-
44
- # @private
45
- def call_discovered_column_callbacks(config)
46
- dialect = (config[:dialect] || config[:driver]).to_s
47
- for matcher, block in self.class.column_types
48
- block.call(config, self) if matcher === dialect
49
- end
50
- end
51
-
52
- public
53
-
54
20
  # Returns the available column types
55
21
  # @return [Hash] of (matcher, block) pairs
56
22
  def self.column_types
@@ -7,7 +7,7 @@ module ArJdbc
7
7
 
8
8
  def jdbc_connection(config)
9
9
  adapter_class = config[:adapter_class] || ::ActiveRecord::ConnectionAdapters::JdbcAdapter
10
- adapter_class.new(nil, logger, nil, config)
10
+ adapter_class.new(config)
11
11
  end
12
12
 
13
13
  def jndi_connection(config); jdbc_connection(config) end
@@ -15,6 +15,8 @@ require 'arjdbc/abstract/database_statements'
15
15
  require 'arjdbc/abstract/statement_cache'
16
16
  require 'arjdbc/abstract/transaction_support'
17
17
 
18
+ require 'arjdbc/mssql/utils'
19
+ require 'arjdbc/mssql/server_version'
18
20
  require 'arjdbc/mssql/column'
19
21
  require 'arjdbc/mssql/types'
20
22
  require 'arjdbc/mssql/quoting'
@@ -34,23 +36,11 @@ module ActiveRecord
34
36
  class MSSQLAdapter < AbstractAdapter
35
37
  ADAPTER_NAME = 'MSSQL'.freeze
36
38
 
37
- MSSQL_VERSION_YEAR = {
38
- 8 => '2000',
39
- 9 => '2005',
40
- 10 => '2008',
41
- 11 => '2012',
42
- 12 => '2014',
43
- 13 => '2016',
44
- 14 => '2017',
45
- 15 => '2019',
46
- 16 => '2022'
47
- }.freeze
48
-
49
- include Jdbc::ConnectionPoolCallbacks
39
+ # include Jdbc::ConnectionPoolCallbacks
50
40
  include ArJdbc::Abstract::Core
51
- include ArJdbc::Abstract::ConnectionManagement
52
- include ArJdbc::Abstract::DatabaseStatements
53
- include ArJdbc::Abstract::StatementCache
41
+ # include ArJdbc::Abstract::ConnectionManagement
42
+ # include ArJdbc::Abstract::DatabaseStatements
43
+ # include ArJdbc::Abstract::StatementCache
54
44
  include ArJdbc::Abstract::TransactionSupport
55
45
 
56
46
  include MSSQL::Quoting
@@ -63,32 +53,50 @@ module ActiveRecord
63
53
 
64
54
  class << self
65
55
  attr_accessor :cs_equality_operator
56
+
57
+ # Returns the (JDBC) connection class to be used for this adapter.
58
+ # The class is defined in the java part
59
+ def jdbc_connection_class
60
+ ::ActiveRecord::ConnectionAdapters::MSSQLJdbcConnection
61
+ end
62
+
63
+ def new_client(conn_params, adapter_instance)
64
+ jdbc_connection_class.new(conn_params, adapter_instance)
65
+ rescue ActiveRecord::JDBCError => error
66
+ if conn_params && conn_params[:database] && error.message.include?(conn_params[:database])
67
+ raise ActiveRecord::NoDatabaseError.db_error(conn_params[:database])
68
+ elsif conn_params && conn_params[:username] && error.message.include?(conn_params[:username])
69
+ raise ActiveRecord::DatabaseConnectionError.username_error(conn_params[:username])
70
+ elsif conn_params && conn_params[:host] && error.message.include?(conn_params[:host])
71
+ raise ActiveRecord::DatabaseConnectionError.hostname_error(conn_params[:host])
72
+ else
73
+ raise ActiveRecord::ConnectionNotEstablished, error.message
74
+ end
75
+ end
66
76
  end
67
77
 
68
- def initialize(connection, logger, _connection_parameters, config = {})
78
+ def initialize(...)
69
79
  # configure_connection happens in super
70
- super(connection, logger, config)
80
+ super
71
81
 
72
- if database_version < '11'
73
- raise "Your #{mssql_product_name} #{mssql_version_year} is too old. This adapter supports #{mssql_product_name} >= 2012."
74
- end
82
+ conn_params = @config.compact
83
+
84
+ @raw_connection = nil
85
+
86
+ @connection_parameters = conn_params
75
87
  end
76
88
 
77
- def self.database_exists?(config)
78
- !!ActiveRecord::Base.sqlserver_connection(config)
79
- rescue ActiveRecord::JDBCError => e
80
- case e.message
81
- when /Cannot open database .* requested by the login/
82
- false
83
- else
84
- raise
85
- end
89
+ # @override
90
+ def active?
91
+ return false unless @raw_connection
92
+
93
+ @raw_connection.active?
86
94
  end
87
95
 
88
- # Returns the (JDBC) connection class to be used for this adapter.
89
- # The class is defined in the java part
90
- def jdbc_connection_class(_spec)
91
- ::ActiveRecord::ConnectionAdapters::MSSQLJdbcConnection
96
+ # @override
97
+ def disconnect!
98
+ super # clear_cache! && reset_transaction
99
+ @raw_connection&.disconnect!
92
100
  end
93
101
 
94
102
  # Returns the (JDBC) `ActiveRecord` column class for this adapter.
@@ -177,10 +185,10 @@ module ActiveRecord
177
185
  !native_database_types[type].nil?
178
186
  end
179
187
 
180
- def clear_cache!
181
- # reload_type_map
182
- super
183
- end
188
+ # def clear_cache!
189
+ # # reload_type_map
190
+ # super
191
+ # end
184
192
 
185
193
  def reset!
186
194
  # execute 'IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION'
@@ -193,12 +201,12 @@ module ActiveRecord
193
201
  tables = tables_with_referential_integrity
194
202
 
195
203
  tables.each do |table_name|
196
- execute "ALTER TABLE #{table_name} NOCHECK CONSTRAINT ALL"
204
+ internal_execute("ALTER TABLE #{table_name} NOCHECK CONSTRAINT ALL")
197
205
  end
198
206
  yield
199
207
  ensure
200
208
  tables.each do |table_name|
201
- execute "ALTER TABLE #{table_name} CHECK CONSTRAINT ALL"
209
+ internal_execute("ALTER TABLE #{table_name} CHECK CONSTRAINT ALL")
202
210
  end
203
211
  end
204
212
 
@@ -222,13 +230,17 @@ module ActiveRecord
222
230
 
223
231
  # Returns the name of the current security context
224
232
  def current_user
225
- @current_user ||= select_value('SELECT CURRENT_USER')
233
+ @current_user ||= internal_execute('SELECT CURRENT_USER').rows.flatten.first
226
234
  end
227
235
 
228
236
  # Returns the default schema (to be used for table resolution)
229
237
  # used for the {#current_user}.
230
238
  def default_schema
231
- @default_schema ||= select_value('SELECT default_schema_name FROM sys.database_principals WHERE name = CURRENT_USER')
239
+ @default_schema ||= internal_execute(default_schema_query).rows.flatten.first
240
+ end
241
+
242
+ def default_schema_query
243
+ 'SELECT default_schema_name FROM sys.database_principals WHERE name = CURRENT_USER'
232
244
  end
233
245
 
234
246
  alias_method :current_schema, :default_schema
@@ -236,7 +248,7 @@ module ActiveRecord
236
248
  # Allows for changing of the default schema.
237
249
  # (to be used during unqualified table name resolution).
238
250
  def default_schema=(default_schema)
239
- execute("ALTER #{current_user} WITH DEFAULT_SCHEMA=#{default_schema}")
251
+ internal_execute("ALTER #{current_user} WITH DEFAULT_SCHEMA=#{default_schema}")
240
252
  @default_schema = nil if defined?(@default_schema)
241
253
  end
242
254
 
@@ -273,7 +285,7 @@ module ActiveRecord
273
285
  end
274
286
 
275
287
  def set_session_transaction_isolation
276
- isolation_level = config[:transaction_isolation]
288
+ isolation_level = @config[:transaction_isolation]
277
289
 
278
290
  self.transaction_isolation = isolation_level if isolation_level
279
291
  end
@@ -282,35 +294,31 @@ module ActiveRecord
282
294
  true
283
295
  end
284
296
 
285
- def mssql_major_version
286
- return @mssql_major_version if defined? @mssql_major_version
287
-
288
- @mssql_major_version = @connection.database_major_version
289
- end
290
-
291
- def mssql_version_year
292
- MSSQL_VERSION_YEAR[mssql_major_version.to_i]
297
+ def get_database_version # :nodoc:
298
+ MSSQLAdapter::Version.new(mssql_version.major, mssql_version.complete)
293
299
  end
294
300
 
295
- def mssql_product_version
296
- return @mssql_product_version if defined? @mssql_product_version
301
+ def check_version # :nodoc:
302
+ return unless database_version.to_s <= mssql_version.min_major
297
303
 
298
- @mssql_product_version = @connection.database_product_version
304
+ raise "Your #{mssql_version.product_name} is too old. #{mssql_version.support_message}"
299
305
  end
300
306
 
301
- def mssql_product_name
302
- return @mssql_product_name if defined? @mssql_product_name
307
+ def mssql_version
308
+ return @mssql_version if defined? @mssql_version
303
309
 
304
- @mssql_product_name = @connection.database_product_name
305
- end
310
+ result = internal_execute("SELECT #{mssql_version_properties.join(', ')}").rows
306
311
 
307
- def get_database_version # :nodoc:
308
- MSSQLAdapter::Version.new(mssql_product_version)
312
+ @mssql_version = MSSQL::Version.new(result.flatten)
309
313
  end
310
314
 
311
- def check_version # :nodoc:
312
- # NOTE: hitting the database from here causes trouble when adapter
313
- # uses JNDI or Data Source setup.
315
+ def mssql_version_properties
316
+ [
317
+ "SERVERPROPERTY('productVersion')",
318
+ "SERVERPROPERTY('productMajorVersion')",
319
+ "SERVERPROPERTY('productLevel')",
320
+ "SERVERPROPERTY('edition')"
321
+ ]
314
322
  end
315
323
 
316
324
  def tables_with_referential_integrity
@@ -404,6 +412,8 @@ module ActiveRecord
404
412
  register_class_with_precision map, %r{\Adatetime2\(\d+\)}i, MSSQL::Type::DateTime2
405
413
  # map.register_type 'datetime2(7)', MSSQL::Type::DateTime2.new
406
414
 
415
+ # map.register_type %r(^json)i, Type::Json.new
416
+
407
417
  # TODO: we should have identity separated from the sql_type
408
418
  # let's say in another attribute (this will help to pass more AR tests),
409
419
  # also we add collation attribute per column.
@@ -440,6 +450,20 @@ module ActiveRecord
440
450
 
441
451
  private
442
452
 
453
+ def connect
454
+ @raw_connection = self.class.new_client(@connection_parameters, self)
455
+ rescue ConnectionNotEstablished => ex
456
+ raise ex.set_pool(@pool)
457
+ end
458
+
459
+ def reconnect
460
+ @raw_connection&.disconnect!
461
+
462
+ @raw_connection = nil
463
+
464
+ connect
465
+ end
466
+
443
467
  def type_map
444
468
  TYPE_MAP
445
469
  end
@@ -454,6 +478,8 @@ module ActiveRecord
454
478
  LockTimeout.new(message, sql: sql, binds: binds)
455
479
  when /The .* statement conflicted with the FOREIGN KEY constraint/
456
480
  InvalidForeignKey.new(message, sql: sql, binds: binds)
481
+ when /Could not drop object .* because it is referenced by a FOREIGN KEY constraint/
482
+ StatementInvalid.new(message, sql: sql, binds: binds)
457
483
  when /The .* statement conflicted with the REFERENCE constraint/
458
484
  InvalidForeignKey.new(message, sql: sql, binds: binds)
459
485
  when /(String or binary data would be truncated)/i
@@ -462,6 +488,8 @@ module ActiveRecord
462
488
  NotNullViolation.new(message, sql: sql, binds: binds)
463
489
  when /Arithmetic overflow error converting expression/
464
490
  RangeError.new(message, sql: sql, binds: binds)
491
+ when /Snapshot isolation transaction aborted due to update conflict. You cannot use snapshot isolation/
492
+ StatementInvalid.new(message, sql: sql, binds: binds)
465
493
  else
466
494
  super
467
495
  end
@@ -472,13 +500,11 @@ module ActiveRecord
472
500
  # NOTE: This is ready, all implemented in the java part of adapter,
473
501
  # it uses MSSQLColumn, SqlTypeMetadata, etc.
474
502
  def column_definitions(table_name)
475
- log('JDBC: GETCOLUMNS', 'SCHEMA') { @connection.columns(table_name, nil, default_schema) }
476
- rescue => e
503
+ log('JDBC: GETCOLUMNS', 'SCHEMA') { valid_raw_connection.columns(table_name, nil, default_schema) }
477
504
  # raise translate_exception_class(e, nil)
478
505
  # FIXME: this breaks one arjdbc test but fixes activerecord tests
479
506
  # (table name alias). Also it behaves similarly to the CRuby adapter
480
507
  # which returns an empty array too. (postgres throws a exception)
481
- []
482
508
  end
483
509
 
484
510
  def arel_visitor # :nodoc:
@@ -491,16 +517,3 @@ module ActiveRecord
491
517
  end
492
518
  end
493
519
  end
494
-
495
- # FIXME: this is not used by the adapter anymore, it is here because
496
- # it is a dependency of old tests that needs to be reviewed
497
- module ArJdbc
498
- module MSSQL
499
- require 'arjdbc/mssql/utils'
500
- require 'arjdbc/mssql/limit_helpers'
501
- require 'arjdbc/mssql/lock_methods'
502
-
503
- include LimitHelpers
504
- include Utils
505
- end
506
- end
@@ -29,6 +29,7 @@ module ActiveRecord
29
29
  def identity?
30
30
  sql_type.downcase.include? 'identity'
31
31
  end
32
+ alias_method :auto_incremented_by_db?, :identity?
32
33
 
33
34
  def ==(other)
34
35
  other.is_a?(MSSQLColumn) &&