activerecord-sqlserver-adapter 5.2.1 → 6.0.0

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/.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 +38 -24
  8. data/{Dockerfile → Dockerfile.ci} +1 -1
  9. data/Gemfile +48 -41
  10. data/Guardfile +9 -8
  11. data/README.md +9 -30
  12. data/RUNNING_UNIT_TESTS.md +3 -0
  13. data/Rakefile +14 -16
  14. data/VERSION +1 -1
  15. data/activerecord-sqlserver-adapter.gemspec +25 -14
  16. data/appveyor.yml +24 -17
  17. data/docker-compose.ci.yml +7 -5
  18. data/guides/RELEASING.md +11 -0
  19. data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +2 -4
  20. data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +3 -4
  21. data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +5 -4
  22. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +3 -3
  23. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +2 -0
  24. data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +8 -7
  25. data/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb +36 -0
  26. data/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb +6 -4
  27. data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +9 -0
  28. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +88 -44
  29. data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +9 -12
  30. data/lib/active_record/connection_adapters/sqlserver/errors.rb +2 -3
  31. data/lib/active_record/connection_adapters/sqlserver/quoting.rb +46 -8
  32. data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +16 -5
  33. data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +9 -7
  34. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +190 -164
  35. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +4 -2
  36. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +3 -1
  37. data/lib/active_record/connection_adapters/sqlserver/showplan.rb +8 -8
  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/big_integer.rb +3 -3
  42. data/lib/active_record/connection_adapters/sqlserver/type/binary.rb +5 -4
  43. data/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +3 -3
  44. data/lib/active_record/connection_adapters/sqlserver/type/char.rb +7 -4
  45. data/lib/active_record/connection_adapters/sqlserver/type/data.rb +2 -2
  46. data/lib/active_record/connection_adapters/sqlserver/type/date.rb +4 -3
  47. data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +8 -8
  48. data/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb +2 -2
  49. data/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +2 -2
  50. data/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +5 -4
  51. data/lib/active_record/connection_adapters/sqlserver/type/float.rb +3 -3
  52. data/lib/active_record/connection_adapters/sqlserver/type/integer.rb +3 -3
  53. data/lib/active_record/connection_adapters/sqlserver/type/json.rb +2 -1
  54. data/lib/active_record/connection_adapters/sqlserver/type/money.rb +4 -4
  55. data/lib/active_record/connection_adapters/sqlserver/type/real.rb +3 -3
  56. data/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +3 -3
  57. data/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +4 -4
  58. data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +3 -3
  59. data/lib/active_record/connection_adapters/sqlserver/type/string.rb +2 -2
  60. data/lib/active_record/connection_adapters/sqlserver/type/text.rb +3 -3
  61. data/lib/active_record/connection_adapters/sqlserver/type/time.rb +6 -6
  62. data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +8 -9
  63. data/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +3 -3
  64. data/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +3 -3
  65. data/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +5 -4
  66. data/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb +2 -2
  67. data/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +3 -3
  68. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +6 -5
  69. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +4 -4
  70. data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +4 -3
  71. data/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +6 -5
  72. data/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +4 -4
  73. data/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +6 -5
  74. data/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +4 -4
  75. data/lib/active_record/connection_adapters/sqlserver/type.rb +37 -35
  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 +128 -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 +214 -171
  87. data/test/cases/change_column_null_test_sqlserver.rb +14 -12
  88. data/test/cases/coerced_tests.rb +631 -356
  89. data/test/cases/column_test_sqlserver.rb +283 -284
  90. data/test/cases/connection_test_sqlserver.rb +17 -20
  91. data/test/cases/execute_procedure_test_sqlserver.rb +20 -20
  92. data/test/cases/fetch_test_sqlserver.rb +16 -22
  93. data/test/cases/fully_qualified_identifier_test_sqlserver.rb +15 -19
  94. data/test/cases/helper_sqlserver.rb +15 -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 +25 -29
  99. data/test/cases/order_test_sqlserver.rb +53 -54
  100. data/test/cases/pessimistic_locking_test_sqlserver.rb +27 -33
  101. data/test/cases/rake_test_sqlserver.rb +33 -45
  102. data/test/cases/schema_dumper_test_sqlserver.rb +107 -109
  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 +28 -35
  106. data/test/cases/specific_schema_test_sqlserver.rb +68 -65
  107. data/test/cases/transaction_test_sqlserver.rb +18 -20
  108. data/test/cases/trigger_test_sqlserver.rb +14 -13
  109. data/test/cases/utils_test_sqlserver.rb +70 -70
  110. data/test/cases/uuid_test_sqlserver.rb +13 -14
  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/sqlserver_specific_schema.rb +22 -22
  140. data/test/support/coerceable_test_sqlserver.rb +15 -9
  141. data/test/support/connection_reflection.rb +3 -2
  142. data/test/support/core_ext/query_cache.rb +4 -1
  143. data/test/support/load_schema_sqlserver.rb +5 -5
  144. data/test/support/minitest_sqlserver.rb +3 -1
  145. data/test/support/paths_sqlserver.rb +11 -11
  146. data/test/support/rake_helpers.rb +13 -10
  147. data/test/support/sql_counter_sqlserver.rb +3 -4
  148. data/test/support/test_in_memory_oltp.rb +9 -7
  149. metadata +17 -7
@@ -1,10 +1,10 @@
1
- require 'cases/helper_sqlserver'
1
+ # frozen_string_literal: true
2
2
 
3
+ require "cases/helper_sqlserver"
3
4
 
4
-
5
- require 'models/event'
5
+ require "models/event"
6
6
  class UniquenessValidationTest < ActiveRecord::TestCase
7
- # So sp_executesql swallows this exception. Run without prpared to see it.
7
+ # So sp_executesql swallows this exception. Run without prepared to see it.
8
8
  coerce_tests! :test_validate_uniqueness_with_limit
9
9
  def test_validate_uniqueness_with_limit_coerced
10
10
  connection.unprepared_statement do
@@ -14,7 +14,7 @@ class UniquenessValidationTest < ActiveRecord::TestCase
14
14
  end
15
15
  end
16
16
 
17
- # So sp_executesql swallows this exception. Run without prpared to see it.
17
+ # So sp_executesql swallows this exception. Run without prepared to see it.
18
18
  coerce_tests! :test_validate_uniqueness_with_limit_and_utf8
19
19
  def test_validate_uniqueness_with_limit_and_utf8_coerced
20
20
  connection.unprepared_statement do
@@ -23,66 +23,125 @@ class UniquenessValidationTest < ActiveRecord::TestCase
23
23
  end
24
24
  end
25
25
  end
26
- end
27
-
28
26
 
27
+ # Skip the test if database is case-insensitive.
28
+ coerce_tests! :test_validate_case_sensitive_uniqueness_by_default
29
+ def test_validate_case_sensitive_uniqueness_by_default_coerced
30
+ database_collation = connection.select_one("SELECT collation_name FROM sys.databases WHERE name = 'activerecord_unittest'").values.first
31
+ skip if database_collation.include?("_CI_")
29
32
 
33
+ original_test_validate_case_sensitive_uniqueness_by_default_coerced
34
+ end
35
+ end
30
36
 
31
- require 'models/event'
37
+ require "models/event"
32
38
  module ActiveRecord
33
39
  class AdapterTest < ActiveRecord::TestCase
34
- # I really dont think we can support legacy binds.
40
+ # I really don`t think we can support legacy binds.
35
41
  coerce_tests! :test_select_all_with_legacy_binds
36
42
  coerce_tests! :test_insert_update_delete_with_legacy_binds
37
43
 
38
44
  # As far as I can tell, SQL Server does not support null bytes in strings.
39
45
  coerce_tests! :test_update_prepared_statement
40
46
 
41
- # So sp_executesql swallows this exception. Run without prpared to see it.
47
+ # So sp_executesql swallows this exception. Run without prepared to see it.
42
48
  coerce_tests! :test_value_limit_violations_are_translated_to_specific_exception
43
49
  def test_value_limit_violations_are_translated_to_specific_exception_coerced
44
50
  connection.unprepared_statement do
45
51
  error = assert_raises(ActiveRecord::ValueTooLong) do
46
- Event.create(title: 'abcdefgh')
52
+ Event.create(title: "abcdefgh")
47
53
  end
48
54
  assert_not_nil error.cause
49
55
  end
50
56
  end
57
+
58
+ # Fix randomly failing test. The loading of the model's schema was affecting the test.
59
+ coerce_tests! :test_errors_when_an_insert_query_is_called_while_preventing_writes
60
+ def test_errors_when_an_insert_query_is_called_while_preventing_writes_coerced
61
+ Subscriber.send(:load_schema!)
62
+ original_test_errors_when_an_insert_query_is_called_while_preventing_writes
63
+ end
51
64
  end
52
65
  end
53
66
 
67
+ module ActiveRecord
68
+ class AdapterTestWithoutTransaction < ActiveRecord::TestCase
69
+ # SQL Server does not allow truncation of tables that are referenced by foreign key
70
+ # constraints. So manually remove/add foreign keys in test.
71
+ coerce_tests! :test_truncate_tables
72
+ def test_truncate_tables_coerced
73
+ # Remove foreign key constraint to allow truncation.
74
+ @connection.remove_foreign_key :authors, :author_addresses
75
+
76
+ assert_operator Post.count, :>, 0
77
+ assert_operator Author.count, :>, 0
78
+ assert_operator AuthorAddress.count, :>, 0
79
+
80
+ @connection.truncate_tables("author_addresses", "authors", "posts")
81
+
82
+ assert_equal 0, Post.count
83
+ assert_equal 0, Author.count
84
+ assert_equal 0, AuthorAddress.count
85
+ ensure
86
+ reset_fixtures("posts", "authors", "author_addresses")
87
+
88
+ # Restore foreign key constraint.
89
+ @connection.add_foreign_key :authors, :author_addresses
90
+ end
91
+
92
+ # SQL Server does not allow truncation of tables that are referenced by foreign key
93
+ # constraints. So manually remove/add foreign keys in test.
94
+ coerce_tests! :test_truncate_tables_with_query_cache
95
+ def test_truncate_tables_with_query_cache
96
+ # Remove foreign key constraint to allow truncation.
97
+ @connection.remove_foreign_key :authors, :author_addresses
98
+
99
+ @connection.enable_query_cache!
100
+
101
+ assert_operator Post.count, :>, 0
102
+ assert_operator Author.count, :>, 0
103
+ assert_operator AuthorAddress.count, :>, 0
54
104
 
105
+ @connection.truncate_tables("author_addresses", "authors", "posts")
55
106
 
107
+ assert_equal 0, Post.count
108
+ assert_equal 0, Author.count
109
+ assert_equal 0, AuthorAddress.count
110
+ ensure
111
+ reset_fixtures("posts", "authors", "author_addresses")
112
+ @connection.disable_query_cache!
56
113
 
57
- require 'models/topic'
114
+ # Restore foreign key constraint.
115
+ @connection.add_foreign_key :authors, :author_addresses
116
+ end
117
+ end
118
+ end
119
+
120
+ require "models/topic"
58
121
  class AttributeMethodsTest < ActiveRecord::TestCase
122
+ # Use IFF for boolean statement in SELECT
59
123
  coerce_tests! %r{typecast attribute from select to false}
60
124
  def test_typecast_attribute_from_select_to_false_coerced
61
- Topic.create(:title => 'Budget')
125
+ Topic.create(:title => "Budget")
62
126
  topic = Topic.all.merge!(:select => "topics.*, IIF (1 = 2, 1, 0) as is_test").first
63
- assert !topic.is_test?
127
+ assert_not_predicate topic, :is_test?
64
128
  end
65
129
 
130
+ # Use IFF for boolean statement in SELECT
66
131
  coerce_tests! %r{typecast attribute from select to true}
67
132
  def test_typecast_attribute_from_select_to_true_coerced
68
- Topic.create(:title => 'Budget')
133
+ Topic.create(:title => "Budget")
69
134
  topic = Topic.all.merge!(:select => "topics.*, IIF (1 = 1, 1, 0) as is_test").first
70
- assert topic.is_test?
135
+ assert_predicate topic, :is_test?
71
136
  end
72
137
  end
73
138
 
74
-
75
- class NumericDataTest < ActiveRecord::TestCase
76
- # We do not have do the DecimalWithoutScale type.
77
- coerce_tests! :test_numeric_fields
78
- coerce_tests! :test_numeric_fields_with_scale
79
- end
80
-
81
139
  class BasicsTest < ActiveRecord::TestCase
140
+ # Use square brackets as SQL Server escaped character
82
141
  coerce_tests! :test_column_names_are_escaped
83
142
  def test_column_names_are_escaped_coerced
84
143
  conn = ActiveRecord::Base.connection
85
- assert_equal '[t]]]', conn.quote_column_name('t]')
144
+ assert_equal "[t]]]", conn.quote_column_name("t]")
86
145
  end
87
146
 
88
147
  # Just like PostgreSQLAdapter does.
@@ -96,52 +155,60 @@ class BasicsTest < ActiveRecord::TestCase
96
155
  Time.use_zone("Eastern Time (US & Canada)") do
97
156
  topic = Topic.find(1)
98
157
  time = Time.zone.parse("2017-07-17 10:56")
99
- topic.update_attributes!(written_on: time)
158
+ topic.update!(written_on: time)
100
159
  assert_equal(time, topic.written_on)
101
160
  end
102
161
  end
103
162
 
104
163
  def test_update_date_time_attributes_with_default_timezone_local
105
- with_env_tz 'America/New_York' do
164
+ with_env_tz "America/New_York" do
106
165
  with_timezone_config default: :local do
107
166
  Time.use_zone("Eastern Time (US & Canada)") do
108
167
  topic = Topic.find(1)
109
168
  time = Time.zone.parse("2017-07-17 10:56")
110
- topic.update_attributes!(written_on: time)
169
+ topic.update!(written_on: time)
111
170
  assert_equal(time, topic.written_on)
112
171
  end
113
172
  end
114
173
  end
115
174
  end
116
175
 
117
- # Need to escape `quoted_id` once it contains brackets
118
- coerce_tests! %r{column names are quoted when using #from clause and model has ignored columns}
119
- test "column names are quoted when using #from clause and model has ignored columns coerced" do
120
- refute_empty Developer.ignored_columns
121
- query = Developer.from("developers").to_sql
122
- quoted_id = "#{Developer.quoted_table_name}.#{Developer.quoted_primary_key}"
123
-
124
- assert_match(/SELECT #{Regexp.escape(quoted_id)}.* FROM developers/, query)
176
+ # SQL Server does not have query for release_savepoint
177
+ coerce_tests! %r{an empty transaction does not raise if preventing writes}
178
+ test "an empty transaction does not raise if preventing writes coerced" do
179
+ ActiveRecord::Base.connection_handler.while_preventing_writes do
180
+ assert_queries(1, ignore_none: true) do
181
+ Bird.transaction do
182
+ ActiveRecord::Base.connection.materialize_transactions
183
+ end
184
+ end
185
+ end
125
186
  end
126
187
  end
127
188
 
128
-
129
-
130
-
131
189
  class BelongsToAssociationsTest < ActiveRecord::TestCase
132
190
  # Since @client.firm is a single first/top, and we use FETCH the order clause is used.
133
191
  coerce_tests! :test_belongs_to_does_not_use_order_by
134
192
 
193
+ # Square brackets around column name
135
194
  coerce_tests! :test_belongs_to_with_primary_key_joins_on_correct_column
136
195
  def test_belongs_to_with_primary_key_joins_on_correct_column_coerced
137
196
  sql = Client.joins(:firm_with_primary_key).to_sql
138
197
  assert_no_match(/\[firm_with_primary_keys_companies\]\.\[id\]/, sql)
139
198
  assert_match(/\[firm_with_primary_keys_companies\]\.\[name\]/, sql)
140
199
  end
141
- end
142
-
143
-
144
200
 
201
+ # Asserted SQL to get one row different from original test.
202
+ coerce_tests! :test_belongs_to
203
+ def test_belongs_to_coerced
204
+ client = Client.find(3)
205
+ first_firm = companies(:first_firm)
206
+ assert_sql(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do
207
+ assert_equal first_firm, client.firm
208
+ assert_equal first_firm.name, client.firm.name
209
+ end
210
+ end
211
+ end
145
212
 
146
213
  module ActiveRecord
147
214
  class BindParameterTest < ActiveRecord::TestCase
@@ -163,62 +230,53 @@ module ActiveRecord
163
230
  # SQL Server adapter does not use a statement cache as query plans are already reused using `EXEC sp_executesql`.
164
231
  coerce_tests! :test_statement_cache
165
232
  coerce_tests! :test_statement_cache_with_query_cache
233
+ coerce_tests! :test_statement_cache_with_find
166
234
  coerce_tests! :test_statement_cache_with_find_by
167
235
  coerce_tests! :test_statement_cache_with_in_clause
168
236
  coerce_tests! :test_statement_cache_with_sql_string_literal
169
237
  end
170
238
  end
171
239
 
172
-
173
240
  module ActiveRecord
174
241
  class InstrumentationTest < ActiveRecord::TestCase
175
- # This fails randomly due to schema cache being lost?
242
+ # Fix randomly failing test. The loading of the model's schema was affecting the test.
176
243
  coerce_tests! :test_payload_name_on_load
177
244
  def test_payload_name_on_load_coerced
178
- Book.create(name: "test book")
179
- Book.first
180
- subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |*args|
181
- event = ActiveSupport::Notifications::Event.new(*args)
182
- if event.payload[:sql].match "SELECT"
183
- assert_equal "Book Load", event.payload[:name]
184
- end
185
- end
186
- Book.first
187
- ensure
188
- ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber
245
+ Book.send(:load_schema!)
246
+ original_test_payload_name_on_load
189
247
  end
190
248
  end
191
249
  end
192
250
 
193
251
  class CalculationsTest < ActiveRecord::TestCase
194
- # This fails randomly due to schema cache being lost?
252
+ # Fix randomly failing test. The loading of the model's schema was affecting the test.
195
253
  coerce_tests! :test_offset_is_kept
196
254
  def test_offset_is_kept_coerced
197
- Account.first
198
- queries = assert_sql { Account.offset(1).count }
199
- assert_equal 1, queries.length
200
- assert_match(/OFFSET/, queries.first)
255
+ Account.send(:load_schema!)
256
+ original_test_offset_is_kept
201
257
  end
202
258
 
203
259
  # Are decimal, not integer.
204
260
  coerce_tests! :test_should_return_decimal_average_of_integer_field
205
261
  def test_should_return_decimal_average_of_integer_field_coerced
206
262
  value = Account.average(:id)
207
- assert_equal BigDecimal('3.0').to_s, BigDecimal(value).to_s
263
+ assert_equal BigDecimal("3.0").to_s, BigDecimal(value).to_s
208
264
  end
209
265
 
266
+ # Match SQL Server limit implementation
210
267
  coerce_tests! :test_limit_is_kept
211
268
  def test_limit_is_kept_coerced
212
269
  queries = capture_sql_ss { Account.limit(1).count }
213
270
  assert_equal 1, queries.length
214
- _(queries.first).must_match %r{ORDER BY \[accounts\]\.\[id\] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1}
271
+ assert_match(/ORDER BY \[accounts\]\.\[id\] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/, queries.first)
215
272
  end
216
273
 
274
+ # Match SQL Server limit implementation
217
275
  coerce_tests! :test_limit_with_offset_is_kept
218
276
  def test_limit_with_offset_is_kept_coerced
219
277
  queries = capture_sql_ss { Account.limit(1).offset(1).count }
220
278
  assert_equal 1, queries.length
221
- _(queries.first).must_match %r{ORDER BY \[accounts\]\.\[id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY.*@0 = 1, @1 = 1}
279
+ assert_match(/ORDER BY \[accounts\]\.\[id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY.*@0 = 1, @1 = 1/, queries.first)
222
280
  end
223
281
 
224
282
  # SQL Server needs an alias for the calculated column
@@ -233,49 +291,57 @@ class CalculationsTest < ActiveRecord::TestCase
233
291
  coerce_tests! :test_having_with_strong_parameters
234
292
  end
235
293
 
236
-
237
-
238
294
  module ActiveRecord
239
295
  class Migration
240
296
  class ChangeSchemaTest < ActiveRecord::TestCase
241
- # We test these.
242
- coerce_tests! :test_create_table_with_bigint,
243
- :test_create_table_with_defaults
244
- end
297
+ # Integer.default is a number and not a string
298
+ coerce_tests! :test_create_table_with_defaults
299
+ def test_create_table_with_defaults_coerce
300
+ connection.create_table :testings do |t|
301
+ t.column :one, :string, default: "hello"
302
+ t.column :two, :boolean, default: true
303
+ t.column :three, :boolean, default: false
304
+ t.column :four, :integer, default: 1
305
+ t.column :five, :text, default: "hello"
306
+ end
245
307
 
246
- class ChangeSchemaWithDependentObjectsTest < ActiveRecord::TestCase
247
- # In SQL Server you have to delete the tables yourself in the right order.
248
- coerce_tests! :test_create_table_with_force_cascade_drops_dependent_objects
308
+ columns = connection.columns(:testings)
309
+ one = columns.detect { |c| c.name == "one" }
310
+ two = columns.detect { |c| c.name == "two" }
311
+ three = columns.detect { |c| c.name == "three" }
312
+ four = columns.detect { |c| c.name == "four" }
313
+ five = columns.detect { |c| c.name == "five" }
314
+
315
+ assert_equal "hello", one.default
316
+ assert_equal true, connection.lookup_cast_type_from_column(two).deserialize(two.default)
317
+ assert_equal false, connection.lookup_cast_type_from_column(three).deserialize(three.default)
318
+ assert_equal 1, four.default
319
+ assert_equal "hello", five.default
320
+ end
249
321
  end
250
322
  end
251
323
  end
252
324
 
253
-
254
-
255
325
  module ActiveRecord
256
326
  module ConnectionAdapters
257
327
  class QuoteARBaseTest < ActiveRecord::TestCase
258
-
259
328
  # Use our date format.
260
329
  coerce_tests! :test_quote_ar_object
261
330
  def test_quote_ar_object_coerced
262
331
  value = DatetimePrimaryKey.new(id: @time)
263
- assert_equal "'02-14-2017 12:34:56.79'", @connection.quote(value)
332
+ assert_equal "'02-14-2017 12:34:56.79'", @connection.quote(value)
264
333
  end
265
334
 
266
335
  # Use our date format.
267
336
  coerce_tests! :test_type_cast_ar_object
268
337
  def test_type_cast_ar_object_coerced
269
338
  value = DatetimePrimaryKey.new(id: @time)
270
- assert_equal "02-14-2017 12:34:56.79", @connection.type_cast(value)
339
+ assert_equal "02-14-2017 12:34:56.79", @connection.type_cast(value)
271
340
  end
272
-
273
341
  end
274
342
  end
275
343
  end
276
344
 
277
-
278
-
279
345
  module ActiveRecord
280
346
  class Migration
281
347
  class ColumnAttributesTest < ActiveRecord::TestCase
@@ -290,16 +356,13 @@ module ActiveRecord
290
356
  end
291
357
  end
292
358
 
293
-
294
-
295
-
296
359
  module ActiveRecord
297
360
  class Migration
298
- class ColumnsTest
361
+ class ColumnsTest < ActiveRecord::TestCase
299
362
  # Our defaults are real 70000 integers vs '70000' strings.
300
363
  coerce_tests! :test_rename_column_preserves_default_value_not_null
301
364
  def test_rename_column_preserves_default_value_not_null_coerced
302
- add_column 'test_models', 'salary', :integer, :default => 70000
365
+ add_column "test_models", "salary", :integer, :default => 70000
303
366
  default_before = connection.columns("test_models").find { |c| c.name == "salary" }.default
304
367
  assert_equal 70000, default_before
305
368
  rename_column "test_models", "salary", "annual_salary"
@@ -315,9 +378,9 @@ module ActiveRecord
315
378
  add_column "test_models", :hat_size, :integer
316
379
  add_column "test_models", :hat_style, :string, :limit => 100
317
380
  add_index "test_models", ["hat_style", "hat_size"], :unique => true
318
- assert_equal 1, connection.indexes('test_models').size
381
+ assert_equal 1, connection.indexes("test_models").size
319
382
  remove_column("test_models", "hat_size")
320
- assert_equal [], connection.indexes('test_models').map(&:name)
383
+ assert_equal [], connection.indexes("test_models").map(&:name)
321
384
  end
322
385
 
323
386
  # Choose `StatementInvalid` vs `ActiveRecordError`.
@@ -332,9 +395,6 @@ module ActiveRecord
332
395
  end
333
396
  end
334
397
 
335
-
336
-
337
-
338
398
  class MigrationTest < ActiveRecord::TestCase
339
399
  # We do not have do the DecimalWithoutScale type.
340
400
  coerce_tests! :test_add_table_with_decimals
@@ -358,7 +418,7 @@ class MigrationTest < ActiveRecord::TestCase
358
418
  assert_not_nil b.my_house_population
359
419
  assert_not_nil b.value_of_e
360
420
  assert_kind_of BigDecimal, b.world_population
361
- assert_equal '6000000000.0', b.world_population.to_s
421
+ assert_equal "6000000000.0", b.world_population.to_s
362
422
  assert_kind_of Integer, b.my_house_population
363
423
  assert_equal 3, b.my_house_population
364
424
  assert_kind_of BigDecimal, b.bank_balance
@@ -374,19 +434,13 @@ class MigrationTest < ActiveRecord::TestCase
374
434
  coerce_tests! :test_internal_metadata_stores_environment
375
435
  end
376
436
 
377
-
378
-
379
-
380
437
  class CoreTest < ActiveRecord::TestCase
381
- # I think fixtures are useing the wrong time zone and the `:first`
438
+ # I think fixtures are using the wrong time zone and the `:first`
382
439
  # `topics`.`bonus_time` attribute of 2005-01-30t15:28:00.00+01:00 is
383
440
  # getting local EST time for me and set to "09:28:00.0000000".
384
441
  coerce_tests! :test_pretty_print_persisted
385
442
  end
386
443
 
387
-
388
-
389
-
390
444
  module ActiveRecord
391
445
  module ConnectionAdapters
392
446
  # Just like PostgreSQLAdapter does.
@@ -400,27 +454,152 @@ module ActiveRecord
400
454
  end
401
455
  end
402
456
 
457
+ module ActiveRecord
458
+ # The original module is hardcoded for PostgreSQL/SQLite/MySQL tests.
459
+ module DatabaseTasksSetupper
460
+ def setup
461
+ @sqlserver_tasks =
462
+ Class.new do
463
+ def create; end
403
464
 
465
+ def drop; end
404
466
 
467
+ def purge; end
468
+
469
+ def charset; end
470
+
471
+ def collation; end
472
+
473
+ def structure_dump(*); end
474
+
475
+ def structure_load(*); end
476
+ end.new
477
+
478
+ $stdout, @original_stdout = StringIO.new, $stdout
479
+ $stderr, @original_stderr = StringIO.new, $stderr
480
+ end
481
+
482
+ def with_stubbed_new
483
+ ActiveRecord::Tasks::SQLServerDatabaseTasks.stub(:new, @sqlserver_tasks) do
484
+ yield
485
+ end
486
+ end
487
+ end
488
+
489
+ class DatabaseTasksCreateTest < ActiveRecord::TestCase
490
+ # Coerce PostgreSQL/SQLite/MySQL tests.
491
+ coerce_all_tests!
492
+
493
+ def test_sqlserver_create
494
+ with_stubbed_new do
495
+ assert_called(eval("@sqlserver_tasks"), :create) do
496
+ ActiveRecord::Tasks::DatabaseTasks.create "adapter" => :sqlserver
497
+ end
498
+ end
499
+ end
500
+ end
501
+
502
+ class DatabaseTasksDropTest < ActiveRecord::TestCase
503
+ # Coerce PostgreSQL/SQLite/MySQL tests.
504
+ coerce_all_tests!
505
+
506
+ def test_sqlserver_drop
507
+ with_stubbed_new do
508
+ assert_called(eval("@sqlserver_tasks"), :drop) do
509
+ ActiveRecord::Tasks::DatabaseTasks.drop "adapter" => :sqlserver
510
+ end
511
+ end
512
+ end
513
+ end
514
+
515
+ class DatabaseTasksPurgeTest < ActiveRecord::TestCase
516
+ # Coerce PostgreSQL/SQLite/MySQL tests.
517
+ coerce_all_tests!
518
+
519
+ def test_sqlserver_purge
520
+ with_stubbed_new do
521
+ assert_called(eval("@sqlserver_tasks"), :purge) do
522
+ ActiveRecord::Tasks::DatabaseTasks.purge "adapter" => :sqlserver
523
+ end
524
+ end
525
+ end
526
+ end
527
+
528
+ class DatabaseTasksCharsetTest < ActiveRecord::TestCase
529
+ # Coerce PostgreSQL/SQLite/MySQL tests.
530
+ coerce_all_tests!
531
+
532
+ def test_sqlserver_charset
533
+ with_stubbed_new do
534
+ assert_called(eval("@sqlserver_tasks"), :charset) do
535
+ ActiveRecord::Tasks::DatabaseTasks.charset "adapter" => :sqlserver
536
+ end
537
+ end
538
+ end
539
+ end
540
+
541
+ class DatabaseTasksCollationTest < ActiveRecord::TestCase
542
+ # Coerce PostgreSQL/SQLite/MySQL tests.
543
+ coerce_all_tests!
544
+
545
+ def test_sqlserver_collation
546
+ with_stubbed_new do
547
+ assert_called(eval("@sqlserver_tasks"), :collation) do
548
+ ActiveRecord::Tasks::DatabaseTasks.collation "adapter" => :sqlserver
549
+ end
550
+ end
551
+ end
552
+ end
553
+
554
+ class DatabaseTasksStructureDumpTest < ActiveRecord::TestCase
555
+ # Coerce PostgreSQL/SQLite/MySQL tests.
556
+ coerce_all_tests!
557
+
558
+ def test_sqlserver_structure_dump
559
+ with_stubbed_new do
560
+ assert_called_with(
561
+ eval("@sqlserver_tasks"), :structure_dump,
562
+ ["awesome-file.sql", nil]
563
+ ) do
564
+ ActiveRecord::Tasks::DatabaseTasks.structure_dump({ "adapter" => :sqlserver }, "awesome-file.sql")
565
+ end
566
+ end
567
+ end
568
+ end
569
+
570
+ class DatabaseTasksStructureLoadTest < ActiveRecord::TestCase
571
+ # Coerce PostgreSQL/SQLite/MySQL tests.
572
+ coerce_all_tests!
573
+
574
+ def test_sqlserver_structure_load
575
+ with_stubbed_new do
576
+ assert_called_with(
577
+ eval("@sqlserver_tasks"),
578
+ :structure_load,
579
+ ["awesome-file.sql", nil]
580
+ ) do
581
+ ActiveRecord::Tasks::DatabaseTasks.structure_load({ "adapter" => :sqlserver }, "awesome-file.sql")
582
+ end
583
+ end
584
+ end
585
+ end
405
586
 
406
- module ActiveRecord
407
587
  class DatabaseTasksDumpSchemaCacheTest < ActiveRecord::TestCase
408
588
  # Skip this test with /tmp/my_schema_cache.yml path on Windows.
409
- coerce_tests! :test_dump_schema_cache if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
589
+ coerce_tests! :test_dump_schema_cache if RbConfig::CONFIG["host_os"] =~ /mswin|mingw/
410
590
  end
591
+
411
592
  class DatabaseTasksCreateAllTest < ActiveRecord::TestCase
412
593
  # We extend `local_database?` so that common VM IPs can be used.
413
594
  coerce_tests! :test_ignores_remote_databases, :test_warning_for_remote_databases
414
595
  end
596
+
415
597
  class DatabaseTasksDropAllTest < ActiveRecord::TestCase
416
598
  # We extend `local_database?` so that common VM IPs can be used.
417
599
  coerce_tests! :test_ignores_remote_databases, :test_warning_for_remote_databases
418
600
  end
419
601
  end
420
602
 
421
-
422
-
423
-
424
603
  class DefaultScopingTest < ActiveRecord::TestCase
425
604
  # We are not doing order duplicate removal anymore.
426
605
  coerce_tests! :test_order_in_default_scope_should_not_prevail
@@ -434,16 +613,13 @@ class DefaultScopingTest < ActiveRecord::TestCase
434
613
  end
435
614
  end
436
615
 
437
-
438
-
439
-
440
- require 'models/post'
441
- require 'models/subscriber'
616
+ require "models/post"
617
+ require "models/subscriber"
442
618
  class EachTest < ActiveRecord::TestCase
443
619
  # Quoting in tests does not cope with bracket quoting.
444
620
  coerce_tests! :test_find_in_batches_should_quote_batch_order
445
621
  def test_find_in_batches_should_quote_batch_order_coerced
446
- c = Post.connection
622
+ Post.connection
447
623
  assert_sql(/ORDER BY \[posts\]\.\[id\]/) do
448
624
  Post.find_in_batches(:batch_size => 1) do |batch|
449
625
  assert_kind_of Array, batch
@@ -455,7 +631,7 @@ class EachTest < ActiveRecord::TestCase
455
631
  # Quoting in tests does not cope with bracket quoting.
456
632
  coerce_tests! :test_in_batches_should_quote_batch_order
457
633
  def test_in_batches_should_quote_batch_order_coerced
458
- c = Post.connection
634
+ Post.connection
459
635
  assert_sql(/ORDER BY \[posts\]\.\[id\]/) do
460
636
  Post.in_batches(of: 1) do |relation|
461
637
  assert_kind_of ActiveRecord::Relation, relation
@@ -465,9 +641,6 @@ class EachTest < ActiveRecord::TestCase
465
641
  end
466
642
  end
467
643
 
468
-
469
-
470
-
471
644
  class EagerAssociationTest < ActiveRecord::TestCase
472
645
  # Use LEN() vs length() function.
473
646
  coerce_tests! :test_count_with_include
@@ -479,14 +652,13 @@ class EagerAssociationTest < ActiveRecord::TestCase
479
652
  coerce_tests! %r{including association based on sql condition and no database column}
480
653
  end
481
654
 
482
-
483
-
484
-
485
- require 'models/topic'
655
+ require "models/topic"
486
656
  class FinderTest < ActiveRecord::TestCase
657
+ # We have implicit ordering, via FETCH.
487
658
  coerce_tests! %r{doesn't have implicit ordering},
488
- :test_find_doesnt_have_implicit_ordering # We have implicit ordering, via FETCH.
659
+ :test_find_doesnt_have_implicit_ordering
489
660
 
661
+ # Square brackets around column name
490
662
  coerce_tests! :test_exists_does_not_select_columns_without_alias
491
663
  def test_exists_does_not_select_columns_without_alias_coerced
492
664
  assert_sql(/SELECT\s+1 AS one FROM \[topics\].*OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) do
@@ -494,6 +666,7 @@ class FinderTest < ActiveRecord::TestCase
494
666
  end
495
667
  end
496
668
 
669
+ # Assert SQL Server limit implementation
497
670
  coerce_tests! :test_take_and_first_and_last_with_integer_should_use_sql_limit
498
671
  def test_take_and_first_and_last_with_integer_should_use_sql_limit_coerced
499
672
  assert_sql(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.* @0 = 3/) { Topic.take(3).entries }
@@ -507,7 +680,7 @@ class FinderTest < ActiveRecord::TestCase
507
680
  # Can not use array condition due to not finding right type and hence fractional second quoting.
508
681
  coerce_tests! :test_condition_utc_time_interpolation_with_default_timezone_local
509
682
  def test_condition_utc_time_interpolation_with_default_timezone_local_coerced
510
- with_env_tz 'America/New_York' do
683
+ with_env_tz "America/New_York" do
511
684
  with_timezone_config default: :local do
512
685
  topic = Topic.first
513
686
  assert_equal topic, Topic.where(written_on: topic.written_on.getutc).first
@@ -518,7 +691,7 @@ class FinderTest < ActiveRecord::TestCase
518
691
  # Can not use array condition due to not finding right type and hence fractional second quoting.
519
692
  coerce_tests! :test_condition_local_time_interpolation_with_default_timezone_utc
520
693
  def test_condition_local_time_interpolation_with_default_timezone_utc_coerced
521
- with_env_tz 'America/New_York' do
694
+ with_env_tz "America/New_York" do
522
695
  with_timezone_config default: :utc do
523
696
  topic = Topic.first
524
697
  assert_equal topic, Topic.where(written_on: topic.written_on.getlocal).first
@@ -527,9 +700,6 @@ class FinderTest < ActiveRecord::TestCase
527
700
  end
528
701
  end
529
702
 
530
-
531
-
532
-
533
703
  module ActiveRecord
534
704
  class Migration
535
705
  class ForeignKeyTest < ActiveRecord::TestCase
@@ -547,90 +717,103 @@ module ActiveRecord
547
717
  end
548
718
  end
549
719
 
550
-
551
-
552
-
553
720
  class HasOneAssociationsTest < ActiveRecord::TestCase
554
721
  # We use OFFSET/FETCH vs TOP. So we always have an order.
555
722
  coerce_tests! :test_has_one_does_not_use_order_by
556
- end
557
-
558
723
 
724
+ # Asserted SQL to get one row different from original test.
725
+ coerce_tests! :test_has_one
726
+ def test_has_one_coerced
727
+ firm = companies(:first_firm)
728
+ first_account = Account.find(1)
729
+ assert_sql(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do
730
+ assert_equal first_account, firm.account
731
+ assert_equal first_account.credit_limit, firm.account.credit_limit
732
+ end
733
+ end
734
+ end
559
735
 
736
+ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
737
+ # Asserted SQL to get one row different from original test.
738
+ coerce_tests! :test_has_one_through_executes_limited_query
739
+ def test_has_one_through_executes_limited_query_coerced
740
+ boring_club = clubs(:boring_club)
741
+ assert_sql(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do
742
+ assert_equal boring_club, @member.general_club
743
+ end
744
+ end
745
+ end
560
746
 
561
- require 'models/company'
747
+ require "models/company"
562
748
  class InheritanceTest < ActiveRecord::TestCase
749
+ # Rails test required inserting to a identity column.
563
750
  coerce_tests! :test_a_bad_type_column
564
751
  def test_a_bad_type_column_coerced
565
- Company.connection.with_identity_insert_enabled('companies') do
752
+ Company.connection.with_identity_insert_enabled("companies") do
566
753
  Company.connection.insert "INSERT INTO companies (id, #{QUOTED_TYPE}, name) VALUES(100, 'bad_class!', 'Not happening')"
567
754
  end
568
755
  assert_raise(ActiveRecord::SubclassNotFound) { Company.find(100) }
569
756
  end
570
757
 
758
+ # Use Square brackets around column name
571
759
  coerce_tests! :test_eager_load_belongs_to_primary_key_quoting
572
760
  def test_eager_load_belongs_to_primary_key_quoting_coerced
573
- con = Account.connection
761
+ Account.connection
574
762
  assert_sql(/\[companies\]\.\[id\] = @0.* @0 = 1/) do
575
763
  Account.all.merge!(:includes => :firm).find(1)
576
764
  end
577
765
  end
578
766
  end
579
767
 
580
-
581
-
582
-
583
768
  class LeftOuterJoinAssociationTest < ActiveRecord::TestCase
584
769
  # Uses || operator in SQL. Just trust core gets value out of this test.
585
770
  coerce_tests! :test_does_not_override_select
586
771
  end
587
772
 
588
-
589
-
590
-
591
- class NamedScopingTest < ActiveRecord::TestCase
592
- # This works now because we add an `order(:id)` sort to break the order tie for deterministic results.
593
- coerce_tests! :test_scopes_honor_current_scopes_from_when_defined
594
- def test_scopes_honor_current_scopes_from_when_defined_coerced
595
- assert !Post.ranked_by_comments.order(:id).limit_by(5).empty?
596
- assert !authors(:david).posts.ranked_by_comments.order(:id).limit_by(5).empty?
597
- assert_not_equal Post.ranked_by_comments.order(:id).limit_by(5), authors(:david).posts.ranked_by_comments.order(:id).limit_by(5)
598
- assert_not_equal Post.order(:id).top(5), authors(:david).posts.order(:id).top(5)
599
- # Oracle sometimes sorts differently if WHERE condition is changed
600
- assert_equal authors(:david).posts.ranked_by_comments.limit_by(5).to_a.sort_by(&:id), authors(:david).posts.top(5).to_a.sort_by(&:id)
601
- assert_equal Post.ranked_by_comments.limit_by(5), Post.top(5)
602
- end
603
- end
604
-
605
-
606
-
607
-
608
- require 'models/developer'
609
- require 'models/computer'
773
+ require "models/developer"
774
+ require "models/computer"
610
775
  class NestedRelationScopingTest < ActiveRecord::TestCase
776
+ # Assert SQL Server limit implementation
611
777
  coerce_tests! :test_merge_options
612
778
  def test_merge_options_coerced
613
- Developer.where('salary = 80000').scoping do
779
+ Developer.where("salary = 80000").scoping do
614
780
  Developer.limit(10).scoping do
615
781
  devs = Developer.all
616
782
  sql = devs.to_sql
617
- assert_match '(salary = 80000)', sql
618
- assert_match 'FETCH NEXT 10 ROWS ONLY', sql
783
+ assert_match "(salary = 80000)", sql
784
+ assert_match "FETCH NEXT 10 ROWS ONLY", sql
619
785
  end
620
786
  end
621
787
  end
622
788
  end
623
789
 
790
+ require "models/topic"
791
+ class PersistenceTest < ActiveRecord::TestCase
792
+ # Rails test required updating a identity column.
793
+ coerce_tests! :test_update_columns_changing_id
624
794
 
795
+ # Rails test required updating a identity column.
796
+ coerce_tests! :test_update
797
+ def test_update_coerced
798
+ topic = Topic.find(1)
799
+ assert_not_predicate topic, :approved?
800
+ assert_equal "The First Topic", topic.title
625
801
 
802
+ topic.update("approved" => true, "title" => "The First Topic Updated")
803
+ topic.reload
804
+ assert_predicate topic, :approved?
805
+ assert_equal "The First Topic Updated", topic.title
626
806
 
627
- require 'models/parrot'
628
- require 'models/topic'
629
- class PersistenceTest < ActiveRecord::TestCase
630
- # We can not UPDATE identity columns.
631
- coerce_tests! :test_update_columns_changing_id
807
+ topic.update(approved: false, title: "The First Topic")
808
+ topic.reload
809
+ assert_not_predicate topic, :approved?
810
+ assert_equal "The First Topic", topic.title
811
+ end
812
+ end
632
813
 
633
- # Previous test required updating a identity column.
814
+ require "models/author"
815
+ class UpdateAllTest < ActiveRecord::TestCase
816
+ # Rails test required updating a identity column.
634
817
  coerce_tests! :test_update_all_doesnt_ignore_order
635
818
  def test_update_all_doesnt_ignore_order_coerced
636
819
  david, mary = authors(:david), authors(:mary)
@@ -638,81 +821,80 @@ class PersistenceTest < ActiveRecord::TestCase
638
821
  _(mary.id).must_equal 2
639
822
  _(david.name).wont_equal mary.name
640
823
  assert_sql(/UPDATE.*\(SELECT \[authors\].\[id\] FROM \[authors\].*ORDER BY \[authors\].\[id\]/i) do
641
- Author.where('[id] > 1').order(:id).update_all(name: 'Test')
824
+ Author.where("[id] > 1").order(:id).update_all(name: "Test")
642
825
  end
643
- _(david.reload.name).must_equal 'David'
644
- _(mary.reload.name).must_equal 'Test'
645
- end
646
-
647
- # We can not UPDATE identity columns.
648
- coerce_tests! :test_update_attributes
649
- def test_update_attributes_coerced
650
- topic = Topic.find(1)
651
- assert !topic.approved?
652
- assert_equal "The First Topic", topic.title
653
- topic.update_attributes("approved" => true, "title" => "The First Topic Updated")
654
- topic.reload
655
- assert topic.approved?
656
- assert_equal "The First Topic Updated", topic.title
657
- topic.update_attributes(approved: false, title: "The First Topic")
658
- topic.reload
659
- assert !topic.approved?
660
- assert_equal "The First Topic", topic.title
661
- # SQLServer: Here is where it breaks down. No exceptions.
662
- # assert_raise(ActiveRecord::RecordNotUnique, ActiveRecord::StatementInvalid) do
663
- # topic.update_attributes(id: 3, title: "Hm is it possible?")
664
- # end
665
- # assert_not_equal "Hm is it possible?", Topic.find(3).title
666
- # topic.update_attributes(id: 1234)
667
- # assert_nothing_raised { topic.reload }
668
- # assert_equal topic.title, Topic.find(1234).title
826
+ _(david.reload.name).must_equal "David"
827
+ _(mary.reload.name).must_equal "Test"
669
828
  end
670
829
  end
671
830
 
672
-
673
-
674
-
675
- require 'models/topic'
831
+ require "models/topic"
676
832
  module ActiveRecord
677
833
  class PredicateBuilderTest < ActiveRecord::TestCase
834
+ # Same as original test except string has `N` prefix to indicate unicode string.
678
835
  coerce_tests! :test_registering_new_handlers
679
836
  def test_registering_new_handlers_coerced
680
- Topic.predicate_builder.register_handler(Regexp, proc do |column, value|
681
- Arel::Nodes::InfixOperation.new('~', column, Arel.sql(value.source))
682
- end)
683
- assert_match %r{\[topics\].\[title\] ~ rails}i, Topic.where(title: /rails/).to_sql
684
- ensure
685
- Topic.reset_column_information
837
+ assert_match %r{#{Regexp.escape(topic_title)} ~ N'rails'}i, Topic.where(title: /rails/).to_sql
838
+ end
839
+
840
+ # Same as original test except string has `N` prefix to indicate unicode string.
841
+ coerce_tests! :test_registering_new_handlers_for_association
842
+ def test_registering_new_handlers_for_association_coerced
843
+ assert_match %r{#{Regexp.escape(topic_title)} ~ N'rails'}i, Reply.joins(:topic).where(topics: { title: /rails/ }).to_sql
686
844
  end
687
845
  end
688
846
  end
689
847
 
690
-
691
-
692
-
693
848
  class PrimaryKeysTest < ActiveRecord::TestCase
694
- # Gonna trust Rails core for this. We end up with 2 querys vs 3 asserted
695
- # but as far as I can tell, this is only one for us anyway.
849
+ # SQL Server does not have query for release_savepoint
696
850
  coerce_tests! :test_create_without_primary_key_no_extra_query
851
+ def test_create_without_primary_key_no_extra_query_coerced
852
+ klass = Class.new(ActiveRecord::Base) do
853
+ self.table_name = "dashboards"
854
+ end
855
+ klass.create! # warmup schema cache
856
+ assert_queries(2, ignore_none: true) { klass.create! }
857
+ end
697
858
  end
698
859
 
699
-
700
-
701
-
702
- require 'models/task'
860
+ require "models/task"
703
861
  class QueryCacheTest < ActiveRecord::TestCase
862
+ # SQL Server adapter not in list of supported adapters in original test.
704
863
  coerce_tests! :test_cache_does_not_wrap_results_in_arrays
705
864
  def test_cache_does_not_wrap_results_in_arrays_coerced
706
865
  Task.cache do
707
- assert_kind_of Numeric, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
866
+ assert_equal 2, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
708
867
  end
709
868
  end
710
- end
711
869
 
870
+ # Same as original test except that we expect one query to be performed to retrieve the table's primary key.
871
+ # When we generate the SQL for the `find` it includes ordering on the primary key. If we reset the column
872
+ # information then the primary key needs to be retrieved from the database again to generate the SQL causing the
873
+ # original test's `assert_no_queries` assertion to fail. Assert that the query was to get the primary key.
874
+ coerce_tests! :test_query_cached_even_when_types_are_reset
875
+ def test_query_cached_even_when_types_are_reset_coerced
876
+ Task.cache do
877
+ # Warm the cache
878
+ Task.find(1)
879
+
880
+ # Preload the type cache again (so we don't have those queries issued during our assertions)
881
+ Task.connection.send(:reload_type_map)
712
882
 
883
+ # Clear places where type information is cached
884
+ Task.reset_column_information
885
+ Task.initialize_find_by_cache
886
+ Task.define_attribute_methods
713
887
 
888
+ assert_queries(1, ignore_none: true) do
889
+ Task.find(1)
890
+ end
714
891
 
715
- require 'models/post'
892
+ assert_includes ActiveRecord::SQLCounter.log_all.first, "TC.CONSTRAINT_TYPE = N''PRIMARY KEY''"
893
+ end
894
+ end
895
+ end
896
+
897
+ require "models/post"
716
898
  class RelationTest < ActiveRecord::TestCase
717
899
  # Use LEN vs LENGTH function.
718
900
  coerce_tests! :test_reverse_order_with_function
@@ -733,6 +915,26 @@ class RelationTest < ActiveRecord::TestCase
733
915
  # We have implicit ordering, via FETCH.
734
916
  coerce_tests! %r{doesn't have implicit ordering}
735
917
 
918
+ # We have implicit ordering, via FETCH.
919
+ coerce_tests! :test_reorder_with_take
920
+ def test_reorder_with_take_coerced
921
+ sql_log = capture_sql do
922
+ assert Post.order(:title).reorder(nil).take
923
+ end
924
+ assert sql_log.none? { |sql| /order by [posts].[title]/i.match?(sql) }, "ORDER BY title was used in the query: #{sql_log}"
925
+ assert sql_log.all? { |sql| /order by \[posts\]\.\[id\]/i.match?(sql) }, "default ORDER BY ID was not used in the query: #{sql_log}"
926
+ end
927
+
928
+ # We have implicit ordering, via FETCH.
929
+ coerce_tests! :test_reorder_with_first
930
+ def test_reorder_with_first_coerced
931
+ sql_log = capture_sql do
932
+ assert Post.order(:title).reorder(nil).first
933
+ end
934
+ assert sql_log.none? { |sql| /order by [posts].[title]/i.match?(sql) }, "ORDER BY title was used in the query: #{sql_log}"
935
+ assert sql_log.all? { |sql| /order by \[posts\]\.\[id\]/i.match?(sql) }, "default ORDER BY ID was not used in the query: #{sql_log}"
936
+ end
937
+
736
938
  # We are not doing order duplicate removal anymore.
737
939
  coerce_tests! :test_order_using_scoping
738
940
 
@@ -756,17 +958,17 @@ class RelationTest < ActiveRecord::TestCase
756
958
  # so we are skipping all together.
757
959
  coerce_tests! :test_empty_complex_chained_relations
758
960
 
759
- # Can't apply offset withour ORDER
961
+ # Can't apply offset without ORDER
760
962
  coerce_tests! %r{using a custom table affects the wheres}
761
- test 'using a custom table affects the wheres coerced' do
963
+ test "using a custom table affects the wheres coerced" do
762
964
  post = posts(:welcome)
763
965
 
764
966
  assert_equal post, custom_post_relation.where!(title: post.title).order(:id).take
765
967
  end
766
968
 
767
- # Can't apply offset withour ORDER
969
+ # Can't apply offset without ORDER
768
970
  coerce_tests! %r{using a custom table with joins affects the joins}
769
- test 'using a custom table with joins affects the joins coerced' do
971
+ test "using a custom table with joins affects the joins coerced" do
770
972
  post = posts(:welcome)
771
973
 
772
974
  assert_equal post, custom_post_relation.joins(:author).where!(title: post.title).order(:id).take
@@ -780,45 +982,31 @@ class RelationTest < ActiveRecord::TestCase
780
982
  end
781
983
  end
782
984
 
783
- class ActiveRecord::RelationTest < ActiveRecord::TestCase
784
- coerce_tests! :test_relation_merging_with_merged_symbol_joins_is_aliased
785
- def test_relation_merging_with_merged_symbol_joins_is_aliased__coerced
786
- categorizations_with_authors = Categorization.joins(:author)
787
- queries = capture_sql { Post.joins(:author, :categorizations).merge(Author.select(:id)).merge(categorizations_with_authors).to_a }
788
-
789
- nb_inner_join = queries.sum { |sql| sql.scan(/INNER\s+JOIN/i).size }
790
- assert_equal 3, nb_inner_join, "Wrong amount of INNER JOIN in query"
791
-
792
- # using `\W` as the column separator
793
- query_matches = queries.any? do |sql|
794
- %r[INNER\s+JOIN\s+#{Regexp.escape(Author.quoted_table_name)}\s+\Wauthors_categorizations\W]i.match?(sql)
795
- end
796
-
797
- assert query_matches, "Should be aliasing the child INNER JOINs in query"
798
- end
799
- end
800
-
801
-
802
-
803
-
804
- require 'models/post'
985
+ require "models/post"
805
986
  class SanitizeTest < ActiveRecord::TestCase
987
+ # Use nvarchar string (N'') in assert
806
988
  coerce_tests! :test_sanitize_sql_like_example_use_case
807
989
  def test_sanitize_sql_like_example_use_case_coerced
808
990
  searchable_post = Class.new(Post) do
809
- def self.search(term)
810
- where("title LIKE ?", sanitize_sql_like(term, '!'))
991
+ def self.search_as_method(term)
992
+ where("title LIKE ?", sanitize_sql_like(term, "!"))
811
993
  end
994
+
995
+ scope :search_as_scope, ->(term) {
996
+ where("title LIKE ?", sanitize_sql_like(term, "!"))
997
+ }
812
998
  end
813
- assert_sql(/\(title LIKE N'20!% !_reduction!_!!'\)/) do
814
- searchable_post.search("20% _reduction_!").to_a
999
+
1000
+ assert_sql(/LIKE N'20!% !_reduction!_!!'/) do
1001
+ searchable_post.search_as_method("20% _reduction_!").to_a
1002
+ end
1003
+
1004
+ assert_sql(/LIKE N'20!% !_reduction!_!!'/) do
1005
+ searchable_post.search_as_scope("20% _reduction_!").to_a
815
1006
  end
816
1007
  end
817
1008
  end
818
1009
 
819
-
820
-
821
-
822
1010
  class SchemaDumperTest < ActiveRecord::TestCase
823
1011
  # We have precision to 38.
824
1012
  coerce_tests! :test_schema_dump_keeps_large_precision_integer_columns_as_decimal
@@ -850,19 +1038,14 @@ class SchemaDumperDefaultsTest < ActiveRecord::TestCase
850
1038
  coerce_tests! :test_schema_dump_defaults_with_universally_supported_types
851
1039
  end
852
1040
 
853
-
854
-
855
-
856
1041
  class TestAdapterWithInvalidConnection < ActiveRecord::TestCase
857
1042
  # We trust Rails on this since we do not want to install mysql.
858
1043
  coerce_tests! %r{inspect on Model class does not raise}
859
1044
  end
860
1045
 
861
-
862
-
863
-
864
- require 'models/topic'
1046
+ require "models/topic"
865
1047
  class TransactionTest < ActiveRecord::TestCase
1048
+ # SQL Server does not have query for release_savepoint
866
1049
  coerce_tests! :test_releasing_named_savepoints
867
1050
  def test_releasing_named_savepoints_coerced
868
1051
  Topic.transaction do
@@ -874,10 +1057,7 @@ class TransactionTest < ActiveRecord::TestCase
874
1057
  end
875
1058
  end
876
1059
 
877
-
878
-
879
-
880
- require 'models/tag'
1060
+ require "models/tag"
881
1061
  class TransactionIsolationTest < ActiveRecord::TestCase
882
1062
  # SQL Server will lock the table for counts even when both
883
1063
  # connections are `READ COMMITTED`. So we bypass with `READPAST`.
@@ -887,7 +1067,7 @@ class TransactionIsolationTest < ActiveRecord::TestCase
887
1067
  assert_equal 0, Tag.count
888
1068
  Tag2.transaction do
889
1069
  Tag2.create
890
- assert_equal 0, Tag.lock('WITH(READPAST)').count
1070
+ assert_equal 0, Tag.lock("WITH(READPAST)").count
891
1071
  end
892
1072
  end
893
1073
  assert_equal 1, Tag.count
@@ -897,10 +1077,7 @@ class TransactionIsolationTest < ActiveRecord::TestCase
897
1077
  coerce_tests! %r{repeatable read}
898
1078
  end
899
1079
 
900
-
901
-
902
-
903
- require 'models/book'
1080
+ require "models/book"
904
1081
  class ViewWithPrimaryKeyTest < ActiveRecord::TestCase
905
1082
  # We have a few view tables. use includes vs equality.
906
1083
  coerce_tests! :test_views
@@ -912,9 +1089,10 @@ class ViewWithPrimaryKeyTest < ActiveRecord::TestCase
912
1089
  coerce_tests! :test_does_not_assume_id_column_as_primary_key
913
1090
  def test_does_not_assume_id_column_as_primary_key_coerced
914
1091
  model = Class.new(ActiveRecord::Base) { self.table_name = "ebooks" }
915
- assert_equal 'id', model.primary_key
1092
+ assert_equal "id", model.primary_key
916
1093
  end
917
1094
  end
1095
+
918
1096
  class ViewWithoutPrimaryKeyTest < ActiveRecord::TestCase
919
1097
  # We have a few view tables. use includes vs equality.
920
1098
  coerce_tests! :test_views
@@ -923,23 +1101,17 @@ class ViewWithoutPrimaryKeyTest < ActiveRecord::TestCase
923
1101
  end
924
1102
  end
925
1103
 
926
-
927
-
928
-
929
- require 'models/author'
1104
+ require "models/author"
930
1105
  class YamlSerializationTest < ActiveRecord::TestCase
931
1106
  coerce_tests! :test_types_of_virtual_columns_are_not_changed_on_round_trip
932
1107
  def test_types_of_virtual_columns_are_not_changed_on_round_trip_coerced
933
- author = Author.select('authors.*, 5 as posts_count').first
1108
+ author = Author.select("authors.*, 5 as posts_count").first
934
1109
  dumped = YAML.load(YAML.dump(author))
935
1110
  assert_equal 5, author.posts_count
936
1111
  assert_equal 5, dumped.posts_count
937
1112
  end
938
1113
  end
939
1114
 
940
-
941
-
942
-
943
1115
  class DateTimePrecisionTest < ActiveRecord::TestCase
944
1116
  # Original test had `7` which we support vs `8` which we use.
945
1117
  coerce_tests! :test_invalid_datetime_precision_raises_error
@@ -955,7 +1127,7 @@ class DateTimePrecisionTest < ActiveRecord::TestCase
955
1127
  coerce_tests! :test_datetime_precision_is_truncated_on_assignment
956
1128
  def test_datetime_precision_is_truncated_on_assignment_coerced
957
1129
  @connection.create_table(:foos, force: true)
958
- @connection.add_column :foos, :created_at, :datetime, precision: 0
1130
+ @connection.add_column :foos, :created_at, :datetime, precision: 0
959
1131
  @connection.add_column :foos, :updated_at, :datetime, precision: 6
960
1132
 
961
1133
  time = ::Time.now.change(nsec: 123456789)
@@ -972,8 +1144,6 @@ class DateTimePrecisionTest < ActiveRecord::TestCase
972
1144
  end
973
1145
  end
974
1146
 
975
-
976
-
977
1147
  class TimePrecisionTest < ActiveRecord::TestCase
978
1148
  # datetime is rounded to increments of .000, .003, or .007 seconds
979
1149
  coerce_tests! :test_time_precision_is_truncated_on_assignment
@@ -994,9 +1164,10 @@ class TimePrecisionTest < ActiveRecord::TestCase
994
1164
  assert_equal 0, foo.start.nsec
995
1165
  assert_equal 123457000, foo.finish.nsec
996
1166
  end
997
- end
998
-
999
1167
 
1168
+ # SQL Server uses default precision for time.
1169
+ coerce_tests! :test_no_time_precision_isnt_truncated_on_assignment
1170
+ end
1000
1171
 
1001
1172
  class DefaultNumbersTest < ActiveRecord::TestCase
1002
1173
  # We do better with native types and do not return strings for everything.
@@ -1006,16 +1177,23 @@ class DefaultNumbersTest < ActiveRecord::TestCase
1006
1177
  assert_equal 7, record.positive_integer
1007
1178
  assert_equal 7, record.positive_integer_before_type_cast
1008
1179
  end
1180
+
1181
+ # We do better with native types and do not return strings for everything.
1009
1182
  coerce_tests! :test_default_negative_integer
1010
1183
  def test_default_negative_integer_coerced
1011
1184
  record = DefaultNumber.new
1012
1185
  assert_equal -5, record.negative_integer
1013
1186
  assert_equal -5, record.negative_integer_before_type_cast
1014
1187
  end
1015
- end
1016
-
1017
-
1018
1188
 
1189
+ # We do better with native types and do not return strings for everything.
1190
+ coerce_tests! :test_default_decimal_number
1191
+ def test_default_decimal_number_coerced
1192
+ record = DefaultNumber.new
1193
+ assert_equal BigDecimal("2.78"), record.decimal_number
1194
+ assert_equal 2.78, record.decimal_number_before_type_cast
1195
+ end
1196
+ end
1019
1197
 
1020
1198
  module ActiveRecord
1021
1199
  class CollectionCacheKeyTest < ActiveRecord::TestCase
@@ -1024,40 +1202,59 @@ module ActiveRecord
1024
1202
  end
1025
1203
  end
1026
1204
 
1205
+ module ActiveRecord
1206
+ class CacheKeyTest < ActiveRecord::TestCase
1207
+ # Like Mysql2 and PostgreSQL, SQL Server doesn't return a string value for updated_at. In the Rails tests
1208
+ # the tests are skipped if adapter is Mysql2 or PostgreSQL.
1209
+ coerce_tests! %r{cache_version is the same when it comes from the DB or from the user}
1210
+ coerce_tests! %r{cache_version does NOT call updated_at when value is from the database}
1211
+ coerce_tests! %r{cache_version does not truncate zeros when timestamp ends in zeros}
1212
+ end
1213
+ end
1027
1214
 
1028
-
1029
-
1215
+ require "models/book"
1030
1216
  module ActiveRecord
1031
1217
  class StatementCacheTest < ActiveRecord::TestCase
1032
1218
  # Getting random failures.
1033
1219
  coerce_tests! :test_find_does_not_use_statement_cache_if_table_name_is_changed
1034
- end
1035
- end
1036
-
1037
1220
 
1221
+ # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
1222
+ coerce_tests! :test_statement_cache_values_differ
1223
+ def test_statement_cache_values_differ_coerced
1224
+ Book.connection.remove_index(:books, column: [:author_id, :name])
1038
1225
 
1226
+ original_test_statement_cache_values_differ
1227
+ ensure
1228
+ Book.connection.add_index(:books, [:author_id, :name], unique: true)
1229
+ end
1230
+ end
1231
+ end
1039
1232
 
1040
1233
  module ActiveRecord
1041
1234
  module ConnectionAdapters
1042
1235
  class SchemaCacheTest < ActiveRecord::TestCase
1043
1236
  private
1237
+
1044
1238
  # We need to give the full path for this to work.
1045
1239
  def schema_dump_path
1046
- File.join ARTest::SQLServer.root_activerecord, 'test/assets/schema_dump_5_1.yml'
1240
+ File.join ARTest::SQLServer.root_activerecord, "test/assets/schema_dump_5_1.yml"
1047
1241
  end
1048
1242
  end
1049
1243
  end
1050
1244
  end
1051
1245
 
1052
1246
  class UnsafeRawSqlTest < ActiveRecord::TestCase
1053
- coerce_tests! %r{always allows Arel}
1054
- test 'order: always allows Arel' do
1247
+ # Use LEN() vs length() function.
1248
+ coerce_tests! %r{order: always allows Arel}
1249
+ test "order: always allows Arel" do
1055
1250
  ids_depr = with_unsafe_raw_sql_deprecated { Post.order(Arel.sql("len(title)")).pluck(:title) }
1056
1251
  ids_disabled = with_unsafe_raw_sql_disabled { Post.order(Arel.sql("len(title)")).pluck(:title) }
1057
1252
 
1058
1253
  assert_equal ids_depr, ids_disabled
1059
1254
  end
1060
1255
 
1256
+ # Use LEN() vs length() function.
1257
+ coerce_tests! %r{pluck: always allows Arel}
1061
1258
  test "pluck: always allows Arel" do
1062
1259
  values_depr = with_unsafe_raw_sql_deprecated { Post.includes(:comments).pluck(:title, Arel.sql("len(title)")) }
1063
1260
  values_disabled = with_unsafe_raw_sql_disabled { Post.includes(:comments).pluck(:title, Arel.sql("len(title)")) }
@@ -1065,74 +1262,19 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
1065
1262
  assert_equal values_depr, values_disabled
1066
1263
  end
1067
1264
 
1068
-
1069
- coerce_tests! %r{order: disallows invalid Array arguments}
1070
- test "order: disallows invalid Array arguments" do
1071
- with_unsafe_raw_sql_disabled do
1072
- assert_raises(ActiveRecord::UnknownAttributeReference) do
1073
- Post.order(["author_id", "len(title)"]).pluck(:id)
1074
- end
1075
- end
1076
- end
1077
-
1265
+ # Use LEN() vs length() function.
1078
1266
  coerce_tests! %r{order: allows valid Array arguments}
1079
1267
  test "order: allows valid Array arguments" do
1080
1268
  ids_expected = Post.order(Arel.sql("author_id, len(title)")).pluck(:id)
1081
1269
 
1082
- ids_depr = with_unsafe_raw_sql_deprecated { Post.order(["author_id", Arel.sql("len(title)")]).pluck(:id) }
1083
- ids_disabled = with_unsafe_raw_sql_disabled { Post.order(["author_id", Arel.sql("len(title)")]).pluck(:id) }
1270
+ ids_depr = with_unsafe_raw_sql_deprecated { Post.order(["author_id", "len(title)"]).pluck(:id) }
1271
+ ids_disabled = with_unsafe_raw_sql_disabled { Post.order(["author_id", "len(title)"]).pluck(:id) }
1084
1272
 
1085
1273
  assert_equal ids_expected, ids_depr
1086
1274
  assert_equal ids_expected, ids_disabled
1087
1275
  end
1088
-
1089
- coerce_tests! %r{order: logs deprecation warning for unrecognized column}
1090
- test "order: logs deprecation warning for unrecognized column" do
1091
- with_unsafe_raw_sql_deprecated do
1092
- assert_deprecated(/Dangerous query method/) do
1093
- Post.order("len(title)")
1094
- end
1095
- end
1096
- end
1097
-
1098
- coerce_tests! %r{pluck: disallows invalid column name}
1099
- test "pluck: disallows invalid column name" do
1100
- with_unsafe_raw_sql_disabled do
1101
- assert_raises(ActiveRecord::UnknownAttributeReference) do
1102
- Post.pluck("len(title)")
1103
- end
1104
- end
1105
- end
1106
-
1107
- coerce_tests! %r{pluck: disallows invalid column name amongst valid names}
1108
- test "pluck: disallows invalid column name amongst valid names" do
1109
- with_unsafe_raw_sql_disabled do
1110
- assert_raises(ActiveRecord::UnknownAttributeReference) do
1111
- Post.pluck(:title, "len(title)")
1112
- end
1113
- end
1114
- end
1115
-
1116
- coerce_tests! %r{pluck: disallows invalid column names with includes}
1117
- test "pluck: disallows invalid column names with includes" do
1118
- with_unsafe_raw_sql_disabled do
1119
- assert_raises(ActiveRecord::UnknownAttributeReference) do
1120
- Post.includes(:comments).pluck(:title, "len(title)")
1121
- end
1122
- end
1123
- end
1124
-
1125
- coerce_tests! %r{pluck: logs deprecation warning}
1126
- test "pluck: logs deprecation warning" do
1127
- with_unsafe_raw_sql_deprecated do
1128
- assert_deprecated(/Dangerous query method/) do
1129
- Post.includes(:comments).pluck(:title, "len(title)")
1130
- end
1131
- end
1132
- end
1133
1276
  end
1134
1277
 
1135
-
1136
1278
  class ReservedWordTest < ActiveRecord::TestCase
1137
1279
  coerce_tests! :test_change_columns
1138
1280
  def test_change_columns_coerced
@@ -1143,32 +1285,29 @@ class ReservedWordTest < ActiveRecord::TestCase
1143
1285
  end
1144
1286
  end
1145
1287
 
1146
-
1147
-
1148
1288
  class OptimisticLockingTest < ActiveRecord::TestCase
1149
1289
  # We do not allow updating identities, but we can test using a non-identity key
1150
1290
  coerce_tests! :test_update_with_dirty_primary_key
1151
1291
  def test_update_with_dirty_primary_key_coerced
1152
1292
  assert_raises(ActiveRecord::RecordNotUnique) do
1153
- record = StringKeyObject.find('record1')
1154
- record.id = 'record2'
1293
+ record = StringKeyObject.find("record1")
1294
+ record.id = "record2"
1155
1295
  record.save!
1156
1296
  end
1157
1297
 
1158
- record = StringKeyObject.find('record1')
1159
- record.id = 'record42'
1298
+ record = StringKeyObject.find("record1")
1299
+ record.id = "record42"
1160
1300
  record.save!
1161
1301
 
1162
- assert StringKeyObject.find('record42')
1302
+ assert StringKeyObject.find("record42")
1163
1303
  assert_raises(ActiveRecord::RecordNotFound) do
1164
- StringKeyObject.find('record1')
1304
+ StringKeyObject.find("record1")
1165
1305
  end
1166
1306
  end
1167
1307
  end
1168
1308
 
1169
-
1170
-
1171
1309
  class RelationMergingTest < ActiveRecord::TestCase
1310
+ # Use nvarchar string (N'') in assert
1172
1311
  coerce_tests! :test_merging_with_order_with_binds
1173
1312
  def test_merging_with_order_with_binds_coerced
1174
1313
  relation = Post.all.merge(Post.order([Arel.sql("title LIKE ?"), "%suffix"]))
@@ -1176,8 +1315,144 @@ class RelationMergingTest < ActiveRecord::TestCase
1176
1315
  end
1177
1316
  end
1178
1317
 
1318
+ module ActiveRecord
1319
+ class DatabaseTasksTruncateAllTest < ActiveRecord::TestCase
1320
+ # SQL Server does not allow truncation of tables that are referenced by foreign key
1321
+ # constraints. As this test truncates all tables we would need to remove all foreign
1322
+ # key constraints and then restore them afterwards to get this test to pass.
1323
+ coerce_tests! :test_truncate_tables
1324
+ end
1325
+ end
1326
+
1327
+ require "models/book"
1328
+ class EnumTest < ActiveRecord::TestCase
1329
+ # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
1330
+ coerce_tests! %r{enums are distinct per class}
1331
+ test "enums are distinct per class coerced" do
1332
+ Book.connection.remove_index(:books, column: [:author_id, :name])
1333
+
1334
+ send(:'original_enums are distinct per class')
1335
+ ensure
1336
+ Book.connection.add_index(:books, [:author_id, :name], unique: true)
1337
+ end
1338
+
1339
+ # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
1340
+ coerce_tests! %r{creating new objects with enum scopes}
1341
+ test "creating new objects with enum scopes coerced" do
1342
+ Book.connection.remove_index(:books, column: [:author_id, :name])
1343
+
1344
+ send(:'original_creating new objects with enum scopes')
1345
+ ensure
1346
+ Book.connection.add_index(:books, [:author_id, :name], unique: true)
1347
+ end
1348
+
1349
+ # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
1350
+ coerce_tests! %r{enums are inheritable}
1351
+ test "enums are inheritable coerced" do
1352
+ Book.connection.remove_index(:books, column: [:author_id, :name])
1353
+
1354
+ send(:'original_enums are inheritable')
1355
+ ensure
1356
+ Book.connection.add_index(:books, [:author_id, :name], unique: true)
1357
+ end
1358
+
1359
+ # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
1360
+ coerce_tests! %r{declare multiple enums at a time}
1361
+ test "declare multiple enums at a time coerced" do
1362
+ Book.connection.remove_index(:books, column: [:author_id, :name])
1363
+
1364
+ send(:'original_declare multiple enums at a time')
1365
+ ensure
1366
+ Book.connection.add_index(:books, [:author_id, :name], unique: true)
1367
+ end
1368
+ end
1369
+
1370
+ require "models/task"
1371
+ class QueryCacheExpiryTest < ActiveRecord::TestCase
1372
+ # SQL Server does not support skipping or upserting duplicates.
1373
+ coerce_tests! :test_insert_all
1374
+ def test_insert_all_coerced
1375
+ assert_raises(ArgumentError, /does not support skipping duplicates/) do
1376
+ Task.cache { Task.insert({ starting: Time.now }) }
1377
+ end
1378
+
1379
+ assert_called(ActiveRecord::Base.connection, :clear_query_cache, times: 2) do
1380
+ Task.cache { Task.insert_all!([{ starting: Time.now }]) }
1381
+ end
1382
+
1383
+ assert_called(ActiveRecord::Base.connection, :clear_query_cache, times: 2) do
1384
+ Task.cache { Task.insert!({ starting: Time.now }) }
1385
+ end
1386
+
1387
+ assert_called(ActiveRecord::Base.connection, :clear_query_cache, times: 2) do
1388
+ Task.cache { Task.insert_all!([{ starting: Time.now }]) }
1389
+ end
1390
+
1391
+ assert_raises(ArgumentError, /does not support upsert/) do
1392
+ Task.cache { Task.upsert({ starting: Time.now }) }
1393
+ end
1394
+
1395
+ assert_raises(ArgumentError, /does not support upsert/) do
1396
+ Task.cache { Task.upsert_all([{ starting: Time.now }]) }
1397
+ end
1398
+ end
1399
+ end
1179
1400
 
1401
+ require "models/citation"
1180
1402
  class EagerLoadingTooManyIdsTest < ActiveRecord::TestCase
1181
- # Temporarily coerce this test due to https://github.com/rails/rails/issues/34945
1403
+ # Original Rails test fails with SQL Server error message "The query processor ran out of internal resources and
1404
+ # could not produce a query plan". This error goes away if you change database compatibility level to 110 (SQL 2012)
1405
+ # (see https://www.mssqltips.com/sqlservertip/5279/sql-server-error-query-processor-ran-out-of-internal-resources-and-could-not-produce-a-query-plan/).
1406
+ # However, you cannot change the compatibility level during a test. The purpose of the test is to ensure that an
1407
+ # unprepared statement is used if the number of values exceeds the adapter's `bind_params_length`. The coerced test
1408
+ # still does this as there will be 32,768 remaining citation records in the database and the `bind_params_length` of
1409
+ # adapter is 2,098.
1182
1410
  coerce_tests! :test_eager_loading_too_may_ids
1411
+ def test_eager_loading_too_may_ids_coerced
1412
+ # Remove excess records.
1413
+ Citation.limit(32768).order(id: :desc).delete_all
1414
+
1415
+ # Perform test
1416
+ citation_count = Citation.count
1417
+ assert_sql(/WHERE \(\[citations\]\.\[id\] IN \(0, 1/) do
1418
+ assert_equal citation_count, Citation.eager_load(:citations).offset(0).size
1419
+ end
1420
+ end
1421
+ end
1422
+
1423
+ class LogSubscriberTest < ActiveRecord::TestCase
1424
+ # Call original test from coerced test. Fixes issue on CI with Rails installed as a gem.
1425
+ coerce_tests! :test_vebose_query_logs
1426
+ def test_vebose_query_logs_coerced
1427
+ original_test_vebose_query_logs
1428
+ end
1429
+ end
1430
+
1431
+ class ActiveRecordSchemaTest < ActiveRecord::TestCase
1432
+ # Workaround for randomly failing test.
1433
+ coerce_tests! :test_has_primary_key
1434
+ def test_has_primary_key_coerced
1435
+ @schema_migration.reset_column_information
1436
+ original_test_has_primary_key
1437
+ end
1438
+ end
1439
+
1440
+ module ActiveRecord
1441
+ module ConnectionAdapters
1442
+ class ReaperTest < ActiveRecord::TestCase
1443
+ # Coerce can be removed if Rails version > 6.0.3
1444
+ coerce_tests! :test_connection_pool_starts_reaper_in_fork unless Process.respond_to?(:fork)
1445
+ end
1446
+ end
1447
+ end
1448
+
1449
+ class FixturesTest < ActiveRecord::TestCase
1450
+ # Skip test on Windows. Skip can be removed when Rails PR https://github.com/rails/rails/pull/39234 has been merged.
1451
+ coerce_tests! :test_binary_in_fixtures if RbConfig::CONFIG["host_os"] =~ /mswin|mingw/
1452
+ end
1453
+
1454
+ class ReloadModelsTest < ActiveRecord::TestCase
1455
+ # Skip test on Windows. The number of arguements passed to `IO.popen` in
1456
+ # `activesupport/lib/active_support/testing/isolation.rb` exceeds what Windows can handle.
1457
+ coerce_tests! :test_has_one_with_reload if RbConfig::CONFIG["host_os"] =~ /mswin|mingw/
1183
1458
  end