activerecord-sqlserver-adapter 5.2.0 → 6.0.1

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