activerecord-sqlserver-adapter 5.2.1 → 6.0.2

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