activerecord-sqlserver-adapter 5.2.1 → 6.0.2

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 (153) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +9 -0
  3. data/.github/issue_template.md +23 -0
  4. data/.github/workflows/ci.yml +26 -0
  5. data/.gitignore +1 -0
  6. data/.rubocop.yml +29 -0
  7. data/CHANGELOG.md +58 -20
  8. data/{Dockerfile → Dockerfile.ci} +1 -1
  9. data/Gemfile +48 -41
  10. data/Guardfile +9 -8
  11. data/README.md +28 -31
  12. data/RUNNING_UNIT_TESTS.md +3 -0
  13. data/Rakefile +14 -16
  14. data/VERSION +1 -1
  15. data/activerecord-sqlserver-adapter.gemspec +25 -14
  16. data/appveyor.yml +24 -17
  17. data/docker-compose.ci.yml +7 -5
  18. data/guides/RELEASING.md +11 -0
  19. data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +2 -4
  20. data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +3 -4
  21. data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +5 -4
  22. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +3 -3
  23. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +2 -0
  24. data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +8 -7
  25. data/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb +36 -0
  26. data/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb +6 -4
  27. data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +9 -0
  28. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +88 -44
  29. data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +9 -12
  30. data/lib/active_record/connection_adapters/sqlserver/errors.rb +2 -3
  31. data/lib/active_record/connection_adapters/sqlserver/quoting.rb +46 -8
  32. data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +16 -5
  33. data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +9 -7
  34. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +210 -163
  35. data/lib/active_record/connection_adapters/sqlserver/showplan.rb +8 -8
  36. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +4 -2
  37. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +3 -1
  38. data/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +2 -2
  39. data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +43 -44
  40. data/lib/active_record/connection_adapters/sqlserver/transaction.rb +7 -9
  41. data/lib/active_record/connection_adapters/sqlserver/type.rb +38 -35
  42. data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +3 -3
  43. data/lib/active_record/connection_adapters/sqlserver/type/binary.rb +5 -4
  44. data/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +3 -3
  45. data/lib/active_record/connection_adapters/sqlserver/type/char.rb +7 -4
  46. data/lib/active_record/connection_adapters/sqlserver/type/data.rb +2 -2
  47. data/lib/active_record/connection_adapters/sqlserver/type/date.rb +4 -3
  48. data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +8 -8
  49. data/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb +2 -2
  50. data/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +2 -2
  51. data/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +5 -4
  52. data/lib/active_record/connection_adapters/sqlserver/type/decimal_without_scale.rb +22 -0
  53. data/lib/active_record/connection_adapters/sqlserver/type/float.rb +3 -3
  54. data/lib/active_record/connection_adapters/sqlserver/type/integer.rb +3 -3
  55. data/lib/active_record/connection_adapters/sqlserver/type/json.rb +2 -1
  56. data/lib/active_record/connection_adapters/sqlserver/type/money.rb +4 -4
  57. data/lib/active_record/connection_adapters/sqlserver/type/real.rb +3 -3
  58. data/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +3 -3
  59. data/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +4 -4
  60. data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +3 -3
  61. data/lib/active_record/connection_adapters/sqlserver/type/string.rb +2 -2
  62. data/lib/active_record/connection_adapters/sqlserver/type/text.rb +3 -3
  63. data/lib/active_record/connection_adapters/sqlserver/type/time.rb +6 -6
  64. data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +8 -9
  65. data/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +3 -3
  66. data/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +3 -3
  67. data/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +5 -4
  68. data/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb +2 -2
  69. data/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +3 -3
  70. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +6 -5
  71. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +4 -4
  72. data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +4 -3
  73. data/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +6 -5
  74. data/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +4 -4
  75. data/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +6 -5
  76. data/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +4 -4
  77. data/lib/active_record/connection_adapters/sqlserver/utils.rb +10 -11
  78. data/lib/active_record/connection_adapters/sqlserver/version.rb +2 -2
  79. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +145 -94
  80. data/lib/active_record/connection_adapters/sqlserver_column.rb +9 -5
  81. data/lib/active_record/sqlserver_base.rb +9 -1
  82. data/lib/active_record/tasks/sqlserver_database_tasks.rb +28 -32
  83. data/lib/activerecord-sqlserver-adapter.rb +3 -1
  84. data/lib/arel/visitors/sqlserver.rb +108 -34
  85. data/lib/arel_sqlserver.rb +4 -2
  86. data/test/appveyor/dbsetup.ps1 +4 -4
  87. data/test/cases/adapter_test_sqlserver.rb +246 -171
  88. data/test/cases/change_column_null_test_sqlserver.rb +14 -12
  89. data/test/cases/coerced_tests.rb +722 -381
  90. data/test/cases/column_test_sqlserver.rb +287 -285
  91. data/test/cases/connection_test_sqlserver.rb +17 -20
  92. data/test/cases/execute_procedure_test_sqlserver.rb +20 -20
  93. data/test/cases/fetch_test_sqlserver.rb +16 -22
  94. data/test/cases/fully_qualified_identifier_test_sqlserver.rb +15 -19
  95. data/test/cases/helper_sqlserver.rb +15 -15
  96. data/test/cases/in_clause_test_sqlserver.rb +36 -0
  97. data/test/cases/index_test_sqlserver.rb +15 -15
  98. data/test/cases/json_test_sqlserver.rb +25 -25
  99. data/test/cases/lateral_test_sqlserver.rb +35 -0
  100. data/test/cases/migration_test_sqlserver.rb +67 -27
  101. data/test/cases/optimizer_hints_test_sqlserver.rb +72 -0
  102. data/test/cases/order_test_sqlserver.rb +53 -54
  103. data/test/cases/pessimistic_locking_test_sqlserver.rb +27 -33
  104. data/test/cases/rake_test_sqlserver.rb +33 -45
  105. data/test/cases/schema_dumper_test_sqlserver.rb +115 -109
  106. data/test/cases/schema_test_sqlserver.rb +20 -26
  107. data/test/cases/scratchpad_test_sqlserver.rb +4 -4
  108. data/test/cases/showplan_test_sqlserver.rb +28 -35
  109. data/test/cases/specific_schema_test_sqlserver.rb +68 -65
  110. data/test/cases/transaction_test_sqlserver.rb +18 -20
  111. data/test/cases/trigger_test_sqlserver.rb +14 -13
  112. data/test/cases/utils_test_sqlserver.rb +70 -70
  113. data/test/cases/uuid_test_sqlserver.rb +13 -14
  114. data/test/debug.rb +8 -6
  115. data/test/migrations/create_clients_and_change_column_null.rb +3 -1
  116. data/test/migrations/transaction_table/1_table_will_never_be_created.rb +4 -4
  117. data/test/models/sqlserver/booking.rb +3 -1
  118. data/test/models/sqlserver/customers_view.rb +3 -1
  119. data/test/models/sqlserver/datatype.rb +2 -0
  120. data/test/models/sqlserver/datatype_migration.rb +2 -0
  121. data/test/models/sqlserver/dollar_table_name.rb +3 -1
  122. data/test/models/sqlserver/edge_schema.rb +3 -3
  123. data/test/models/sqlserver/fk_has_fk.rb +3 -1
  124. data/test/models/sqlserver/fk_has_pk.rb +3 -1
  125. data/test/models/sqlserver/natural_pk_data.rb +4 -2
  126. data/test/models/sqlserver/natural_pk_int_data.rb +3 -1
  127. data/test/models/sqlserver/no_pk_data.rb +3 -1
  128. data/test/models/sqlserver/object_default.rb +3 -1
  129. data/test/models/sqlserver/quoted_table.rb +4 -2
  130. data/test/models/sqlserver/quoted_view_1.rb +3 -1
  131. data/test/models/sqlserver/quoted_view_2.rb +3 -1
  132. data/test/models/sqlserver/sst_memory.rb +3 -1
  133. data/test/models/sqlserver/string_default.rb +3 -1
  134. data/test/models/sqlserver/string_defaults_big_view.rb +3 -1
  135. data/test/models/sqlserver/string_defaults_view.rb +3 -1
  136. data/test/models/sqlserver/tinyint_pk.rb +3 -1
  137. data/test/models/sqlserver/trigger.rb +4 -2
  138. data/test/models/sqlserver/trigger_history.rb +3 -1
  139. data/test/models/sqlserver/upper.rb +3 -1
  140. data/test/models/sqlserver/uppered.rb +3 -1
  141. data/test/models/sqlserver/uuid.rb +3 -1
  142. data/test/schema/sqlserver_specific_schema.rb +31 -21
  143. data/test/support/coerceable_test_sqlserver.rb +15 -9
  144. data/test/support/connection_reflection.rb +3 -2
  145. data/test/support/core_ext/query_cache.rb +4 -1
  146. data/test/support/load_schema_sqlserver.rb +5 -5
  147. data/test/support/minitest_sqlserver.rb +3 -1
  148. data/test/support/paths_sqlserver.rb +11 -11
  149. data/test/support/rake_helpers.rb +13 -10
  150. data/test/support/sql_counter_sqlserver.rb +3 -4
  151. data/test/support/test_in_memory_oltp.rb +9 -7
  152. metadata +27 -12
  153. data/.travis.yml +0 -25
@@ -1,17 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
5
  module SQLServer
4
6
  module Type
5
7
  class Timestamp < Binary
6
-
7
8
  def type
8
9
  :ss_timestamp
9
10
  end
10
11
 
11
12
  def sqlserver_type
12
- 'timestamp'.freeze
13
+ "timestamp"
13
14
  end
14
-
15
15
  end
16
16
  end
17
17
  end
@@ -1,11 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
5
  module SQLServer
4
6
  module Type
5
7
  class TinyInteger < Integer
6
-
7
8
  def sqlserver_type
8
- 'tinyint'.freeze
9
+ "tinyint"
9
10
  end
10
11
 
11
12
  private
@@ -17,7 +18,6 @@ module ActiveRecord
17
18
  def min_value
18
19
  0
19
20
  end
20
-
21
21
  end
22
22
  end
23
23
  end
@@ -1,19 +1,20 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
5
  module SQLServer
4
6
  module Type
5
7
  class UnicodeChar < UnicodeString
6
-
7
8
  def type
8
9
  :nchar
9
10
  end
10
11
 
11
12
  def sqlserver_type
12
- 'nchar'.tap do |type|
13
- type << "(#{limit})" if limit
13
+ "nchar".yield_self do |type|
14
+ type += "(#{limit})" if limit
15
+ type
14
16
  end
15
17
  end
16
-
17
18
  end
18
19
  end
19
20
  end
@@ -1,10 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
5
  module SQLServer
4
6
  module Type
5
7
  class UnicodeString < String
6
-
7
-
8
8
  end
9
9
  end
10
10
  end
@@ -1,17 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
5
  module SQLServer
4
6
  module Type
5
7
  class UnicodeText < UnicodeVarcharMax
6
-
7
8
  def type
8
9
  :ntext
9
10
  end
10
11
 
11
12
  def sqlserver_type
12
- 'ntext'.freeze
13
+ "ntext"
13
14
  end
14
-
15
15
  end
16
16
  end
17
17
  end
@@ -1,10 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
5
  module SQLServer
4
6
  module Type
5
7
  class UnicodeVarchar < UnicodeChar
6
-
7
- def initialize(*args)
8
+ def initialize(**args)
8
9
  super
9
10
  @limit = 4000 if @limit.to_i == 0
10
11
  end
@@ -14,11 +15,11 @@ module ActiveRecord
14
15
  end
15
16
 
16
17
  def sqlserver_type
17
- 'nvarchar'.tap do |type|
18
- type << "(#{limit})" if limit
18
+ "nvarchar".yield_self do |type|
19
+ type += "(#{limit})" if limit
20
+ type
19
21
  end
20
22
  end
21
-
22
23
  end
23
24
  end
24
25
  end
@@ -1,10 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
5
  module SQLServer
4
6
  module Type
5
7
  class UnicodeVarcharMax < UnicodeVarchar
6
-
7
- def initialize(*args)
8
+ def initialize(**args)
8
9
  super
9
10
  @limit = 2_147_483_647
10
11
  end
@@ -14,9 +15,8 @@ module ActiveRecord
14
15
  end
15
16
 
16
17
  def sqlserver_type
17
- 'nvarchar(max)'.freeze
18
+ "nvarchar(max)"
18
19
  end
19
-
20
20
  end
21
21
  end
22
22
  end
@@ -1,9 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
5
  module SQLServer
4
6
  module Type
5
7
  class Uuid < String
6
-
7
8
  ACCEPTABLE_UUID = %r{\A\{?([a-fA-F0-9]{4}-?){8}\}?\z}x
8
9
 
9
10
  alias_method :serialize, :deserialize
@@ -13,11 +14,12 @@ module ActiveRecord
13
14
  end
14
15
 
15
16
  def sqlserver_type
16
- 'uniqueidentifier'.freeze
17
+ "uniqueidentifier"
17
18
  end
18
19
 
19
20
  def serialize(value)
20
21
  return unless value
22
+
21
23
  Data.new super, self
22
24
  end
23
25
 
@@ -28,7 +30,6 @@ module ActiveRecord
28
30
  def quoted(value)
29
31
  Utils.quote_string_single(value) if value
30
32
  end
31
-
32
33
  end
33
34
  end
34
35
  end
@@ -1,10 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
5
  module SQLServer
4
6
  module Type
5
7
  class Varbinary < Binary
6
-
7
- def initialize(*args)
8
+ def initialize(**args)
8
9
  super
9
10
  @limit = 8000 if @limit.to_i == 0
10
11
  end
@@ -14,11 +15,11 @@ module ActiveRecord
14
15
  end
15
16
 
16
17
  def sqlserver_type
17
- 'varbinary'.tap do |type|
18
- type << "(#{limit})" if limit
18
+ "varbinary".yield_self do |type|
19
+ type += "(#{limit})" if limit
20
+ type
19
21
  end
20
22
  end
21
-
22
23
  end
23
24
  end
24
25
  end
@@ -1,10 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
5
  module SQLServer
4
6
  module Type
5
7
  class VarbinaryMax < Varbinary
6
-
7
- def initialize(*args)
8
+ def initialize(**args)
8
9
  super
9
10
  @limit = 2_147_483_647
10
11
  end
@@ -14,9 +15,8 @@ module ActiveRecord
14
15
  end
15
16
 
16
17
  def sqlserver_type
17
- 'varbinary(max)'.freeze
18
+ "varbinary(max)"
18
19
  end
19
-
20
20
  end
21
21
  end
22
22
  end
@@ -1,10 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
5
  module SQLServer
4
6
  module Type
5
7
  class Varchar < Char
6
-
7
- def initialize(*args)
8
+ def initialize(**args)
8
9
  super
9
10
  @limit = 8000 if @limit.to_i == 0
10
11
  end
@@ -14,11 +15,11 @@ module ActiveRecord
14
15
  end
15
16
 
16
17
  def sqlserver_type
17
- 'varchar'.tap do |type|
18
- type << "(#{limit})" if limit
18
+ "varchar".yield_self do |type|
19
+ type += "(#{limit})" if limit
20
+ type
19
21
  end
20
22
  end
21
-
22
23
  end
23
24
  end
24
25
  end
@@ -1,10 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
5
  module SQLServer
4
6
  module Type
5
7
  class VarcharMax < Varchar
6
-
7
- def initialize(*args)
8
+ def initialize(**args)
8
9
  super
9
10
  @limit = 2_147_483_647
10
11
  end
@@ -14,9 +15,8 @@ module ActiveRecord
14
15
  end
15
16
 
16
17
  def sqlserver_type
17
- 'varchar(max)'.freeze
18
+ "varchar(max)"
18
19
  end
19
-
20
20
  end
21
21
  end
22
22
  end
@@ -1,17 +1,17 @@
1
- require 'strscan'
1
+ # frozen_string_literal: true
2
+
3
+ require "strscan"
2
4
 
3
5
  module ActiveRecord
4
6
  module ConnectionAdapters
5
7
  module SQLServer
6
8
  module Utils
7
-
8
- QUOTED_STRING_PREFIX = 'N'
9
+ QUOTED_STRING_PREFIX = "N"
9
10
 
10
11
  # Value object to return identifiers from SQL Server names http://bit.ly/1CZ3EiL
11
12
  # Inspiried from Rails PostgreSQL::Name adapter object in their own Utils.
12
13
  #
13
14
  class Name
14
-
15
15
  SEPARATOR = "."
16
16
  UNQUOTED_SCANNER = /\]?\./
17
17
  QUOTED_SCANNER = /\A\[.*?\]\./
@@ -54,15 +54,15 @@ module ActiveRecord
54
54
  end
55
55
 
56
56
  def quoted
57
- parts.map{ |p| quote(p) if p }.join SEPARATOR
57
+ parts.map { |p| quote(p) if p }.join SEPARATOR
58
58
  end
59
59
 
60
60
  def quoted_raw
61
61
  quote @raw_name
62
62
  end
63
63
 
64
- def ==(o)
65
- o.class == self.class && o.parts == parts
64
+ def ==(other)
65
+ other.class == self.class && other.parts == parts
66
66
  end
67
67
  alias_method :eql?, :==
68
68
 
@@ -75,6 +75,7 @@ module ActiveRecord
75
75
  def parse_raw_name
76
76
  @parts = []
77
77
  return if raw_name.blank?
78
+
78
79
  scanner = StringScanner.new(raw_name)
79
80
  matched = scanner.exist?(QUOTED_CHECKER) ? scanner.scan_until(QUOTED_SCANNER) : scanner.scan_until(UNQUOTED_SCANNER)
80
81
  while matched
@@ -91,7 +92,7 @@ module ActiveRecord
91
92
  @schema = @parts.first
92
93
  end
93
94
  rest = scanner.rest
94
- rest = rest.starts_with?('.') ? rest[1..-1] : rest[0..-1]
95
+ rest = rest.starts_with?(".") ? rest[1..-1] : rest[0..-1]
95
96
  @object = unquote(rest)
96
97
  @parts << @object
97
98
  end
@@ -101,7 +102,7 @@ module ActiveRecord
101
102
  end
102
103
 
103
104
  def unquote(part)
104
- if part && part.start_with?('[')
105
+ if part && part.start_with?("[")
105
106
  part[1..-2]
106
107
  else
107
108
  part
@@ -111,7 +112,6 @@ module ActiveRecord
111
112
  def parts
112
113
  @parts
113
114
  end
114
-
115
115
  end
116
116
 
117
117
  extend self
@@ -139,7 +139,6 @@ module ActiveRecord
139
139
  def extract_identifiers(name)
140
140
  SQLServer::Utils::Name.new(name)
141
141
  end
142
-
143
142
  end
144
143
  end
145
144
  end
@@ -1,10 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
5
  module SQLServer
4
6
  module Version
5
-
6
7
  VERSION = File.read(File.expand_path("../../../../../VERSION", __FILE__)).chomp
7
-
8
8
  end
9
9
  end
10
10
  end
@@ -1,37 +1,39 @@
1
- require 'base64'
2
- require 'active_record'
3
- require 'arel_sqlserver'
4
- require 'active_record/connection_adapters/abstract_adapter'
5
- require 'active_record/connection_adapters/sqlserver/core_ext/active_record'
6
- require 'active_record/connection_adapters/sqlserver/core_ext/calculations'
7
- require 'active_record/connection_adapters/sqlserver/core_ext/explain'
8
- require 'active_record/connection_adapters/sqlserver/core_ext/explain_subscriber'
9
- require 'active_record/connection_adapters/sqlserver/core_ext/attribute_methods'
10
- require 'active_record/connection_adapters/sqlserver/core_ext/finder_methods'
11
- require 'active_record/connection_adapters/sqlserver/core_ext/query_methods'
12
- require 'active_record/connection_adapters/sqlserver/version'
13
- require 'active_record/connection_adapters/sqlserver/type'
14
- require 'active_record/connection_adapters/sqlserver/database_limits'
15
- require 'active_record/connection_adapters/sqlserver/database_statements'
16
- require 'active_record/connection_adapters/sqlserver/database_tasks'
17
- require 'active_record/connection_adapters/sqlserver/transaction'
18
- require 'active_record/connection_adapters/sqlserver/errors'
19
- require 'active_record/connection_adapters/sqlserver/schema_creation'
20
- require 'active_record/connection_adapters/sqlserver/schema_dumper'
21
- require 'active_record/connection_adapters/sqlserver/schema_statements'
22
- require 'active_record/connection_adapters/sqlserver/sql_type_metadata'
23
- require 'active_record/connection_adapters/sqlserver/showplan'
24
- require 'active_record/connection_adapters/sqlserver/table_definition'
25
- require 'active_record/connection_adapters/sqlserver/quoting'
26
- require 'active_record/connection_adapters/sqlserver/utils'
27
- require 'active_record/sqlserver_base'
28
- require 'active_record/connection_adapters/sqlserver_column'
29
- require 'active_record/tasks/sqlserver_database_tasks'
1
+ # frozen_string_literal: true
2
+
3
+ require "base64"
4
+ require "active_record"
5
+ require "arel_sqlserver"
6
+ require "active_record/connection_adapters/abstract_adapter"
7
+ require "active_record/connection_adapters/sqlserver/core_ext/active_record"
8
+ require "active_record/connection_adapters/sqlserver/core_ext/calculations"
9
+ require "active_record/connection_adapters/sqlserver/core_ext/explain"
10
+ require "active_record/connection_adapters/sqlserver/core_ext/explain_subscriber"
11
+ require "active_record/connection_adapters/sqlserver/core_ext/attribute_methods"
12
+ require "active_record/connection_adapters/sqlserver/core_ext/finder_methods"
13
+ require "active_record/connection_adapters/sqlserver/core_ext/query_methods"
14
+ require "active_record/connection_adapters/sqlserver/core_ext/preloader"
15
+ require "active_record/connection_adapters/sqlserver/version"
16
+ require "active_record/connection_adapters/sqlserver/type"
17
+ require "active_record/connection_adapters/sqlserver/database_limits"
18
+ require "active_record/connection_adapters/sqlserver/database_statements"
19
+ require "active_record/connection_adapters/sqlserver/database_tasks"
20
+ require "active_record/connection_adapters/sqlserver/transaction"
21
+ require "active_record/connection_adapters/sqlserver/errors"
22
+ require "active_record/connection_adapters/sqlserver/schema_creation"
23
+ require "active_record/connection_adapters/sqlserver/schema_dumper"
24
+ require "active_record/connection_adapters/sqlserver/schema_statements"
25
+ require "active_record/connection_adapters/sqlserver/sql_type_metadata"
26
+ require "active_record/connection_adapters/sqlserver/showplan"
27
+ require "active_record/connection_adapters/sqlserver/table_definition"
28
+ require "active_record/connection_adapters/sqlserver/quoting"
29
+ require "active_record/connection_adapters/sqlserver/utils"
30
+ require "active_record/sqlserver_base"
31
+ require "active_record/connection_adapters/sqlserver_column"
32
+ require "active_record/tasks/sqlserver_database_tasks"
30
33
 
31
34
  module ActiveRecord
32
35
  module ConnectionAdapters
33
36
  class SQLServerAdapter < AbstractAdapter
34
-
35
37
  include SQLServer::Version,
36
38
  SQLServer::Quoting,
37
39
  SQLServer::DatabaseStatements,
@@ -40,7 +42,7 @@ module ActiveRecord
40
42
  SQLServer::DatabaseLimits,
41
43
  SQLServer::DatabaseTasks
42
44
 
43
- ADAPTER_NAME = 'SQLServer'.freeze
45
+ ADAPTER_NAME = "SQLServer".freeze
44
46
 
45
47
  # Default precision for 'time' (See https://docs.microsoft.com/en-us/sql/t-sql/data-types/time-transact-sql)
46
48
  DEFAULT_TIME_PRECISION = 7
@@ -53,7 +55,7 @@ module ActiveRecord
53
55
  cattr_accessor :showplan_option, instance_accessor: false
54
56
  cattr_accessor :lowercase_schema_reflection
55
57
 
56
- self.cs_equality_operator = 'COLLATE Latin1_General_CS_AS_WS'
58
+ self.cs_equality_operator = "COLLATE Latin1_General_CS_AS_WS"
57
59
  self.use_output_inserted = true
58
60
  self.exclude_output_inserted_table_names = Concurrent::Map.new { false }
59
61
 
@@ -80,6 +82,12 @@ module ActiveRecord
80
82
  SQLServer::SchemaCreation.new self
81
83
  end
82
84
 
85
+ def self.database_exists?(config)
86
+ !!ActiveRecord::Base.sqlserver_connection(config)
87
+ rescue ActiveRecord::NoDatabaseError
88
+ false
89
+ end
90
+
83
91
  def supports_ddl_transactions?
84
92
  true
85
93
  end
@@ -144,10 +152,34 @@ module ActiveRecord
144
152
  true
145
153
  end
146
154
 
155
+ def supports_optimizer_hints?
156
+ true
157
+ end
158
+
159
+ def supports_lazy_transactions?
160
+ true
161
+ end
162
+
147
163
  def supports_in_memory_oltp?
148
164
  @version_year >= 2014
149
165
  end
150
166
 
167
+ def supports_insert_returning?
168
+ true
169
+ end
170
+
171
+ def supports_insert_on_duplicate_skip?
172
+ false
173
+ end
174
+
175
+ def supports_insert_on_duplicate_update?
176
+ false
177
+ end
178
+
179
+ def supports_insert_conflict_target?
180
+ false
181
+ end
182
+
151
183
  def disable_referential_integrity
152
184
  tables = tables_with_referential_integrity
153
185
  tables.each { |t| do_execute "ALTER TABLE #{quote_table_name(t)} NOCHECK CONSTRAINT ALL" }
@@ -160,7 +192,8 @@ module ActiveRecord
160
192
 
161
193
  def active?
162
194
  return false unless @connection
163
- raw_connection_do 'SELECT 1'
195
+
196
+ raw_connection_do "SELECT 1"
164
197
  true
165
198
  rescue *connection_errors
166
199
  false
@@ -190,13 +223,13 @@ module ActiveRecord
190
223
 
191
224
  def reset!
192
225
  reset_transaction
193
- do_execute 'IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION'
226
+ do_execute "IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION"
194
227
  end
195
228
 
196
229
  # === Abstract Adapter (Misc Support) =========================== #
197
230
 
198
231
  def tables_with_referential_integrity
199
- schemas_and_tables = select_rows <<-SQL.strip_heredoc
232
+ schemas_and_tables = select_rows <<~SQL.squish
200
233
  SELECT DISTINCT s.name, o.name
201
234
  FROM sys.foreign_keys i
202
235
  INNER JOIN sys.objects o ON i.parent_object_id = o.OBJECT_ID
@@ -225,6 +258,7 @@ module ActiveRecord
225
258
 
226
259
  def database_prefix_remote_server?
227
260
  return false if database_prefix.blank?
261
+
228
262
  name = SQLServer::Utils.extract_identifiers(database_prefix)
229
263
  name.fully_qualified? && name.object.blank?
230
264
  end
@@ -256,37 +290,47 @@ module ActiveRecord
256
290
  result
257
291
  end
258
292
 
293
+ def get_database_version # :nodoc:
294
+ version_year
295
+ end
259
296
 
260
297
  protected
261
298
 
262
299
  # === Abstract Adapter (Misc Support) =========================== #
263
300
 
264
301
  def initialize_type_map(m = type_map)
265
- m.register_type %r{.*}, SQLServer::Type::UnicodeString.new
302
+ m.register_type %r{.*}, SQLServer::Type::UnicodeString.new
303
+
266
304
  # Exact Numerics
267
- register_class_with_limit m, 'bigint(8)', SQLServer::Type::BigInteger
268
- m.alias_type 'bigint', 'bigint(8)'
269
- register_class_with_limit m, 'int(4)', SQLServer::Type::Integer
270
- m.alias_type 'integer', 'int(4)'
271
- m.alias_type 'int', 'int(4)'
272
- register_class_with_limit m, 'smallint(2)', SQLServer::Type::SmallInteger
273
- m.alias_type 'smallint', 'smallint(2)'
274
- register_class_with_limit m, 'tinyint(1)', SQLServer::Type::TinyInteger
275
- m.alias_type 'tinyint', 'tinyint(1)'
276
- m.register_type 'bit', SQLServer::Type::Boolean.new
305
+ register_class_with_limit m, "bigint(8)", SQLServer::Type::BigInteger
306
+ m.alias_type "bigint", "bigint(8)"
307
+ register_class_with_limit m, "int(4)", SQLServer::Type::Integer
308
+ m.alias_type "integer", "int(4)"
309
+ m.alias_type "int", "int(4)"
310
+ register_class_with_limit m, "smallint(2)", SQLServer::Type::SmallInteger
311
+ m.alias_type "smallint", "smallint(2)"
312
+ register_class_with_limit m, "tinyint(1)", SQLServer::Type::TinyInteger
313
+ m.alias_type "tinyint", "tinyint(1)"
314
+ m.register_type "bit", SQLServer::Type::Boolean.new
277
315
  m.register_type %r{\Adecimal}i do |sql_type|
278
- scale = extract_scale(sql_type)
316
+ scale = extract_scale(sql_type)
279
317
  precision = extract_precision(sql_type)
280
- SQLServer::Type::Decimal.new precision: precision, scale: scale
318
+ if scale == 0
319
+ SQLServer::Type::DecimalWithoutScale.new(precision: precision)
320
+ else
321
+ SQLServer::Type::Decimal.new(precision: precision, scale: scale)
322
+ end
281
323
  end
282
- m.alias_type %r{\Anumeric}i, 'decimal'
283
- m.register_type 'money', SQLServer::Type::Money.new
284
- m.register_type 'smallmoney', SQLServer::Type::SmallMoney.new
324
+ m.alias_type %r{\Anumeric}i, "decimal"
325
+ m.register_type "money", SQLServer::Type::Money.new
326
+ m.register_type "smallmoney", SQLServer::Type::SmallMoney.new
327
+
285
328
  # Approximate Numerics
286
- m.register_type 'float', SQLServer::Type::Float.new
287
- m.register_type 'real', SQLServer::Type::Real.new
329
+ m.register_type "float", SQLServer::Type::Float.new
330
+ m.register_type "real", SQLServer::Type::Real.new
331
+
288
332
  # Date and Time
289
- m.register_type 'date', SQLServer::Type::Date.new
333
+ m.register_type "date", SQLServer::Type::Date.new
290
334
  m.register_type %r{\Adatetime} do |sql_type|
291
335
  precision = extract_precision(sql_type)
292
336
  if precision
@@ -295,48 +339,54 @@ module ActiveRecord
295
339
  SQLServer::Type::DateTime.new
296
340
  end
297
341
  end
298
- m.register_type %r{\Adatetimeoffset}i do |sql_type|
342
+ m.register_type %r{\Adatetimeoffset}i do |sql_type|
299
343
  precision = extract_precision(sql_type)
300
344
  SQLServer::Type::DateTimeOffset.new precision: precision
301
345
  end
302
- m.register_type 'smalldatetime', SQLServer::Type::SmallDateTime.new
346
+ m.register_type "smalldatetime", SQLServer::Type::SmallDateTime.new
303
347
  m.register_type %r{\Atime}i do |sql_type|
304
348
  precision = extract_precision(sql_type) || DEFAULT_TIME_PRECISION
305
349
  SQLServer::Type::Time.new precision: precision
306
350
  end
351
+
307
352
  # Character Strings
308
353
  register_class_with_limit m, %r{\Achar}i, SQLServer::Type::Char
309
354
  register_class_with_limit m, %r{\Avarchar}i, SQLServer::Type::Varchar
310
- m.register_type 'varchar(max)', SQLServer::Type::VarcharMax.new
311
- m.register_type 'text', SQLServer::Type::Text.new
355
+ m.register_type "varchar(max)", SQLServer::Type::VarcharMax.new
356
+ m.register_type "text", SQLServer::Type::Text.new
357
+
312
358
  # Unicode Character Strings
313
359
  register_class_with_limit m, %r{\Anchar}i, SQLServer::Type::UnicodeChar
314
360
  register_class_with_limit m, %r{\Anvarchar}i, SQLServer::Type::UnicodeVarchar
315
- m.alias_type 'string', 'nvarchar(4000)'
316
- m.register_type 'nvarchar(max)', SQLServer::Type::UnicodeVarcharMax.new
317
- m.register_type 'nvarchar(max)', SQLServer::Type::UnicodeVarcharMax.new
318
- m.register_type 'ntext', SQLServer::Type::UnicodeText.new
361
+ m.alias_type "string", "nvarchar(4000)"
362
+ m.register_type "nvarchar(max)", SQLServer::Type::UnicodeVarcharMax.new
363
+ m.register_type "nvarchar(max)", SQLServer::Type::UnicodeVarcharMax.new
364
+ m.register_type "ntext", SQLServer::Type::UnicodeText.new
365
+
319
366
  # Binary Strings
320
367
  register_class_with_limit m, %r{\Abinary}i, SQLServer::Type::Binary
321
368
  register_class_with_limit m, %r{\Avarbinary}i, SQLServer::Type::Varbinary
322
- m.register_type 'varbinary(max)', SQLServer::Type::VarbinaryMax.new
369
+ m.register_type "varbinary(max)", SQLServer::Type::VarbinaryMax.new
370
+
323
371
  # Other Data Types
324
- m.register_type 'uniqueidentifier', SQLServer::Type::Uuid.new
325
- m.register_type 'timestamp', SQLServer::Type::Timestamp.new
372
+ m.register_type "uniqueidentifier", SQLServer::Type::Uuid.new
373
+ m.register_type "timestamp", SQLServer::Type::Timestamp.new
326
374
  end
327
375
 
328
- def translate_exception(e, message)
376
+ def translate_exception(e, message:, sql:, binds:)
329
377
  case message
330
378
  when /(cannot insert duplicate key .* with unique index) | (violation of unique key constraint)/i
331
- RecordNotUnique.new(message)
332
- when /conflicted with the foreign key constraint/i
333
- InvalidForeignKey.new(message)
379
+ RecordNotUnique.new(message, sql: sql, binds: binds)
380
+ when /(conflicted with the foreign key constraint) | (The DELETE statement conflicted with the REFERENCE constraint)/i
381
+ InvalidForeignKey.new(message, sql: sql, binds: binds)
334
382
  when /has been chosen as the deadlock victim/i
335
- DeadlockVictim.new(message)
383
+ DeadlockVictim.new(message, sql: sql, binds: binds)
336
384
  when /database .* does not exist/i
337
- NoDatabaseError.new(message)
385
+ NoDatabaseError.new(message, sql: sql, binds: binds)
338
386
  when /data would be truncated/
339
- ValueTooLong.new(message)
387
+ ValueTooLong.new(message, sql: sql, binds: binds)
388
+ when /connection timed out/
389
+ StatementTimeout.new(message, sql: sql, binds: binds)
340
390
  when /Column '(.*)' is not the same data type as referencing column '(.*)' in foreign key/
341
391
  pk_id, fk_id = SQLServer::Utils.extract_identifiers($1), SQLServer::Utils.extract_identifiers($2)
342
392
  MismatchedForeignKey.new(
@@ -348,9 +398,9 @@ module ActiveRecord
348
398
  primary_key: pk_id.object
349
399
  )
350
400
  when /Cannot insert the value NULL into column.*does not allow nulls/
351
- NotNullViolation.new(message)
401
+ NotNullViolation.new(message, sql: sql, binds: binds)
352
402
  when /Arithmetic overflow error/
353
- RangeError.new(message)
403
+ RangeError.new(message, sql: sql, binds: binds)
354
404
  else
355
405
  super
356
406
  end
@@ -364,7 +414,7 @@ module ActiveRecord
364
414
  when :dblib
365
415
  dblib_connect(config)
366
416
  end
367
- @spid = _raw_select('SELECT @@SPID', fetch: :rows).first.first
417
+ @spid = _raw_select("SELECT @@SPID", fetch: :rows).first.first
368
418
  @version_year = version_year
369
419
  configure_connection
370
420
  end
@@ -383,32 +433,32 @@ module ActiveRecord
383
433
  username: config[:username],
384
434
  password: config[:password],
385
435
  database: config[:database],
386
- tds_version: config[:tds_version] || '7.3',
436
+ tds_version: config[:tds_version] || "7.3",
387
437
  appname: config_appname(config),
388
438
  login_timeout: config_login_timeout(config),
389
439
  timeout: config_timeout(config),
390
- encoding: config_encoding(config),
440
+ encoding: config_encoding(config),
391
441
  azure: config[:azure],
392
442
  contained: config[:contained]
393
443
  ).tap do |client|
394
444
  if config[:azure]
395
- client.execute('SET ANSI_NULLS ON').do
396
- client.execute('SET ANSI_NULL_DFLT_ON ON').do
397
- client.execute('SET ANSI_PADDING ON').do
398
- client.execute('SET ANSI_WARNINGS ON').do
445
+ client.execute("SET ANSI_NULLS ON").do
446
+ client.execute("SET ANSI_NULL_DFLT_ON ON").do
447
+ client.execute("SET ANSI_PADDING ON").do
448
+ client.execute("SET ANSI_WARNINGS ON").do
399
449
  else
400
- client.execute('SET ANSI_DEFAULTS ON').do
450
+ client.execute("SET ANSI_DEFAULTS ON").do
401
451
  end
402
- client.execute('SET QUOTED_IDENTIFIER ON').do
403
- client.execute('SET CURSOR_CLOSE_ON_COMMIT OFF').do
404
- client.execute('SET IMPLICIT_TRANSACTIONS OFF').do
405
- client.execute('SET TEXTSIZE 2147483647').do
406
- client.execute('SET CONCAT_NULL_YIELDS_NULL ON').do
452
+ client.execute("SET QUOTED_IDENTIFIER ON").do
453
+ client.execute("SET CURSOR_CLOSE_ON_COMMIT OFF").do
454
+ client.execute("SET IMPLICIT_TRANSACTIONS OFF").do
455
+ client.execute("SET TEXTSIZE 2147483647").do
456
+ client.execute("SET CONCAT_NULL_YIELDS_NULL ON").do
407
457
  end
408
458
  end
409
459
 
410
460
  def config_appname(config)
411
- config[:appname] || configure_application_name || Rails.application.class.name.split('::').first rescue nil
461
+ config[:appname] || configure_application_name || Rails.application.class.name.split("::").first rescue nil
412
462
  end
413
463
 
414
464
  def config_login_timeout(config)
@@ -423,18 +473,18 @@ module ActiveRecord
423
473
  config[:encoding].present? ? config[:encoding] : nil
424
474
  end
425
475
 
426
- def configure_connection ; end
476
+ def configure_connection; end
427
477
 
428
- def configure_application_name ; end
478
+ def configure_application_name; end
429
479
 
430
480
  def initialize_dateformatter
431
481
  @database_dateformat = user_options_dateformat
432
482
  a, b, c = @database_dateformat.each_char.to_a
433
- [a, b, c].each { |f| f.upcase! if f == 'y' }
483
+ [a, b, c].each { |f| f.upcase! if f == "y" }
434
484
  dateformat = "%#{a}-%#{b}-%#{c}"
435
485
  ::Date::DATE_FORMATS[:_sqlserver_dateformat] = dateformat
436
486
  ::Time::DATE_FORMATS[:_sqlserver_dateformat] = dateformat
437
- ::Time::DATE_FORMATS[:_sqlserver_time] = '%H:%M:%S'
487
+ ::Time::DATE_FORMATS[:_sqlserver_time] = "%H:%M:%S"
438
488
  ::Time::DATE_FORMATS[:_sqlserver_datetime] = "#{dateformat} %H:%M:%S"
439
489
  ::Time::DATE_FORMATS[:_sqlserver_datetimeoffset] = lambda { |time|
440
490
  time.strftime "#{dateformat} %H:%M:%S.%9N #{time.formatted_offset}"
@@ -443,13 +493,14 @@ module ActiveRecord
443
493
 
444
494
  def version_year
445
495
  return 2016 if sqlserver_version =~ /vNext/
496
+
446
497
  /SQL Server (\d+)/.match(sqlserver_version).to_a.last.to_s.to_i
447
- rescue StandardError => e
498
+ rescue StandardError
448
499
  2016
449
500
  end
450
501
 
451
502
  def sqlserver_version
452
- @sqlserver_version ||= _raw_select('SELECT @@version', fetch: :rows).first.first.to_s
503
+ @sqlserver_version ||= _raw_select("SELECT @@version", fetch: :rows).first.first.to_s
453
504
  end
454
505
  end
455
506
  end