activerecord-sqlserver-adapter 6.0.0.rc1 → 6.1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (149) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +26 -0
  3. data/.gitignore +1 -0
  4. data/.rubocop.yml +29 -0
  5. data/CHANGELOG.md +18 -23
  6. data/Gemfile +11 -5
  7. data/Guardfile +9 -8
  8. data/README.md +32 -3
  9. data/RUNNING_UNIT_TESTS.md +1 -1
  10. data/Rakefile +12 -16
  11. data/VERSION +1 -1
  12. data/activerecord-sqlserver-adapter.gemspec +4 -4
  13. data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +0 -4
  14. data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +1 -4
  15. data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +3 -13
  16. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +8 -5
  17. data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +2 -3
  18. data/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb +2 -3
  19. data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +0 -4
  20. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +58 -43
  21. data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +7 -12
  22. data/lib/active_record/connection_adapters/sqlserver/errors.rb +0 -3
  23. data/lib/active_record/connection_adapters/sqlserver/quoting.rb +15 -15
  24. data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +22 -3
  25. data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +16 -10
  26. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +131 -105
  27. data/lib/active_record/connection_adapters/sqlserver/showplan.rb +6 -8
  28. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +2 -2
  29. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +1 -1
  30. data/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +25 -7
  31. data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +1 -5
  32. data/lib/active_record/connection_adapters/sqlserver/transaction.rb +6 -10
  33. data/lib/active_record/connection_adapters/sqlserver/type.rb +36 -35
  34. data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +0 -2
  35. data/lib/active_record/connection_adapters/sqlserver/type/binary.rb +0 -2
  36. data/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +0 -2
  37. data/lib/active_record/connection_adapters/sqlserver/type/char.rb +2 -2
  38. data/lib/active_record/connection_adapters/sqlserver/type/data.rb +0 -2
  39. data/lib/active_record/connection_adapters/sqlserver/type/date.rb +2 -3
  40. data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +2 -3
  41. data/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb +0 -2
  42. data/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +0 -2
  43. data/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +0 -2
  44. data/lib/active_record/connection_adapters/sqlserver/type/decimal_without_scale.rb +22 -0
  45. data/lib/active_record/connection_adapters/sqlserver/type/float.rb +0 -2
  46. data/lib/active_record/connection_adapters/sqlserver/type/integer.rb +0 -2
  47. data/lib/active_record/connection_adapters/sqlserver/type/json.rb +0 -1
  48. data/lib/active_record/connection_adapters/sqlserver/type/money.rb +0 -2
  49. data/lib/active_record/connection_adapters/sqlserver/type/real.rb +0 -2
  50. data/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +0 -2
  51. data/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +0 -2
  52. data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +0 -2
  53. data/lib/active_record/connection_adapters/sqlserver/type/string.rb +0 -2
  54. data/lib/active_record/connection_adapters/sqlserver/type/text.rb +0 -2
  55. data/lib/active_record/connection_adapters/sqlserver/type/time.rb +2 -3
  56. data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +6 -9
  57. data/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +0 -2
  58. data/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +0 -2
  59. data/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +1 -3
  60. data/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb +0 -2
  61. data/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +0 -2
  62. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +0 -2
  63. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +0 -2
  64. data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +1 -2
  65. data/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +1 -3
  66. data/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +0 -2
  67. data/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +1 -3
  68. data/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +0 -2
  69. data/lib/active_record/connection_adapters/sqlserver/utils.rb +8 -11
  70. data/lib/active_record/connection_adapters/sqlserver/version.rb +0 -2
  71. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +170 -136
  72. data/lib/active_record/connection_adapters/sqlserver_column.rb +16 -1
  73. data/lib/active_record/sqlserver_base.rb +9 -15
  74. data/lib/active_record/tasks/sqlserver_database_tasks.rb +36 -39
  75. data/lib/activerecord-sqlserver-adapter.rb +1 -1
  76. data/lib/arel/visitors/sqlserver.rb +126 -50
  77. data/lib/arel_sqlserver.rb +2 -2
  78. data/test/cases/adapter_test_sqlserver.rb +203 -190
  79. data/test/cases/change_column_collation_test_sqlserver.rb +33 -0
  80. data/test/cases/change_column_null_test_sqlserver.rb +12 -12
  81. data/test/cases/coerced_tests.rb +656 -318
  82. data/test/cases/column_test_sqlserver.rb +285 -284
  83. data/test/cases/connection_test_sqlserver.rb +15 -20
  84. data/test/cases/disconnected_test_sqlserver.rb +39 -0
  85. data/test/cases/execute_procedure_test_sqlserver.rb +26 -19
  86. data/test/cases/fetch_test_sqlserver.rb +14 -22
  87. data/test/cases/fully_qualified_identifier_test_sqlserver.rb +12 -18
  88. data/test/cases/helper_sqlserver.rb +13 -15
  89. data/test/cases/in_clause_test_sqlserver.rb +36 -9
  90. data/test/cases/index_test_sqlserver.rb +13 -15
  91. data/test/cases/json_test_sqlserver.rb +23 -25
  92. data/test/cases/lateral_test_sqlserver.rb +35 -0
  93. data/test/cases/migration_test_sqlserver.rb +71 -26
  94. data/test/cases/optimizer_hints_test_sqlserver.rb +72 -0
  95. data/test/cases/order_test_sqlserver.rb +57 -53
  96. data/test/cases/pessimistic_locking_test_sqlserver.rb +25 -33
  97. data/test/cases/primary_keys_test_sqlserver.rb +103 -0
  98. data/test/cases/rake_test_sqlserver.rb +33 -46
  99. data/test/cases/schema_dumper_test_sqlserver.rb +121 -108
  100. data/test/cases/schema_test_sqlserver.rb +18 -26
  101. data/test/cases/scratchpad_test_sqlserver.rb +2 -4
  102. data/test/cases/showplan_test_sqlserver.rb +24 -33
  103. data/test/cases/specific_schema_test_sqlserver.rb +66 -65
  104. data/test/cases/transaction_test_sqlserver.rb +16 -19
  105. data/test/cases/trigger_test_sqlserver.rb +12 -12
  106. data/test/cases/utils_test_sqlserver.rb +68 -70
  107. data/test/cases/uuid_test_sqlserver.rb +11 -13
  108. data/test/debug.rb +6 -6
  109. data/test/migrations/create_clients_and_change_column_collation.rb +19 -0
  110. data/test/migrations/create_clients_and_change_column_null.rb +1 -1
  111. data/test/migrations/transaction_table/1_table_will_never_be_created.rb +2 -4
  112. data/test/models/sqlserver/booking.rb +1 -1
  113. data/test/models/sqlserver/customers_view.rb +1 -1
  114. data/test/models/sqlserver/dollar_table_name.rb +1 -1
  115. data/test/models/sqlserver/edge_schema.rb +1 -3
  116. data/test/models/sqlserver/fk_has_fk.rb +1 -1
  117. data/test/models/sqlserver/fk_has_pk.rb +1 -1
  118. data/test/models/sqlserver/natural_pk_data.rb +2 -2
  119. data/test/models/sqlserver/natural_pk_int_data.rb +1 -1
  120. data/test/models/sqlserver/no_pk_data.rb +1 -1
  121. data/test/models/sqlserver/object_default.rb +1 -1
  122. data/test/models/sqlserver/quoted_table.rb +2 -2
  123. data/test/models/sqlserver/quoted_view_1.rb +1 -1
  124. data/test/models/sqlserver/quoted_view_2.rb +1 -1
  125. data/test/models/sqlserver/sst_memory.rb +1 -1
  126. data/test/models/sqlserver/sst_string_collation.rb +3 -0
  127. data/test/models/sqlserver/string_default.rb +1 -1
  128. data/test/models/sqlserver/string_defaults_big_view.rb +1 -1
  129. data/test/models/sqlserver/string_defaults_view.rb +1 -1
  130. data/test/models/sqlserver/tinyint_pk.rb +1 -1
  131. data/test/models/sqlserver/trigger.rb +2 -2
  132. data/test/models/sqlserver/trigger_history.rb +1 -1
  133. data/test/models/sqlserver/upper.rb +1 -1
  134. data/test/models/sqlserver/uppered.rb +1 -1
  135. data/test/models/sqlserver/uuid.rb +1 -1
  136. data/test/schema/sqlserver_specific_schema.rb +36 -21
  137. data/test/support/coerceable_test_sqlserver.rb +1 -4
  138. data/test/support/connection_reflection.rb +1 -2
  139. data/test/support/core_ext/query_cache.rb +1 -1
  140. data/test/support/load_schema_sqlserver.rb +3 -5
  141. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic.dump +0 -0
  142. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic_associations.dump +0 -0
  143. data/test/support/minitest_sqlserver.rb +1 -1
  144. data/test/support/paths_sqlserver.rb +9 -11
  145. data/test/support/rake_helpers.rb +12 -10
  146. data/test/support/sql_counter_sqlserver.rb +14 -16
  147. data/test/support/test_in_memory_oltp.rb +7 -7
  148. metadata +31 -11
  149. data/.travis.yml +0 -23
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cases/helper_sqlserver"
4
+ require "migrations/create_clients_and_change_column_collation"
5
+
6
+ class ChangeColumnCollationTestSqlServer < ActiveRecord::TestCase
7
+ before do
8
+ @old_verbose = ActiveRecord::Migration.verbose
9
+ ActiveRecord::Migration.verbose = false
10
+ CreateClientsAndChangeColumnCollation.new.up
11
+ end
12
+
13
+ after do
14
+ CreateClientsAndChangeColumnCollation.new.down
15
+ ActiveRecord::Migration.verbose = @old_verbose
16
+ end
17
+
18
+ def find_column(table, name)
19
+ table.find { |column| column.name == name }
20
+ end
21
+
22
+ let(:clients_table) { connection.columns("clients") }
23
+ let(:name_column) { find_column(clients_table, "name") }
24
+ let(:code_column) { find_column(clients_table, "code") }
25
+
26
+ it "change column collation to other than default" do
27
+ _(name_column.collation).must_equal "SQL_Latin1_General_CP1_CS_AS"
28
+ end
29
+
30
+ it "change column collation to default" do
31
+ _(code_column.collation).must_equal "SQL_Latin1_General_CP1_CI_AS"
32
+ end
33
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'cases/helper_sqlserver'
4
- require 'migrations/create_clients_and_change_column_null'
3
+ require "cases/helper_sqlserver"
4
+ require "migrations/create_clients_and_change_column_null"
5
5
 
6
6
  class ChangeColumnNullTestSqlServer < ActiveRecord::TestCase
7
7
  before do
@@ -19,25 +19,25 @@ class ChangeColumnNullTestSqlServer < ActiveRecord::TestCase
19
19
  table.find { |column| column.name == name }
20
20
  end
21
21
 
22
- let(:clients_table) { connection.columns('clients') }
23
- let(:name_column) { find_column(clients_table, 'name') }
24
- let(:code_column) { find_column(clients_table, 'code') }
25
- let(:value_column) { find_column(clients_table, 'value') }
22
+ let(:clients_table) { connection.columns("clients") }
23
+ let(:name_column) { find_column(clients_table, "name") }
24
+ let(:code_column) { find_column(clients_table, "code") }
25
+ let(:value_column) { find_column(clients_table, "value") }
26
26
 
27
- describe '#change_column_null' do
28
- it 'does not change the column limit' do
27
+ describe "#change_column_null" do
28
+ it "does not change the column limit" do
29
29
  _(name_column.limit).must_equal 15
30
30
  end
31
31
 
32
- it 'does not change the column default' do
33
- _(code_column.default).must_equal 'n/a'
32
+ it "does not change the column default" do
33
+ _(code_column.default).must_equal "n/a"
34
34
  end
35
35
 
36
- it 'does not change the column precision' do
36
+ it "does not change the column precision" do
37
37
  _(value_column.precision).must_equal 32
38
38
  end
39
39
 
40
- it 'does not change the column scale' do
40
+ it "does not change the column scale" do
41
41
  _(value_column.scale).must_equal 8
42
42
  end
43
43
  end
@@ -1,10 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'cases/helper_sqlserver'
3
+ require "cases/helper_sqlserver"
4
4
 
5
-
6
-
7
- require 'models/event'
5
+ require "models/event"
8
6
  class UniquenessValidationTest < ActiveRecord::TestCase
9
7
  # So sp_executesql swallows this exception. Run without prepared to see it.
10
8
  coerce_tests! :test_validate_uniqueness_with_limit
@@ -26,25 +24,35 @@ class UniquenessValidationTest < ActiveRecord::TestCase
26
24
  end
27
25
  end
28
26
 
29
- # Skip the test if database is case-insensitive.
30
- coerce_tests! :test_validate_case_sensitive_uniqueness_by_default
31
- def test_validate_case_sensitive_uniqueness_by_default_coerced
32
- database_collation = connection.select_one("SELECT collation_name FROM sys.databases WHERE name = 'activerecord_unittest'").values.first
33
- skip if database_collation.include?('_CI_')
27
+ # Same as original coerced test except that it handles default SQL Server case-insensitive collation.
28
+ coerce_tests! :test_validate_uniqueness_by_default_database_collation
29
+ def test_validate_uniqueness_by_default_database_collation_coerced
30
+ Topic.validates_uniqueness_of(:author_email_address)
34
31
 
35
- original_test_validate_case_sensitive_uniqueness_by_default_coerced
36
- end
37
- end
32
+ topic1 = Topic.new(author_email_address: "david@loudthinking.com")
33
+ topic2 = Topic.new(author_email_address: "David@loudthinking.com")
34
+
35
+ assert_equal 1, Topic.where(author_email_address: "david@loudthinking.com").count
38
36
 
37
+ assert_not topic1.valid?
38
+ assert_not topic1.save
39
39
 
40
+ # Case insensitive collation (SQL_Latin1_General_CP1_CI_AS) by default.
41
+ # Should not allow "David" if "david" exists.
42
+ assert_not topic2.valid?
43
+ assert_not topic2.save
40
44
 
45
+ assert_equal 1, Topic.where(author_email_address: "david@loudthinking.com").count
46
+ assert_equal 1, Topic.where(author_email_address: "David@loudthinking.com").count
47
+ end
48
+ end
41
49
 
42
- require 'models/event'
50
+ require "models/event"
43
51
  module ActiveRecord
44
52
  class AdapterTest < ActiveRecord::TestCase
45
- # I really don`t think we can support legacy binds.
46
- coerce_tests! :test_select_all_with_legacy_binds
47
- coerce_tests! :test_insert_update_delete_with_legacy_binds
53
+ # Legacy binds are not supported.
54
+ coerce_tests! :test_select_all_insert_update_delete_with_casted_binds
55
+ coerce_tests! :test_select_all_insert_update_delete_with_legacy_binds
48
56
 
49
57
  # As far as I can tell, SQL Server does not support null bytes in strings.
50
58
  coerce_tests! :test_update_prepared_statement
@@ -54,23 +62,70 @@ module ActiveRecord
54
62
  def test_value_limit_violations_are_translated_to_specific_exception_coerced
55
63
  connection.unprepared_statement do
56
64
  error = assert_raises(ActiveRecord::ValueTooLong) do
57
- Event.create(title: 'abcdefgh')
65
+ Event.create(title: "abcdefgh")
58
66
  end
59
67
  assert_not_nil error.cause
60
68
  end
61
69
  end
70
+ end
71
+ end
62
72
 
73
+ module ActiveRecord
74
+ class AdapterPreventWritesTest < ActiveRecord::TestCase
63
75
  # Fix randomly failing test. The loading of the model's schema was affecting the test.
64
76
  coerce_tests! :test_errors_when_an_insert_query_is_called_while_preventing_writes
65
77
  def test_errors_when_an_insert_query_is_called_while_preventing_writes_coerced
66
78
  Subscriber.send(:load_schema!)
67
79
  original_test_errors_when_an_insert_query_is_called_while_preventing_writes
68
80
  end
81
+
82
+ # Fix randomly failing test. The loading of the model's schema was affecting the test.
83
+ coerce_tests! :test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_containing_read_command_is_called_while_preventing_writes
84
+ def test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_containing_read_command_is_called_while_preventing_writes_coerced
85
+ Subscriber.send(:load_schema!)
86
+ original_test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_containing_read_command_is_called_while_preventing_writes
87
+ end
88
+
89
+ # Fix randomly failing test. The loading of the model's schema was affecting the test.
90
+ coerce_tests! :test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_is_called_while_preventing_writes
91
+ def test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_is_called_while_preventing_writes_coerced
92
+ Subscriber.send(:load_schema!)
93
+ original_test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_is_called_while_preventing_writes
94
+ end
69
95
  end
70
96
  end
71
97
 
98
+ module ActiveRecord
99
+ class AdapterPreventWritesLegacyTest < ActiveRecord::TestCase
100
+ # Fix randomly failing test. The loading of the model's schema was affecting the test.
101
+ coerce_tests! :test_errors_when_an_insert_query_is_called_while_preventing_writes
102
+ def test_errors_when_an_insert_query_is_called_while_preventing_writes_coerced
103
+ Subscriber.send(:load_schema!)
104
+ original_test_errors_when_an_insert_query_is_called_while_preventing_writes
105
+ end
72
106
 
107
+ # Fix randomly failing test. The loading of the model's schema was affecting the test.
108
+ coerce_tests! :test_errors_when_an_insert_query_prefixed_by_a_slash_star_comment_is_called_while_preventing_writes
109
+ def test_errors_when_an_insert_query_prefixed_by_a_slash_star_comment_is_called_while_preventing_writes_coerced
110
+ Subscriber.send(:load_schema!)
111
+ original_test_errors_when_an_insert_query_prefixed_by_a_slash_star_comment_is_called_while_preventing_writes
112
+ end
73
113
 
114
+ # Fix randomly failing test. The loading of the model's schema was affecting the test.
115
+ coerce_tests! :test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_is_called_while_preventing_writes
116
+ def test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_is_called_while_preventing_writes_coerced
117
+ Subscriber.send(:load_schema!)
118
+ original_test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_is_called_while_preventing_writes
119
+ end
120
+
121
+ # Fix randomly failing test. The loading of the model's schema was affecting the test.
122
+ coerce_tests! :test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_containing_read_command_is_called_while_preventing_writes
123
+ def test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_containing_read_command_is_called_while_preventing_writes_coerced
124
+ Subscriber.send(:load_schema!)
125
+ original_test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_containing_read_command_is_called_while_preventing_writes
126
+ end
127
+ end
128
+ end
74
129
 
75
130
  module ActiveRecord
76
131
  class AdapterTestWithoutTransaction < ActiveRecord::TestCase
@@ -125,15 +180,12 @@ module ActiveRecord
125
180
  end
126
181
  end
127
182
 
128
-
129
-
130
-
131
- require 'models/topic'
183
+ require "models/topic"
132
184
  class AttributeMethodsTest < ActiveRecord::TestCase
133
185
  # Use IFF for boolean statement in SELECT
134
186
  coerce_tests! %r{typecast attribute from select to false}
135
187
  def test_typecast_attribute_from_select_to_false_coerced
136
- Topic.create(:title => 'Budget')
188
+ Topic.create(:title => "Budget")
137
189
  topic = Topic.all.merge!(:select => "topics.*, IIF (1 = 2, 1, 0) as is_test").first
138
190
  assert_not_predicate topic, :is_test?
139
191
  end
@@ -141,21 +193,18 @@ class AttributeMethodsTest < ActiveRecord::TestCase
141
193
  # Use IFF for boolean statement in SELECT
142
194
  coerce_tests! %r{typecast attribute from select to true}
143
195
  def test_typecast_attribute_from_select_to_true_coerced
144
- Topic.create(:title => 'Budget')
196
+ Topic.create(:title => "Budget")
145
197
  topic = Topic.all.merge!(:select => "topics.*, IIF (1 = 1, 1, 0) as is_test").first
146
198
  assert_predicate topic, :is_test?
147
199
  end
148
200
  end
149
201
 
150
-
151
-
152
-
153
202
  class BasicsTest < ActiveRecord::TestCase
154
203
  # Use square brackets as SQL Server escaped character
155
204
  coerce_tests! :test_column_names_are_escaped
156
205
  def test_column_names_are_escaped_coerced
157
206
  conn = ActiveRecord::Base.connection
158
- assert_equal '[t]]]', conn.quote_column_name('t]')
207
+ assert_equal "[t]]]", conn.quote_column_name("t]")
159
208
  end
160
209
 
161
210
  # Just like PostgreSQLAdapter does.
@@ -175,7 +224,7 @@ class BasicsTest < ActiveRecord::TestCase
175
224
  end
176
225
 
177
226
  def test_update_date_time_attributes_with_default_timezone_local
178
- with_env_tz 'America/New_York' do
227
+ with_env_tz "America/New_York" do
179
228
  with_timezone_config default: :local do
180
229
  Time.use_zone("Eastern Time (US & Canada)") do
181
230
  topic = Topic.find(1)
@@ -190,7 +239,7 @@ class BasicsTest < ActiveRecord::TestCase
190
239
  # SQL Server does not have query for release_savepoint
191
240
  coerce_tests! %r{an empty transaction does not raise if preventing writes}
192
241
  test "an empty transaction does not raise if preventing writes coerced" do
193
- ActiveRecord::Base.connection_handler.while_preventing_writes do
242
+ ActiveRecord::Base.while_preventing_writes do
194
243
  assert_queries(1, ignore_none: true) do
195
244
  Bird.transaction do
196
245
  ActiveRecord::Base.connection.materialize_transactions
@@ -200,9 +249,6 @@ class BasicsTest < ActiveRecord::TestCase
200
249
  end
201
250
  end
202
251
 
203
-
204
-
205
-
206
252
  class BelongsToAssociationsTest < ActiveRecord::TestCase
207
253
  # Since @client.firm is a single first/top, and we use FETCH the order clause is used.
208
254
  coerce_tests! :test_belongs_to_does_not_use_order_by
@@ -227,9 +273,6 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
227
273
  end
228
274
  end
229
275
 
230
-
231
-
232
-
233
276
  module ActiveRecord
234
277
  class BindParameterTest < ActiveRecord::TestCase
235
278
  # Same as original coerced test except log is found using `EXEC sp_executesql` wrapper.
@@ -254,11 +297,57 @@ module ActiveRecord
254
297
  coerce_tests! :test_statement_cache_with_find_by
255
298
  coerce_tests! :test_statement_cache_with_in_clause
256
299
  coerce_tests! :test_statement_cache_with_sql_string_literal
257
- end
258
- end
259
300
 
301
+ # Same as original coerced test except prepared statements include `EXEC sp_executesql` wrapper.
302
+ coerce_tests! :test_bind_params_to_sql_with_prepared_statements, :test_bind_params_to_sql_with_unprepared_statements
303
+ def test_bind_params_to_sql_with_prepared_statements_coerced
304
+ assert_bind_params_to_sql_coerced(prepared: true)
305
+ end
260
306
 
307
+ def test_bind_params_to_sql_with_unprepared_statements_coerced
308
+ @connection.unprepared_statement do
309
+ assert_bind_params_to_sql_coerced(prepared: false)
310
+ end
311
+ end
261
312
 
313
+ private
314
+
315
+ def assert_bind_params_to_sql_coerced(prepared:)
316
+ table = Author.quoted_table_name
317
+ pk = "#{table}.#{Author.quoted_primary_key}"
318
+
319
+ # prepared_statements: true
320
+ #
321
+ # EXEC sp_executesql N'SELECT [authors].* FROM [authors] WHERE [authors].[id] IN (@0, @1, @2) OR [authors].[id] IS NULL)', N'@0 bigint, @1 bigint, @2 bigint', @0 = 1, @1 = 2, @2 = 3
322
+ #
323
+ # prepared_statements: false
324
+ #
325
+ # SELECT [authors].* FROM [authors] WHERE ([authors].[id] IN (1, 2, 3) OR [authors].[id] IS NULL)
326
+ #
327
+ sql_unprepared = "SELECT #{table}.* FROM #{table} WHERE (#{pk} IN (#{bind_params(1..3)}) OR #{pk} IS NULL)"
328
+ sql_prepared = "EXEC sp_executesql N'SELECT #{table}.* FROM #{table} WHERE (#{pk} IN (#{bind_params(1..3)}) OR #{pk} IS NULL)', N'@0 bigint, @1 bigint, @2 bigint', @0 = 1, @1 = 2, @2 = 3"
329
+
330
+ authors = Author.where(id: [1, 2, 3, nil])
331
+ assert_equal sql_unprepared, @connection.to_sql(authors.arel)
332
+ assert_sql(prepared ? sql_prepared : sql_unprepared) { assert_equal 3, authors.length }
333
+
334
+ # prepared_statements: true
335
+ #
336
+ # EXEC sp_executesql N'SELECT [authors].* FROM [authors] WHERE [authors].[id] IN (@0, @1, @2)', N'@0 bigint, @1 bigint, @2 bigint', @0 = 1, @1 = 2, @2 = 3
337
+ #
338
+ # prepared_statements: false
339
+ #
340
+ # SELECT [authors].* FROM [authors] WHERE [authors].[id] IN (1, 2, 3)
341
+ #
342
+ sql_unprepared = "SELECT #{table}.* FROM #{table} WHERE #{pk} IN (#{bind_params(1..3)})"
343
+ sql_prepared = "EXEC sp_executesql N'SELECT #{table}.* FROM #{table} WHERE #{pk} IN (#{bind_params(1..3)})', N'@0 bigint, @1 bigint, @2 bigint', @0 = 1, @1 = 2, @2 = 3"
344
+
345
+ authors = Author.where(id: [1, 2, 3, 9223372036854775808])
346
+ assert_equal sql_unprepared, @connection.to_sql(authors.arel)
347
+ assert_sql(prepared ? sql_prepared : sql_unprepared) { assert_equal 3, authors.length }
348
+ end
349
+ end
350
+ end
262
351
 
263
352
  module ActiveRecord
264
353
  class InstrumentationTest < ActiveRecord::TestCase
@@ -271,9 +360,6 @@ module ActiveRecord
271
360
  end
272
361
  end
273
362
 
274
-
275
-
276
-
277
363
  class CalculationsTest < ActiveRecord::TestCase
278
364
  # Fix randomly failing test. The loading of the model's schema was affecting the test.
279
365
  coerce_tests! :test_offset_is_kept
@@ -282,11 +368,112 @@ class CalculationsTest < ActiveRecord::TestCase
282
368
  original_test_offset_is_kept
283
369
  end
284
370
 
285
- # Are decimal, not integer.
371
+ # The SQL Server `AVG()` function for a list of integers returns an integer (not a decimal).
286
372
  coerce_tests! :test_should_return_decimal_average_of_integer_field
287
373
  def test_should_return_decimal_average_of_integer_field_coerced
288
374
  value = Account.average(:id)
289
- assert_equal BigDecimal('3.0').to_s, BigDecimal(value).to_s
375
+ assert_equal 3, value
376
+ end
377
+
378
+ # In SQL Server the `AVG()` function for a list of integers returns an integer so need to cast values as decimals before averaging.
379
+ # Match SQL Server limit implementation.
380
+ coerce_tests! :test_select_avg_with_group_by_as_virtual_attribute_with_sql
381
+ def test_select_avg_with_group_by_as_virtual_attribute_with_sql_coerced
382
+ rails_core = companies(:rails_core)
383
+
384
+ sql = <<~SQL
385
+ SELECT firm_id, AVG(CAST(credit_limit AS DECIMAL)) AS avg_credit_limit
386
+ FROM accounts
387
+ WHERE firm_id = ?
388
+ GROUP BY firm_id
389
+ ORDER BY firm_id
390
+ OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY
391
+ SQL
392
+
393
+ account = Account.find_by_sql([sql, rails_core]).first
394
+
395
+ # id was not selected, so it should be nil
396
+ # (cannot select id because it wasn't used in the GROUP BY clause)
397
+ assert_nil account.id
398
+
399
+ # firm_id was explicitly selected, so it should be present
400
+ assert_equal(rails_core, account.firm)
401
+
402
+ # avg_credit_limit should be present as a virtual attribute
403
+ assert_equal(52.5, account.avg_credit_limit)
404
+ end
405
+
406
+ # In SQL Server the `AVG()` function for a list of integers returns an integer so need to cast values as decimals before averaging.
407
+ # Order column must be in the GROUP clause.
408
+ coerce_tests! :test_select_avg_with_group_by_as_virtual_attribute_with_ar
409
+ def test_select_avg_with_group_by_as_virtual_attribute_with_ar_coerced
410
+ rails_core = companies(:rails_core)
411
+
412
+ account = Account
413
+ .select(:firm_id, "AVG(CAST(credit_limit AS DECIMAL)) AS avg_credit_limit")
414
+ .where(firm: rails_core)
415
+ .group(:firm_id)
416
+ .order(:firm_id)
417
+ .take!
418
+
419
+ # id was not selected, so it should be nil
420
+ # (cannot select id because it wasn't used in the GROUP BY clause)
421
+ assert_nil account.id
422
+
423
+ # firm_id was explicitly selected, so it should be present
424
+ assert_equal(rails_core, account.firm)
425
+
426
+ # avg_credit_limit should be present as a virtual attribute
427
+ assert_equal(52.5, account.avg_credit_limit)
428
+ end
429
+
430
+ # In SQL Server the `AVG()` function for a list of integers returns an integer so need to cast values as decimals before averaging.
431
+ # SELECT columns must be in the GROUP clause.
432
+ # Match SQL Server limit implementation.
433
+ coerce_tests! :test_select_avg_with_joins_and_group_by_as_virtual_attribute_with_sql
434
+ def test_select_avg_with_joins_and_group_by_as_virtual_attribute_with_sql_coerced
435
+ rails_core = companies(:rails_core)
436
+
437
+ sql = <<~SQL
438
+ SELECT companies.*, AVG(CAST(accounts.credit_limit AS DECIMAL)) AS avg_credit_limit
439
+ FROM companies
440
+ INNER JOIN accounts ON companies.id = accounts.firm_id
441
+ WHERE companies.id = ?
442
+ GROUP BY companies.id, companies.type, companies.firm_id, companies.firm_name, companies.name, companies.client_of, companies.rating, companies.account_id, companies.description
443
+ ORDER BY companies.id
444
+ OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY
445
+ SQL
446
+
447
+ firm = DependentFirm.find_by_sql([sql, rails_core]).first
448
+
449
+ # all the DependentFirm attributes should be present
450
+ assert_equal rails_core, firm
451
+ assert_equal rails_core.name, firm.name
452
+
453
+ # avg_credit_limit should be present as a virtual attribute
454
+ assert_equal(52.5, firm.avg_credit_limit)
455
+ end
456
+
457
+
458
+ # In SQL Server the `AVG()` function for a list of integers returns an integer so need to cast values as decimals before averaging.
459
+ # SELECT columns must be in the GROUP clause.
460
+ coerce_tests! :test_select_avg_with_joins_and_group_by_as_virtual_attribute_with_ar
461
+ def test_select_avg_with_joins_and_group_by_as_virtual_attribute_with_ar_coerced
462
+ rails_core = companies(:rails_core)
463
+
464
+ firm = DependentFirm
465
+ .select("companies.*", "AVG(CAST(accounts.credit_limit AS DECIMAL)) AS avg_credit_limit")
466
+ .where(id: rails_core)
467
+ .joins(:account)
468
+ .group(:id, :type, :firm_id, :firm_name, :name, :client_of, :rating, :account_id, :description)
469
+ .take!
470
+
471
+ # all the DependentFirm attributes should be present
472
+ assert_equal rails_core, firm
473
+ assert_equal rails_core.name, firm.name
474
+
475
+ # avg_credit_limit should be present as a virtual attribute
476
+ assert_equal(52.5, firm.avg_credit_limit)
290
477
  end
291
478
 
292
479
  # Match SQL Server limit implementation
@@ -317,9 +504,6 @@ class CalculationsTest < ActiveRecord::TestCase
317
504
  coerce_tests! :test_having_with_strong_parameters
318
505
  end
319
506
 
320
-
321
-
322
-
323
507
  module ActiveRecord
324
508
  class Migration
325
509
  class ChangeSchemaTest < ActiveRecord::TestCase
@@ -351,9 +535,6 @@ module ActiveRecord
351
535
  end
352
536
  end
353
537
 
354
-
355
-
356
-
357
538
  module ActiveRecord
358
539
  module ConnectionAdapters
359
540
  class QuoteARBaseTest < ActiveRecord::TestCase
@@ -361,22 +542,23 @@ module ActiveRecord
361
542
  coerce_tests! :test_quote_ar_object
362
543
  def test_quote_ar_object_coerced
363
544
  value = DatetimePrimaryKey.new(id: @time)
364
- assert_equal "'02-14-2017 12:34:56.79'", @connection.quote(value)
545
+ assert_deprecated do
546
+ assert_equal "'02-14-2017 12:34:56.79'", @connection.quote(value)
547
+ end
365
548
  end
366
549
 
367
550
  # Use our date format.
368
551
  coerce_tests! :test_type_cast_ar_object
369
552
  def test_type_cast_ar_object_coerced
370
553
  value = DatetimePrimaryKey.new(id: @time)
371
- assert_equal "02-14-2017 12:34:56.79", @connection.type_cast(value)
554
+ assert_deprecated do
555
+ assert_equal "02-14-2017 12:34:56.79", @connection.type_cast(value)
556
+ end
372
557
  end
373
558
  end
374
559
  end
375
560
  end
376
561
 
377
-
378
-
379
-
380
562
  module ActiveRecord
381
563
  class Migration
382
564
  class ColumnAttributesTest < ActiveRecord::TestCase
@@ -391,16 +573,13 @@ module ActiveRecord
391
573
  end
392
574
  end
393
575
 
394
-
395
-
396
-
397
576
  module ActiveRecord
398
577
  class Migration
399
578
  class ColumnsTest < ActiveRecord::TestCase
400
579
  # Our defaults are real 70000 integers vs '70000' strings.
401
580
  coerce_tests! :test_rename_column_preserves_default_value_not_null
402
581
  def test_rename_column_preserves_default_value_not_null_coerced
403
- add_column 'test_models', 'salary', :integer, :default => 70000
582
+ add_column "test_models", "salary", :integer, :default => 70000
404
583
  default_before = connection.columns("test_models").find { |c| c.name == "salary" }.default
405
584
  assert_equal 70000, default_before
406
585
  rename_column "test_models", "salary", "annual_salary"
@@ -416,9 +595,9 @@ module ActiveRecord
416
595
  add_column "test_models", :hat_size, :integer
417
596
  add_column "test_models", :hat_style, :string, :limit => 100
418
597
  add_index "test_models", ["hat_style", "hat_size"], :unique => true
419
- assert_equal 1, connection.indexes('test_models').size
598
+ assert_equal 1, connection.indexes("test_models").size
420
599
  remove_column("test_models", "hat_size")
421
- assert_equal [], connection.indexes('test_models').map(&:name)
600
+ assert_equal [], connection.indexes("test_models").map(&:name)
422
601
  end
423
602
 
424
603
  # Choose `StatementInvalid` vs `ActiveRecordError`.
@@ -433,51 +612,12 @@ module ActiveRecord
433
612
  end
434
613
  end
435
614
 
436
-
437
-
438
-
439
615
  class MigrationTest < ActiveRecord::TestCase
440
- # We do not have do the DecimalWithoutScale type.
441
- coerce_tests! :test_add_table_with_decimals
442
- def test_add_table_with_decimals_coerced
443
- Person.connection.drop_table :big_numbers rescue nil
444
- assert !BigNumber.table_exists?
445
- GiveMeBigNumbers.up
446
- BigNumber.reset_column_information
447
- assert BigNumber.create(
448
- :bank_balance => 1586.43,
449
- :big_bank_balance => BigDecimal("1000234000567.95"),
450
- :world_population => 6000000000,
451
- :my_house_population => 3,
452
- :value_of_e => BigDecimal("2.7182818284590452353602875")
453
- )
454
- b = BigNumber.first
455
- assert_not_nil b
456
- assert_not_nil b.bank_balance
457
- assert_not_nil b.big_bank_balance
458
- assert_not_nil b.world_population
459
- assert_not_nil b.my_house_population
460
- assert_not_nil b.value_of_e
461
- assert_kind_of BigDecimal, b.world_population
462
- assert_equal '6000000000.0', b.world_population.to_s
463
- assert_kind_of Integer, b.my_house_population
464
- assert_equal 3, b.my_house_population
465
- assert_kind_of BigDecimal, b.bank_balance
466
- assert_equal BigDecimal("1586.43"), b.bank_balance
467
- assert_kind_of BigDecimal, b.big_bank_balance
468
- assert_equal BigDecimal("1000234000567.95"), b.big_bank_balance
469
- GiveMeBigNumbers.down
470
- assert_raise(ActiveRecord::StatementInvalid) { BigNumber.first }
471
- end
472
-
473
616
  # For some reason our tests set Rails.@_env which breaks test env switching.
474
617
  coerce_tests! :test_internal_metadata_stores_environment_when_other_data_exists
475
618
  coerce_tests! :test_internal_metadata_stores_environment
476
619
  end
477
620
 
478
-
479
-
480
-
481
621
  class CoreTest < ActiveRecord::TestCase
482
622
  # I think fixtures are using the wrong time zone and the `:first`
483
623
  # `topics`.`bonus_time` attribute of 2005-01-30t15:28:00.00+01:00 is
@@ -485,9 +625,6 @@ class CoreTest < ActiveRecord::TestCase
485
625
  coerce_tests! :test_pretty_print_persisted
486
626
  end
487
627
 
488
-
489
-
490
-
491
628
  module ActiveRecord
492
629
  module ConnectionAdapters
493
630
  # Just like PostgreSQLAdapter does.
@@ -501,23 +638,26 @@ module ActiveRecord
501
638
  end
502
639
  end
503
640
 
504
-
505
-
506
-
507
641
  module ActiveRecord
508
642
  # The original module is hardcoded for PostgreSQL/SQLite/MySQL tests.
509
643
  module DatabaseTasksSetupper
510
644
  def setup
511
645
  @sqlserver_tasks =
512
- Class.new do
513
- def create; end
514
- def drop; end
515
- def purge; end
516
- def charset; end
517
- def collation; end
518
- def structure_dump(*); end
519
- def structure_load(*); end
520
- end.new
646
+ Class.new do
647
+ def create; end
648
+
649
+ def drop; end
650
+
651
+ def purge; end
652
+
653
+ def charset; end
654
+
655
+ def collation; end
656
+
657
+ def structure_dump(*); end
658
+
659
+ def structure_load(*); end
660
+ end.new
521
661
 
522
662
  $stdout, @original_stdout = StringIO.new, $stdout
523
663
  $stderr, @original_stderr = StringIO.new, $stderr
@@ -580,7 +720,6 @@ module ActiveRecord
580
720
  end
581
721
  end
582
722
  end
583
-
584
723
  end
585
724
 
586
725
  class DatabaseTasksCollationTest < ActiveRecord::TestCase
@@ -603,8 +742,8 @@ module ActiveRecord
603
742
  def test_sqlserver_structure_dump
604
743
  with_stubbed_new do
605
744
  assert_called_with(
606
- eval("@sqlserver_tasks"), :structure_dump,
607
- ["awesome-file.sql", nil]
745
+ eval("@sqlserver_tasks"), :structure_dump,
746
+ ["awesome-file.sql", nil]
608
747
  ) do
609
748
  ActiveRecord::Tasks::DatabaseTasks.structure_dump({ "adapter" => :sqlserver }, "awesome-file.sql")
610
749
  end
@@ -619,9 +758,9 @@ module ActiveRecord
619
758
  def test_sqlserver_structure_load
620
759
  with_stubbed_new do
621
760
  assert_called_with(
622
- eval("@sqlserver_tasks"),
623
- :structure_load,
624
- ["awesome-file.sql", nil]
761
+ eval("@sqlserver_tasks"),
762
+ :structure_load,
763
+ ["awesome-file.sql", nil]
625
764
  ) do
626
765
  ActiveRecord::Tasks::DatabaseTasks.structure_load({ "adapter" => :sqlserver }, "awesome-file.sql")
627
766
  end
@@ -629,11 +768,6 @@ module ActiveRecord
629
768
  end
630
769
  end
631
770
 
632
- class DatabaseTasksDumpSchemaCacheTest < ActiveRecord::TestCase
633
- # Skip this test with /tmp/my_schema_cache.yml path on Windows.
634
- coerce_tests! :test_dump_schema_cache if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
635
- end
636
-
637
771
  class DatabaseTasksCreateAllTest < ActiveRecord::TestCase
638
772
  # We extend `local_database?` so that common VM IPs can be used.
639
773
  coerce_tests! :test_ignores_remote_databases, :test_warning_for_remote_databases
@@ -645,9 +779,6 @@ module ActiveRecord
645
779
  end
646
780
  end
647
781
 
648
-
649
-
650
-
651
782
  class DefaultScopingTest < ActiveRecord::TestCase
652
783
  # We are not doing order duplicate removal anymore.
653
784
  coerce_tests! :test_order_in_default_scope_should_not_prevail
@@ -661,16 +792,13 @@ class DefaultScopingTest < ActiveRecord::TestCase
661
792
  end
662
793
  end
663
794
 
664
-
665
-
666
-
667
- require 'models/post'
668
- require 'models/subscriber'
795
+ require "models/post"
796
+ require "models/subscriber"
669
797
  class EachTest < ActiveRecord::TestCase
670
798
  # Quoting in tests does not cope with bracket quoting.
671
799
  coerce_tests! :test_find_in_batches_should_quote_batch_order
672
800
  def test_find_in_batches_should_quote_batch_order_coerced
673
- c = Post.connection
801
+ Post.connection
674
802
  assert_sql(/ORDER BY \[posts\]\.\[id\]/) do
675
803
  Post.find_in_batches(:batch_size => 1) do |batch|
676
804
  assert_kind_of Array, batch
@@ -682,7 +810,7 @@ class EachTest < ActiveRecord::TestCase
682
810
  # Quoting in tests does not cope with bracket quoting.
683
811
  coerce_tests! :test_in_batches_should_quote_batch_order
684
812
  def test_in_batches_should_quote_batch_order_coerced
685
- c = Post.connection
813
+ Post.connection
686
814
  assert_sql(/ORDER BY \[posts\]\.\[id\]/) do
687
815
  Post.in_batches(of: 1) do |relation|
688
816
  assert_kind_of ActiveRecord::Relation, relation
@@ -692,9 +820,6 @@ class EachTest < ActiveRecord::TestCase
692
820
  end
693
821
  end
694
822
 
695
-
696
-
697
-
698
823
  class EagerAssociationTest < ActiveRecord::TestCase
699
824
  # Use LEN() vs length() function.
700
825
  coerce_tests! :test_count_with_include
@@ -706,11 +831,12 @@ class EagerAssociationTest < ActiveRecord::TestCase
706
831
  coerce_tests! %r{including association based on sql condition and no database column}
707
832
  end
708
833
 
709
-
710
-
711
-
712
- require 'models/topic'
834
+ require "models/topic"
835
+ require "models/customer"
836
+ require "models/non_primary_key"
713
837
  class FinderTest < ActiveRecord::TestCase
838
+ fixtures :customers, :topics, :authors
839
+
714
840
  # We have implicit ordering, via FETCH.
715
841
  coerce_tests! %r{doesn't have implicit ordering},
716
842
  :test_find_doesnt_have_implicit_ordering
@@ -737,7 +863,7 @@ class FinderTest < ActiveRecord::TestCase
737
863
  # Can not use array condition due to not finding right type and hence fractional second quoting.
738
864
  coerce_tests! :test_condition_utc_time_interpolation_with_default_timezone_local
739
865
  def test_condition_utc_time_interpolation_with_default_timezone_local_coerced
740
- with_env_tz 'America/New_York' do
866
+ with_env_tz "America/New_York" do
741
867
  with_timezone_config default: :local do
742
868
  topic = Topic.first
743
869
  assert_equal topic, Topic.where(written_on: topic.written_on.getutc).first
@@ -748,17 +874,95 @@ class FinderTest < ActiveRecord::TestCase
748
874
  # Can not use array condition due to not finding right type and hence fractional second quoting.
749
875
  coerce_tests! :test_condition_local_time_interpolation_with_default_timezone_utc
750
876
  def test_condition_local_time_interpolation_with_default_timezone_utc_coerced
751
- with_env_tz 'America/New_York' do
877
+ with_env_tz "America/New_York" do
752
878
  with_timezone_config default: :utc do
753
879
  topic = Topic.first
754
880
  assert_equal topic, Topic.where(written_on: topic.written_on.getlocal).first
755
881
  end
756
882
  end
757
883
  end
758
- end
759
884
 
885
+ # Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
886
+ coerce_tests! :test_include_on_unloaded_relation_with_match
887
+ def test_include_on_unloaded_relation_with_match_coerced
888
+ assert_sql(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do
889
+ assert_equal true, Customer.where(name: "David").include?(customers(:david))
890
+ end
891
+ end
892
+
893
+ # Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
894
+ coerce_tests! :test_include_on_unloaded_relation_without_match
895
+ def test_include_on_unloaded_relation_without_match_coerced
896
+ assert_sql(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do
897
+ assert_equal false, Customer.where(name: "David").include?(customers(:mary))
898
+ end
899
+ end
900
+
901
+ # Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
902
+ coerce_tests! :test_member_on_unloaded_relation_with_match
903
+ def test_member_on_unloaded_relation_with_match_coerced
904
+ assert_sql(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do
905
+ assert_equal true, Customer.where(name: "David").member?(customers(:david))
906
+ end
907
+ end
908
+
909
+ # Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
910
+ coerce_tests! :test_member_on_unloaded_relation_without_match
911
+ def test_member_on_unloaded_relation_without_match_coerced
912
+ assert_sql(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do
913
+ assert_equal false, Customer.where(name: "David").member?(customers(:mary))
914
+ end
915
+ end
916
+
917
+ # Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
918
+ coerce_tests! :test_implicit_order_column_is_configurable
919
+ def test_implicit_order_column_is_configurable_coerced
920
+ old_implicit_order_column = Topic.implicit_order_column
921
+ Topic.implicit_order_column = "title"
760
922
 
923
+ assert_equal topics(:fifth), Topic.first
924
+ assert_equal topics(:third), Topic.last
761
925
 
926
+ c = Topic.connection
927
+ assert_sql(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.title"))} DESC, #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) {
928
+ Topic.last
929
+ }
930
+ ensure
931
+ Topic.implicit_order_column = old_implicit_order_column
932
+ end
933
+
934
+ # Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
935
+ coerce_tests! :test_implicit_order_set_to_primary_key
936
+ def test_implicit_order_set_to_primary_key_coerced
937
+ old_implicit_order_column = Topic.implicit_order_column
938
+ Topic.implicit_order_column = "id"
939
+
940
+ c = Topic.connection
941
+ assert_sql(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) {
942
+ Topic.last
943
+ }
944
+ ensure
945
+ Topic.implicit_order_column = old_implicit_order_column
946
+ end
947
+
948
+ # Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
949
+ coerce_tests! :test_implicit_order_for_model_without_primary_key
950
+ def test_implicit_order_for_model_without_primary_key_coerced
951
+ old_implicit_order_column = NonPrimaryKey.implicit_order_column
952
+ NonPrimaryKey.implicit_order_column = "created_at"
953
+
954
+ c = NonPrimaryKey.connection
955
+
956
+ assert_sql(/ORDER BY #{Regexp.escape(c.quote_table_name("non_primary_keys.created_at"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) {
957
+ NonPrimaryKey.last
958
+ }
959
+ ensure
960
+ NonPrimaryKey.implicit_order_column = old_implicit_order_column
961
+ end
962
+
963
+ # SQL Server is unable to use aliased SELECT in the HAVING clause.
964
+ coerce_tests! :test_include_on_unloaded_relation_with_having_referencing_aliased_select
965
+ end
762
966
 
763
967
  module ActiveRecord
764
968
  class Migration
@@ -777,9 +981,6 @@ module ActiveRecord
777
981
  end
778
982
  end
779
983
 
780
-
781
-
782
-
783
984
  class HasOneAssociationsTest < ActiveRecord::TestCase
784
985
  # We use OFFSET/FETCH vs TOP. So we always have an order.
785
986
  coerce_tests! :test_has_one_does_not_use_order_by
@@ -796,9 +997,6 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
796
997
  end
797
998
  end
798
999
 
799
-
800
-
801
-
802
1000
  class HasOneThroughAssociationsTest < ActiveRecord::TestCase
803
1001
  # Asserted SQL to get one row different from original test.
804
1002
  coerce_tests! :test_has_one_through_executes_limited_query
@@ -810,16 +1008,12 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
810
1008
  end
811
1009
  end
812
1010
 
813
-
814
-
815
-
816
-
817
- require 'models/company'
1011
+ require "models/company"
818
1012
  class InheritanceTest < ActiveRecord::TestCase
819
1013
  # Rails test required inserting to a identity column.
820
1014
  coerce_tests! :test_a_bad_type_column
821
1015
  def test_a_bad_type_column_coerced
822
- Company.connection.with_identity_insert_enabled('companies') do
1016
+ Company.connection.with_identity_insert_enabled("companies") do
823
1017
  Company.connection.insert "INSERT INTO companies (id, #{QUOTED_TYPE}, name) VALUES(100, 'bad_class!', 'Not happening')"
824
1018
  end
825
1019
  assert_raise(ActiveRecord::SubclassNotFound) { Company.find(100) }
@@ -828,45 +1022,36 @@ class InheritanceTest < ActiveRecord::TestCase
828
1022
  # Use Square brackets around column name
829
1023
  coerce_tests! :test_eager_load_belongs_to_primary_key_quoting
830
1024
  def test_eager_load_belongs_to_primary_key_quoting_coerced
831
- con = Account.connection
1025
+ Account.connection
832
1026
  assert_sql(/\[companies\]\.\[id\] = @0.* @0 = 1/) do
833
1027
  Account.all.merge!(:includes => :firm).find(1)
834
1028
  end
835
1029
  end
836
1030
  end
837
1031
 
838
-
839
-
840
-
841
1032
  class LeftOuterJoinAssociationTest < ActiveRecord::TestCase
842
1033
  # Uses || operator in SQL. Just trust core gets value out of this test.
843
1034
  coerce_tests! :test_does_not_override_select
844
1035
  end
845
1036
 
846
-
847
-
848
-
849
- require 'models/developer'
850
- require 'models/computer'
1037
+ require "models/developer"
1038
+ require "models/computer"
851
1039
  class NestedRelationScopingTest < ActiveRecord::TestCase
852
1040
  # Assert SQL Server limit implementation
853
1041
  coerce_tests! :test_merge_options
854
1042
  def test_merge_options_coerced
855
- Developer.where('salary = 80000').scoping do
1043
+ Developer.where("salary = 80000").scoping do
856
1044
  Developer.limit(10).scoping do
857
1045
  devs = Developer.all
858
1046
  sql = devs.to_sql
859
- assert_match '(salary = 80000)', sql
860
- assert_match 'FETCH NEXT 10 ROWS ONLY', sql
1047
+ assert_match "(salary = 80000)", sql
1048
+ assert_match "FETCH NEXT 10 ROWS ONLY", sql
861
1049
  end
862
1050
  end
863
1051
  end
864
1052
  end
865
1053
 
866
-
867
-
868
-
869
- require 'models/topic'
1054
+ require "models/topic"
870
1055
  class PersistenceTest < ActiveRecord::TestCase
871
1056
  # Rails test required updating a identity column.
872
1057
  coerce_tests! :test_update_columns_changing_id
@@ -890,10 +1075,7 @@ class PersistenceTest < ActiveRecord::TestCase
890
1075
  end
891
1076
  end
892
1077
 
893
-
894
-
895
-
896
- require 'models/author'
1078
+ require "models/author"
897
1079
  class UpdateAllTest < ActiveRecord::TestCase
898
1080
  # Rails test required updating a identity column.
899
1081
  coerce_tests! :test_update_all_doesnt_ignore_order
@@ -903,17 +1085,14 @@ class UpdateAllTest < ActiveRecord::TestCase
903
1085
  _(mary.id).must_equal 2
904
1086
  _(david.name).wont_equal mary.name
905
1087
  assert_sql(/UPDATE.*\(SELECT \[authors\].\[id\] FROM \[authors\].*ORDER BY \[authors\].\[id\]/i) do
906
- Author.where('[id] > 1').order(:id).update_all(name: 'Test')
1088
+ Author.where("[id] > 1").order(:id).update_all(name: "Test")
907
1089
  end
908
- _(david.reload.name).must_equal 'David'
909
- _(mary.reload.name).must_equal 'Test'
1090
+ _(david.reload.name).must_equal "David"
1091
+ _(mary.reload.name).must_equal "Test"
910
1092
  end
911
1093
  end
912
1094
 
913
-
914
-
915
-
916
- require 'models/topic'
1095
+ require "models/topic"
917
1096
  module ActiveRecord
918
1097
  class PredicateBuilderTest < ActiveRecord::TestCase
919
1098
  # Same as original test except string has `N` prefix to indicate unicode string.
@@ -930,9 +1109,6 @@ module ActiveRecord
930
1109
  end
931
1110
  end
932
1111
 
933
-
934
-
935
-
936
1112
  class PrimaryKeysTest < ActiveRecord::TestCase
937
1113
  # SQL Server does not have query for release_savepoint
938
1114
  coerce_tests! :test_create_without_primary_key_no_extra_query
@@ -945,10 +1121,7 @@ class PrimaryKeysTest < ActiveRecord::TestCase
945
1121
  end
946
1122
  end
947
1123
 
948
-
949
-
950
-
951
- require 'models/task'
1124
+ require "models/task"
952
1125
  class QueryCacheTest < ActiveRecord::TestCase
953
1126
  # SQL Server adapter not in list of supported adapters in original test.
954
1127
  coerce_tests! :test_cache_does_not_wrap_results_in_arrays
@@ -980,15 +1153,12 @@ class QueryCacheTest < ActiveRecord::TestCase
980
1153
  Task.find(1)
981
1154
  end
982
1155
 
983
- assert_includes ActiveRecord::SQLCounter.log_all.first , "TC.CONSTRAINT_TYPE = N''PRIMARY KEY''"
1156
+ assert_includes ActiveRecord::SQLCounter.log_all.first, "TC.CONSTRAINT_TYPE = N''PRIMARY KEY''"
984
1157
  end
985
1158
  end
986
1159
  end
987
1160
 
988
-
989
-
990
-
991
- require 'models/post'
1161
+ require "models/post"
992
1162
  class RelationTest < ActiveRecord::TestCase
993
1163
  # Use LEN vs LENGTH function.
994
1164
  coerce_tests! :test_reverse_order_with_function
@@ -1023,13 +1193,19 @@ class RelationTest < ActiveRecord::TestCase
1023
1193
  coerce_tests! :test_reorder_with_first
1024
1194
  def test_reorder_with_first_coerced
1025
1195
  sql_log = capture_sql do
1026
- assert Post.order(:title).reorder(nil).first
1196
+ message = <<~MSG.squish
1197
+ `.reorder(nil)` with `.first` / `.first!` no longer
1198
+ takes non-deterministic result in Rails 6.2.
1199
+ To continue taking non-deterministic result, use `.take` / `.take!` instead.
1200
+ MSG
1201
+ assert_deprecated(message) do
1202
+ assert Post.order(:title).reorder(nil).first
1203
+ end
1027
1204
  end
1028
1205
  assert sql_log.none? { |sql| /order by [posts].[title]/i.match?(sql) }, "ORDER BY title was used in the query: #{sql_log}"
1029
1206
  assert sql_log.all? { |sql| /order by \[posts\]\.\[id\]/i.match?(sql) }, "default ORDER BY ID was not used in the query: #{sql_log}"
1030
1207
  end
1031
1208
 
1032
-
1033
1209
  # We are not doing order duplicate removal anymore.
1034
1210
  coerce_tests! :test_order_using_scoping
1035
1211
 
@@ -1055,7 +1231,7 @@ class RelationTest < ActiveRecord::TestCase
1055
1231
 
1056
1232
  # Can't apply offset without ORDER
1057
1233
  coerce_tests! %r{using a custom table affects the wheres}
1058
- test 'using a custom table affects the wheres coerced' do
1234
+ test "using a custom table affects the wheres coerced" do
1059
1235
  post = posts(:welcome)
1060
1236
 
1061
1237
  assert_equal post, custom_post_relation.where!(title: post.title).order(:id).take
@@ -1063,7 +1239,7 @@ class RelationTest < ActiveRecord::TestCase
1063
1239
 
1064
1240
  # Can't apply offset without ORDER
1065
1241
  coerce_tests! %r{using a custom table with joins affects the joins}
1066
- test 'using a custom table with joins affects the joins coerced' do
1242
+ test "using a custom table with joins affects the joins coerced" do
1067
1243
  post = posts(:welcome)
1068
1244
 
1069
1245
  assert_equal post, custom_post_relation.joins(:author).where!(title: post.title).order(:id).take
@@ -1077,10 +1253,32 @@ class RelationTest < ActiveRecord::TestCase
1077
1253
  end
1078
1254
  end
1079
1255
 
1256
+ module ActiveRecord
1257
+ class RelationTest < ActiveRecord::TestCase
1258
+ # Skipping this test. SQL Server doesn't support optimizer hint as comments
1259
+ coerce_tests! :test_relation_with_optimizer_hints_filters_sql_comment_delimiters
1260
+
1261
+ coerce_tests! :test_does_not_duplicate_optimizer_hints_on_merge
1262
+ def test_does_not_duplicate_optimizer_hints_on_merge_coerced
1263
+ escaped_table = Post.connection.quote_table_name("posts")
1264
+ expected = "SELECT #{escaped_table}.* FROM #{escaped_table} OPTION (OMGHINT)"
1265
+ query = Post.optimizer_hints("OMGHINT").merge(Post.optimizer_hints("OMGHINT")).to_sql
1266
+ assert_equal expected, query
1267
+ end
1080
1268
 
1269
+ # Original Rails test fails on Windows CI because the dump file was not being binary read.
1270
+ coerce_tests! :test_marshal_load_legacy_relation
1271
+ def test_marshal_load_legacy_relation_coerced
1272
+ path = File.expand_path(
1273
+ "support/marshal_compatibility_fixtures/legacy_relation.dump",
1274
+ ARTest::SQLServer.root_activerecord_test
1275
+ )
1276
+ assert_equal 11, Marshal.load(File.binread(path)).size
1277
+ end
1278
+ end
1279
+ end
1081
1280
 
1082
-
1083
- require 'models/post'
1281
+ require "models/post"
1084
1282
  class SanitizeTest < ActiveRecord::TestCase
1085
1283
  # Use nvarchar string (N'') in assert
1086
1284
  coerce_tests! :test_sanitize_sql_like_example_use_case
@@ -1090,7 +1288,7 @@ class SanitizeTest < ActiveRecord::TestCase
1090
1288
  where("title LIKE ?", sanitize_sql_like(term, "!"))
1091
1289
  end
1092
1290
 
1093
- scope :search_as_scope, -> (term) {
1291
+ scope :search_as_scope, ->(term) {
1094
1292
  where("title LIKE ?", sanitize_sql_like(term, "!"))
1095
1293
  }
1096
1294
  end
@@ -1105,9 +1303,6 @@ class SanitizeTest < ActiveRecord::TestCase
1105
1303
  end
1106
1304
  end
1107
1305
 
1108
-
1109
-
1110
-
1111
1306
  class SchemaDumperTest < ActiveRecord::TestCase
1112
1307
  # We have precision to 38.
1113
1308
  coerce_tests! :test_schema_dump_keeps_large_precision_integer_columns_as_decimal
@@ -1134,26 +1329,17 @@ class SchemaDumperTest < ActiveRecord::TestCase
1134
1329
  end
1135
1330
  end
1136
1331
 
1137
-
1138
-
1139
-
1140
1332
  class SchemaDumperDefaultsTest < ActiveRecord::TestCase
1141
1333
  # These date formats do not match ours. We got these covered in our dumper tests.
1142
1334
  coerce_tests! :test_schema_dump_defaults_with_universally_supported_types
1143
1335
  end
1144
1336
 
1145
-
1146
-
1147
-
1148
1337
  class TestAdapterWithInvalidConnection < ActiveRecord::TestCase
1149
1338
  # We trust Rails on this since we do not want to install mysql.
1150
1339
  coerce_tests! %r{inspect on Model class does not raise}
1151
1340
  end
1152
1341
 
1153
-
1154
-
1155
-
1156
- require 'models/topic'
1342
+ require "models/topic"
1157
1343
  class TransactionTest < ActiveRecord::TestCase
1158
1344
  # SQL Server does not have query for release_savepoint
1159
1345
  coerce_tests! :test_releasing_named_savepoints
@@ -1167,10 +1353,7 @@ class TransactionTest < ActiveRecord::TestCase
1167
1353
  end
1168
1354
  end
1169
1355
 
1170
-
1171
-
1172
-
1173
- require 'models/tag'
1356
+ require "models/tag"
1174
1357
  class TransactionIsolationTest < ActiveRecord::TestCase
1175
1358
  # SQL Server will lock the table for counts even when both
1176
1359
  # connections are `READ COMMITTED`. So we bypass with `READPAST`.
@@ -1180,7 +1363,7 @@ class TransactionIsolationTest < ActiveRecord::TestCase
1180
1363
  assert_equal 0, Tag.count
1181
1364
  Tag2.transaction do
1182
1365
  Tag2.create
1183
- assert_equal 0, Tag.lock('WITH(READPAST)').count
1366
+ assert_equal 0, Tag.lock("WITH(READPAST)").count
1184
1367
  end
1185
1368
  end
1186
1369
  assert_equal 1, Tag.count
@@ -1190,10 +1373,7 @@ class TransactionIsolationTest < ActiveRecord::TestCase
1190
1373
  coerce_tests! %r{repeatable read}
1191
1374
  end
1192
1375
 
1193
-
1194
-
1195
-
1196
- require 'models/book'
1376
+ require "models/book"
1197
1377
  class ViewWithPrimaryKeyTest < ActiveRecord::TestCase
1198
1378
  # We have a few view tables. use includes vs equality.
1199
1379
  coerce_tests! :test_views
@@ -1205,13 +1385,10 @@ class ViewWithPrimaryKeyTest < ActiveRecord::TestCase
1205
1385
  coerce_tests! :test_does_not_assume_id_column_as_primary_key
1206
1386
  def test_does_not_assume_id_column_as_primary_key_coerced
1207
1387
  model = Class.new(ActiveRecord::Base) { self.table_name = "ebooks" }
1208
- assert_equal 'id', model.primary_key
1388
+ assert_equal "id", model.primary_key
1209
1389
  end
1210
1390
  end
1211
1391
 
1212
-
1213
-
1214
-
1215
1392
  class ViewWithoutPrimaryKeyTest < ActiveRecord::TestCase
1216
1393
  # We have a few view tables. use includes vs equality.
1217
1394
  coerce_tests! :test_views
@@ -1220,23 +1397,17 @@ class ViewWithoutPrimaryKeyTest < ActiveRecord::TestCase
1220
1397
  end
1221
1398
  end
1222
1399
 
1223
-
1224
-
1225
-
1226
- require 'models/author'
1400
+ require "models/author"
1227
1401
  class YamlSerializationTest < ActiveRecord::TestCase
1228
1402
  coerce_tests! :test_types_of_virtual_columns_are_not_changed_on_round_trip
1229
1403
  def test_types_of_virtual_columns_are_not_changed_on_round_trip_coerced
1230
- author = Author.select('authors.*, 5 as posts_count').first
1404
+ author = Author.select("authors.*, 5 as posts_count").first
1231
1405
  dumped = YAML.load(YAML.dump(author))
1232
1406
  assert_equal 5, author.posts_count
1233
1407
  assert_equal 5, dumped.posts_count
1234
1408
  end
1235
1409
  end
1236
1410
 
1237
-
1238
-
1239
-
1240
1411
  class DateTimePrecisionTest < ActiveRecord::TestCase
1241
1412
  # Original test had `7` which we support vs `8` which we use.
1242
1413
  coerce_tests! :test_invalid_datetime_precision_raises_error
@@ -1252,7 +1423,7 @@ class DateTimePrecisionTest < ActiveRecord::TestCase
1252
1423
  coerce_tests! :test_datetime_precision_is_truncated_on_assignment
1253
1424
  def test_datetime_precision_is_truncated_on_assignment_coerced
1254
1425
  @connection.create_table(:foos, force: true)
1255
- @connection.add_column :foos, :created_at, :datetime, precision: 0
1426
+ @connection.add_column :foos, :created_at, :datetime, precision: 0
1256
1427
  @connection.add_column :foos, :updated_at, :datetime, precision: 6
1257
1428
 
1258
1429
  time = ::Time.now.change(nsec: 123456789)
@@ -1269,9 +1440,6 @@ class DateTimePrecisionTest < ActiveRecord::TestCase
1269
1440
  end
1270
1441
  end
1271
1442
 
1272
-
1273
-
1274
-
1275
1443
  class TimePrecisionTest < ActiveRecord::TestCase
1276
1444
  # datetime is rounded to increments of .000, .003, or .007 seconds
1277
1445
  coerce_tests! :test_time_precision_is_truncated_on_assignment
@@ -1297,9 +1465,6 @@ class TimePrecisionTest < ActiveRecord::TestCase
1297
1465
  coerce_tests! :test_no_time_precision_isnt_truncated_on_assignment
1298
1466
  end
1299
1467
 
1300
-
1301
-
1302
-
1303
1468
  class DefaultNumbersTest < ActiveRecord::TestCase
1304
1469
  # We do better with native types and do not return strings for everything.
1305
1470
  coerce_tests! :test_default_positive_integer
@@ -1326,9 +1491,6 @@ class DefaultNumbersTest < ActiveRecord::TestCase
1326
1491
  end
1327
1492
  end
1328
1493
 
1329
-
1330
-
1331
-
1332
1494
  module ActiveRecord
1333
1495
  class CollectionCacheKeyTest < ActiveRecord::TestCase
1334
1496
  # Will trust rails has this sorted since you cant offset without a limit.
@@ -1336,9 +1498,6 @@ module ActiveRecord
1336
1498
  end
1337
1499
  end
1338
1500
 
1339
-
1340
-
1341
-
1342
1501
  module ActiveRecord
1343
1502
  class CacheKeyTest < ActiveRecord::TestCase
1344
1503
  # Like Mysql2 and PostgreSQL, SQL Server doesn't return a string value for updated_at. In the Rails tests
@@ -1349,9 +1508,6 @@ module ActiveRecord
1349
1508
  end
1350
1509
  end
1351
1510
 
1352
-
1353
-
1354
-
1355
1511
  require "models/book"
1356
1512
  module ActiveRecord
1357
1513
  class StatementCacheTest < ActiveRecord::TestCase
@@ -1370,41 +1526,74 @@ module ActiveRecord
1370
1526
  end
1371
1527
  end
1372
1528
 
1373
-
1374
-
1375
-
1376
1529
  module ActiveRecord
1377
1530
  module ConnectionAdapters
1378
1531
  class SchemaCacheTest < ActiveRecord::TestCase
1532
+ # Tests fail on Windows AppVeyor CI with 'Permission denied' error when renaming file during `File.atomic_write` call.
1533
+ coerce_tests! :test_yaml_dump_and_load, :test_yaml_dump_and_load_with_gzip if RbConfig::CONFIG["host_os"] =~ /mswin|mingw/
1534
+
1535
+ # Ruby 2.5 and 2.6 have issues to marshal Time before 1900. 2012.sql has one column with default value 1753
1536
+ coerce_tests! :test_marshal_dump_and_load_with_gzip, :test_marshal_dump_and_load_via_disk
1537
+
1538
+ # Tests fail on Windows AppVeyor CI with 'Permission denied' error when renaming file during `File.atomic_write` call.
1539
+ unless RbConfig::CONFIG["host_os"] =~ /mswin|mingw/
1540
+ def test_marshal_dump_and_load_with_gzip_coerced
1541
+ with_marshable_time_defaults { original_test_marshal_dump_and_load_with_gzip }
1542
+ end
1543
+ def test_marshal_dump_and_load_via_disk_coerced
1544
+ with_marshable_time_defaults { original_test_marshal_dump_and_load_via_disk }
1545
+ end
1546
+ end
1547
+
1379
1548
  private
1549
+
1550
+ def with_marshable_time_defaults
1551
+ # Detect problems
1552
+ if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.7")
1553
+ column = @connection.columns(:sst_datatypes).find { |c| c.name == "datetime" }
1554
+ current_default = column.default if column.default.is_a?(Time) && column.default.year < 1900
1555
+ end
1556
+
1557
+ # Correct problems
1558
+ if current_default.present?
1559
+ @connection.change_column_default(:sst_datatypes, :datetime, current_default.dup.change(year: 1900))
1560
+ end
1561
+
1562
+ # Run original test
1563
+ yield
1564
+ ensure
1565
+ # Revert changes
1566
+ @connection.change_column_default(:sst_datatypes, :datetime, current_default) if current_default.present?
1567
+ end
1568
+
1380
1569
  # We need to give the full path for this to work.
1381
1570
  def schema_dump_path
1382
- File.join ARTest::SQLServer.root_activerecord, 'test/assets/schema_dump_5_1.yml'
1571
+ File.join ARTest::SQLServer.root_activerecord, "test/assets/schema_dump_5_1.yml"
1383
1572
  end
1384
1573
  end
1385
1574
  end
1386
1575
  end
1387
1576
 
1388
-
1389
-
1390
-
1577
+ require "models/post"
1578
+ require "models/comment"
1391
1579
  class UnsafeRawSqlTest < ActiveRecord::TestCase
1392
- # Use LEN() vs length() function.
1580
+ fixtures :posts
1581
+
1582
+ # Use LEN() vs length() function.
1393
1583
  coerce_tests! %r{order: always allows Arel}
1394
- test 'order: always allows Arel' do
1395
- ids_depr = with_unsafe_raw_sql_deprecated { Post.order(Arel.sql("len(title)")).pluck(:title) }
1396
- ids_disabled = with_unsafe_raw_sql_disabled { Post.order(Arel.sql("len(title)")).pluck(:title) }
1584
+ test "order: always allows Arel" do
1585
+ titles = Post.order(Arel.sql("len(title)")).pluck(:title)
1397
1586
 
1398
- assert_equal ids_depr, ids_disabled
1587
+ assert_not_empty titles
1399
1588
  end
1400
1589
 
1401
1590
  # Use LEN() vs length() function.
1402
1591
  coerce_tests! %r{pluck: always allows Arel}
1403
1592
  test "pluck: always allows Arel" do
1404
- values_depr = with_unsafe_raw_sql_deprecated { Post.includes(:comments).pluck(:title, Arel.sql("len(title)")) }
1405
- values_disabled = with_unsafe_raw_sql_disabled { Post.includes(:comments).pluck(:title, Arel.sql("len(title)")) }
1593
+ excepted_values = Post.includes(:comments).pluck(:title).map { |title| [title, title.size] }
1594
+ values = Post.includes(:comments).pluck(:title, Arel.sql("len(title)"))
1406
1595
 
1407
- assert_equal values_depr, values_disabled
1596
+ assert_equal excepted_values, values
1408
1597
  end
1409
1598
 
1410
1599
  # Use LEN() vs length() function.
@@ -1412,16 +1601,75 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
1412
1601
  test "order: allows valid Array arguments" do
1413
1602
  ids_expected = Post.order(Arel.sql("author_id, len(title)")).pluck(:id)
1414
1603
 
1415
- ids_depr = with_unsafe_raw_sql_deprecated { Post.order(["author_id", "len(title)"]).pluck(:id) }
1416
- ids_disabled = with_unsafe_raw_sql_disabled { Post.order(["author_id", "len(title)"]).pluck(:id) }
1604
+ ids = Post.order(["author_id", "len(title)"]).pluck(:id)
1417
1605
 
1418
- assert_equal ids_expected, ids_depr
1419
- assert_equal ids_expected, ids_disabled
1606
+ assert_equal ids_expected, ids
1420
1607
  end
1421
- end
1422
1608
 
1609
+ test "order: allows string column names that are quoted" do
1610
+ ids_expected = Post.order(Arel.sql("id")).pluck(:id)
1611
+
1612
+ ids = Post.order("[id]").pluck(:id)
1613
+
1614
+ assert_equal ids_expected, ids
1615
+ end
1616
+
1617
+ test "order: allows string column names that are quoted with table" do
1618
+ ids_expected = Post.order(Arel.sql("id")).pluck(:id)
1619
+
1620
+ ids = Post.order("[posts].[id]").pluck(:id)
1621
+
1622
+ assert_equal ids_expected, ids
1623
+ end
1624
+
1625
+ test "order: allows string column names that are quoted with table and user" do
1626
+ ids_expected = Post.order(Arel.sql("id")).pluck(:id)
1627
+
1628
+ ids = Post.order("[dbo].[posts].[id]").pluck(:id)
1629
+
1630
+ assert_equal ids_expected, ids
1631
+ end
1632
+
1633
+ test "order: allows string column names that are quoted with table, user and database" do
1634
+ ids_expected = Post.order(Arel.sql("id")).pluck(:id)
1635
+
1636
+ ids = Post.order("[activerecord_unittest].[dbo].[posts].[id]").pluck(:id)
1637
+
1638
+ assert_equal ids_expected, ids
1639
+ end
1640
+
1641
+ test "pluck: allows string column name that are quoted" do
1642
+ titles_expected = Post.pluck(Arel.sql("title"))
1643
+
1644
+ titles = Post.pluck("[title]")
1645
+
1646
+ assert_equal titles_expected, titles
1647
+ end
1423
1648
 
1649
+ test "pluck: allows string column name that are quoted with table" do
1650
+ titles_expected = Post.pluck(Arel.sql("title"))
1424
1651
 
1652
+ titles = Post.pluck("[posts].[title]")
1653
+
1654
+ assert_equal titles_expected, titles
1655
+ end
1656
+
1657
+ test "pluck: allows string column name that are quoted with table and user" do
1658
+ titles_expected = Post.pluck(Arel.sql("title"))
1659
+
1660
+ titles = Post.pluck("[dbo].[posts].[title]")
1661
+
1662
+ assert_equal titles_expected, titles
1663
+ end
1664
+
1665
+ test "pluck: allows string column name that are quoted with table, user and database" do
1666
+ titles_expected = Post.pluck(Arel.sql("title"))
1667
+
1668
+ titles = Post.pluck("[activerecord_unittest].[dbo].[posts].[title]")
1669
+
1670
+ assert_equal titles_expected, titles
1671
+ end
1672
+ end
1425
1673
 
1426
1674
  class ReservedWordTest < ActiveRecord::TestCase
1427
1675
  coerce_tests! :test_change_columns
@@ -1433,33 +1681,27 @@ class ReservedWordTest < ActiveRecord::TestCase
1433
1681
  end
1434
1682
  end
1435
1683
 
1436
-
1437
-
1438
-
1439
1684
  class OptimisticLockingTest < ActiveRecord::TestCase
1440
1685
  # We do not allow updating identities, but we can test using a non-identity key
1441
1686
  coerce_tests! :test_update_with_dirty_primary_key
1442
1687
  def test_update_with_dirty_primary_key_coerced
1443
1688
  assert_raises(ActiveRecord::RecordNotUnique) do
1444
- record = StringKeyObject.find('record1')
1445
- record.id = 'record2'
1689
+ record = StringKeyObject.find("record1")
1690
+ record.id = "record2"
1446
1691
  record.save!
1447
1692
  end
1448
1693
 
1449
- record = StringKeyObject.find('record1')
1450
- record.id = 'record42'
1694
+ record = StringKeyObject.find("record1")
1695
+ record.id = "record42"
1451
1696
  record.save!
1452
1697
 
1453
- assert StringKeyObject.find('record42')
1698
+ assert StringKeyObject.find("record42")
1454
1699
  assert_raises(ActiveRecord::RecordNotFound) do
1455
- StringKeyObject.find('record1')
1700
+ StringKeyObject.find("record1")
1456
1701
  end
1457
1702
  end
1458
1703
  end
1459
1704
 
1460
-
1461
-
1462
-
1463
1705
  class RelationMergingTest < ActiveRecord::TestCase
1464
1706
  # Use nvarchar string (N'') in assert
1465
1707
  coerce_tests! :test_merging_with_order_with_binds
@@ -1467,10 +1709,26 @@ class RelationMergingTest < ActiveRecord::TestCase
1467
1709
  relation = Post.all.merge(Post.order([Arel.sql("title LIKE ?"), "%suffix"]))
1468
1710
  assert_equal ["title LIKE N'%suffix'"], relation.order_values
1469
1711
  end
1470
- end
1471
1712
 
1713
+ # Same as original but change first regexp to match sp_executesql binding syntax
1714
+ coerce_tests! :test_merge_doesnt_duplicate_same_clauses
1715
+ def test_merge_doesnt_duplicate_same_clauses_coerced
1716
+ david, mary, bob = authors(:david, :mary, :bob)
1717
+
1718
+ non_mary_and_bob = Author.where.not(id: [mary, bob])
1472
1719
 
1720
+ author_id = Author.connection.quote_table_name("authors.id")
1721
+ assert_sql(/WHERE #{Regexp.escape(author_id)} NOT IN \((@\d), \g<1>\)'/) do
1722
+ assert_equal [david], non_mary_and_bob.merge(non_mary_and_bob)
1723
+ end
1473
1724
 
1725
+ only_david = Author.where("#{author_id} IN (?)", david)
1726
+
1727
+ assert_sql(/WHERE \(#{Regexp.escape(author_id)} IN \(1\)\)\z/) do
1728
+ assert_equal [david], only_david.merge(only_david)
1729
+ end
1730
+ end
1731
+ end
1474
1732
 
1475
1733
  module ActiveRecord
1476
1734
  class DatabaseTasksTruncateAllTest < ActiveRecord::TestCase
@@ -1481,9 +1739,6 @@ module ActiveRecord
1481
1739
  end
1482
1740
  end
1483
1741
 
1484
-
1485
-
1486
-
1487
1742
  require "models/book"
1488
1743
  class EnumTest < ActiveRecord::TestCase
1489
1744
  # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
@@ -1527,13 +1782,8 @@ class EnumTest < ActiveRecord::TestCase
1527
1782
  end
1528
1783
  end
1529
1784
 
1530
-
1531
-
1532
-
1533
-
1534
- require 'models/task'
1785
+ require "models/task"
1535
1786
  class QueryCacheExpiryTest < ActiveRecord::TestCase
1536
-
1537
1787
  # SQL Server does not support skipping or upserting duplicates.
1538
1788
  coerce_tests! :test_insert_all
1539
1789
  def test_insert_all_coerced
@@ -1563,10 +1813,10 @@ class QueryCacheExpiryTest < ActiveRecord::TestCase
1563
1813
  end
1564
1814
  end
1565
1815
 
1566
-
1567
-
1568
- require 'models/citation'
1816
+ require "models/citation"
1569
1817
  class EagerLoadingTooManyIdsTest < ActiveRecord::TestCase
1818
+ fixtures :citations
1819
+
1570
1820
  # Original Rails test fails with SQL Server error message "The query processor ran out of internal resources and
1571
1821
  # could not produce a query plan". This error goes away if you change database compatibility level to 110 (SQL 2012)
1572
1822
  # (see https://www.mssqltips.com/sqlservertip/5279/sql-server-error-query-processor-ran-out-of-internal-resources-and-could-not-produce-a-query-plan/).
@@ -1574,22 +1824,19 @@ class EagerLoadingTooManyIdsTest < ActiveRecord::TestCase
1574
1824
  # unprepared statement is used if the number of values exceeds the adapter's `bind_params_length`. The coerced test
1575
1825
  # still does this as there will be 32,768 remaining citation records in the database and the `bind_params_length` of
1576
1826
  # adapter is 2,098.
1577
- coerce_tests! :test_eager_loading_too_may_ids
1578
- def test_eager_loading_too_may_ids_coerced
1827
+ coerce_tests! :test_eager_loading_too_many_ids
1828
+ def test_eager_loading_too_many_ids_coerced
1579
1829
  # Remove excess records.
1580
1830
  Citation.limit(32768).order(id: :desc).delete_all
1581
1831
 
1582
1832
  # Perform test
1583
1833
  citation_count = Citation.count
1584
- assert_sql(/WHERE \(\[citations\]\.\[id\] IN \(0, 1/) do
1834
+ assert_sql(/WHERE \[citations\]\.\[id\] IN \(0, 1/) do
1585
1835
  assert_equal citation_count, Citation.eager_load(:citations).offset(0).size
1586
1836
  end
1587
1837
  end
1588
1838
  end
1589
1839
 
1590
-
1591
-
1592
-
1593
1840
  class LogSubscriberTest < ActiveRecord::TestCase
1594
1841
  # Call original test from coerced test. Fixes issue on CI with Rails installed as a gem.
1595
1842
  coerce_tests! :test_vebose_query_logs
@@ -1598,9 +1845,6 @@ class LogSubscriberTest < ActiveRecord::TestCase
1598
1845
  end
1599
1846
  end
1600
1847
 
1601
-
1602
-
1603
-
1604
1848
  class ActiveRecordSchemaTest < ActiveRecord::TestCase
1605
1849
  # Workaround for randomly failing test.
1606
1850
  coerce_tests! :test_has_primary_key
@@ -1610,31 +1854,125 @@ class ActiveRecordSchemaTest < ActiveRecord::TestCase
1610
1854
  end
1611
1855
  end
1612
1856
 
1857
+ class ReloadModelsTest < ActiveRecord::TestCase
1858
+ # Skip test on Windows. The number of arguments passed to `IO.popen` in
1859
+ # `activesupport/lib/active_support/testing/isolation.rb` exceeds what Windows can handle.
1860
+ coerce_tests! :test_has_one_with_reload if RbConfig::CONFIG["host_os"] =~ /mswin|mingw/
1861
+ end
1613
1862
 
1863
+ require "models/post"
1864
+ class AnnotateTest < ActiveRecord::TestCase
1865
+ # Same as original coerced test except our SQL starts with `EXEC sp_executesql`.
1866
+ # TODO: Remove coerce after Rails 7 (see https://github.com/rails/rails/pull/42027)
1867
+ coerce_tests! :test_annotate_wraps_content_in_an_inline_comment
1868
+ def test_annotate_wraps_content_in_an_inline_comment_coerced
1869
+ quoted_posts_id, quoted_posts = regexp_escape_table_name("posts.id"), regexp_escape_table_name("posts")
1870
+
1871
+ assert_sql(%r{SELECT #{quoted_posts_id} FROM #{quoted_posts} /\* foo \*/}i) do
1872
+ posts = Post.select(:id).annotate("foo")
1873
+ assert posts.first
1874
+ end
1875
+ end
1614
1876
 
1877
+ # Same as original coerced test except our SQL starts with `EXEC sp_executesql`.
1878
+ # TODO: Remove coerce after Rails 7 (see https://github.com/rails/rails/pull/42027)
1879
+ coerce_tests! :test_annotate_is_sanitized
1880
+ def test_annotate_is_sanitized_coerced
1881
+ quoted_posts_id, quoted_posts = regexp_escape_table_name("posts.id"), regexp_escape_table_name("posts")
1615
1882
 
1616
- module ActiveRecord
1617
- module ConnectionAdapters
1618
- class ReaperTest < ActiveRecord::TestCase
1619
- # Coerce can be removed if Rails version > 6.0.3
1620
- coerce_tests! :test_connection_pool_starts_reaper_in_fork unless Process.respond_to?(:fork)
1883
+ assert_sql(%r{SELECT #{quoted_posts_id} FROM #{quoted_posts} /\* foo \*/}i) do
1884
+ posts = Post.select(:id).annotate("*/foo/*")
1885
+ assert posts.first
1886
+ end
1887
+
1888
+ assert_sql(%r{SELECT #{quoted_posts_id} FROM #{quoted_posts} /\* foo \*/}i) do
1889
+ posts = Post.select(:id).annotate("**//foo//**")
1890
+ assert posts.first
1891
+ end
1892
+
1893
+ assert_sql(%r{SELECT #{quoted_posts_id} FROM #{quoted_posts} /\* foo \*/ /\* bar \*/}i) do
1894
+ posts = Post.select(:id).annotate("*/foo/*").annotate("*/bar")
1895
+ assert posts.first
1896
+ end
1897
+
1898
+ assert_sql(%r{SELECT #{quoted_posts_id} FROM #{quoted_posts} /\* \+ MAX_EXECUTION_TIME\(1\) \*/}i) do
1899
+ posts = Post.select(:id).annotate("+ MAX_EXECUTION_TIME(1)")
1900
+ assert posts.first
1621
1901
  end
1622
1902
  end
1623
1903
  end
1624
1904
 
1905
+ class MarshalSerializationTest < ActiveRecord::TestCase
1906
+ private
1907
+
1908
+ def marshal_fixture_path(file_name)
1909
+ File.expand_path(
1910
+ "support/marshal_compatibility_fixtures/#{ActiveRecord::Base.connection.adapter_name}/#{file_name}.dump",
1911
+ ARTest::SQLServer.test_root_sqlserver
1912
+ )
1913
+ end
1914
+ end
1625
1915
 
1916
+ class NestedThroughAssociationsTest < ActiveRecord::TestCase
1917
+ # Same as original but replace order with "order(:id)" to ensure that assert_includes_and_joins_equal doesn't raise
1918
+ # "A column has been specified more than once in the order by list"
1919
+ # Example: original test generate queries like "ORDER BY authors.id, [authors].[id]". We don't support duplicate columns in the order list
1920
+ coerce_tests! :test_has_many_through_has_many_with_has_many_through_habtm_source_reflection_preload_via_joins, :test_has_many_through_has_and_belongs_to_many_with_has_many_source_reflection_preload_via_joins
1921
+ def test_has_many_through_has_many_with_has_many_through_habtm_source_reflection_preload_via_joins_coerced
1922
+ # preload table schemas
1923
+ Author.joins(:category_post_comments).first
1924
+
1925
+ assert_includes_and_joins_equal(
1926
+ Author.where("comments.id" => comments(:does_it_hurt).id).order(:id),
1927
+ [authors(:david), authors(:mary)], :category_post_comments
1928
+ )
1929
+ end
1626
1930
 
1931
+ def test_has_many_through_has_and_belongs_to_many_with_has_many_source_reflection_preload_via_joins_coerced
1932
+ # preload table schemas
1933
+ Category.joins(:post_comments).first
1627
1934
 
1628
- class FixturesTest < ActiveRecord::TestCase
1629
- # Skip test on Windows. Skip can be removed when Rails PR https://github.com/rails/rails/pull/39234 has been merged.
1630
- coerce_tests! :test_binary_in_fixtures if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
1935
+ assert_includes_and_joins_equal(
1936
+ Category.where("comments.id" => comments(:more_greetings).id).order(:id),
1937
+ [categories(:general), categories(:technology)], :post_comments
1938
+ )
1939
+ end
1631
1940
  end
1632
1941
 
1942
+ class BasePreventWritesTest < ActiveRecord::TestCase
1943
+ # SQL Server does not have query for release_savepoint
1944
+ coerce_tests! %r{an empty transaction does not raise if preventing writes}
1945
+ test "an empty transaction does not raise if preventing writes coerced" do
1946
+ ActiveRecord::Base.while_preventing_writes do
1947
+ assert_queries(1, ignore_none: true) do
1948
+ Bird.transaction do
1949
+ ActiveRecord::Base.connection.materialize_transactions
1950
+ end
1951
+ end
1952
+ end
1953
+ end
1633
1954
 
1955
+ class BasePreventWritesLegacyTest < ActiveRecord::TestCase
1956
+ # SQL Server does not have query for release_savepoint
1957
+ coerce_tests! %r{an empty transaction does not raise if preventing writes}
1958
+ test "an empty transaction does not raise if preventing writes coerced" do
1959
+ ActiveRecord::Base.connection_handler.while_preventing_writes do
1960
+ assert_queries(1, ignore_none: true) do
1961
+ Bird.transaction do
1962
+ ActiveRecord::Base.connection.materialize_transactions
1963
+ end
1964
+ end
1965
+ end
1966
+ end
1967
+ end
1968
+ end
1634
1969
 
1970
+ class MigratorTest < ActiveRecord::TestCase
1971
+ # Test fails on Windows AppVeyor CI for unknown reason.
1972
+ coerce_tests! :test_migrator_db_has_no_schema_migrations_table if RbConfig::CONFIG["host_os"] =~ /mswin|mingw/
1973
+ end
1635
1974
 
1636
- class ReloadModelsTest < ActiveRecord::TestCase
1637
- # Skip test on Windows. The number of arguements passed to `IO.popen` in
1638
- # `activesupport/lib/active_support/testing/isolation.rb` exceeds what Windows can handle.
1639
- coerce_tests! :test_has_one_with_reload if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
1975
+ class MultiDbMigratorTest < ActiveRecord::TestCase
1976
+ # Test fails on Windows AppVeyor CI for unknown reason.
1977
+ coerce_tests! :test_migrator_db_has_no_schema_migrations_table if RbConfig::CONFIG["host_os"] =~ /mswin|mingw/
1640
1978
  end