activerecord-sqlserver-adapter 5.2.0 → 6.0.1

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