activerecord-sqlserver-adapter 5.2.1 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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