activerecord-sqlserver-adapter 5.2.0 → 6.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +9 -0
  3. data/.github/issue_template.md +23 -0
  4. data/.gitignore +1 -0
  5. data/.rubocop.yml +29 -0
  6. data/.travis.yml +6 -8
  7. data/CHANGELOG.md +46 -11
  8. data/{Dockerfile → Dockerfile.ci} +2 -2
  9. data/Gemfile +48 -41
  10. data/Guardfile +9 -8
  11. data/README.md +9 -37
  12. data/RUNNING_UNIT_TESTS.md +3 -0
  13. data/Rakefile +14 -16
  14. data/VERSION +1 -1
  15. data/activerecord-sqlserver-adapter.gemspec +25 -14
  16. data/appveyor.yml +24 -17
  17. data/docker-compose.ci.yml +7 -5
  18. data/guides/RELEASING.md +11 -0
  19. data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +2 -4
  20. data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +3 -4
  21. data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +22 -2
  22. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +3 -3
  23. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +2 -0
  24. data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +44 -0
  25. data/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb +36 -0
  26. data/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb +28 -0
  27. data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +9 -0
  28. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +88 -44
  29. data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +10 -12
  30. data/lib/active_record/connection_adapters/sqlserver/errors.rb +2 -3
  31. data/lib/active_record/connection_adapters/sqlserver/quoting.rb +46 -8
  32. data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +16 -5
  33. data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +9 -7
  34. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +197 -165
  35. data/lib/active_record/connection_adapters/sqlserver/showplan.rb +8 -8
  36. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +4 -2
  37. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +3 -1
  38. data/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +2 -2
  39. data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +43 -44
  40. data/lib/active_record/connection_adapters/sqlserver/transaction.rb +7 -9
  41. data/lib/active_record/connection_adapters/sqlserver/type.rb +37 -35
  42. data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +3 -3
  43. data/lib/active_record/connection_adapters/sqlserver/type/binary.rb +5 -4
  44. data/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +3 -3
  45. data/lib/active_record/connection_adapters/sqlserver/type/char.rb +7 -4
  46. data/lib/active_record/connection_adapters/sqlserver/type/data.rb +2 -2
  47. data/lib/active_record/connection_adapters/sqlserver/type/date.rb +4 -3
  48. data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +8 -8
  49. data/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb +2 -2
  50. data/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +2 -2
  51. data/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +5 -4
  52. data/lib/active_record/connection_adapters/sqlserver/type/float.rb +3 -3
  53. data/lib/active_record/connection_adapters/sqlserver/type/integer.rb +3 -3
  54. data/lib/active_record/connection_adapters/sqlserver/type/json.rb +2 -1
  55. data/lib/active_record/connection_adapters/sqlserver/type/money.rb +4 -4
  56. data/lib/active_record/connection_adapters/sqlserver/type/real.rb +3 -3
  57. data/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +3 -3
  58. data/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +4 -4
  59. data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +3 -3
  60. data/lib/active_record/connection_adapters/sqlserver/type/string.rb +2 -2
  61. data/lib/active_record/connection_adapters/sqlserver/type/text.rb +3 -3
  62. data/lib/active_record/connection_adapters/sqlserver/type/time.rb +6 -6
  63. data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +8 -9
  64. data/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +3 -3
  65. data/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +3 -3
  66. data/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +5 -4
  67. data/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb +2 -2
  68. data/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +3 -3
  69. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +6 -5
  70. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +4 -4
  71. data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +4 -3
  72. data/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +6 -5
  73. data/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +4 -4
  74. data/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +6 -5
  75. data/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +4 -4
  76. data/lib/active_record/connection_adapters/sqlserver/utils.rb +10 -11
  77. data/lib/active_record/connection_adapters/sqlserver/version.rb +2 -2
  78. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +132 -92
  79. data/lib/active_record/connection_adapters/sqlserver_column.rb +9 -5
  80. data/lib/active_record/sqlserver_base.rb +9 -1
  81. data/lib/active_record/tasks/sqlserver_database_tasks.rb +28 -32
  82. data/lib/activerecord-sqlserver-adapter.rb +3 -1
  83. data/lib/arel/visitors/sqlserver.rb +58 -24
  84. data/lib/arel_sqlserver.rb +4 -2
  85. data/test/appveyor/dbsetup.ps1 +4 -4
  86. data/test/cases/adapter_test_sqlserver.rb +223 -180
  87. data/test/cases/change_column_null_test_sqlserver.rb +17 -15
  88. data/test/cases/coerced_tests.rb +654 -360
  89. data/test/cases/column_test_sqlserver.rb +635 -604
  90. data/test/cases/connection_test_sqlserver.rb +18 -21
  91. data/test/cases/execute_procedure_test_sqlserver.rb +20 -20
  92. data/test/cases/fetch_test_sqlserver.rb +17 -23
  93. data/test/cases/fully_qualified_identifier_test_sqlserver.rb +15 -19
  94. data/test/cases/helper_sqlserver.rb +20 -15
  95. data/test/cases/in_clause_test_sqlserver.rb +36 -0
  96. data/test/cases/index_test_sqlserver.rb +15 -15
  97. data/test/cases/json_test_sqlserver.rb +25 -25
  98. data/test/cases/migration_test_sqlserver.rb +30 -26
  99. data/test/cases/order_test_sqlserver.rb +53 -54
  100. data/test/cases/pessimistic_locking_test_sqlserver.rb +31 -37
  101. data/test/cases/rake_test_sqlserver.rb +44 -56
  102. data/test/cases/schema_dumper_test_sqlserver.rb +117 -112
  103. data/test/cases/schema_test_sqlserver.rb +20 -26
  104. data/test/cases/scratchpad_test_sqlserver.rb +4 -4
  105. data/test/cases/showplan_test_sqlserver.rb +32 -39
  106. data/test/cases/specific_schema_test_sqlserver.rb +75 -72
  107. data/test/cases/transaction_test_sqlserver.rb +27 -29
  108. data/test/cases/trigger_test_sqlserver.rb +18 -17
  109. data/test/cases/utils_test_sqlserver.rb +78 -78
  110. data/test/cases/uuid_test_sqlserver.rb +19 -20
  111. data/test/debug.rb +8 -6
  112. data/test/migrations/create_clients_and_change_column_null.rb +3 -1
  113. data/test/migrations/transaction_table/1_table_will_never_be_created.rb +4 -4
  114. data/test/models/sqlserver/booking.rb +3 -1
  115. data/test/models/sqlserver/customers_view.rb +3 -1
  116. data/test/models/sqlserver/datatype.rb +2 -0
  117. data/test/models/sqlserver/datatype_migration.rb +2 -0
  118. data/test/models/sqlserver/dollar_table_name.rb +3 -1
  119. data/test/models/sqlserver/edge_schema.rb +3 -3
  120. data/test/models/sqlserver/fk_has_fk.rb +3 -1
  121. data/test/models/sqlserver/fk_has_pk.rb +3 -1
  122. data/test/models/sqlserver/natural_pk_data.rb +4 -2
  123. data/test/models/sqlserver/natural_pk_int_data.rb +3 -1
  124. data/test/models/sqlserver/no_pk_data.rb +3 -1
  125. data/test/models/sqlserver/object_default.rb +3 -1
  126. data/test/models/sqlserver/quoted_table.rb +4 -2
  127. data/test/models/sqlserver/quoted_view_1.rb +3 -1
  128. data/test/models/sqlserver/quoted_view_2.rb +3 -1
  129. data/test/models/sqlserver/sst_memory.rb +3 -1
  130. data/test/models/sqlserver/string_default.rb +3 -1
  131. data/test/models/sqlserver/string_defaults_big_view.rb +3 -1
  132. data/test/models/sqlserver/string_defaults_view.rb +3 -1
  133. data/test/models/sqlserver/tinyint_pk.rb +3 -1
  134. data/test/models/sqlserver/trigger.rb +4 -2
  135. data/test/models/sqlserver/trigger_history.rb +3 -1
  136. data/test/models/sqlserver/upper.rb +3 -1
  137. data/test/models/sqlserver/uppered.rb +3 -1
  138. data/test/models/sqlserver/uuid.rb +3 -1
  139. data/test/schema/datatypes/2012.sql +1 -0
  140. data/test/schema/sqlserver_specific_schema.rb +31 -21
  141. data/test/support/coerceable_test_sqlserver.rb +15 -9
  142. data/test/support/connection_reflection.rb +3 -2
  143. data/test/support/core_ext/query_cache.rb +4 -1
  144. data/test/support/load_schema_sqlserver.rb +5 -5
  145. data/test/support/minitest_sqlserver.rb +3 -1
  146. data/test/support/paths_sqlserver.rb +11 -11
  147. data/test/support/rake_helpers.rb +13 -10
  148. data/test/support/sql_counter_sqlserver.rb +3 -4
  149. data/test/support/test_in_memory_oltp.rb +9 -7
  150. metadata +23 -13
  151. data/BACKERS.md +0 -32
  152. data/circle.yml +0 -38
@@ -1,5 +1,7 @@
1
- require 'cases/helper_sqlserver'
2
- require 'migrations/create_clients_and_change_column_null'
1
+ # frozen_string_literal: true
2
+
3
+ require "cases/helper_sqlserver"
4
+ require "migrations/create_clients_and_change_column_null"
3
5
 
4
6
  class ChangeColumnNullTestSqlServer < ActiveRecord::TestCase
5
7
  before do
@@ -17,26 +19,26 @@ class ChangeColumnNullTestSqlServer < ActiveRecord::TestCase
17
19
  table.find { |column| column.name == name }
18
20
  end
19
21
 
20
- let(:clients_table) { connection.columns('clients') }
21
- let(:name_column) { find_column(clients_table, 'name') }
22
- let(:code_column) { find_column(clients_table, 'code') }
23
- let(:value_column) { find_column(clients_table, 'value') }
22
+ let(:clients_table) { connection.columns("clients") }
23
+ let(:name_column) { find_column(clients_table, "name") }
24
+ let(:code_column) { find_column(clients_table, "code") }
25
+ let(:value_column) { find_column(clients_table, "value") }
24
26
 
25
- describe '#change_column_null' do
26
- it 'does not change the column limit' do
27
- name_column.limit.must_equal 15
27
+ describe "#change_column_null" do
28
+ it "does not change the column limit" do
29
+ _(name_column.limit).must_equal 15
28
30
  end
29
31
 
30
- it 'does not change the column default' do
31
- code_column.default.must_equal 'n/a'
32
+ it "does not change the column default" do
33
+ _(code_column.default).must_equal "n/a"
32
34
  end
33
35
 
34
- it 'does not change the column precision' do
35
- value_column.precision.must_equal 32
36
+ it "does not change the column precision" do
37
+ _(value_column.precision).must_equal 32
36
38
  end
37
39
 
38
- it 'does not change the column scale' do
39
- value_column.scale.must_equal 8
40
+ it "does not change the column scale" do
41
+ _(value_column.scale).must_equal 8
40
42
  end
41
43
  end
42
44
  end
@@ -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,110 +155,128 @@ 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
148
- # Never finds `sql` since we use `EXEC sp_executesql` wrappers.
215
+ # Same as original coerced test except log is found using `EXEC sp_executesql` wrapper.
149
216
  coerce_tests! :test_binds_are_logged
217
+ def test_binds_are_logged_coerced
218
+ sub = Arel::Nodes::BindParam.new(1)
219
+ binds = [Relation::QueryAttribute.new("id", 1, Type::Value.new)]
220
+ sql = "select * from topics where id = #{sub.to_sql}"
221
+
222
+ @connection.exec_query(sql, "SQL", binds)
223
+
224
+ logged_sql = "EXEC sp_executesql N'#{sql}', N'#{sub.to_sql} int', #{sub.to_sql} = 1"
225
+ message = @subscriber.calls.find { |args| args[4][:sql] == logged_sql }
226
+
227
+ assert_equal binds, message[4][:binds]
228
+ end
229
+
230
+ # SQL Server adapter does not use a statement cache as query plans are already reused using `EXEC sp_executesql`.
231
+ coerce_tests! :test_statement_cache
232
+ coerce_tests! :test_statement_cache_with_query_cache
233
+ coerce_tests! :test_statement_cache_with_find
234
+ coerce_tests! :test_statement_cache_with_find_by
235
+ coerce_tests! :test_statement_cache_with_in_clause
236
+ coerce_tests! :test_statement_cache_with_sql_string_literal
150
237
  end
151
238
  end
152
239
 
153
-
154
240
  module ActiveRecord
155
241
  class InstrumentationTest < ActiveRecord::TestCase
156
- # 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.
157
243
  coerce_tests! :test_payload_name_on_load
158
244
  def test_payload_name_on_load_coerced
159
- Book.create(name: "test book")
160
- Book.first
161
- subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |*args|
162
- event = ActiveSupport::Notifications::Event.new(*args)
163
- if event.payload[:sql].match "SELECT"
164
- assert_equal "Book Load", event.payload[:name]
165
- end
166
- end
167
- Book.first
168
- ensure
169
- ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber
245
+ Book.send(:load_schema!)
246
+ original_test_payload_name_on_load
170
247
  end
171
248
  end
172
249
  end
173
250
 
174
251
  class CalculationsTest < ActiveRecord::TestCase
175
- # 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.
176
253
  coerce_tests! :test_offset_is_kept
177
254
  def test_offset_is_kept_coerced
178
- Account.first
179
- queries = assert_sql { Account.offset(1).count }
180
- assert_equal 1, queries.length
181
- assert_match(/OFFSET/, queries.first)
255
+ Account.send(:load_schema!)
256
+ original_test_offset_is_kept
182
257
  end
183
258
 
184
259
  # Are decimal, not integer.
185
260
  coerce_tests! :test_should_return_decimal_average_of_integer_field
186
261
  def test_should_return_decimal_average_of_integer_field_coerced
187
262
  value = Account.average(:id)
188
- assert_equal BigDecimal('3.0').to_s, BigDecimal(value).to_s
263
+ assert_equal BigDecimal("3.0").to_s, BigDecimal(value).to_s
189
264
  end
190
265
 
266
+ # Match SQL Server limit implementation
191
267
  coerce_tests! :test_limit_is_kept
192
268
  def test_limit_is_kept_coerced
193
269
  queries = capture_sql_ss { Account.limit(1).count }
194
270
  assert_equal 1, queries.length
195
- 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)
196
272
  end
197
273
 
274
+ # Match SQL Server limit implementation
198
275
  coerce_tests! :test_limit_with_offset_is_kept
199
276
  def test_limit_with_offset_is_kept_coerced
200
277
  queries = capture_sql_ss { Account.limit(1).offset(1).count }
201
278
  assert_equal 1, queries.length
202
- 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)
203
280
  end
204
281
 
205
282
  # SQL Server needs an alias for the calculated column
@@ -214,49 +291,57 @@ class CalculationsTest < ActiveRecord::TestCase
214
291
  coerce_tests! :test_having_with_strong_parameters
215
292
  end
216
293
 
217
-
218
-
219
294
  module ActiveRecord
220
295
  class Migration
221
296
  class ChangeSchemaTest < ActiveRecord::TestCase
222
- # We test these.
223
- coerce_tests! :test_create_table_with_bigint,
224
- :test_create_table_with_defaults
225
- 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
226
307
 
227
- class ChangeSchemaWithDependentObjectsTest < ActiveRecord::TestCase
228
- # In SQL Server you have to delete the tables yourself in the right order.
229
- 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
230
321
  end
231
322
  end
232
323
  end
233
324
 
234
-
235
-
236
325
  module ActiveRecord
237
326
  module ConnectionAdapters
238
327
  class QuoteARBaseTest < ActiveRecord::TestCase
239
-
240
328
  # Use our date format.
241
329
  coerce_tests! :test_quote_ar_object
242
330
  def test_quote_ar_object_coerced
243
331
  value = DatetimePrimaryKey.new(id: @time)
244
- 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)
245
333
  end
246
334
 
247
335
  # Use our date format.
248
336
  coerce_tests! :test_type_cast_ar_object
249
337
  def test_type_cast_ar_object_coerced
250
338
  value = DatetimePrimaryKey.new(id: @time)
251
- 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)
252
340
  end
253
-
254
341
  end
255
342
  end
256
343
  end
257
344
 
258
-
259
-
260
345
  module ActiveRecord
261
346
  class Migration
262
347
  class ColumnAttributesTest < ActiveRecord::TestCase
@@ -265,22 +350,19 @@ module ActiveRecord
265
350
  def test_add_column_without_limit_coerced
266
351
  add_column :test_models, :description, :string, limit: nil
267
352
  TestModel.reset_column_information
268
- TestModel.columns_hash["description"].limit.must_equal 4000
353
+ _(TestModel.columns_hash["description"].limit).must_equal 4000
269
354
  end
270
355
  end
271
356
  end
272
357
  end
273
358
 
274
-
275
-
276
-
277
359
  module ActiveRecord
278
360
  class Migration
279
- class ColumnsTest
361
+ class ColumnsTest < ActiveRecord::TestCase
280
362
  # Our defaults are real 70000 integers vs '70000' strings.
281
363
  coerce_tests! :test_rename_column_preserves_default_value_not_null
282
364
  def test_rename_column_preserves_default_value_not_null_coerced
283
- add_column 'test_models', 'salary', :integer, :default => 70000
365
+ add_column "test_models", "salary", :integer, :default => 70000
284
366
  default_before = connection.columns("test_models").find { |c| c.name == "salary" }.default
285
367
  assert_equal 70000, default_before
286
368
  rename_column "test_models", "salary", "annual_salary"
@@ -296,9 +378,9 @@ module ActiveRecord
296
378
  add_column "test_models", :hat_size, :integer
297
379
  add_column "test_models", :hat_style, :string, :limit => 100
298
380
  add_index "test_models", ["hat_style", "hat_size"], :unique => true
299
- assert_equal 1, connection.indexes('test_models').size
381
+ assert_equal 1, connection.indexes("test_models").size
300
382
  remove_column("test_models", "hat_size")
301
- assert_equal [], connection.indexes('test_models').map(&:name)
383
+ assert_equal [], connection.indexes("test_models").map(&:name)
302
384
  end
303
385
 
304
386
  # Choose `StatementInvalid` vs `ActiveRecordError`.
@@ -313,9 +395,6 @@ module ActiveRecord
313
395
  end
314
396
  end
315
397
 
316
-
317
-
318
-
319
398
  class MigrationTest < ActiveRecord::TestCase
320
399
  # We do not have do the DecimalWithoutScale type.
321
400
  coerce_tests! :test_add_table_with_decimals
@@ -339,7 +418,7 @@ class MigrationTest < ActiveRecord::TestCase
339
418
  assert_not_nil b.my_house_population
340
419
  assert_not_nil b.value_of_e
341
420
  assert_kind_of BigDecimal, b.world_population
342
- assert_equal '6000000000.0', b.world_population.to_s
421
+ assert_equal "6000000000.0", b.world_population.to_s
343
422
  assert_kind_of Integer, b.my_house_population
344
423
  assert_equal 3, b.my_house_population
345
424
  assert_kind_of BigDecimal, b.bank_balance
@@ -355,19 +434,13 @@ class MigrationTest < ActiveRecord::TestCase
355
434
  coerce_tests! :test_internal_metadata_stores_environment
356
435
  end
357
436
 
358
-
359
-
360
-
361
437
  class CoreTest < ActiveRecord::TestCase
362
- # 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`
363
439
  # `topics`.`bonus_time` attribute of 2005-01-30t15:28:00.00+01:00 is
364
440
  # getting local EST time for me and set to "09:28:00.0000000".
365
441
  coerce_tests! :test_pretty_print_persisted
366
442
  end
367
443
 
368
-
369
-
370
-
371
444
  module ActiveRecord
372
445
  module ConnectionAdapters
373
446
  # Just like PostgreSQLAdapter does.
@@ -381,27 +454,152 @@ module ActiveRecord
381
454
  end
382
455
  end
383
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
384
464
 
465
+ def drop; end
385
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
386
586
 
387
- module ActiveRecord
388
587
  class DatabaseTasksDumpSchemaCacheTest < ActiveRecord::TestCase
389
588
  # Skip this test with /tmp/my_schema_cache.yml path on Windows.
390
- 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/
391
590
  end
591
+
392
592
  class DatabaseTasksCreateAllTest < ActiveRecord::TestCase
393
593
  # We extend `local_database?` so that common VM IPs can be used.
394
594
  coerce_tests! :test_ignores_remote_databases, :test_warning_for_remote_databases
395
595
  end
596
+
396
597
  class DatabaseTasksDropAllTest < ActiveRecord::TestCase
397
598
  # We extend `local_database?` so that common VM IPs can be used.
398
599
  coerce_tests! :test_ignores_remote_databases, :test_warning_for_remote_databases
399
600
  end
400
601
  end
401
602
 
402
-
403
-
404
-
405
603
  class DefaultScopingTest < ActiveRecord::TestCase
406
604
  # We are not doing order duplicate removal anymore.
407
605
  coerce_tests! :test_order_in_default_scope_should_not_prevail
@@ -415,16 +613,13 @@ class DefaultScopingTest < ActiveRecord::TestCase
415
613
  end
416
614
  end
417
615
 
418
-
419
-
420
-
421
- require 'models/post'
422
- require 'models/subscriber'
616
+ require "models/post"
617
+ require "models/subscriber"
423
618
  class EachTest < ActiveRecord::TestCase
424
619
  # Quoting in tests does not cope with bracket quoting.
425
620
  coerce_tests! :test_find_in_batches_should_quote_batch_order
426
621
  def test_find_in_batches_should_quote_batch_order_coerced
427
- c = Post.connection
622
+ Post.connection
428
623
  assert_sql(/ORDER BY \[posts\]\.\[id\]/) do
429
624
  Post.find_in_batches(:batch_size => 1) do |batch|
430
625
  assert_kind_of Array, batch
@@ -436,7 +631,7 @@ class EachTest < ActiveRecord::TestCase
436
631
  # Quoting in tests does not cope with bracket quoting.
437
632
  coerce_tests! :test_in_batches_should_quote_batch_order
438
633
  def test_in_batches_should_quote_batch_order_coerced
439
- c = Post.connection
634
+ Post.connection
440
635
  assert_sql(/ORDER BY \[posts\]\.\[id\]/) do
441
636
  Post.in_batches(of: 1) do |relation|
442
637
  assert_kind_of ActiveRecord::Relation, relation
@@ -446,9 +641,6 @@ class EachTest < ActiveRecord::TestCase
446
641
  end
447
642
  end
448
643
 
449
-
450
-
451
-
452
644
  class EagerAssociationTest < ActiveRecord::TestCase
453
645
  # Use LEN() vs length() function.
454
646
  coerce_tests! :test_count_with_include
@@ -460,14 +652,13 @@ class EagerAssociationTest < ActiveRecord::TestCase
460
652
  coerce_tests! %r{including association based on sql condition and no database column}
461
653
  end
462
654
 
463
-
464
-
465
-
466
- require 'models/topic'
655
+ require "models/topic"
467
656
  class FinderTest < ActiveRecord::TestCase
657
+ # We have implicit ordering, via FETCH.
468
658
  coerce_tests! %r{doesn't have implicit ordering},
469
- :test_find_doesnt_have_implicit_ordering # We have implicit ordering, via FETCH.
659
+ :test_find_doesnt_have_implicit_ordering
470
660
 
661
+ # Square brackets around column name
471
662
  coerce_tests! :test_exists_does_not_select_columns_without_alias
472
663
  def test_exists_does_not_select_columns_without_alias_coerced
473
664
  assert_sql(/SELECT\s+1 AS one FROM \[topics\].*OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) do
@@ -475,6 +666,7 @@ class FinderTest < ActiveRecord::TestCase
475
666
  end
476
667
  end
477
668
 
669
+ # Assert SQL Server limit implementation
478
670
  coerce_tests! :test_take_and_first_and_last_with_integer_should_use_sql_limit
479
671
  def test_take_and_first_and_last_with_integer_should_use_sql_limit_coerced
480
672
  assert_sql(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.* @0 = 3/) { Topic.take(3).entries }
@@ -488,7 +680,7 @@ class FinderTest < ActiveRecord::TestCase
488
680
  # Can not use array condition due to not finding right type and hence fractional second quoting.
489
681
  coerce_tests! :test_condition_utc_time_interpolation_with_default_timezone_local
490
682
  def test_condition_utc_time_interpolation_with_default_timezone_local_coerced
491
- with_env_tz 'America/New_York' do
683
+ with_env_tz "America/New_York" do
492
684
  with_timezone_config default: :local do
493
685
  topic = Topic.first
494
686
  assert_equal topic, Topic.where(written_on: topic.written_on.getutc).first
@@ -499,7 +691,7 @@ class FinderTest < ActiveRecord::TestCase
499
691
  # Can not use array condition due to not finding right type and hence fractional second quoting.
500
692
  coerce_tests! :test_condition_local_time_interpolation_with_default_timezone_utc
501
693
  def test_condition_local_time_interpolation_with_default_timezone_utc_coerced
502
- with_env_tz 'America/New_York' do
694
+ with_env_tz "America/New_York" do
503
695
  with_timezone_config default: :utc do
504
696
  topic = Topic.first
505
697
  assert_equal topic, Topic.where(written_on: topic.written_on.getlocal).first
@@ -508,9 +700,6 @@ class FinderTest < ActiveRecord::TestCase
508
700
  end
509
701
  end
510
702
 
511
-
512
-
513
-
514
703
  module ActiveRecord
515
704
  class Migration
516
705
  class ForeignKeyTest < ActiveRecord::TestCase
@@ -528,172 +717,184 @@ module ActiveRecord
528
717
  end
529
718
  end
530
719
 
531
-
532
-
533
-
534
720
  class HasOneAssociationsTest < ActiveRecord::TestCase
535
721
  # We use OFFSET/FETCH vs TOP. So we always have an order.
536
722
  coerce_tests! :test_has_one_does_not_use_order_by
537
- end
538
-
539
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
540
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
541
746
 
542
- require 'models/company'
747
+ require "models/company"
543
748
  class InheritanceTest < ActiveRecord::TestCase
749
+ # Rails test required inserting to a identity column.
544
750
  coerce_tests! :test_a_bad_type_column
545
751
  def test_a_bad_type_column_coerced
546
- Company.connection.with_identity_insert_enabled('companies') do
752
+ Company.connection.with_identity_insert_enabled("companies") do
547
753
  Company.connection.insert "INSERT INTO companies (id, #{QUOTED_TYPE}, name) VALUES(100, 'bad_class!', 'Not happening')"
548
754
  end
549
755
  assert_raise(ActiveRecord::SubclassNotFound) { Company.find(100) }
550
756
  end
551
757
 
758
+ # Use Square brackets around column name
552
759
  coerce_tests! :test_eager_load_belongs_to_primary_key_quoting
553
760
  def test_eager_load_belongs_to_primary_key_quoting_coerced
554
- con = Account.connection
761
+ Account.connection
555
762
  assert_sql(/\[companies\]\.\[id\] = @0.* @0 = 1/) do
556
763
  Account.all.merge!(:includes => :firm).find(1)
557
764
  end
558
765
  end
559
766
  end
560
767
 
561
-
562
-
563
-
564
768
  class LeftOuterJoinAssociationTest < ActiveRecord::TestCase
565
769
  # Uses || operator in SQL. Just trust core gets value out of this test.
566
770
  coerce_tests! :test_does_not_override_select
567
771
  end
568
772
 
569
-
570
-
571
-
572
- class NamedScopingTest < ActiveRecord::TestCase
573
- # This works now because we add an `order(:id)` sort to break the order tie for deterministic results.
574
- coerce_tests! :test_scopes_honor_current_scopes_from_when_defined
575
- def test_scopes_honor_current_scopes_from_when_defined_coerced
576
- assert !Post.ranked_by_comments.order(:id).limit_by(5).empty?
577
- assert !authors(:david).posts.ranked_by_comments.order(:id).limit_by(5).empty?
578
- assert_not_equal Post.ranked_by_comments.order(:id).limit_by(5), authors(:david).posts.ranked_by_comments.order(:id).limit_by(5)
579
- assert_not_equal Post.order(:id).top(5), authors(:david).posts.order(:id).top(5)
580
- # Oracle sometimes sorts differently if WHERE condition is changed
581
- 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)
582
- assert_equal Post.ranked_by_comments.limit_by(5), Post.top(5)
583
- end
584
- end
585
-
586
-
587
-
588
-
589
- require 'models/developer'
590
- require 'models/computer'
773
+ require "models/developer"
774
+ require "models/computer"
591
775
  class NestedRelationScopingTest < ActiveRecord::TestCase
776
+ # Assert SQL Server limit implementation
592
777
  coerce_tests! :test_merge_options
593
778
  def test_merge_options_coerced
594
- Developer.where('salary = 80000').scoping do
779
+ Developer.where("salary = 80000").scoping do
595
780
  Developer.limit(10).scoping do
596
781
  devs = Developer.all
597
782
  sql = devs.to_sql
598
- assert_match '(salary = 80000)', sql
599
- assert_match 'FETCH NEXT 10 ROWS ONLY', sql
783
+ assert_match "(salary = 80000)", sql
784
+ assert_match "FETCH NEXT 10 ROWS ONLY", sql
600
785
  end
601
786
  end
602
787
  end
603
788
  end
604
789
 
605
-
606
-
607
-
608
- require 'models/parrot'
609
- require 'models/topic'
790
+ require "models/topic"
610
791
  class PersistenceTest < ActiveRecord::TestCase
611
- # We can not UPDATE identity columns.
792
+ # Rails test required updating a identity column.
612
793
  coerce_tests! :test_update_columns_changing_id
613
794
 
614
- # Previous test required updating a identity column.
615
- coerce_tests! :test_update_all_doesnt_ignore_order
616
- def test_update_all_doesnt_ignore_order_coerced
617
- david, mary = authors(:david), authors(:mary)
618
- david.id.must_equal 1
619
- mary.id.must_equal 2
620
- david.name.wont_equal mary.name
621
- assert_sql(/UPDATE.*\(SELECT \[authors\].\[id\] FROM \[authors\].*ORDER BY \[authors\].\[id\]/i) do
622
- Author.where('[id] > 1').order(:id).update_all(name: 'Test')
623
- end
624
- david.reload.name.must_equal 'David'
625
- mary.reload.name.must_equal 'Test'
626
- end
627
-
628
- # We can not UPDATE identity columns.
629
- coerce_tests! :test_update_attributes
630
- def test_update_attributes_coerced
795
+ # Rails test required updating a identity column.
796
+ coerce_tests! :test_update
797
+ def test_update_coerced
631
798
  topic = Topic.find(1)
632
- assert !topic.approved?
799
+ assert_not_predicate topic, :approved?
633
800
  assert_equal "The First Topic", topic.title
634
- topic.update_attributes("approved" => true, "title" => "The First Topic Updated")
801
+
802
+ topic.update("approved" => true, "title" => "The First Topic Updated")
635
803
  topic.reload
636
- assert topic.approved?
804
+ assert_predicate topic, :approved?
637
805
  assert_equal "The First Topic Updated", topic.title
638
- topic.update_attributes(approved: false, title: "The First Topic")
806
+
807
+ topic.update(approved: false, title: "The First Topic")
639
808
  topic.reload
640
- assert !topic.approved?
809
+ assert_not_predicate topic, :approved?
641
810
  assert_equal "The First Topic", topic.title
642
- # SQLServer: Here is where it breaks down. No exceptions.
643
- # assert_raise(ActiveRecord::RecordNotUnique, ActiveRecord::StatementInvalid) do
644
- # topic.update_attributes(id: 3, title: "Hm is it possible?")
645
- # end
646
- # assert_not_equal "Hm is it possible?", Topic.find(3).title
647
- # topic.update_attributes(id: 1234)
648
- # assert_nothing_raised { topic.reload }
649
- # assert_equal topic.title, Topic.find(1234).title
650
811
  end
651
812
  end
652
813
 
814
+ require "models/author"
815
+ class UpdateAllTest < ActiveRecord::TestCase
816
+ # Rails test required updating a identity column.
817
+ coerce_tests! :test_update_all_doesnt_ignore_order
818
+ def test_update_all_doesnt_ignore_order_coerced
819
+ david, mary = authors(:david), authors(:mary)
820
+ _(david.id).must_equal 1
821
+ _(mary.id).must_equal 2
822
+ _(david.name).wont_equal mary.name
823
+ assert_sql(/UPDATE.*\(SELECT \[authors\].\[id\] FROM \[authors\].*ORDER BY \[authors\].\[id\]/i) do
824
+ Author.where("[id] > 1").order(:id).update_all(name: "Test")
825
+ end
826
+ _(david.reload.name).must_equal "David"
827
+ _(mary.reload.name).must_equal "Test"
828
+ end
829
+ end
653
830
 
654
-
655
-
656
- require 'models/topic'
831
+ require "models/topic"
657
832
  module ActiveRecord
658
833
  class PredicateBuilderTest < ActiveRecord::TestCase
834
+ # Same as original test except string has `N` prefix to indicate unicode string.
659
835
  coerce_tests! :test_registering_new_handlers
660
836
  def test_registering_new_handlers_coerced
661
- Topic.predicate_builder.register_handler(Regexp, proc do |column, value|
662
- Arel::Nodes::InfixOperation.new('~', column, Arel.sql(value.source))
663
- end)
664
- assert_match %r{\[topics\].\[title\] ~ rails}i, Topic.where(title: /rails/).to_sql
665
- ensure
666
- 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
667
844
  end
668
845
  end
669
846
  end
670
847
 
671
-
672
-
673
-
674
848
  class PrimaryKeysTest < ActiveRecord::TestCase
675
- # Gonna trust Rails core for this. We end up with 2 querys vs 3 asserted
676
- # but as far as I can tell, this is only one for us anyway.
849
+ # SQL Server does not have query for release_savepoint
677
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
678
858
  end
679
859
 
680
-
681
-
682
-
683
- require 'models/task'
860
+ require "models/task"
684
861
  class QueryCacheTest < ActiveRecord::TestCase
862
+ # SQL Server adapter not in list of supported adapters in original test.
685
863
  coerce_tests! :test_cache_does_not_wrap_results_in_arrays
686
864
  def test_cache_does_not_wrap_results_in_arrays_coerced
687
865
  Task.cache do
688
- 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")
689
867
  end
690
868
  end
691
- end
692
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)
693
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
694
887
 
888
+ assert_queries(1, ignore_none: true) do
889
+ Task.find(1)
890
+ end
891
+
892
+ assert_includes ActiveRecord::SQLCounter.log_all.first, "TC.CONSTRAINT_TYPE = N''PRIMARY KEY''"
893
+ end
894
+ end
895
+ end
695
896
 
696
- require 'models/post'
897
+ require "models/post"
697
898
  class RelationTest < ActiveRecord::TestCase
698
899
  # Use LEN vs LENGTH function.
699
900
  coerce_tests! :test_reverse_order_with_function
@@ -714,6 +915,26 @@ class RelationTest < ActiveRecord::TestCase
714
915
  # We have implicit ordering, via FETCH.
715
916
  coerce_tests! %r{doesn't have implicit ordering}
716
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
+
717
938
  # We are not doing order duplicate removal anymore.
718
939
  coerce_tests! :test_order_using_scoping
719
940
 
@@ -737,17 +958,17 @@ class RelationTest < ActiveRecord::TestCase
737
958
  # so we are skipping all together.
738
959
  coerce_tests! :test_empty_complex_chained_relations
739
960
 
740
- # Can't apply offset withour ORDER
961
+ # Can't apply offset without ORDER
741
962
  coerce_tests! %r{using a custom table affects the wheres}
742
- test 'using a custom table affects the wheres coerced' do
963
+ test "using a custom table affects the wheres coerced" do
743
964
  post = posts(:welcome)
744
965
 
745
966
  assert_equal post, custom_post_relation.where!(title: post.title).order(:id).take
746
967
  end
747
968
 
748
- # Can't apply offset withour ORDER
969
+ # Can't apply offset without ORDER
749
970
  coerce_tests! %r{using a custom table with joins affects the joins}
750
- 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
751
972
  post = posts(:welcome)
752
973
 
753
974
  assert_equal post, custom_post_relation.joins(:author).where!(title: post.title).order(:id).take
@@ -761,45 +982,31 @@ class RelationTest < ActiveRecord::TestCase
761
982
  end
762
983
  end
763
984
 
764
- class ActiveRecord::RelationTest < ActiveRecord::TestCase
765
- coerce_tests! :test_relation_merging_with_merged_symbol_joins_is_aliased
766
- def test_relation_merging_with_merged_symbol_joins_is_aliased__coerced
767
- categorizations_with_authors = Categorization.joins(:author)
768
- queries = capture_sql { Post.joins(:author, :categorizations).merge(Author.select(:id)).merge(categorizations_with_authors).to_a }
769
-
770
- nb_inner_join = queries.sum { |sql| sql.scan(/INNER\s+JOIN/i).size }
771
- assert_equal 3, nb_inner_join, "Wrong amount of INNER JOIN in query"
772
-
773
- # using `\W` as the column separator
774
- query_matches = queries.any? do |sql|
775
- %r[INNER\s+JOIN\s+#{Regexp.escape(Author.quoted_table_name)}\s+\Wauthors_categorizations\W]i.match?(sql)
776
- end
777
-
778
- assert query_matches, "Should be aliasing the child INNER JOINs in query"
779
- end
780
- end
781
-
782
-
783
-
784
-
785
- require 'models/post'
985
+ require "models/post"
786
986
  class SanitizeTest < ActiveRecord::TestCase
987
+ # Use nvarchar string (N'') in assert
787
988
  coerce_tests! :test_sanitize_sql_like_example_use_case
788
989
  def test_sanitize_sql_like_example_use_case_coerced
789
990
  searchable_post = Class.new(Post) do
790
- def self.search(term)
791
- where("title LIKE ?", sanitize_sql_like(term, '!'))
991
+ def self.search_as_method(term)
992
+ where("title LIKE ?", sanitize_sql_like(term, "!"))
792
993
  end
994
+
995
+ scope :search_as_scope, ->(term) {
996
+ where("title LIKE ?", sanitize_sql_like(term, "!"))
997
+ }
793
998
  end
794
- assert_sql(/\(title LIKE N'20!% !_reduction!_!!'\)/) do
795
- 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
796
1006
  end
797
1007
  end
798
1008
  end
799
1009
 
800
-
801
-
802
-
803
1010
  class SchemaDumperTest < ActiveRecord::TestCase
804
1011
  # We have precision to 38.
805
1012
  coerce_tests! :test_schema_dump_keeps_large_precision_integer_columns_as_decimal
@@ -831,19 +1038,14 @@ class SchemaDumperDefaultsTest < ActiveRecord::TestCase
831
1038
  coerce_tests! :test_schema_dump_defaults_with_universally_supported_types
832
1039
  end
833
1040
 
834
-
835
-
836
-
837
1041
  class TestAdapterWithInvalidConnection < ActiveRecord::TestCase
838
1042
  # We trust Rails on this since we do not want to install mysql.
839
1043
  coerce_tests! %r{inspect on Model class does not raise}
840
1044
  end
841
1045
 
842
-
843
-
844
-
845
- require 'models/topic'
1046
+ require "models/topic"
846
1047
  class TransactionTest < ActiveRecord::TestCase
1048
+ # SQL Server does not have query for release_savepoint
847
1049
  coerce_tests! :test_releasing_named_savepoints
848
1050
  def test_releasing_named_savepoints_coerced
849
1051
  Topic.transaction do
@@ -855,10 +1057,7 @@ class TransactionTest < ActiveRecord::TestCase
855
1057
  end
856
1058
  end
857
1059
 
858
-
859
-
860
-
861
- require 'models/tag'
1060
+ require "models/tag"
862
1061
  class TransactionIsolationTest < ActiveRecord::TestCase
863
1062
  # SQL Server will lock the table for counts even when both
864
1063
  # connections are `READ COMMITTED`. So we bypass with `READPAST`.
@@ -868,7 +1067,7 @@ class TransactionIsolationTest < ActiveRecord::TestCase
868
1067
  assert_equal 0, Tag.count
869
1068
  Tag2.transaction do
870
1069
  Tag2.create
871
- assert_equal 0, Tag.lock('WITH(READPAST)').count
1070
+ assert_equal 0, Tag.lock("WITH(READPAST)").count
872
1071
  end
873
1072
  end
874
1073
  assert_equal 1, Tag.count
@@ -878,10 +1077,7 @@ class TransactionIsolationTest < ActiveRecord::TestCase
878
1077
  coerce_tests! %r{repeatable read}
879
1078
  end
880
1079
 
881
-
882
-
883
-
884
- require 'models/book'
1080
+ require "models/book"
885
1081
  class ViewWithPrimaryKeyTest < ActiveRecord::TestCase
886
1082
  # We have a few view tables. use includes vs equality.
887
1083
  coerce_tests! :test_views
@@ -893,9 +1089,10 @@ class ViewWithPrimaryKeyTest < ActiveRecord::TestCase
893
1089
  coerce_tests! :test_does_not_assume_id_column_as_primary_key
894
1090
  def test_does_not_assume_id_column_as_primary_key_coerced
895
1091
  model = Class.new(ActiveRecord::Base) { self.table_name = "ebooks" }
896
- assert_equal 'id', model.primary_key
1092
+ assert_equal "id", model.primary_key
897
1093
  end
898
1094
  end
1095
+
899
1096
  class ViewWithoutPrimaryKeyTest < ActiveRecord::TestCase
900
1097
  # We have a few view tables. use includes vs equality.
901
1098
  coerce_tests! :test_views
@@ -904,23 +1101,17 @@ class ViewWithoutPrimaryKeyTest < ActiveRecord::TestCase
904
1101
  end
905
1102
  end
906
1103
 
907
-
908
-
909
-
910
- require 'models/author'
1104
+ require "models/author"
911
1105
  class YamlSerializationTest < ActiveRecord::TestCase
912
1106
  coerce_tests! :test_types_of_virtual_columns_are_not_changed_on_round_trip
913
1107
  def test_types_of_virtual_columns_are_not_changed_on_round_trip_coerced
914
- author = Author.select('authors.*, 5 as posts_count').first
1108
+ author = Author.select("authors.*, 5 as posts_count").first
915
1109
  dumped = YAML.load(YAML.dump(author))
916
1110
  assert_equal 5, author.posts_count
917
1111
  assert_equal 5, dumped.posts_count
918
1112
  end
919
1113
  end
920
1114
 
921
-
922
-
923
-
924
1115
  class DateTimePrecisionTest < ActiveRecord::TestCase
925
1116
  # Original test had `7` which we support vs `8` which we use.
926
1117
  coerce_tests! :test_invalid_datetime_precision_raises_error
@@ -936,7 +1127,7 @@ class DateTimePrecisionTest < ActiveRecord::TestCase
936
1127
  coerce_tests! :test_datetime_precision_is_truncated_on_assignment
937
1128
  def test_datetime_precision_is_truncated_on_assignment_coerced
938
1129
  @connection.create_table(:foos, force: true)
939
- @connection.add_column :foos, :created_at, :datetime, precision: 0
1130
+ @connection.add_column :foos, :created_at, :datetime, precision: 0
940
1131
  @connection.add_column :foos, :updated_at, :datetime, precision: 6
941
1132
 
942
1133
  time = ::Time.now.change(nsec: 123456789)
@@ -953,8 +1144,6 @@ class DateTimePrecisionTest < ActiveRecord::TestCase
953
1144
  end
954
1145
  end
955
1146
 
956
-
957
-
958
1147
  class TimePrecisionTest < ActiveRecord::TestCase
959
1148
  # datetime is rounded to increments of .000, .003, or .007 seconds
960
1149
  coerce_tests! :test_time_precision_is_truncated_on_assignment
@@ -975,9 +1164,10 @@ class TimePrecisionTest < ActiveRecord::TestCase
975
1164
  assert_equal 0, foo.start.nsec
976
1165
  assert_equal 123457000, foo.finish.nsec
977
1166
  end
978
- end
979
-
980
1167
 
1168
+ # SQL Server uses default precision for time.
1169
+ coerce_tests! :test_no_time_precision_isnt_truncated_on_assignment
1170
+ end
981
1171
 
982
1172
  class DefaultNumbersTest < ActiveRecord::TestCase
983
1173
  # We do better with native types and do not return strings for everything.
@@ -987,16 +1177,23 @@ class DefaultNumbersTest < ActiveRecord::TestCase
987
1177
  assert_equal 7, record.positive_integer
988
1178
  assert_equal 7, record.positive_integer_before_type_cast
989
1179
  end
1180
+
1181
+ # We do better with native types and do not return strings for everything.
990
1182
  coerce_tests! :test_default_negative_integer
991
1183
  def test_default_negative_integer_coerced
992
1184
  record = DefaultNumber.new
993
1185
  assert_equal -5, record.negative_integer
994
1186
  assert_equal -5, record.negative_integer_before_type_cast
995
1187
  end
996
- end
997
-
998
-
999
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
1000
1197
 
1001
1198
  module ActiveRecord
1002
1199
  class CollectionCacheKeyTest < ActiveRecord::TestCase
@@ -1005,40 +1202,59 @@ module ActiveRecord
1005
1202
  end
1006
1203
  end
1007
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
1008
1214
 
1009
-
1010
-
1215
+ require "models/book"
1011
1216
  module ActiveRecord
1012
1217
  class StatementCacheTest < ActiveRecord::TestCase
1013
1218
  # Getting random failures.
1014
1219
  coerce_tests! :test_find_does_not_use_statement_cache_if_table_name_is_changed
1015
- end
1016
- end
1017
-
1018
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])
1019
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
1020
1232
 
1021
1233
  module ActiveRecord
1022
1234
  module ConnectionAdapters
1023
1235
  class SchemaCacheTest < ActiveRecord::TestCase
1024
1236
  private
1237
+
1025
1238
  # We need to give the full path for this to work.
1026
1239
  def schema_dump_path
1027
- 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"
1028
1241
  end
1029
1242
  end
1030
1243
  end
1031
1244
  end
1032
1245
 
1033
1246
  class UnsafeRawSqlTest < ActiveRecord::TestCase
1034
- coerce_tests! %r{always allows Arel}
1035
- 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
1036
1250
  ids_depr = with_unsafe_raw_sql_deprecated { Post.order(Arel.sql("len(title)")).pluck(:title) }
1037
1251
  ids_disabled = with_unsafe_raw_sql_disabled { Post.order(Arel.sql("len(title)")).pluck(:title) }
1038
1252
 
1039
1253
  assert_equal ids_depr, ids_disabled
1040
1254
  end
1041
1255
 
1256
+ # Use LEN() vs length() function.
1257
+ coerce_tests! %r{pluck: always allows Arel}
1042
1258
  test "pluck: always allows Arel" do
1043
1259
  values_depr = with_unsafe_raw_sql_deprecated { Post.includes(:comments).pluck(:title, Arel.sql("len(title)")) }
1044
1260
  values_disabled = with_unsafe_raw_sql_disabled { Post.includes(:comments).pluck(:title, Arel.sql("len(title)")) }
@@ -1046,74 +1262,19 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
1046
1262
  assert_equal values_depr, values_disabled
1047
1263
  end
1048
1264
 
1049
-
1050
- coerce_tests! %r{order: disallows invalid Array arguments}
1051
- test "order: disallows invalid Array arguments" do
1052
- with_unsafe_raw_sql_disabled do
1053
- assert_raises(ActiveRecord::UnknownAttributeReference) do
1054
- Post.order(["author_id", "len(title)"]).pluck(:id)
1055
- end
1056
- end
1057
- end
1058
-
1265
+ # Use LEN() vs length() function.
1059
1266
  coerce_tests! %r{order: allows valid Array arguments}
1060
1267
  test "order: allows valid Array arguments" do
1061
1268
  ids_expected = Post.order(Arel.sql("author_id, len(title)")).pluck(:id)
1062
1269
 
1063
- ids_depr = with_unsafe_raw_sql_deprecated { Post.order(["author_id", Arel.sql("len(title)")]).pluck(:id) }
1064
- 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) }
1065
1272
 
1066
1273
  assert_equal ids_expected, ids_depr
1067
1274
  assert_equal ids_expected, ids_disabled
1068
1275
  end
1069
-
1070
- coerce_tests! %r{order: logs deprecation warning for unrecognized column}
1071
- test "order: logs deprecation warning for unrecognized column" do
1072
- with_unsafe_raw_sql_deprecated do
1073
- assert_deprecated(/Dangerous query method/) do
1074
- Post.order("len(title)")
1075
- end
1076
- end
1077
- end
1078
-
1079
- coerce_tests! %r{pluck: disallows invalid column name}
1080
- test "pluck: disallows invalid column name" do
1081
- with_unsafe_raw_sql_disabled do
1082
- assert_raises(ActiveRecord::UnknownAttributeReference) do
1083
- Post.pluck("len(title)")
1084
- end
1085
- end
1086
- end
1087
-
1088
- coerce_tests! %r{pluck: disallows invalid column name amongst valid names}
1089
- test "pluck: disallows invalid column name amongst valid names" do
1090
- with_unsafe_raw_sql_disabled do
1091
- assert_raises(ActiveRecord::UnknownAttributeReference) do
1092
- Post.pluck(:title, "len(title)")
1093
- end
1094
- end
1095
- end
1096
-
1097
- coerce_tests! %r{pluck: disallows invalid column names with includes}
1098
- test "pluck: disallows invalid column names with includes" do
1099
- with_unsafe_raw_sql_disabled do
1100
- assert_raises(ActiveRecord::UnknownAttributeReference) do
1101
- Post.includes(:comments).pluck(:title, "len(title)")
1102
- end
1103
- end
1104
- end
1105
-
1106
- coerce_tests! %r{pluck: logs deprecation warning}
1107
- test "pluck: logs deprecation warning" do
1108
- with_unsafe_raw_sql_deprecated do
1109
- assert_deprecated(/Dangerous query method/) do
1110
- Post.includes(:comments).pluck(:title, "len(title)")
1111
- end
1112
- end
1113
- end
1114
1276
  end
1115
1277
 
1116
-
1117
1278
  class ReservedWordTest < ActiveRecord::TestCase
1118
1279
  coerce_tests! :test_change_columns
1119
1280
  def test_change_columns_coerced
@@ -1124,32 +1285,29 @@ class ReservedWordTest < ActiveRecord::TestCase
1124
1285
  end
1125
1286
  end
1126
1287
 
1127
-
1128
-
1129
1288
  class OptimisticLockingTest < ActiveRecord::TestCase
1130
1289
  # We do not allow updating identities, but we can test using a non-identity key
1131
1290
  coerce_tests! :test_update_with_dirty_primary_key
1132
1291
  def test_update_with_dirty_primary_key_coerced
1133
1292
  assert_raises(ActiveRecord::RecordNotUnique) do
1134
- record = StringKeyObject.find('record1')
1135
- record.id = 'record2'
1293
+ record = StringKeyObject.find("record1")
1294
+ record.id = "record2"
1136
1295
  record.save!
1137
1296
  end
1138
1297
 
1139
- record = StringKeyObject.find('record1')
1140
- record.id = 'record42'
1298
+ record = StringKeyObject.find("record1")
1299
+ record.id = "record42"
1141
1300
  record.save!
1142
1301
 
1143
- assert StringKeyObject.find('record42')
1302
+ assert StringKeyObject.find("record42")
1144
1303
  assert_raises(ActiveRecord::RecordNotFound) do
1145
- StringKeyObject.find('record1')
1304
+ StringKeyObject.find("record1")
1146
1305
  end
1147
1306
  end
1148
1307
  end
1149
1308
 
1150
-
1151
-
1152
1309
  class RelationMergingTest < ActiveRecord::TestCase
1310
+ # Use nvarchar string (N'') in assert
1153
1311
  coerce_tests! :test_merging_with_order_with_binds
1154
1312
  def test_merging_with_order_with_binds_coerced
1155
1313
  relation = Post.all.merge(Post.order([Arel.sql("title LIKE ?"), "%suffix"]))
@@ -1157,8 +1315,144 @@ class RelationMergingTest < ActiveRecord::TestCase
1157
1315
  end
1158
1316
  end
1159
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
1160
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
1400
+
1401
+ require "models/citation"
1161
1402
  class EagerLoadingTooManyIdsTest < ActiveRecord::TestCase
1162
- # 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.
1163
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/
1164
1458
  end