activerecord-sqlserver-adapter 5.2.0 → 6.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (152) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +9 -0
  3. data/.github/issue_template.md +23 -0
  4. data/.gitignore +1 -0
  5. data/.rubocop.yml +29 -0
  6. data/.travis.yml +6 -8
  7. data/CHANGELOG.md +46 -11
  8. data/{Dockerfile → Dockerfile.ci} +2 -2
  9. data/Gemfile +48 -41
  10. data/Guardfile +9 -8
  11. data/README.md +9 -37
  12. data/RUNNING_UNIT_TESTS.md +3 -0
  13. data/Rakefile +14 -16
  14. data/VERSION +1 -1
  15. data/activerecord-sqlserver-adapter.gemspec +25 -14
  16. data/appveyor.yml +24 -17
  17. data/docker-compose.ci.yml +7 -5
  18. data/guides/RELEASING.md +11 -0
  19. data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +2 -4
  20. data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +3 -4
  21. data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +22 -2
  22. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +3 -3
  23. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +2 -0
  24. data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +44 -0
  25. data/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb +36 -0
  26. data/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb +28 -0
  27. data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +9 -0
  28. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +88 -44
  29. data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +10 -12
  30. data/lib/active_record/connection_adapters/sqlserver/errors.rb +2 -3
  31. data/lib/active_record/connection_adapters/sqlserver/quoting.rb +46 -8
  32. data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +16 -5
  33. data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +9 -7
  34. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +197 -165
  35. data/lib/active_record/connection_adapters/sqlserver/showplan.rb +8 -8
  36. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +4 -2
  37. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +3 -1
  38. data/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +2 -2
  39. data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +43 -44
  40. data/lib/active_record/connection_adapters/sqlserver/transaction.rb +7 -9
  41. data/lib/active_record/connection_adapters/sqlserver/type.rb +37 -35
  42. data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +3 -3
  43. data/lib/active_record/connection_adapters/sqlserver/type/binary.rb +5 -4
  44. data/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +3 -3
  45. data/lib/active_record/connection_adapters/sqlserver/type/char.rb +7 -4
  46. data/lib/active_record/connection_adapters/sqlserver/type/data.rb +2 -2
  47. data/lib/active_record/connection_adapters/sqlserver/type/date.rb +4 -3
  48. data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +8 -8
  49. data/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb +2 -2
  50. data/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +2 -2
  51. data/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +5 -4
  52. data/lib/active_record/connection_adapters/sqlserver/type/float.rb +3 -3
  53. data/lib/active_record/connection_adapters/sqlserver/type/integer.rb +3 -3
  54. data/lib/active_record/connection_adapters/sqlserver/type/json.rb +2 -1
  55. data/lib/active_record/connection_adapters/sqlserver/type/money.rb +4 -4
  56. data/lib/active_record/connection_adapters/sqlserver/type/real.rb +3 -3
  57. data/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +3 -3
  58. data/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +4 -4
  59. data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +3 -3
  60. data/lib/active_record/connection_adapters/sqlserver/type/string.rb +2 -2
  61. data/lib/active_record/connection_adapters/sqlserver/type/text.rb +3 -3
  62. data/lib/active_record/connection_adapters/sqlserver/type/time.rb +6 -6
  63. data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +8 -9
  64. data/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +3 -3
  65. data/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +3 -3
  66. data/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +5 -4
  67. data/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb +2 -2
  68. data/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +3 -3
  69. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +6 -5
  70. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +4 -4
  71. data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +4 -3
  72. data/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +6 -5
  73. data/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +4 -4
  74. data/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +6 -5
  75. data/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +4 -4
  76. data/lib/active_record/connection_adapters/sqlserver/utils.rb +10 -11
  77. data/lib/active_record/connection_adapters/sqlserver/version.rb +2 -2
  78. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +132 -92
  79. data/lib/active_record/connection_adapters/sqlserver_column.rb +9 -5
  80. data/lib/active_record/sqlserver_base.rb +9 -1
  81. data/lib/active_record/tasks/sqlserver_database_tasks.rb +28 -32
  82. data/lib/activerecord-sqlserver-adapter.rb +3 -1
  83. data/lib/arel/visitors/sqlserver.rb +58 -24
  84. data/lib/arel_sqlserver.rb +4 -2
  85. data/test/appveyor/dbsetup.ps1 +4 -4
  86. data/test/cases/adapter_test_sqlserver.rb +223 -180
  87. data/test/cases/change_column_null_test_sqlserver.rb +17 -15
  88. data/test/cases/coerced_tests.rb +654 -360
  89. data/test/cases/column_test_sqlserver.rb +635 -604
  90. data/test/cases/connection_test_sqlserver.rb +18 -21
  91. data/test/cases/execute_procedure_test_sqlserver.rb +20 -20
  92. data/test/cases/fetch_test_sqlserver.rb +17 -23
  93. data/test/cases/fully_qualified_identifier_test_sqlserver.rb +15 -19
  94. data/test/cases/helper_sqlserver.rb +20 -15
  95. data/test/cases/in_clause_test_sqlserver.rb +36 -0
  96. data/test/cases/index_test_sqlserver.rb +15 -15
  97. data/test/cases/json_test_sqlserver.rb +25 -25
  98. data/test/cases/migration_test_sqlserver.rb +30 -26
  99. data/test/cases/order_test_sqlserver.rb +53 -54
  100. data/test/cases/pessimistic_locking_test_sqlserver.rb +31 -37
  101. data/test/cases/rake_test_sqlserver.rb +44 -56
  102. data/test/cases/schema_dumper_test_sqlserver.rb +117 -112
  103. data/test/cases/schema_test_sqlserver.rb +20 -26
  104. data/test/cases/scratchpad_test_sqlserver.rb +4 -4
  105. data/test/cases/showplan_test_sqlserver.rb +32 -39
  106. data/test/cases/specific_schema_test_sqlserver.rb +75 -72
  107. data/test/cases/transaction_test_sqlserver.rb +27 -29
  108. data/test/cases/trigger_test_sqlserver.rb +18 -17
  109. data/test/cases/utils_test_sqlserver.rb +78 -78
  110. data/test/cases/uuid_test_sqlserver.rb +19 -20
  111. data/test/debug.rb +8 -6
  112. data/test/migrations/create_clients_and_change_column_null.rb +3 -1
  113. data/test/migrations/transaction_table/1_table_will_never_be_created.rb +4 -4
  114. data/test/models/sqlserver/booking.rb +3 -1
  115. data/test/models/sqlserver/customers_view.rb +3 -1
  116. data/test/models/sqlserver/datatype.rb +2 -0
  117. data/test/models/sqlserver/datatype_migration.rb +2 -0
  118. data/test/models/sqlserver/dollar_table_name.rb +3 -1
  119. data/test/models/sqlserver/edge_schema.rb +3 -3
  120. data/test/models/sqlserver/fk_has_fk.rb +3 -1
  121. data/test/models/sqlserver/fk_has_pk.rb +3 -1
  122. data/test/models/sqlserver/natural_pk_data.rb +4 -2
  123. data/test/models/sqlserver/natural_pk_int_data.rb +3 -1
  124. data/test/models/sqlserver/no_pk_data.rb +3 -1
  125. data/test/models/sqlserver/object_default.rb +3 -1
  126. data/test/models/sqlserver/quoted_table.rb +4 -2
  127. data/test/models/sqlserver/quoted_view_1.rb +3 -1
  128. data/test/models/sqlserver/quoted_view_2.rb +3 -1
  129. data/test/models/sqlserver/sst_memory.rb +3 -1
  130. data/test/models/sqlserver/string_default.rb +3 -1
  131. data/test/models/sqlserver/string_defaults_big_view.rb +3 -1
  132. data/test/models/sqlserver/string_defaults_view.rb +3 -1
  133. data/test/models/sqlserver/tinyint_pk.rb +3 -1
  134. data/test/models/sqlserver/trigger.rb +4 -2
  135. data/test/models/sqlserver/trigger_history.rb +3 -1
  136. data/test/models/sqlserver/upper.rb +3 -1
  137. data/test/models/sqlserver/uppered.rb +3 -1
  138. data/test/models/sqlserver/uuid.rb +3 -1
  139. data/test/schema/datatypes/2012.sql +1 -0
  140. data/test/schema/sqlserver_specific_schema.rb +31 -21
  141. data/test/support/coerceable_test_sqlserver.rb +15 -9
  142. data/test/support/connection_reflection.rb +3 -2
  143. data/test/support/core_ext/query_cache.rb +4 -1
  144. data/test/support/load_schema_sqlserver.rb +5 -5
  145. data/test/support/minitest_sqlserver.rb +3 -1
  146. data/test/support/paths_sqlserver.rb +11 -11
  147. data/test/support/rake_helpers.rb +13 -10
  148. data/test/support/sql_counter_sqlserver.rb +3 -4
  149. data/test/support/test_in_memory_oltp.rb +9 -7
  150. metadata +23 -13
  151. data/BACKERS.md +0 -32
  152. data/circle.yml +0 -38
@@ -1,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