activerecord-jdbc-alt-adapter 61.2.0-java → 70.0.0.rc2-java

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 (78) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/main.yml +118 -0
  3. data/.github/workflows/ruby.yml +273 -0
  4. data/.gitignore +1 -0
  5. data/.travis.yml +3 -4
  6. data/Gemfile +9 -7
  7. data/README.md +5 -1
  8. data/Rakefile +1 -1
  9. data/activerecord-jdbc-adapter.gemspec +2 -2
  10. data/activerecord-jdbc-alt-adapter.gemspec +2 -2
  11. data/lib/arel/visitors/compat.rb +5 -33
  12. data/lib/arel/visitors/h2.rb +1 -13
  13. data/lib/arel/visitors/hsqldb.rb +1 -21
  14. data/lib/arel/visitors/sql_server.rb +2 -103
  15. data/lib/arjdbc/abstract/core.rb +8 -9
  16. data/lib/arjdbc/abstract/database_statements.rb +4 -4
  17. data/lib/arjdbc/discover.rb +0 -67
  18. data/lib/arjdbc/hsqldb/adapter.rb +2 -2
  19. data/lib/arjdbc/jdbc/adapter.rb +3 -3
  20. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  21. data/lib/arjdbc/jdbc/adapter_require.rb +3 -1
  22. data/lib/arjdbc/jdbc/column.rb +1 -26
  23. data/lib/arjdbc/jdbc/type_cast.rb +2 -2
  24. data/lib/arjdbc/jdbc.rb +0 -7
  25. data/lib/arjdbc/mssql/adapter.rb +138 -108
  26. data/lib/arjdbc/mssql/quoting.rb +26 -27
  27. data/lib/arjdbc/mssql/schema_creation.rb +1 -1
  28. data/lib/arjdbc/mssql/schema_definitions.rb +32 -17
  29. data/lib/arjdbc/mssql/schema_dumper.rb +13 -1
  30. data/lib/arjdbc/mssql/schema_statements.rb +61 -36
  31. data/lib/arjdbc/mssql/transaction.rb +2 -2
  32. data/lib/arjdbc/mssql/types/date_and_time_types.rb +6 -6
  33. data/lib/arjdbc/mssql/types/numeric_types.rb +2 -2
  34. data/lib/arjdbc/mssql.rb +1 -1
  35. data/lib/arjdbc/mysql/adapter.rb +2 -1
  36. data/lib/arjdbc/oracle/adapter.rb +4 -23
  37. data/lib/arjdbc/postgresql/adapter.rb +152 -4
  38. data/lib/arjdbc/postgresql/oid_types.rb +142 -106
  39. data/lib/arjdbc/sqlite3/adapter.rb +132 -88
  40. data/lib/arjdbc/tasks/database_tasks.rb +0 -12
  41. data/lib/arjdbc/util/serialized_attributes.rb +0 -22
  42. data/lib/arjdbc/util/table_copier.rb +2 -1
  43. data/lib/arjdbc/version.rb +1 -1
  44. data/rakelib/02-test.rake +3 -18
  45. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +17 -2
  46. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +5 -0
  47. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +33 -0
  48. metadata +9 -40
  49. data/lib/active_record/connection_adapters/as400_adapter.rb +0 -2
  50. data/lib/active_record/connection_adapters/db2_adapter.rb +0 -1
  51. data/lib/active_record/connection_adapters/derby_adapter.rb +0 -1
  52. data/lib/active_record/connection_adapters/informix_adapter.rb +0 -1
  53. data/lib/arel/visitors/db2.rb +0 -137
  54. data/lib/arel/visitors/derby.rb +0 -112
  55. data/lib/arel/visitors/firebird.rb +0 -79
  56. data/lib/arjdbc/db2/adapter.rb +0 -808
  57. data/lib/arjdbc/db2/as400.rb +0 -142
  58. data/lib/arjdbc/db2/column.rb +0 -131
  59. data/lib/arjdbc/db2/connection_methods.rb +0 -48
  60. data/lib/arjdbc/db2.rb +0 -4
  61. data/lib/arjdbc/derby/active_record_patch.rb +0 -13
  62. data/lib/arjdbc/derby/adapter.rb +0 -521
  63. data/lib/arjdbc/derby/connection_methods.rb +0 -20
  64. data/lib/arjdbc/derby/schema_creation.rb +0 -15
  65. data/lib/arjdbc/derby.rb +0 -3
  66. data/lib/arjdbc/firebird/adapter.rb +0 -413
  67. data/lib/arjdbc/firebird/connection_methods.rb +0 -23
  68. data/lib/arjdbc/firebird.rb +0 -4
  69. data/lib/arjdbc/informix/adapter.rb +0 -139
  70. data/lib/arjdbc/informix/connection_methods.rb +0 -9
  71. data/lib/arjdbc/sybase/adapter.rb +0 -47
  72. data/lib/arjdbc/sybase.rb +0 -2
  73. data/lib/arjdbc/tasks/db2_database_tasks.rb +0 -104
  74. data/lib/arjdbc/tasks/derby_database_tasks.rb +0 -95
  75. data/src/java/arjdbc/derby/DerbyModule.java +0 -178
  76. data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +0 -152
  77. data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +0 -174
  78. data/src/java/arjdbc/informix/InformixRubyJdbcConnection.java +0 -75
@@ -35,14 +35,15 @@ module ActiveRecord
35
35
  ADAPTER_NAME = 'MSSQL'.freeze
36
36
 
37
37
  MSSQL_VERSION_YEAR = {
38
- 8 => '2000',
39
- 9 => '2005',
38
+ 8 => '2000',
39
+ 9 => '2005',
40
40
  10 => '2008',
41
41
  11 => '2012',
42
42
  12 => '2014',
43
43
  13 => '2016',
44
44
  14 => '2017',
45
- 15 => '2019'
45
+ 15 => '2019',
46
+ 16 => '2022'
46
47
  }.freeze
47
48
 
48
49
  include Jdbc::ConnectionPoolCallbacks
@@ -143,13 +144,32 @@ module ActiveRecord
143
144
  def supports_insert_on_conflict?
144
145
  false
145
146
  end
147
+
146
148
  alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
147
149
  alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
148
150
  alias supports_insert_conflict_target? supports_insert_on_conflict?
149
151
 
152
+ def supports_insert_returning?
153
+ true
154
+ end
155
+
150
156
  def build_insert_sql(insert) # :nodoc:
151
157
  # TODO: hope we can implement an upsert like feature
152
- "INSERT #{insert.into} #{insert.values_list}"
158
+ # "INSERT #{insert.into} #{insert.values_list}"
159
+ sql = +"INSERT #{insert.into}"
160
+
161
+ if returning = insert.send(:insert_all).returning
162
+ returning_sql = if returning.is_a?(String)
163
+ returning
164
+ else
165
+ returning.map { |column| "INSERTED.#{quote_column_name(column)}" }.join(', ')
166
+ end
167
+
168
+ sql << " OUTPUT #{returning_sql}"
169
+ end
170
+
171
+ sql << " #{insert.values_list}"
172
+ sql
153
173
  end
154
174
 
155
175
  # Overrides abstract method which always returns false
@@ -158,7 +178,7 @@ module ActiveRecord
158
178
  end
159
179
 
160
180
  def clear_cache!
161
- reload_type_map
181
+ # reload_type_map
162
182
  super
163
183
  end
164
184
 
@@ -309,8 +329,121 @@ module ActiveRecord
309
329
  end
310
330
  end
311
331
 
332
+ class << self
333
+ private
334
+
335
+ # This method is called indirectly by the abstract method
336
+ # 'fetch_type_metadata' which then it is called by the java part when
337
+ # calculating a table's columns.
338
+ def initialize_type_map(map)
339
+ # Build the type mapping from SQL Server to ActiveRecord
340
+
341
+ # Integer types.
342
+ map.register_type 'int', MSSQL::Type::Integer.new(limit: 4)
343
+ map.register_type 'tinyint', MSSQL::Type::TinyInteger.new(limit: 1)
344
+ map.register_type 'smallint', MSSQL::Type::SmallInteger.new(limit: 2)
345
+ map.register_type 'bigint', MSSQL::Type::BigInteger.new(limit: 8)
346
+
347
+ # Exact Numeric types.
348
+ map.register_type %r{\Adecimal}i do |sql_type|
349
+ scale = extract_scale(sql_type)
350
+ precision = extract_precision(sql_type)
351
+ if scale == 0
352
+ MSSQL::Type::DecimalWithoutScale.new(precision: precision)
353
+ else
354
+ MSSQL::Type::Decimal.new(precision: precision, scale: scale)
355
+ end
356
+ end
357
+ map.register_type %r{\Amoney\z}i, MSSQL::Type::Money.new
358
+ map.register_type %r{\Asmallmoney\z}i, MSSQL::Type::SmallMoney.new
359
+
360
+ # Approximate Numeric types.
361
+ map.register_type %r{\Afloat\z}i, MSSQL::Type::Float.new
362
+ map.register_type %r{\Areal\z}i, MSSQL::Type::Real.new
363
+
364
+ # Character strings CHAR and VARCHAR (it can become Unicode UTF-8)
365
+ map.register_type 'varchar(max)', MSSQL::Type::VarcharMax.new
366
+ map.register_type %r{\Avarchar\(\d+\)} do |sql_type|
367
+ limit = extract_limit(sql_type)
368
+ MSSQL::Type::Varchar.new(limit: limit)
369
+ end
370
+ map.register_type %r{\Achar\(\d+\)} do |sql_type|
371
+ limit = extract_limit(sql_type)
372
+ MSSQL::Type::Char.new(limit: limit)
373
+ end
374
+
375
+ # Character strings NCHAR and NVARCHAR (by default Unicode UTF-16)
376
+ map.register_type %r{\Anvarchar\(\d+\)} do |sql_type|
377
+ limit = extract_limit(sql_type)
378
+ MSSQL::Type::Nvarchar.new(limit: limit)
379
+ end
380
+ map.register_type %r{\Anchar\(\d+\)} do |sql_type|
381
+ limit = extract_limit(sql_type)
382
+ MSSQL::Type::Nchar.new(limit: limit)
383
+ end
384
+ map.register_type 'nvarchar(max)', MSSQL::Type::NvarcharMax.new
385
+ map.register_type 'nvarchar(4000)', MSSQL::Type::Nvarchar.new
386
+
387
+ # Binary data types.
388
+ map.register_type 'varbinary(max)', MSSQL::Type::VarbinaryMax.new
389
+ register_class_with_limit map, %r{\Abinary\(\d+\)}, MSSQL::Type::BinaryBasic
390
+ register_class_with_limit map, %r{\Avarbinary\(\d+\)}, MSSQL::Type::Varbinary
391
+
392
+ # Miscellaneous types, Boolean, XML, UUID
393
+ # FIXME The xml data needs to be reviewed and fixed
394
+ map.register_type 'bit', MSSQL::Type::Boolean.new
395
+ map.register_type %r{\Auniqueidentifier\z}i, MSSQL::Type::UUID.new
396
+ map.register_type %r{\Axml\z}i, MSSQL::Type::XML.new
397
+
398
+ # Date and time types
399
+ map.register_type 'date', MSSQL::Type::Date.new
400
+ map.register_type 'datetime', MSSQL::Type::DateTime.new
401
+ map.register_type 'smalldatetime', MSSQL::Type::SmallDateTime.new
402
+ register_class_with_precision map, %r{\Atime\(\d+\)}i, MSSQL::Type::Time
403
+ map.register_type 'time(7)', MSSQL::Type::Time.new
404
+ register_class_with_precision map, %r{\Adatetime2\(\d+\)}i, MSSQL::Type::DateTime2
405
+ # map.register_type 'datetime2(7)', MSSQL::Type::DateTime2.new
406
+
407
+ # TODO: we should have identity separated from the sql_type
408
+ # let's say in another attribute (this will help to pass more AR tests),
409
+ # also we add collation attribute per column.
410
+ # aliases
411
+ map.alias_type 'int identity', 'int'
412
+ map.alias_type 'bigint identity', 'bigint'
413
+ map.alias_type 'integer', 'int'
414
+ map.alias_type 'integer', 'int'
415
+ map.alias_type 'INTEGER', 'int'
416
+ map.alias_type 'TINYINT', 'tinyint'
417
+ map.alias_type 'SMALLINT', 'smallint'
418
+ map.alias_type 'BIGINT', 'bigint'
419
+ map.alias_type %r{\Anumeric}i, 'decimal'
420
+ map.alias_type %r{\Anumber}i, 'decimal'
421
+ map.alias_type %r{\Adouble\z}i, 'float'
422
+ map.alias_type 'string', 'nvarchar(4000)'
423
+ map.alias_type %r{\Aboolean\z}i, 'bit'
424
+ map.alias_type 'DATE', 'date'
425
+ map.alias_type 'DATETIME', 'datetime'
426
+ map.alias_type 'SMALLDATETIME', 'smalldatetime'
427
+ map.alias_type %r{\Atime\z}i, 'time(7)'
428
+ map.alias_type %r{\Abinary\z}i, 'varbinary(max)'
429
+ map.alias_type %r{\Ablob\z}i, 'varbinary(max)'
430
+ map.alias_type %r{\Adatetime2\z}i, 'datetime2(7)'
431
+
432
+ # Deprecated SQL Server types.
433
+ map.register_type 'text', MSSQL::Type::Text.new
434
+ map.register_type 'ntext', MSSQL::Type::Ntext.new
435
+ map.register_type 'image', MSSQL::Type::Image.new
436
+ end
437
+ end
438
+
439
+ TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
440
+
312
441
  private
313
442
 
443
+ def type_map
444
+ TYPE_MAP
445
+ end
446
+
314
447
  def translate_exception(exception, message:, sql:, binds:)
315
448
  case message
316
449
  when /no connection available/i
@@ -334,109 +467,6 @@ module ActiveRecord
334
467
  end
335
468
  end
336
469
 
337
- # This method is called indirectly by the abstract method
338
- # 'fetch_type_metadata' which then it is called by the java part when
339
- # calculating a table's columns.
340
- def initialize_type_map(map = type_map)
341
- # Build the type mapping from SQL Server to ActiveRecord
342
-
343
- # Integer types.
344
- map.register_type 'int', MSSQL::Type::Integer.new(limit: 4)
345
- map.register_type 'tinyint', MSSQL::Type::TinyInteger.new(limit: 1)
346
- map.register_type 'smallint', MSSQL::Type::SmallInteger.new(limit: 2)
347
- map.register_type 'bigint', MSSQL::Type::BigInteger.new(limit: 8)
348
-
349
- # Exact Numeric types.
350
- map.register_type %r{\Adecimal}i do |sql_type|
351
- scale = extract_scale(sql_type)
352
- precision = extract_precision(sql_type)
353
- if scale == 0
354
- MSSQL::Type::DecimalWithoutScale.new(precision: precision)
355
- else
356
- MSSQL::Type::Decimal.new(precision: precision, scale: scale)
357
- end
358
- end
359
- map.register_type %r{\Amoney\z}i, MSSQL::Type::Money.new
360
- map.register_type %r{\Asmallmoney\z}i, MSSQL::Type::SmallMoney.new
361
-
362
- # Approximate Numeric types.
363
- map.register_type %r{\Afloat\z}i, MSSQL::Type::Float.new
364
- map.register_type %r{\Areal\z}i, MSSQL::Type::Real.new
365
-
366
- # Character strings CHAR and VARCHAR (it can become Unicode UTF-8)
367
- map.register_type 'varchar(max)', MSSQL::Type::VarcharMax.new
368
- map.register_type %r{\Avarchar\(\d+\)} do |sql_type|
369
- limit = extract_limit(sql_type)
370
- MSSQL::Type::Varchar.new(limit: limit)
371
- end
372
- map.register_type %r{\Achar\(\d+\)} do |sql_type|
373
- limit = extract_limit(sql_type)
374
- MSSQL::Type::Char.new(limit: limit)
375
- end
376
-
377
- # Character strings NCHAR and NVARCHAR (by default Unicode UTF-16)
378
- map.register_type %r{\Anvarchar\(\d+\)} do |sql_type|
379
- limit = extract_limit(sql_type)
380
- MSSQL::Type::Nvarchar.new(limit: limit)
381
- end
382
- map.register_type %r{\Anchar\(\d+\)} do |sql_type|
383
- limit = extract_limit(sql_type)
384
- MSSQL::Type::Nchar.new(limit: limit)
385
- end
386
- map.register_type 'nvarchar(max)', MSSQL::Type::NvarcharMax.new
387
- map.register_type 'nvarchar(4000)', MSSQL::Type::Nvarchar.new
388
-
389
- # Binary data types.
390
- map.register_type 'varbinary(max)', MSSQL::Type::VarbinaryMax.new
391
- register_class_with_limit map, %r{\Abinary\(\d+\)}, MSSQL::Type::BinaryBasic
392
- register_class_with_limit map, %r{\Avarbinary\(\d+\)}, MSSQL::Type::Varbinary
393
-
394
- # Miscellaneous types, Boolean, XML, UUID
395
- # FIXME The xml data needs to be reviewed and fixed
396
- map.register_type 'bit', MSSQL::Type::Boolean.new
397
- map.register_type %r{\Auniqueidentifier\z}i, MSSQL::Type::UUID.new
398
- map.register_type %r{\Axml\z}i, MSSQL::Type::XML.new
399
-
400
- # Date and time types
401
- map.register_type 'date', MSSQL::Type::Date.new
402
- map.register_type 'datetime', MSSQL::Type::DateTime.new
403
- map.register_type 'smalldatetime', MSSQL::Type::SmallDateTime.new
404
- register_class_with_precision map, %r{\Atime\(\d+\)}i, MSSQL::Type::Time
405
- map.register_type 'time(7)', MSSQL::Type::Time.new
406
- register_class_with_precision map, %r{\Adatetime2\(\d+\)}i, MSSQL::Type::DateTime2
407
- map.register_type 'datetime2(7)', MSSQL::Type::DateTime2.new
408
-
409
- # TODO: we should have identity separated from the sql_type
410
- # let's say in another attribute (this will help to pass more AR tests),
411
- # also we add collation attribute per column.
412
- # aliases
413
- map.alias_type 'int identity', 'int'
414
- map.alias_type 'bigint identity', 'bigint'
415
- map.alias_type 'integer', 'int'
416
- map.alias_type 'integer', 'int'
417
- map.alias_type 'INTEGER', 'int'
418
- map.alias_type 'TINYINT', 'tinyint'
419
- map.alias_type 'SMALLINT', 'smallint'
420
- map.alias_type 'BIGINT', 'bigint'
421
- map.alias_type %r{\Anumeric}i, 'decimal'
422
- map.alias_type %r{\Anumber}i, 'decimal'
423
- map.alias_type %r{\Adouble\z}i, 'float'
424
- map.alias_type 'string', 'nvarchar(4000)'
425
- map.alias_type %r{\Aboolean\z}i, 'bit'
426
- map.alias_type 'DATE', 'date'
427
- map.alias_type 'DATETIME', 'datetime'
428
- map.alias_type 'SMALLDATETIME', 'smalldatetime'
429
- map.alias_type %r{\Atime\z}i, 'time(7)'
430
- map.alias_type %r{\Abinary\z}i, 'varbinary(max)'
431
- map.alias_type %r{\Ablob\z}i, 'varbinary(max)'
432
- map.alias_type %r{\Adatetime2\z}i, 'datetime2(7)'
433
-
434
- # Deprecated SQL Server types.
435
- map.register_type 'text', MSSQL::Type::Text.new
436
- map.register_type 'ntext', MSSQL::Type::Ntext.new
437
- map.register_type 'image', MSSQL::Type::Image.new
438
- end
439
-
440
470
  # Returns an array of Column objects for the table specified by +table_name+.
441
471
  # See the concrete implementation for details on the expected parameter values.
442
472
  # NOTE: This is ready, all implemented in the java part of adapter,
@@ -4,8 +4,30 @@ module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module MSSQL
6
6
  module Quoting
7
- QUOTED_TRUE = '1'.freeze
8
- QUOTED_FALSE = '0'.freeze
7
+ QUOTED_TRUE = '1'
8
+ QUOTED_FALSE = '0'
9
+
10
+ def quote(value)
11
+ # FIXME: this needs improvements to handle other custom types.
12
+ # Also check if it's possible insert integer into a NVARCHAR
13
+ case value
14
+ when ActiveRecord::Type::Binary::Data
15
+ "0x#{value.hex}"
16
+ # when SomeOtherBinaryData then BLOB_VALUE_MARKER
17
+ # when SomeOtherData then "yyy"
18
+ when String, ActiveSupport::Multibyte::Chars
19
+ "N'#{quote_string(value)}'"
20
+ # when OnlyTimeType then "'#{quoted_time(value)}'"
21
+ when Date, Time
22
+ "'#{quoted_date(value)}'"
23
+ when TrueClass
24
+ quoted_true
25
+ when FalseClass
26
+ quoted_false
27
+ else
28
+ super
29
+ end
30
+ end
9
31
 
10
32
  # Quote date/time values for use in SQL input, includes microseconds
11
33
  # with three digits only if the value is a Time responding to usec.
@@ -15,7 +37,7 @@ module ActiveRecord
15
37
  value = time_with_db_timezone(value)
16
38
  end
17
39
 
18
- result = value.to_s(:db)
40
+ result = value.to_fs(:db)
19
41
 
20
42
  if value.respond_to?(:usec) && value.usec > 0
21
43
  "#{result}.#{sprintf("%06d", value.usec)}"
@@ -105,7 +127,7 @@ module ActiveRecord
105
127
  private
106
128
 
107
129
  def time_with_db_timezone(value)
108
- zone_conv_method = if ActiveRecord::Base.default_timezone == :utc
130
+ zone_conv_method = if ActiveRecord.default_timezone == :utc
109
131
  :getutc
110
132
  else
111
133
  :getlocal
@@ -117,29 +139,6 @@ module ActiveRecord
117
139
  value
118
140
  end
119
141
  end
120
-
121
- # @override
122
- # FIXME: it need to be improved to handle other custom types.
123
- # Also check if it's possible insert integer into a NVARCHAR
124
- def _quote(value)
125
- case value
126
- when ActiveRecord::Type::Binary::Data
127
- "0x#{value.hex}"
128
- # when SomeOtherBinaryData then BLOB_VALUE_MARKER
129
- # when SomeOtherData then "yyy"
130
- when String, ActiveSupport::Multibyte::Chars
131
- "N'#{quote_string(value)}'"
132
- # when OnlyTimeType then "'#{quoted_time(value)}'"
133
- when Date, Time
134
- "'#{quoted_date(value)}'"
135
- when TrueClass
136
- quoted_true
137
- when FalseClass
138
- quoted_false
139
- else
140
- super
141
- end
142
- end
143
142
  end
144
143
  end
145
144
  end
@@ -29,7 +29,7 @@ module ActiveRecord
29
29
  end
30
30
 
31
31
  if supports_foreign_keys?
32
- statements.concat(o.foreign_keys.map { |to_table, options| foreign_key_in_create(o.name, to_table, options) })
32
+ statements.concat(o.foreign_keys.map { |fk| accept fk })
33
33
  end
34
34
 
35
35
  create_sql << "(#{statements.join(', ')})" if statements.present?
@@ -14,80 +14,95 @@ module ActiveRecord
14
14
 
15
15
  # datetime with seconds always zero (:00) and without fractional seconds
16
16
  def smalldatetime(*args, **options)
17
- args.each { |name| column(name, :smalldatetime, options) }
17
+ args.each { |name| column(name, :smalldatetime, **options) }
18
18
  end
19
19
 
20
20
  # this is the old sql server datetime type, the precision is as follow
21
21
  # xx1, xx3, and xx7
22
22
  def datetime_basic(*args, **options)
23
- args.each { |name| column(name, :datetime_basic, options) }
23
+ args.each { |name| column(name, :datetime_basic, **options) }
24
24
  end
25
25
 
26
26
  def real(*args, **options)
27
- args.each { |name| column(name, :real, options) }
27
+ args.each { |name| column(name, :real, **options) }
28
28
  end
29
29
 
30
30
  def money(*args, **options)
31
- args.each { |name| column(name, :money, options) }
31
+ args.each { |name| column(name, :money, **options) }
32
32
  end
33
33
 
34
34
  def smallmoney(*args, **options)
35
- args.each { |name| column(name, :smallmoney, options) }
35
+ args.each { |name| column(name, :smallmoney, **options) }
36
36
  end
37
37
 
38
38
  def char(*args, **options)
39
- args.each { |name| column(name, :char, options) }
39
+ args.each { |name| column(name, :char, **options) }
40
40
  end
41
41
 
42
42
  def varchar(*args, **options)
43
- args.each { |name| column(name, :varchar, options) }
43
+ args.each { |name| column(name, :varchar, **options) }
44
44
  end
45
45
 
46
46
  def varchar_max(*args, **options)
47
- args.each { |name| column(name, :varchar_max, options) }
47
+ args.each { |name| column(name, :varchar_max, **options) }
48
48
  end
49
49
 
50
50
  def text_basic(*args, **options)
51
- args.each { |name| column(name, :text_basic, options) }
51
+ args.each { |name| column(name, :text_basic, **options) }
52
52
  end
53
53
 
54
54
  def nchar(*args, **options)
55
- args.each { |name| column(name, :nchar, options) }
55
+ args.each { |name| column(name, :nchar, **options) }
56
56
  end
57
57
 
58
58
  def ntext(*args, **options)
59
- args.each { |name| column(name, :ntext, options) }
59
+ args.each { |name| column(name, :ntext, **options) }
60
60
  end
61
61
 
62
62
  def binary_basic(*args, **options)
63
- args.each { |name| column(name, :binary_basic, options) }
63
+ args.each { |name| column(name, :binary_basic, **options) }
64
64
  end
65
65
 
66
66
  def varbinary(*args, **options)
67
- args.each { |name| column(name, :varbinary, options) }
67
+ args.each { |name| column(name, :varbinary, **options) }
68
68
  end
69
69
 
70
70
  def uuid(*args, **options)
71
- args.each { |name| column(name, :uniqueidentifier, options) }
71
+ args.each { |name| column(name, :uniqueidentifier, **options) }
72
72
  end
73
73
  end
74
74
 
75
75
  class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
76
76
  include ColumnMethods
77
77
 
78
+ def column(name, type, index: nil, **options)
79
+ # TODO: remove this when the below changed is released
80
+ # Fix erroneous nil default precision on virtual datetime columns #46110
81
+ # https://github.com/rails/rails/pull/46110
82
+ #
83
+ if @conn.supports_datetime_with_precision?
84
+ if type == :datetime && !options.key?(:precision)
85
+ options[:precision] = 7
86
+ end
87
+ end
88
+
89
+ super
90
+ end
91
+
92
+
78
93
  def new_column_definition(name, type, **options)
79
94
  case type
80
95
  when :primary_key
81
96
  options[:is_identity] = true
97
+ when :datetime
98
+ options[:precision] = 7 if !options.key?(:precision) && @conn.supports_datetime_with_precision?
82
99
  end
83
100
 
84
101
  super
85
102
  end
86
103
 
87
104
  def timestamps(**options)
88
- if !options.key?(:precision) && @conn.supports_datetime_with_precision?
89
- options[:precision] = 7
90
- end
105
+ options[:precision] = 7 if !options.key?(:precision) && @conn.supports_datetime_with_precision?
91
106
 
92
107
  super
93
108
  end
@@ -28,11 +28,23 @@ module ActiveRecord
28
28
  super && column.identity?
29
29
  end
30
30
 
31
+ def schema_precision(column)
32
+ case column.type
33
+ when :datetime
34
+ if column.precision == 7
35
+ nil
36
+ else
37
+ column.precision.inspect
38
+ end
39
+ else
40
+ super
41
+ end
42
+ end
43
+
31
44
  # def schema_collation(column)
32
45
  # return unless column.collation
33
46
  # column.collation if column.collation != collation
34
47
  # end
35
-
36
48
  end
37
49
  end
38
50
  end
@@ -7,36 +7,36 @@ module ActiveRecord
7
7
 
8
8
  NATIVE_DATABASE_TYPES = {
9
9
  # Logical Rails types to SQL Server types
10
- primary_key: 'bigint NOT NULL IDENTITY(1,1) PRIMARY KEY',
11
- integer: { name: 'int', limit: 4 },
12
- boolean: { name: 'bit' },
13
- decimal: { name: 'decimal' },
14
- float: { name: 'float' },
15
- date: { name: 'date' },
16
- time: { name: 'time' },
17
- datetime: { name: 'datetime2' },
18
- string: { name: 'nvarchar', limit: 4000 },
19
- text: { name: 'nvarchar(max)' },
20
- binary: { name: 'varbinary(max)' },
10
+ primary_key: 'bigint NOT NULL IDENTITY(1,1) PRIMARY KEY',
11
+ integer: { name: 'int', limit: 4 },
12
+ boolean: { name: 'bit' },
13
+ decimal: { name: 'decimal' },
14
+ float: { name: 'float' },
15
+ date: { name: 'date' },
16
+ time: { name: 'time' },
17
+ datetime: { name: 'datetime2' },
18
+ string: { name: 'nvarchar', limit: 4000 },
19
+ text: { name: 'nvarchar(max)' },
20
+ binary: { name: 'varbinary(max)' },
21
21
  # Other types or SQL Server specific
22
- bigint: { name: 'bigint' },
23
- smalldatetime: { name: 'smalldatetime' },
22
+ bigint: { name: 'bigint' },
23
+ smalldatetime: { name: 'smalldatetime' },
24
24
  datetime_basic: { name: 'datetime' },
25
- timestamp: { name: 'datetime' },
26
- real: { name: 'real' },
27
- money: { name: 'money' },
28
- smallmoney: { name: 'smallmoney' },
29
- char: { name: 'char' },
30
- nchar: { name: 'nchar' },
31
- varchar: { name: 'varchar', limit: 8000 },
32
- varchar_max: { name: 'varchar(max)' },
33
- uuid: { name: 'uniqueidentifier' },
34
- binary_basic: { name: 'binary' },
35
- varbinary: { name: 'varbinary', limit: 8000 },
25
+ timestamp: { name: 'datetime' },
26
+ real: { name: 'real' },
27
+ money: { name: 'money' },
28
+ smallmoney: { name: 'smallmoney' },
29
+ char: { name: 'char' },
30
+ nchar: { name: 'nchar' },
31
+ varchar: { name: 'varchar', limit: 8000 },
32
+ varchar_max: { name: 'varchar(max)' },
33
+ uuid: { name: 'uniqueidentifier' },
34
+ binary_basic: { name: 'binary' },
35
+ varbinary: { name: 'varbinary', limit: 8000 },
36
36
  # Deprecated SQL Server types
37
- image: { name: 'image' },
38
- ntext: { name: 'ntext' },
39
- text_basic: { name: 'text' }
37
+ image: { name: 'image' },
38
+ ntext: { name: 'ntext' },
39
+ text_basic: { name: 'text' }
40
40
  }.freeze
41
41
 
42
42
  def native_database_types
@@ -127,9 +127,17 @@ module ActiveRecord
127
127
  create_database(name, options)
128
128
  end
129
129
 
130
- def remove_column(table_name, column_name, type = nil, options = {})
131
- raise ArgumentError.new('You must specify at least one column name. Example: remove_column(:people, :first_name)') if column_name.is_a? Array
130
+ def remove_columns(table_name, *column_names, type: nil, **options)
131
+ if column_names.empty?
132
+ raise ArgumentError.new('You must specify at least one column name. Example: remove_columns(:people, :first_name)')
133
+ end
134
+
135
+ column_names.each do |column_name|
136
+ remove_column(table_name, column_name, type, **options)
137
+ end
138
+ end
132
139
 
140
+ def remove_column(table_name, column_name, _type = nil, **options)
133
141
  return if options[:if_exists] == true && !column_exists?(table_name, column_name)
134
142
 
135
143
  remove_check_constraints(table_name, column_name)
@@ -138,7 +146,7 @@ module ActiveRecord
138
146
  execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}"
139
147
  end
140
148
 
141
- def drop_table(table_name, options = {})
149
+ def drop_table(table_name, **options)
142
150
  # mssql cannot recreate referenced table with force: :cascade
143
151
  # https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-table-transact-sql?view=sql-server-2017
144
152
  if options[:force] == :cascade
@@ -248,7 +256,7 @@ module ActiveRecord
248
256
  (order_columns << super).join(', ')
249
257
  end
250
258
 
251
- def add_timestamps(table_name, options = {})
259
+ def add_timestamps(table_name, **options)
252
260
  if !options.key?(:precision) && supports_datetime_with_precision?
253
261
  options[:precision] = 7
254
262
  end
@@ -256,6 +264,16 @@ module ActiveRecord
256
264
  super
257
265
  end
258
266
 
267
+ def add_column(table_name, column_name, type, **options)
268
+ if supports_datetime_with_precision?
269
+ if type == :datetime && !options.key?(:precision)
270
+ options[:precision] = 7
271
+ end
272
+ end
273
+
274
+ super
275
+ end
276
+
259
277
  def create_schema_dumper(options)
260
278
  MSSQL::SchemaDumper.create(self, options)
261
279
  end
@@ -323,16 +341,20 @@ module ActiveRecord
323
341
  quoted_table = quote_table_name(table_name)
324
342
  quoted_column = quote_column_name(column_name)
325
343
  quoted_default = quote(default)
344
+
326
345
  unless null || default.nil?
327
346
  execute("UPDATE #{quoted_table} SET #{quoted_column}=#{quoted_default} WHERE #{quoted_column} IS NULL")
328
347
  end
348
+
349
+ options = { limit: column.limit, precision: column.precision, scale: column.scale }
350
+
329
351
  sql_alter = [
330
352
  "ALTER TABLE #{quoted_table}",
331
- "ALTER COLUMN #{quoted_column} #{type_to_sql(column.type, limit: column.limit, precision: column.precision, scale: column.scale)}",
332
- (' NOT NULL' unless null)
353
+ "ALTER COLUMN #{quoted_column} #{type_to_sql(column.type, **options)}",
354
+ ('NOT NULL' unless null)
333
355
  ]
334
356
 
335
- execute(sql_alter.join(' '))
357
+ execute(sql_alter.compact.join(' '))
336
358
  end
337
359
 
338
360
  def update_table_definition(table_name, base) #:nodoc:
@@ -345,11 +367,14 @@ module ActiveRecord
345
367
  MSSQL::SchemaCreation.new(self)
346
368
  end
347
369
 
348
- def create_table_definition(*args)
349
- MSSQL::TableDefinition.new(self, *args)
370
+ def create_table_definition(name, **options)
371
+ MSSQL::TableDefinition.new(self, name, **options)
350
372
  end
351
373
 
352
374
  def new_column_from_field(table_name, field)
375
+ # NOTE: this method is used by the columns method in the abstract Class
376
+ # to map column_definitions. It would be good if column_definitions is
377
+ # implemented in ruby
353
378
  field
354
379
  end
355
380