activerecord-sqlserver-adapter 5.2.1 → 6.0.2

Sign up to get free protection for your applications and to get access to all the features.
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