activerecord-jdbcsqlserver-adapter 50.1.0 → 51.0.0

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -1
  3. data/.travis.yml +4 -5
  4. data/BACKERS.md +32 -0
  5. data/CHANGELOG.md +21 -87
  6. data/README.md +2 -3
  7. data/VERSION +1 -1
  8. data/activerecord-jdbcsqlserver-adapter.gemspec +3 -2
  9. data/appveyor.yml +1 -1
  10. data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +3 -1
  11. data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +3 -1
  12. data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +55 -0
  13. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +4 -2
  14. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +5 -3
  15. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +41 -18
  16. data/lib/active_record/connection_adapters/sqlserver/jdbc_overrides.rb +2 -12
  17. data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +22 -0
  18. data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +21 -0
  19. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +72 -52
  20. data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +15 -7
  21. data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +0 -4
  22. data/lib/active_record/connection_adapters/sqlserver/type/data.rb +5 -0
  23. data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +3 -6
  24. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +27 -10
  25. data/lib/active_record/tasks/sqlserver_database_tasks.rb +2 -2
  26. data/lib/activerecord-jdbcsqlserver-adapter.rb +1 -1
  27. data/lib/arel/visitors/sqlserver.rb +16 -3
  28. data/test/bin/setup.sh +19 -0
  29. data/test/cases/adapter_test_sqlserver.rb +17 -20
  30. data/test/cases/coerced_tests.rb +117 -11
  31. data/test/cases/column_test_sqlserver.rb +1 -1
  32. data/test/cases/helper_sqlserver.rb +6 -1
  33. data/test/cases/pessimistic_locking_test_sqlserver.rb +28 -11
  34. data/test/cases/schema_dumper_test_sqlserver.rb +10 -10
  35. data/test/cases/specific_schema_test_sqlserver.rb +0 -6
  36. data/test/cases/trigger_test_sqlserver.rb +31 -0
  37. data/test/config.yml +2 -2
  38. data/test/models/sqlserver/trigger.rb +7 -0
  39. data/test/models/sqlserver/trigger_history.rb +3 -0
  40. data/test/schema/sqlserver_specific_schema.rb +39 -5
  41. data/test/support/sql_counter_sqlserver.rb +3 -2
  42. metadata +23 -16
  43. data/RAILS5-TODO.md +0 -5
  44. data/lib/jdbc_mssql_driver_loader.rb +0 -22
  45. data/test/models/sqlserver/dot_table_name.rb +0 -3
@@ -1,3 +1,5 @@
1
+ require 'pry'
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
5
  module SQLServer
@@ -90,18 +92,6 @@ module ActiveRecord
90
92
  @connection.configure_connection
91
93
  end
92
94
 
93
- # @Overwrite
94
- # Had some special logic and skipped using gem's internal query methods
95
- def select_rows(sql, name = nil, binds = [])
96
-
97
- # In some cases the limit is converted to a `TOP(1)` but the bind parameter is still in the array
98
- if !binds.empty? && sql.include?('TOP(1)')
99
- binds = binds.delete_if {|b| b.name == 'LIMIT' }
100
- end
101
-
102
- exec_query(sql, name, binds).rows
103
- end
104
-
105
95
  # Have to reset this because the default arjdbc functionality is to return false unless a level is passed in
106
96
  def supports_transaction_isolation?
107
97
  true
@@ -16,6 +16,20 @@ module ActiveRecord
16
16
  end
17
17
  end
18
18
 
19
+ def add_column_options!(sql, options)
20
+ sql << " DEFAULT #{quote_default_expression(options[:default], options[:column])}" if options_include_default?(options)
21
+ if options[:null] == false
22
+ sql << " NOT NULL"
23
+ end
24
+ if options[:is_identity] == true
25
+ sql << " IDENTITY(1,1)"
26
+ end
27
+ if options[:primary_key] == true
28
+ sql << " PRIMARY KEY"
29
+ end
30
+ sql
31
+ end
32
+
19
33
  def action_sql(action, dependency)
20
34
  case dependency
21
35
  when :restrict
@@ -28,6 +42,14 @@ module ActiveRecord
28
42
  end
29
43
  end
30
44
 
45
+ def options_include_default?(options)
46
+ super || options_primary_key_with_nil_default?(options)
47
+ end
48
+
49
+ def options_primary_key_with_nil_default?(options)
50
+ options[:primary_key] && options.include?(:default) && options[:default].nil?
51
+ end
52
+
31
53
  end
32
54
  end
33
55
  end
@@ -3,13 +3,34 @@ module ActiveRecord
3
3
  module SQLServer
4
4
  module SchemaDumper
5
5
 
6
+ SQLSEVER_NO_LIMIT_TYPES = [
7
+ 'text',
8
+ 'ntext',
9
+ 'varchar(max)',
10
+ 'nvarchar(max)',
11
+ 'varbinary(max)'
12
+ ].freeze
13
+
6
14
  private
7
15
 
16
+ def explicit_primary_key_default?(column)
17
+ column.is_primary? && !column.is_identity?
18
+ end
19
+
20
+ def schema_limit(column)
21
+ return if SQLSEVER_NO_LIMIT_TYPES.include?(column.sql_type)
22
+ super
23
+ end
24
+
8
25
  def schema_collation(column)
9
26
  return unless column.collation
10
27
  column.collation if column.collation != collation
11
28
  end
12
29
 
30
+ def default_primary_key?(column)
31
+ super && column.is_primary? && column.is_identity?
32
+ end
33
+
13
34
  end
14
35
  end
15
36
  end
@@ -7,35 +7,6 @@ module ActiveRecord
7
7
  @native_database_types ||= initialize_native_database_types.freeze
8
8
  end
9
9
 
10
- def tables(name = nil)
11
- ActiveSupport::Deprecation.warn 'Passing arguments to #tables is deprecated without replacement.' if name
12
- tables_sql('BASE TABLE')
13
- end
14
-
15
- def table_exists?(table_name)
16
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
17
- #table_exists? currently checks both tables and views.
18
- This behavior is deprecated and will be changed with Rails 5.1 to only check tables.
19
- Use #data_source_exists? instead.
20
- MSG
21
- data_source_exists?(table_name)
22
- end
23
-
24
- def data_source_exists?(table_name)
25
- return false if table_name.blank?
26
- unquoted_table_name = SQLServer::Utils.extract_identifiers(table_name).object
27
- super(unquoted_table_name)
28
- end
29
-
30
- def views
31
- tables_sql('VIEW')
32
- end
33
-
34
- def view_exists?(table_name)
35
- identifier = SQLServer::Utils.extract_identifiers(table_name)
36
- super(identifier.object)
37
- end
38
-
39
10
  def create_table(table_name, comment: nil, **options)
40
11
  res = super
41
12
  clear_cache!
@@ -43,7 +14,18 @@ module ActiveRecord
43
14
  end
44
15
 
45
16
  def drop_table(table_name, options = {})
46
- if options[:if_exists] && @version_year != 2016
17
+ # Mimic CASCADE option as best we can.
18
+ if options[:force] == :cascade
19
+ execute_procedure(:sp_fkeys, pktable_name: table_name).each do |fkdata|
20
+ fktable = fkdata['FKTABLE_NAME']
21
+ fkcolmn = fkdata['FKCOLUMN_NAME']
22
+ pktable = fkdata['PKTABLE_NAME']
23
+ pkcolmn = fkdata['PKCOLUMN_NAME']
24
+ remove_foreign_key fktable, name: fkdata['FK_NAME']
25
+ do_execute "DELETE FROM #{quote_table_name(fktable)} WHERE #{quote_column_name(fkcolmn)} IN ( SELECT #{quote_column_name(pkcolmn)} FROM #{quote_table_name(pktable)} )"
26
+ end
27
+ end
28
+ if options[:if_exists] && @version_year < 2016
47
29
  execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = #{quote(table_name)}) DROP TABLE #{quote_table_name(table_name)}"
48
30
  else
49
31
  super
@@ -103,10 +85,36 @@ module ActiveRecord
103
85
  end
104
86
 
105
87
  def primary_keys(table_name)
106
- primaries = schema_cache.columns(table_name).select(&:is_primary?).map(&:name)
88
+ primaries = primary_keys_select(table_name)
107
89
  primaries.present? ? primaries : identity_columns(table_name).map(&:name)
108
90
  end
109
91
 
92
+ def primary_keys_select(table_name)
93
+ identifier = database_prefix_identifier(table_name)
94
+ database = identifier.fully_qualified_database_quoted
95
+ sql = %{
96
+ SELECT KCU.COLUMN_NAME AS [name]
97
+ FROM #{database}.INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU
98
+ LEFT OUTER JOIN #{database}.INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC
99
+ ON KCU.CONSTRAINT_NAME = TC.CONSTRAINT_NAME
100
+ AND KCU.CONSTRAINT_NAME = TC.CONSTRAINT_NAME
101
+ AND KCU.CONSTRAINT_CATALOG = TC.CONSTRAINT_CATALOG
102
+ AND KCU.CONSTRAINT_SCHEMA = TC.CONSTRAINT_SCHEMA
103
+ AND TC.CONSTRAINT_TYPE = N'PRIMARY KEY'
104
+ WHERE KCU.TABLE_NAME = #{prepared_statements ? COLUMN_DEFINITION_BIND_STRING_0 : quote(identifier.object)}
105
+ AND KCU.TABLE_SCHEMA = #{identifier.schema.blank? ? 'schema_name()' : (prepared_statements ? COLUMN_DEFINITION_BIND_STRING_1 : quote(identifier.schema))}
106
+ AND TC.CONSTRAINT_TYPE = N'PRIMARY KEY'
107
+ ORDER BY KCU.ORDINAL_POSITION ASC
108
+ }.gsub(/[[:space:]]/, ' ')
109
+ binds = []
110
+ if prepared_statements
111
+ nv128 = SQLServer::Type::UnicodeVarchar.new limit: 128
112
+ binds << Relation::QueryAttribute.new('TABLE_NAME', identifier.object, nv128)
113
+ binds << Relation::QueryAttribute.new('TABLE_SCHEMA', identifier.schema, nv128) unless identifier.schema.blank?
114
+ end
115
+ sp_executesql(sql, 'SCHEMA', binds).map { |r| r['name'] }
116
+ end
117
+
110
118
  def rename_table(table_name, new_name)
111
119
  do_execute "EXEC sp_rename '#{table_name}', '#{new_name}'"
112
120
  rename_table_indexes(table_name, new_name)
@@ -130,9 +138,11 @@ module ActiveRecord
130
138
  remove_indexes(table_name, column_name)
131
139
  end
132
140
  sql_commands << "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_expression(options[:default], column_object)} WHERE #{quote_column_name(column_name)} IS NULL" if !options[:null].nil? && options[:null] == false && !options[:default].nil?
133
- sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
134
- sql_commands[-1] << ' NOT NULL' if !options[:null].nil? && options[:null] == false
135
- if options_include_default?(options)
141
+ sql_commands << "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])}"
142
+ sql_commands.last << ' NOT NULL' if !options[:null].nil? && options[:null] == false
143
+ if options.key?(:default) && default_constraint_name(table_name, column_name).present?
144
+ change_column_default(table_name, column_name, options[:default])
145
+ elsif options_include_default?(options)
136
146
  sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{quote_default_expression(options[:default], column_object)} FOR #{quote_column_name(column_name)}"
137
147
  end
138
148
  # Add any removed indexes back
@@ -140,6 +150,7 @@ module ActiveRecord
140
150
  sql_commands << "CREATE INDEX #{quote_table_name(index.name)} ON #{quote_table_name(table_name)} (#{index.columns.map { |c| quote_column_name(c) }.join(', ')})"
141
151
  end
142
152
  sql_commands.each { |c| do_execute(c) }
153
+ clear_cache!
143
154
  end
144
155
 
145
156
  def change_column_default(table_name, column_name, default_or_changes)
@@ -194,7 +205,7 @@ module ActiveRecord
194
205
  end
195
206
  end
196
207
 
197
- def type_to_sql(type, limit = nil, precision = nil, scale = nil)
208
+ def type_to_sql(type, limit: nil, precision: nil, scale: nil, **)
198
209
  type_limitable = %w(string integer float char nchar varchar nvarchar).include?(type.to_s)
199
210
  limit = nil unless type_limitable
200
211
  case type.to_s
@@ -240,19 +251,40 @@ module ActiveRecord
240
251
  if !allow_null.nil? && allow_null == false && !default.nil?
241
252
  do_execute("UPDATE #{table_id} SET #{column_id}=#{quote(default)} WHERE #{column_id} IS NULL")
242
253
  end
243
- sql = "ALTER TABLE #{table_id} ALTER COLUMN #{column_id} #{type_to_sql column.type, column.limit, column.precision, column.scale}"
254
+ sql = "ALTER TABLE #{table_id} ALTER COLUMN #{column_id} #{type_to_sql column.type, limit: column.limit, precision: column.precision, scale: column.scale}"
244
255
  sql << ' NOT NULL' if !allow_null.nil? && allow_null == false
245
256
  do_execute sql
246
257
  end
247
258
 
259
+ private
248
260
 
249
- protected
261
+ def data_source_sql(name = nil, type: nil)
262
+ scope = quoted_scope name, type: type
263
+ table_name = lowercase_schema_reflection_sql 'TABLE_NAME'
264
+ sql = "SELECT #{table_name}"
265
+ sql << ' FROM INFORMATION_SCHEMA.TABLES'
266
+ sql << ' WHERE TABLE_CATALOG = DB_NAME()'
267
+ sql << " AND TABLE_SCHEMA = #{quote(scope[:schema])}"
268
+ sql << " AND TABLE_NAME = #{quote(scope[:name])}" if scope[:name]
269
+ sql << " AND TABLE_TYPE = #{quote(scope[:type])}" if scope[:type]
270
+ sql << " ORDER BY #{table_name}"
271
+ sql
272
+ end
273
+
274
+ def quoted_scope(name = nil, type: nil)
275
+ identifier = SQLServer::Utils.extract_identifiers(name)
276
+ {}.tap do |scope|
277
+ scope[:schema] = identifier.schema || 'dbo'
278
+ scope[:name] = identifier.object if identifier.object
279
+ scope[:type] = type if type
280
+ end
281
+ end
250
282
 
251
283
  # === SQLServer Specific ======================================== #
252
284
 
253
285
  def initialize_native_database_types
254
286
  {
255
- primary_key: 'int NOT NULL IDENTITY(1,1) PRIMARY KEY',
287
+ primary_key: 'bigint NOT NULL IDENTITY(1,1) PRIMARY KEY',
256
288
  primary_key_nonclustered: 'int NOT NULL IDENTITY(1,1) PRIMARY KEY NONCLUSTERED',
257
289
  integer: { name: 'int', limit: 4 },
258
290
  bigint: { name: 'bigint' },
@@ -286,21 +318,11 @@ module ActiveRecord
286
318
  }
287
319
  end
288
320
 
289
- def tables_sql(type)
290
- tn = lowercase_schema_reflection_sql 'TABLE_NAME'
291
- sql = "SELECT #{tn} FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = '#{type}' ORDER BY TABLE_NAME"
292
- select_values sql, 'SCHEMA'
293
- end
294
-
295
321
  COLUMN_DEFINITION_BIND_STRING_0 = defined?(JRUBY_VERSION) ? '?' : '@0'
296
322
  COLUMN_DEFINITION_BIND_STRING_1 = defined?(JRUBY_VERSION) ? '?' : '@1'
297
323
 
298
324
  def column_definitions(table_name)
299
- identifier = if database_prefix_remote_server?
300
- SQLServer::Utils.extract_identifiers("#{database_prefix}#{table_name}")
301
- else
302
- SQLServer::Utils.extract_identifiers(table_name)
303
- end
325
+ identifier = database_prefix_identifier(table_name)
304
326
  database = identifier.fully_qualified_database_quoted
305
327
  view_exists = view_exists?(table_name)
306
328
  view_tblnm = view_table_name(table_name) if view_exists
@@ -482,7 +504,7 @@ module ActiveRecord
482
504
  @view_information ||= {}
483
505
  @view_information[table_name] ||= begin
484
506
  identifier = SQLServer::Utils.extract_identifiers(table_name)
485
- view_info = select_one "SELECT * FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = '#{identifier.object}'", 'SCHEMA'
507
+ view_info = select_one "SELECT * FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = #{quote(identifier.object)}", 'SCHEMA'
486
508
  if view_info
487
509
  view_info = view_info.with_indifferent_access
488
510
  if view_info[:VIEW_DEFINITION].blank? || view_info[:VIEW_DEFINITION].length == 4000
@@ -505,8 +527,6 @@ module ActiveRecord
505
527
  match_data ? match_data[1] : column_name
506
528
  end
507
529
 
508
- private
509
-
510
530
  def create_table_definition(*args)
511
531
  SQLServer::TableDefinition.new(*args)
512
532
  end
@@ -5,10 +5,13 @@ module ActiveRecord
5
5
  module ColumnMethods
6
6
 
7
7
  def primary_key(name, type = :primary_key, **options)
8
- return super unless type == :uuid
9
- options[:default] = options.fetch(:default, 'NEWID()')
10
- options[:primary_key] = true
11
- column name, type, options
8
+ if [:integer, :bigint].include?(type)
9
+ options[:is_identity] = true unless options.key?(:default)
10
+ elsif type == :uuid
11
+ options[:default] = options.fetch(:default, 'NEWID()')
12
+ options[:primary_key] = true
13
+ end
14
+ super
12
15
  end
13
16
 
14
17
  def primary_key_nonclustered(*args, **options)
@@ -98,9 +101,14 @@ module ActiveRecord
98
101
  class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
99
102
  include ColumnMethods
100
103
 
101
- def new_column_definition(name, type, options)
102
- type = :datetime2 if type == :datetime && options[:precision]
103
- super name, type, options
104
+ def new_column_definition(name, type, **options)
105
+ case type
106
+ when :datetime
107
+ type = :datetime2 if options[:precision]
108
+ when :primary_key
109
+ options[:is_identity] = true
110
+ end
111
+ super
104
112
  end
105
113
  end
106
114
 
@@ -4,10 +4,6 @@ module ActiveRecord
4
4
  module Type
5
5
  class BigInteger < Integer
6
6
 
7
- def type
8
- :bigint
9
- end
10
-
11
7
  def sqlserver_type
12
8
  'bigint'.freeze
13
9
  end
@@ -23,6 +23,11 @@ module ActiveRecord
23
23
  @value.inspect
24
24
  end
25
25
 
26
+ def eql?(other)
27
+ self.class == other.class && self.value == other.value
28
+ end
29
+ alias :== :eql?
30
+
26
31
  end
27
32
  end
28
33
  end
@@ -51,7 +51,9 @@ module ActiveRecord
51
51
  private
52
52
 
53
53
  def fast_string_to_time(string)
54
- fast_string_to_time_zone.strptime(string, fast_string_to_time_format).time
54
+ time = ActiveSupport::TimeZone['UTC'].strptime(string, fast_string_to_time_format)
55
+ new_time(time.year, time.month, time.day, time.hour,
56
+ time.min, time.sec, Rational(time.nsec, 1_000))
55
57
  rescue ArgumentError
56
58
  super
57
59
  end
@@ -59,11 +61,6 @@ module ActiveRecord
59
61
  def fast_string_to_time_format
60
62
  "#{::Time::DATE_FORMATS[:_sqlserver_datetime]}.%N".freeze
61
63
  end
62
-
63
- def fast_string_to_time_zone
64
- ::Time.zone || ActiveSupport::TimeZone['UTC']
65
- end
66
-
67
64
  end
68
65
  end
69
66
  end
@@ -3,6 +3,7 @@ require 'active_record'
3
3
  require 'arel_sqlserver'
4
4
  require 'active_record/connection_adapters/abstract_adapter'
5
5
  require 'active_record/connection_adapters/sqlserver/core_ext/active_record'
6
+ require 'active_record/connection_adapters/sqlserver/core_ext/calculations'
6
7
  require 'active_record/connection_adapters/sqlserver/core_ext/explain' unless defined? JRUBY_VERSION
7
8
  require 'active_record/connection_adapters/sqlserver/core_ext/explain_subscriber'
8
9
  require 'active_record/connection_adapters/sqlserver/core_ext/attribute_methods'
@@ -56,11 +57,13 @@ module ActiveRecord
56
57
 
57
58
  cattr_accessor :cs_equality_operator, instance_accessor: false
58
59
  cattr_accessor :use_output_inserted, instance_accessor: false
60
+ cattr_accessor :exclude_output_inserted_table_names, instance_accessor: false
59
61
  cattr_accessor :showplan_option, instance_accessor: false
60
62
  cattr_accessor :lowercase_schema_reflection
61
63
 
62
64
  self.cs_equality_operator = 'COLLATE Latin1_General_CS_AS_WS'
63
65
  self.use_output_inserted = true
66
+ self.exclude_output_inserted_table_names = Concurrent::Map.new { false }
64
67
 
65
68
  def initialize(connection, logger = nil, config = {})
66
69
  super(connection, logger, config)
@@ -85,14 +88,6 @@ module ActiveRecord
85
88
  SQLServer::SchemaCreation.new self
86
89
  end
87
90
 
88
- def supports_migrations?
89
- true
90
- end
91
-
92
- def supports_primary_key?
93
- true
94
- end
95
-
96
91
  def supports_ddl_transactions?
97
92
  true
98
93
  end
@@ -159,10 +154,10 @@ module ActiveRecord
159
154
 
160
155
  def disable_referential_integrity
161
156
  tables = tables_with_referential_integrity
162
- tables.each { |t| do_execute "ALTER TABLE #{t} NOCHECK CONSTRAINT ALL" }
157
+ tables.each { |t| do_execute "ALTER TABLE #{quote_table_name(t)} NOCHECK CONSTRAINT ALL" }
163
158
  yield
164
159
  ensure
165
- tables.each { |t| do_execute "ALTER TABLE #{t} CHECK CONSTRAINT ALL" }
160
+ tables.each { |t| do_execute "ALTER TABLE #{quote_table_name(t)} CHECK CONSTRAINT ALL" }
166
161
  end
167
162
 
168
163
  # === Abstract Adapter (Connection Management) ================== #
@@ -242,6 +237,14 @@ module ActiveRecord
242
237
  @connection_options[:database_prefix]
243
238
  end
244
239
 
240
+ def database_prefix_identifier(name)
241
+ if database_prefix_remote_server?
242
+ SQLServer::Utils.extract_identifiers("#{database_prefix}#{name}")
243
+ else
244
+ SQLServer::Utils.extract_identifiers(name)
245
+ end
246
+ end
247
+
245
248
  def version
246
249
  self.class::VERSION
247
250
  end
@@ -339,6 +342,20 @@ module ActiveRecord
339
342
  NoDatabaseError.new(message)
340
343
  when /data would be truncated/
341
344
  ValueTooLong.new(message)
345
+ when /Column '(.*)' is not the same data type as referencing column '(.*)' in foreign key/
346
+ pk_id, fk_id = SQLServer::Utils.extract_identifiers($1), SQLServer::Utils.extract_identifiers($2)
347
+ MismatchedForeignKey.new(
348
+ self,
349
+ message: message,
350
+ table: fk_id.schema,
351
+ foreign_key: fk_id.object,
352
+ target_table: pk_id.schema,
353
+ primary_key: pk_id.object
354
+ )
355
+ when /Cannot insert the value NULL into column.*does not allow nulls/
356
+ NotNullViolation.new(message)
357
+ when /Arithmetic overflow error/
358
+ RangeError.new(message)
342
359
  else
343
360
  super
344
361
  end