activerecord-sqlserver-adapter 5.2.1 → 6.0.2

Sign up to get free protection for your applications and to get access to all the features.
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