activerecord-sqlserver-adapter 7.1.7 → 7.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.devcontainer/Dockerfile +3 -3
  3. data/.github/workflows/ci.yml +10 -4
  4. data/CHANGELOG.md +5 -99
  5. data/Gemfile +4 -4
  6. data/README.md +43 -19
  7. data/VERSION +1 -1
  8. data/activerecord-sqlserver-adapter.gemspec +2 -2
  9. data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +6 -4
  10. data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +6 -5
  11. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +7 -4
  12. data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +6 -4
  13. data/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb +14 -12
  14. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +50 -32
  15. data/lib/active_record/connection_adapters/sqlserver/quoting.rb +44 -46
  16. data/lib/active_record/connection_adapters/sqlserver/savepoints.rb +2 -0
  17. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +1 -1
  18. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +2 -5
  19. data/lib/active_record/tasks/sqlserver_database_tasks.rb +38 -32
  20. data/lib/arel/visitors/sqlserver.rb +57 -12
  21. data/test/cases/active_schema_test_sqlserver.rb +6 -6
  22. data/test/cases/adapter_test_sqlserver.rb +17 -18
  23. data/test/cases/coerced_tests.rb +279 -167
  24. data/test/cases/disconnected_test_sqlserver.rb +9 -3
  25. data/test/cases/eager_load_too_many_ids_test_sqlserver.rb +1 -1
  26. data/test/cases/enum_test_sqlserver.rb +1 -1
  27. data/test/cases/execute_procedure_test_sqlserver.rb +9 -5
  28. data/test/cases/helper_sqlserver.rb +11 -5
  29. data/test/cases/index_test_sqlserver.rb +8 -6
  30. data/test/cases/json_test_sqlserver.rb +1 -1
  31. data/test/cases/lateral_test_sqlserver.rb +2 -2
  32. data/test/cases/migration_test_sqlserver.rb +1 -1
  33. data/test/cases/optimizer_hints_test_sqlserver.rb +12 -12
  34. data/test/cases/pessimistic_locking_test_sqlserver.rb +8 -7
  35. data/test/cases/primary_keys_test_sqlserver.rb +2 -2
  36. data/test/cases/rake_test_sqlserver.rb +8 -4
  37. data/test/cases/schema_dumper_test_sqlserver.rb +4 -5
  38. data/test/cases/showplan_test_sqlserver.rb +7 -7
  39. data/test/cases/specific_schema_test_sqlserver.rb +17 -13
  40. data/test/cases/view_test_sqlserver.rb +1 -1
  41. data/test/schema/sqlserver_specific_schema.rb +4 -4
  42. data/test/support/connection_reflection.rb +1 -1
  43. data/test/support/core_ext/query_cache.rb +2 -2
  44. data/test/support/query_assertions.rb +49 -0
  45. data/test/support/table_definition_sqlserver.rb +24 -0
  46. data/test/support/test_in_memory_oltp.rb +2 -2
  47. metadata +12 -13
  48. data/lib/active_record/sqlserver_base.rb +0 -13
  49. data/test/cases/scratchpad_test_sqlserver.rb +0 -8
  50. data/test/support/sql_counter_sqlserver.rb +0 -14
@@ -56,7 +56,7 @@ class UniquenessValidationWithIndexTest < ActiveRecord::TestCase
56
56
 
57
57
  t = Topic.create!(title: "abc")
58
58
  t.author_name = "John"
59
- assert_queries(1) do
59
+ assert_queries_count(1) do
60
60
  t.valid?
61
61
  end
62
62
  end
@@ -197,7 +197,7 @@ class BasicsTest < ActiveRecord::TestCase
197
197
  # Use square brackets as SQL Server escaped character
198
198
  coerce_tests! :test_column_names_are_escaped
199
199
  def test_column_names_are_escaped_coerced
200
- conn = ActiveRecord::Base.connection
200
+ conn = ActiveRecord::Base.lease_connection
201
201
  assert_equal "[t]]]", conn.quote_column_name("t]")
202
202
  end
203
203
 
@@ -229,18 +229,6 @@ class BasicsTest < ActiveRecord::TestCase
229
229
  end
230
230
  end
231
231
  end
232
-
233
- # SQL Server does not have query for release_savepoint
234
- coerce_tests! %r{an empty transaction does not raise if preventing writes}
235
- test "an empty transaction does not raise if preventing writes coerced" do
236
- ActiveRecord::Base.while_preventing_writes do
237
- assert_queries(1, ignore_none: true) do
238
- Bird.transaction do
239
- ActiveRecord::Base.connection.materialize_transactions
240
- end
241
- end
242
- end
243
- end
244
232
  end
245
233
 
246
234
  class BelongsToAssociationsTest < ActiveRecord::TestCase
@@ -260,7 +248,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
260
248
  def test_belongs_to_coerced
261
249
  client = Client.find(3)
262
250
  first_firm = companies(:first_firm)
263
- assert_sql(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do
251
+ assert_queries_match(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do
264
252
  assert_equal first_firm, client.firm
265
253
  assert_equal first_firm.name, client.firm.name
266
254
  end
@@ -323,7 +311,7 @@ module ActiveRecord
323
311
 
324
312
  authors = Author.where(id: [1, 2, 3, nil])
325
313
  assert_equal sql_unprepared, @connection.to_sql(authors.arel)
326
- assert_sql(prepared ? sql_prepared : sql_unprepared) { assert_equal 3, authors.length }
314
+ assert_queries_match(prepared ? sql_prepared : sql_unprepared) { assert_equal 3, authors.length }
327
315
 
328
316
  # prepared_statements: true
329
317
  #
@@ -338,7 +326,7 @@ module ActiveRecord
338
326
 
339
327
  authors = Author.where(id: [1, 2, 3, 9223372036854775808])
340
328
  assert_equal sql_unprepared, @connection.to_sql(authors.arel)
341
- assert_sql(prepared ? sql_prepared : sql_unprepared) { assert_equal 3, authors.length }
329
+ assert_queries_match(prepared ? sql_prepared : sql_unprepared) { assert_equal 3, authors.length }
342
330
  end
343
331
  end
344
332
  end
@@ -351,6 +339,39 @@ module ActiveRecord
351
339
  Book.send(:load_schema!)
352
340
  original_test_payload_name_on_load
353
341
  end
342
+
343
+ # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
344
+ coerce_tests! :test_payload_row_count_on_select_all
345
+ def test_payload_row_count_on_select_all_coerced
346
+ connection.remove_index(:books, column: [:author_id, :name])
347
+
348
+ original_test_payload_row_count_on_select_all
349
+ ensure
350
+ Book.where(author_id: nil, name: 'row count book 1').delete_all
351
+ Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
352
+ end
353
+
354
+ # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
355
+ coerce_tests! :test_payload_row_count_on_pluck
356
+ def test_payload_row_count_on_pluck_coerced
357
+ connection.remove_index(:books, column: [:author_id, :name])
358
+
359
+ original_test_payload_row_count_on_pluck
360
+ ensure
361
+ Book.where(author_id: nil, name: 'row count book 2').delete_all
362
+ Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
363
+ end
364
+
365
+ # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
366
+ coerce_tests! :test_payload_row_count_on_raw_sql
367
+ def test_payload_row_count_on_raw_sql_coerced
368
+ connection.remove_index(:books, column: [:author_id, :name])
369
+
370
+ original_test_payload_row_count_on_raw_sql
371
+ ensure
372
+ Book.where(author_id: nil, name: 'row count book 3').delete_all
373
+ Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
374
+ end
354
375
  end
355
376
  end
356
377
 
@@ -473,7 +494,7 @@ class CalculationsTest < ActiveRecord::TestCase
473
494
  # Match SQL Server limit implementation
474
495
  coerce_tests! :test_limit_is_kept
475
496
  def test_limit_is_kept_coerced
476
- queries = capture_sql_ss { Account.limit(1).count }
497
+ queries = capture_sql { Account.limit(1).count }
477
498
  assert_equal 1, queries.length
478
499
  assert_match(/ORDER BY \[accounts\]\.\[id\] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/, queries.first)
479
500
  end
@@ -481,7 +502,7 @@ class CalculationsTest < ActiveRecord::TestCase
481
502
  # Match SQL Server limit implementation
482
503
  coerce_tests! :test_limit_with_offset_is_kept
483
504
  def test_limit_with_offset_is_kept_coerced
484
- queries = capture_sql_ss { Account.limit(1).offset(1).count }
505
+ queries = capture_sql { Account.limit(1).offset(1).count }
485
506
  assert_equal 1, queries.length
486
507
  assert_match(/ORDER BY \[accounts\]\.\[id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY.*@0 = 1, @1 = 1/, queries.first)
487
508
  end
@@ -490,8 +511,8 @@ class CalculationsTest < ActiveRecord::TestCase
490
511
  coerce_tests! :test_distinct_count_all_with_custom_select_and_order
491
512
  def test_distinct_count_all_with_custom_select_and_order_coerced
492
513
  accounts = Account.distinct.select("credit_limit % 10 AS the_limit").order(Arel.sql("credit_limit % 10"))
493
- assert_queries(1) { assert_equal 3, accounts.count(:all) }
494
- assert_queries(1) { assert_equal 3, accounts.load.size }
514
+ assert_queries_count(1) { assert_equal 3, accounts.count(:all) }
515
+ assert_queries_count(1) { assert_equal 3, accounts.load.size }
495
516
  end
496
517
 
497
518
  # Leave it up to users to format selects/functions so HAVING works correctly.
@@ -657,7 +678,7 @@ class MigrationTest < ActiveRecord::TestCase
657
678
  ensure
658
679
  Person.reset_column_information
659
680
  if Person.column_names.include?("last_name")
660
- Person.connection.remove_column("people", "last_name")
681
+ Person.lease_connection.remove_column("people", "last_name")
661
682
  end
662
683
  end
663
684
  end
@@ -947,9 +968,9 @@ class FinderTest < ActiveRecord::TestCase
947
968
  # Assert SQL Server limit implementation
948
969
  coerce_tests! :test_take_and_first_and_last_with_integer_should_use_sql_limit
949
970
  def test_take_and_first_and_last_with_integer_should_use_sql_limit_coerced
950
- assert_sql(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.* @0 = 3/) { Topic.take(3).entries }
951
- assert_sql(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.* @0 = 2/) { Topic.first(2).entries }
952
- assert_sql(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.* @0 = 5/) { Topic.last(5).entries }
971
+ assert_queries_match(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.* @0 = 3/) { Topic.take(3).entries }
972
+ assert_queries_match(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.* @0 = 2/) { Topic.first(2).entries }
973
+ assert_queries_match(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.* @0 = 5/) { Topic.last(5).entries }
953
974
  end
954
975
 
955
976
  # This fails only when run in the full test suite task. Just taking it out of the mix.
@@ -980,7 +1001,7 @@ class FinderTest < ActiveRecord::TestCase
980
1001
  # Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
981
1002
  coerce_tests! :test_include_on_unloaded_relation_with_match
982
1003
  def test_include_on_unloaded_relation_with_match_coerced
983
- assert_sql(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do
1004
+ assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do
984
1005
  assert_equal true, Customer.where(name: "David").include?(customers(:david))
985
1006
  end
986
1007
  end
@@ -988,7 +1009,7 @@ class FinderTest < ActiveRecord::TestCase
988
1009
  # Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
989
1010
  coerce_tests! :test_include_on_unloaded_relation_without_match
990
1011
  def test_include_on_unloaded_relation_without_match_coerced
991
- assert_sql(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do
1012
+ assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do
992
1013
  assert_equal false, Customer.where(name: "David").include?(customers(:mary))
993
1014
  end
994
1015
  end
@@ -996,7 +1017,7 @@ class FinderTest < ActiveRecord::TestCase
996
1017
  # Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
997
1018
  coerce_tests! :test_member_on_unloaded_relation_with_match
998
1019
  def test_member_on_unloaded_relation_with_match_coerced
999
- assert_sql(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do
1020
+ assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do
1000
1021
  assert_equal true, Customer.where(name: "David").member?(customers(:david))
1001
1022
  end
1002
1023
  end
@@ -1004,7 +1025,7 @@ class FinderTest < ActiveRecord::TestCase
1004
1025
  # Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
1005
1026
  coerce_tests! :test_member_on_unloaded_relation_without_match
1006
1027
  def test_member_on_unloaded_relation_without_match_coerced
1007
- assert_sql(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do
1028
+ assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do
1008
1029
  assert_equal false, Customer.where(name: "David").member?(customers(:mary))
1009
1030
  end
1010
1031
  end
@@ -1018,8 +1039,8 @@ class FinderTest < ActiveRecord::TestCase
1018
1039
  assert_equal topics(:fifth), Topic.first
1019
1040
  assert_equal topics(:third), Topic.last
1020
1041
 
1021
- c = Topic.connection
1022
- assert_sql(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.title"))} DESC, #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) {
1042
+ c = Topic.lease_connection
1043
+ assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.title"))} DESC, #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) {
1023
1044
  Topic.last
1024
1045
  }
1025
1046
  ensure
@@ -1032,8 +1053,8 @@ class FinderTest < ActiveRecord::TestCase
1032
1053
  old_implicit_order_column = Topic.implicit_order_column
1033
1054
  Topic.implicit_order_column = "id"
1034
1055
 
1035
- c = Topic.connection
1036
- assert_sql(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) {
1056
+ c = Topic.lease_connection
1057
+ assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) {
1037
1058
  Topic.last
1038
1059
  }
1039
1060
  ensure
@@ -1046,9 +1067,9 @@ class FinderTest < ActiveRecord::TestCase
1046
1067
  old_implicit_order_column = NonPrimaryKey.implicit_order_column
1047
1068
  NonPrimaryKey.implicit_order_column = "created_at"
1048
1069
 
1049
- c = NonPrimaryKey.connection
1070
+ c = NonPrimaryKey.lease_connection
1050
1071
 
1051
- assert_sql(/ORDER BY #{Regexp.escape(c.quote_table_name("non_primary_keys.created_at"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) {
1072
+ assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("non_primary_keys.created_at"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) {
1052
1073
  NonPrimaryKey.last
1053
1074
  }
1054
1075
  ensure
@@ -1058,7 +1079,7 @@ class FinderTest < ActiveRecord::TestCase
1058
1079
  # Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
1059
1080
  coerce_tests! :test_member_on_unloaded_relation_with_composite_primary_key
1060
1081
  def test_member_on_unloaded_relation_with_composite_primary_key_coerced
1061
- assert_sql(/1 AS one.* FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/) do
1082
+ assert_queries_match(/1 AS one.* FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/) do
1062
1083
  book = cpk_books(:cpk_great_author_first_book)
1063
1084
  assert Cpk::Book.where(title: "The first book").member?(book)
1064
1085
  end
@@ -1067,13 +1088,13 @@ class FinderTest < ActiveRecord::TestCase
1067
1088
  # Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
1068
1089
  coerce_tests! :test_implicit_order_column_prepends_query_constraints
1069
1090
  def test_implicit_order_column_prepends_query_constraints_coerced
1070
- c = ClothingItem.connection
1091
+ c = ClothingItem.lease_connection
1071
1092
  ClothingItem.implicit_order_column = "description"
1072
1093
  quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type"))
1073
1094
  quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color"))
1074
1095
  quoted_descrption = Regexp.escape(c.quote_table_name("clothing_items.description"))
1075
1096
 
1076
- assert_sql(/ORDER BY #{quoted_descrption} ASC, #{quoted_type} ASC, #{quoted_color} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do
1097
+ assert_queries_match(/ORDER BY #{quoted_descrption} ASC, #{quoted_type} ASC, #{quoted_color} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do
1077
1098
  assert_kind_of ClothingItem, ClothingItem.first
1078
1099
  end
1079
1100
  ensure
@@ -1083,11 +1104,11 @@ class FinderTest < ActiveRecord::TestCase
1083
1104
  # Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
1084
1105
  coerce_tests! %r{#last for a model with composite query constraints}
1085
1106
  test "#last for a model with composite query constraints coerced" do
1086
- c = ClothingItem.connection
1107
+ c = ClothingItem.lease_connection
1087
1108
  quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type"))
1088
1109
  quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color"))
1089
1110
 
1090
- assert_sql(/ORDER BY #{quoted_type} DESC, #{quoted_color} DESC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do
1111
+ assert_queries_match(/ORDER BY #{quoted_type} DESC, #{quoted_color} DESC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do
1091
1112
  assert_kind_of ClothingItem, ClothingItem.last
1092
1113
  end
1093
1114
  end
@@ -1095,11 +1116,11 @@ class FinderTest < ActiveRecord::TestCase
1095
1116
  # Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
1096
1117
  coerce_tests! %r{#first for a model with composite query constraints}
1097
1118
  test "#first for a model with composite query constraints coerced" do
1098
- c = ClothingItem.connection
1119
+ c = ClothingItem.lease_connection
1099
1120
  quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type"))
1100
1121
  quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color"))
1101
1122
 
1102
- assert_sql(/ORDER BY #{quoted_type} ASC, #{quoted_color} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do
1123
+ assert_queries_match(/ORDER BY #{quoted_type} ASC, #{quoted_color} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do
1103
1124
  assert_kind_of ClothingItem, ClothingItem.first
1104
1125
  end
1105
1126
  end
@@ -1107,12 +1128,12 @@ class FinderTest < ActiveRecord::TestCase
1107
1128
  # Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
1108
1129
  coerce_tests! :test_implicit_order_column_reorders_query_constraints
1109
1130
  def test_implicit_order_column_reorders_query_constraints_coerced
1110
- c = ClothingItem.connection
1131
+ c = ClothingItem.lease_connection
1111
1132
  ClothingItem.implicit_order_column = "color"
1112
1133
  quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type"))
1113
1134
  quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color"))
1114
1135
 
1115
- assert_sql(/ORDER BY #{quoted_color} ASC, #{quoted_type} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do
1136
+ assert_queries_match(/ORDER BY #{quoted_color} ASC, #{quoted_type} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do
1116
1137
  assert_kind_of ClothingItem, ClothingItem.first
1117
1138
  end
1118
1139
  ensure
@@ -1122,7 +1143,7 @@ class FinderTest < ActiveRecord::TestCase
1122
1143
  # Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
1123
1144
  coerce_tests! :test_include_on_unloaded_relation_with_composite_primary_key
1124
1145
  def test_include_on_unloaded_relation_with_composite_primary_key_coerced
1125
- assert_sql(/1 AS one.*OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/) do
1146
+ assert_queries_match(/1 AS one.*OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/) do
1126
1147
  book = cpk_books(:cpk_great_author_first_book)
1127
1148
  assert Cpk::Book.where(title: "The first book").include?(book)
1128
1149
  end
@@ -1131,12 +1152,12 @@ class FinderTest < ActiveRecord::TestCase
1131
1152
  # Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
1132
1153
  coerce_tests! :test_nth_to_last_with_order_uses_limit
1133
1154
  def test_nth_to_last_with_order_uses_limit_coerced
1134
- c = Topic.connection
1135
- assert_sql(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET @(\d) ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1.*@\2 = 1/i) do
1155
+ c = Topic.lease_connection
1156
+ assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET @(\d) ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1.*@\2 = 1/i) do
1136
1157
  Topic.second_to_last
1137
1158
  end
1138
1159
 
1139
- assert_sql(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.updated_at"))} DESC OFFSET @(\d) ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1.*@\2 = 1/i) do
1160
+ assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.updated_at"))} DESC OFFSET @(\d) ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1.*@\2 = 1/i) do
1140
1161
  Topic.order(:updated_at).second_to_last
1141
1162
  end
1142
1163
  end
@@ -1184,7 +1205,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
1184
1205
  def test_has_one_coerced
1185
1206
  firm = companies(:first_firm)
1186
1207
  first_account = Account.find(1)
1187
- assert_sql(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do
1208
+ assert_queries_match(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do
1188
1209
  assert_equal first_account, firm.account
1189
1210
  assert_equal first_account.credit_limit, firm.account.credit_limit
1190
1211
  end
@@ -1196,7 +1217,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
1196
1217
  coerce_tests! :test_has_one_through_executes_limited_query
1197
1218
  def test_has_one_through_executes_limited_query_coerced
1198
1219
  boring_club = clubs(:boring_club)
1199
- assert_sql(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do
1220
+ assert_queries_match(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do
1200
1221
  assert_equal boring_club, @member.general_club
1201
1222
  end
1202
1223
  end
@@ -1246,6 +1267,9 @@ class PersistenceTest < ActiveRecord::TestCase
1246
1267
  assert_not_predicate topic, :approved?
1247
1268
  assert_equal "The First Topic", topic.title
1248
1269
  end
1270
+
1271
+ # In SQL Server it's not possible to set the primary key column using a trigger and to get it then to return.
1272
+ coerce_tests! :test_model_with_no_auto_populated_fields_still_returns_primary_key_after_insert
1249
1273
  end
1250
1274
 
1251
1275
  require "models/author"
@@ -1257,7 +1281,7 @@ class UpdateAllTest < ActiveRecord::TestCase
1257
1281
  _(david.id).must_equal 1
1258
1282
  _(mary.id).must_equal 2
1259
1283
  _(david.name).wont_equal mary.name
1260
- assert_sql(/UPDATE.*\(SELECT \[authors\].\[id\] FROM \[authors\].*ORDER BY \[authors\].\[id\]/i) do
1284
+ assert_queries_match(/UPDATE.*\(SELECT \[authors\].\[id\] FROM \[authors\].*ORDER BY \[authors\].\[id\]/i) do
1261
1285
  Author.where("[id] > 1").order(:id).update_all(name: "Test")
1262
1286
  end
1263
1287
  _(david.reload.name).must_equal "David"
@@ -1316,49 +1340,13 @@ module ActiveRecord
1316
1340
  end
1317
1341
  end
1318
1342
 
1319
- class PrimaryKeysTest < ActiveRecord::TestCase
1320
- # SQL Server does not have query for release_savepoint
1321
- coerce_tests! :test_create_without_primary_key_no_extra_query
1322
- def test_create_without_primary_key_no_extra_query_coerced
1323
- klass = Class.new(ActiveRecord::Base) do
1324
- self.table_name = "dashboards"
1325
- end
1326
- klass.create! # warmup schema cache
1327
- assert_queries(2, ignore_none: true) { klass.create! }
1328
- end
1329
- end
1330
-
1331
1343
  require "models/task"
1332
1344
  class QueryCacheTest < ActiveRecord::TestCase
1333
1345
  # SQL Server adapter not in list of supported adapters in original test.
1334
1346
  coerce_tests! :test_cache_does_not_wrap_results_in_arrays
1335
1347
  def test_cache_does_not_wrap_results_in_arrays_coerced
1336
1348
  Task.cache do
1337
- assert_equal 2, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
1338
- end
1339
- end
1340
-
1341
- # Same as original test except that we expect one query to be performed to retrieve the table's primary key
1342
- # and we don't call `reload_type_map` because SQL Server adapter doesn't support it.
1343
- # When we generate the SQL for the `find` it includes ordering on the primary key. If we reset the column
1344
- # information then the primary key needs to be retrieved from the database again to generate the SQL causing the
1345
- # original test's `assert_no_queries` assertion to fail. Assert that the query was to get the primary key.
1346
- coerce_tests! :test_query_cached_even_when_types_are_reset
1347
- def test_query_cached_even_when_types_are_reset_coerced
1348
- Task.cache do
1349
- # Warm the cache
1350
- Task.find(1)
1351
-
1352
- # Clear places where type information is cached
1353
- Task.reset_column_information
1354
- Task.initialize_find_by_cache
1355
- Task.define_attribute_methods
1356
-
1357
- assert_queries(1, ignore_none: true) do
1358
- Task.find(1)
1359
- end
1360
-
1361
- assert_includes ActiveRecord::SQLCounter.log_all.first, "TC.CONSTRAINT_TYPE = N''PRIMARY KEY''"
1349
+ assert_equal 2, Task.lease_connection.select_value("SELECT count(*) AS count_all FROM tasks")
1362
1350
  end
1363
1351
  end
1364
1352
  end
@@ -1436,7 +1424,7 @@ class RelationTest < ActiveRecord::TestCase
1436
1424
  # Find any limit via our expression.
1437
1425
  coerce_tests! %r{relations don't load all records in #inspect}
1438
1426
  def test_relations_dont_load_all_records_in_inspect_coerced
1439
- assert_sql(/NEXT @0 ROWS.*@0 = \d+/) do
1427
+ assert_queries_match(/NEXT @0 ROWS.*@0 = \d+/) do
1440
1428
  Post.all.inspect
1441
1429
  end
1442
1430
  end
@@ -1444,7 +1432,7 @@ class RelationTest < ActiveRecord::TestCase
1444
1432
  # Find any limit via our expression.
1445
1433
  coerce_tests! %r{relations don't load all records in #pretty_print}
1446
1434
  def test_relations_dont_load_all_records_in_pretty_print_coerced
1447
- assert_sql(/FETCH NEXT @(\d) ROWS ONLY/) do
1435
+ assert_queries_match(/FETCH NEXT @(\d) ROWS ONLY/) do
1448
1436
  PP.pp Post.all, StringIO.new # avoid outputting.
1449
1437
  end
1450
1438
  end
@@ -1454,11 +1442,11 @@ class RelationTest < ActiveRecord::TestCase
1454
1442
  def test_empty_complex_chained_relations_coerced
1455
1443
  posts = Post.select("comments_count").where("id is not null").group("author_id", "id").where("legacy_comments_count > 0")
1456
1444
 
1457
- assert_queries(1) { assert_equal false, posts.empty? }
1445
+ assert_queries_count(1) { assert_equal false, posts.empty? }
1458
1446
  assert_not_predicate posts, :loaded?
1459
1447
 
1460
1448
  no_posts = posts.where(title: "")
1461
- assert_queries(1) { assert_equal true, no_posts.empty? }
1449
+ assert_queries_count(1) { assert_equal true, no_posts.empty? }
1462
1450
  assert_not_predicate no_posts, :loaded?
1463
1451
  end
1464
1452
 
@@ -1493,7 +1481,7 @@ module ActiveRecord
1493
1481
 
1494
1482
  coerce_tests! :test_does_not_duplicate_optimizer_hints_on_merge
1495
1483
  def test_does_not_duplicate_optimizer_hints_on_merge_coerced
1496
- escaped_table = Post.connection.quote_table_name("posts")
1484
+ escaped_table = Post.lease_connection.quote_table_name("posts")
1497
1485
  expected = "SELECT #{escaped_table}.* FROM #{escaped_table} OPTION (OMGHINT)"
1498
1486
  query = Post.optimizer_hints("OMGHINT").merge(Post.optimizer_hints("OMGHINT")).to_sql
1499
1487
  assert_equal expected, query
@@ -1516,11 +1504,11 @@ class SanitizeTest < ActiveRecord::TestCase
1516
1504
  }
1517
1505
  end
1518
1506
 
1519
- assert_sql(/LIKE N'20!% !_reduction!_!!'/) do
1507
+ assert_queries_match(/LIKE @0/) do
1520
1508
  searchable_post.search_as_method("20% _reduction_!").to_a
1521
1509
  end
1522
1510
 
1523
- assert_sql(/LIKE N'20!% !_reduction!_!!'/) do
1511
+ assert_queries_match(/LIKE @0/) do
1524
1512
  searchable_post.search_as_scope("20% _reduction_!").to_a
1525
1513
  end
1526
1514
  end
@@ -1542,9 +1530,9 @@ class SchemaDumperTest < ActiveRecord::TestCase
1542
1530
  @schema_migration.create_version(v)
1543
1531
  end
1544
1532
 
1545
- schema_info = ActiveRecord::Base.connection.dump_schema_information
1533
+ schema_info = ActiveRecord::Base.lease_connection.dump_schema_information
1546
1534
  expected = <<~STR
1547
- INSERT INTO #{ActiveRecord::Base.connection.quote_table_name("schema_migrations")} (version) VALUES
1535
+ INSERT INTO #{ActiveRecord::Base.lease_connection.quote_table_name("schema_migrations")} (version) VALUES
1548
1536
  (N'20100301010101'),
1549
1537
  (N'20100201010101'),
1550
1538
  (N'20100101010101');
@@ -1602,7 +1590,7 @@ class SchemaDumperDefaultsCoerceTest < ActiveRecord::TestCase
1602
1590
  include SchemaDumpingHelper
1603
1591
 
1604
1592
  setup do
1605
- @connection = ActiveRecord::Base.connection
1593
+ @connection = ActiveRecord::Base.lease_connection
1606
1594
  @connection.create_table :dump_defaults, force: true do |t|
1607
1595
  t.string :string_with_default, default: "Hello!"
1608
1596
  t.date :date_with_default, default: "2014-06-05"
@@ -1634,21 +1622,24 @@ class TransactionTest < ActiveRecord::TestCase
1634
1622
  coerce_tests! :test_releasing_named_savepoints
1635
1623
  def test_releasing_named_savepoints_coerced
1636
1624
  Topic.transaction do
1637
- Topic.connection.materialize_transactions
1625
+ Topic.lease_connection.materialize_transactions
1626
+
1627
+ Topic.lease_connection.create_savepoint("another")
1628
+ Topic.lease_connection.release_savepoint("another")
1638
1629
 
1639
- Topic.connection.create_savepoint("another")
1640
- Topic.connection.release_savepoint("another")
1641
- # We do not have a notion of releasing, so this does nothing vs raise an error.
1642
- Topic.connection.release_savepoint("another")
1630
+ # We do not have a notion of releasing, so this does nothing and doesn't raise an error.
1631
+ assert_nothing_raised do
1632
+ Topic.lease_connection.release_savepoint("another")
1633
+ end
1643
1634
  end
1644
1635
  end
1645
1636
 
1646
1637
  # SQL Server does not have query for release_savepoint.
1647
1638
  coerce_tests! :test_nested_transactions_after_disable_lazy_transactions
1648
1639
  def test_nested_transactions_after_disable_lazy_transactions_coerced
1649
- Topic.connection.disable_lazy_transactions!
1640
+ Topic.lease_connection.disable_lazy_transactions!
1650
1641
 
1651
- capture_sql do
1642
+ actual_queries = capture_sql(include_schema: true) do
1652
1643
  # RealTransaction (begin..commit)
1653
1644
  Topic.transaction(requires_new: true) do
1654
1645
  # ResetParentTransaction (no queries)
@@ -1666,8 +1657,6 @@ class TransactionTest < ActiveRecord::TestCase
1666
1657
  end
1667
1658
  end
1668
1659
 
1669
- actual_queries = ActiveRecord::SQLCounter.log_all
1670
-
1671
1660
  expected_queries = [
1672
1661
  /BEGIN/i,
1673
1662
  /DELETE/i,
@@ -1685,7 +1674,7 @@ class TransactionTest < ActiveRecord::TestCase
1685
1674
  # SQL Server does not have query for release_savepoint.
1686
1675
  coerce_tests! :test_nested_transactions_skip_excess_savepoints
1687
1676
  def test_nested_transactions_skip_excess_savepoints_coerced
1688
- capture_sql do
1677
+ actual_queries = capture_sql(include_schema: true) do
1689
1678
  # RealTransaction (begin..commit)
1690
1679
  Topic.transaction(requires_new: true) do
1691
1680
  # ResetParentTransaction (no queries)
@@ -1703,8 +1692,6 @@ class TransactionTest < ActiveRecord::TestCase
1703
1692
  end
1704
1693
  end
1705
1694
 
1706
- actual_queries = ActiveRecord::SQLCounter.log_all
1707
-
1708
1695
  expected_queries = [
1709
1696
  /BEGIN/i,
1710
1697
  /DELETE/i,
@@ -1889,12 +1876,12 @@ module ActiveRecord
1889
1876
  # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
1890
1877
  coerce_tests! :test_statement_cache_values_differ
1891
1878
  def test_statement_cache_values_differ_coerced
1892
- Book.connection.remove_index(:books, column: [:author_id, :name])
1879
+ Book.lease_connection.remove_index(:books, column: [:author_id, :name])
1893
1880
 
1894
1881
  original_test_statement_cache_values_differ
1895
1882
  ensure
1896
1883
  Book.where(author_id: nil, name: 'my book').delete_all
1897
- Book.connection.add_index(:books, [:author_id, :name], unique: true)
1884
+ Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
1898
1885
  end
1899
1886
  end
1900
1887
  end
@@ -2124,14 +2111,14 @@ class RelationMergingTest < ActiveRecord::TestCase
2124
2111
 
2125
2112
  non_mary_and_bob = Author.where.not(id: [mary, bob])
2126
2113
 
2127
- author_id = Author.connection.quote_table_name("authors.id")
2128
- assert_sql(/WHERE #{Regexp.escape(author_id)} NOT IN \((@\d), \g<1>\)'/) do
2114
+ author_id = Author.lease_connection.quote_table_name("authors.id")
2115
+ assert_queries_match(/WHERE #{Regexp.escape(author_id)} NOT IN \((@\d), \g<1>\)'/) do
2129
2116
  assert_equal [david], non_mary_and_bob.merge(non_mary_and_bob)
2130
2117
  end
2131
2118
 
2132
2119
  only_david = Author.where("#{author_id} IN (?)", david)
2133
2120
 
2134
- assert_sql(/WHERE \(#{Regexp.escape(author_id)} IN \(1\)\)\z/) do
2121
+ assert_queries_match(/WHERE \(#{Regexp.escape(author_id)} IN \(@\d\)\)/) do
2135
2122
  assert_equal [david], only_david.merge(only_david)
2136
2123
  end
2137
2124
  end
@@ -2151,56 +2138,56 @@ class EnumTest < ActiveRecord::TestCase
2151
2138
  # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
2152
2139
  coerce_tests! %r{enums are distinct per class}
2153
2140
  test "enums are distinct per class coerced" do
2154
- Book.connection.remove_index(:books, column: [:author_id, :name])
2141
+ Book.lease_connection.remove_index(:books, column: [:author_id, :name])
2155
2142
 
2156
2143
  send(:'original_enums are distinct per class')
2157
2144
  ensure
2158
2145
  Book.where(author_id: nil, name: nil).delete_all
2159
- Book.connection.add_index(:books, [:author_id, :name], unique: true)
2146
+ Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
2160
2147
  end
2161
2148
 
2162
2149
  # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
2163
2150
  coerce_tests! %r{creating new objects with enum scopes}
2164
2151
  test "creating new objects with enum scopes coerced" do
2165
- Book.connection.remove_index(:books, column: [:author_id, :name])
2152
+ Book.lease_connection.remove_index(:books, column: [:author_id, :name])
2166
2153
 
2167
2154
  send(:'original_creating new objects with enum scopes')
2168
2155
  ensure
2169
2156
  Book.where(author_id: nil, name: nil).delete_all
2170
- Book.connection.add_index(:books, [:author_id, :name], unique: true)
2157
+ Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
2171
2158
  end
2172
2159
 
2173
2160
  # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
2174
2161
  coerce_tests! %r{enums are inheritable}
2175
2162
  test "enums are inheritable coerced" do
2176
- Book.connection.remove_index(:books, column: [:author_id, :name])
2163
+ Book.lease_connection.remove_index(:books, column: [:author_id, :name])
2177
2164
 
2178
2165
  send(:'original_enums are inheritable')
2179
2166
  ensure
2180
2167
  Book.where(author_id: nil, name: nil).delete_all
2181
- Book.connection.add_index(:books, [:author_id, :name], unique: true)
2168
+ Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
2182
2169
  end
2183
2170
 
2184
2171
  # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
2185
2172
  coerce_tests! %r{declare multiple enums at a time}
2186
2173
  test "declare multiple enums at a time coerced" do
2187
- Book.connection.remove_index(:books, column: [:author_id, :name])
2174
+ Book.lease_connection.remove_index(:books, column: [:author_id, :name])
2188
2175
 
2189
2176
  send(:'original_declare multiple enums at a time')
2190
2177
  ensure
2191
2178
  Book.where(author_id: nil, name: nil).delete_all
2192
- Book.connection.add_index(:books, [:author_id, :name], unique: true)
2179
+ Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
2193
2180
  end
2194
2181
 
2195
2182
  # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
2196
2183
  coerce_tests! %r{serializable\? with large number label}
2197
2184
  test "serializable? with large number label coerced" do
2198
- Book.connection.remove_index(:books, column: [:author_id, :name])
2185
+ Book.lease_connection.remove_index(:books, column: [:author_id, :name])
2199
2186
 
2200
2187
  send(:'original_serializable\? with large number label')
2201
2188
  ensure
2202
2189
  Book.where(author_id: nil, name: nil).delete_all
2203
- Book.connection.add_index(:books, [:author_id, :name], unique: true)
2190
+ Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
2204
2191
  end
2205
2192
  end
2206
2193
 
@@ -2213,18 +2200,6 @@ class QueryCacheExpiryTest < ActiveRecord::TestCase
2213
2200
  Task.cache { Task.insert({ starting: Time.now }) }
2214
2201
  end
2215
2202
 
2216
- assert_called(ActiveRecord::Base.connection, :clear_query_cache, times: 2) do
2217
- Task.cache { Task.insert_all!([{ starting: Time.now }]) }
2218
- end
2219
-
2220
- assert_called(ActiveRecord::Base.connection, :clear_query_cache, times: 2) do
2221
- Task.cache { Task.insert!({ starting: Time.now }) }
2222
- end
2223
-
2224
- assert_called(ActiveRecord::Base.connection, :clear_query_cache, times: 2) do
2225
- Task.cache { Task.insert_all!([{ starting: Time.now }]) }
2226
- end
2227
-
2228
2203
  assert_raises(ArgumentError, /does not support upsert/) do
2229
2204
  Task.cache { Task.upsert({ starting: Time.now }) }
2230
2205
  end
@@ -2232,6 +2207,16 @@ class QueryCacheExpiryTest < ActiveRecord::TestCase
2232
2207
  assert_raises(ArgumentError, /does not support upsert/) do
2233
2208
  Task.cache { Task.upsert_all([{ starting: Time.now }]) }
2234
2209
  end
2210
+
2211
+ Task.cache do
2212
+ assert_called(ActiveRecord::Base.connection_pool.query_cache, :clear, times: 1) do
2213
+ Task.insert_all!([ starting: Time.now ])
2214
+ end
2215
+
2216
+ assert_called(ActiveRecord::Base.connection_pool.query_cache, :clear, times: 1) do
2217
+ Task.insert!({ starting: Time.now })
2218
+ end
2219
+ end
2235
2220
  end
2236
2221
  end
2237
2222
 
@@ -2253,7 +2238,7 @@ class EagerLoadingTooManyIdsTest < ActiveRecord::TestCase
2253
2238
 
2254
2239
  # Perform test
2255
2240
  citation_count = Citation.count
2256
- assert_sql(/WHERE \[citations\]\.\[id\] IN \(0, 1/) do
2241
+ assert_queries_match(/WHERE \[citations\]\.\[id\] IN \(0, 1/) do
2257
2242
  assert_equal citation_count, Citation.eager_load(:citations).offset(0).size
2258
2243
  end
2259
2244
  end
@@ -2287,7 +2272,7 @@ class MarshalSerializationTest < ActiveRecord::TestCase
2287
2272
  undef_method :marshal_fixture_path
2288
2273
  def marshal_fixture_path(file_name)
2289
2274
  File.expand_path(
2290
- "support/marshal_compatibility_fixtures/#{ActiveRecord::Base.connection.adapter_name}/#{file_name}.dump",
2275
+ "support/marshal_compatibility_fixtures/#{ActiveRecord::Base.lease_connection.adapter_name}/#{file_name}.dump",
2291
2276
  ARTest::SQLServer.test_root_sqlserver
2292
2277
  )
2293
2278
  end
@@ -2338,7 +2323,7 @@ class PreloaderTest < ActiveRecord::TestCase
2338
2323
  assert_equal 2, sql.size
2339
2324
  preload_sql = sql.last
2340
2325
 
2341
- c = Cpk::OrderAgreement.connection
2326
+ c = Cpk::OrderAgreement.lease_connection
2342
2327
  order_id_column = Regexp.escape(c.quote_table_name("cpk_order_agreements.order_id"))
2343
2328
  order_id_constraint = /#{order_id_column} = @0.*@0 = \d+$/
2344
2329
  expectation = /SELECT.*WHERE.* #{order_id_constraint}/
@@ -2362,7 +2347,7 @@ class PreloaderTest < ActiveRecord::TestCase
2362
2347
  assert_equal 2, sql.size
2363
2348
  preload_sql = sql.last
2364
2349
 
2365
- c = Cpk::Order.connection
2350
+ c = Cpk::Order.lease_connection
2366
2351
  order_id = Regexp.escape(c.quote_table_name("cpk_orders.id"))
2367
2352
  order_constraint = /#{order_id} = @0.*@0 = \d+$/
2368
2353
  expectation = /SELECT.*WHERE.* #{order_constraint}/
@@ -2372,20 +2357,6 @@ class PreloaderTest < ActiveRecord::TestCase
2372
2357
  end
2373
2358
  end
2374
2359
 
2375
- class BasePreventWritesTest < ActiveRecord::TestCase
2376
- # SQL Server does not have query for release_savepoint
2377
- coerce_tests! %r{an empty transaction does not raise if preventing writes}
2378
- test "an empty transaction does not raise if preventing writes coerced" do
2379
- ActiveRecord::Base.while_preventing_writes do
2380
- assert_queries(1, ignore_none: true) do
2381
- Bird.transaction do
2382
- ActiveRecord::Base.connection.materialize_transactions
2383
- end
2384
- end
2385
- end
2386
- end
2387
- end
2388
-
2389
2360
  class MigratorTest < ActiveRecord::TestCase
2390
2361
  # Test fails on Windows AppVeyor CI for unknown reason.
2391
2362
  coerce_tests! :test_migrator_db_has_no_schema_migrations_table if RbConfig::CONFIG["host_os"] =~ /mswin|mingw/
@@ -2401,45 +2372,45 @@ class FieldOrderedValuesTest < ActiveRecord::TestCase
2401
2372
  # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
2402
2373
  coerce_tests! :test_in_order_of_with_enums_values
2403
2374
  def test_in_order_of_with_enums_values_coerced
2404
- Book.connection.remove_index(:books, column: [:author_id, :name])
2375
+ Book.lease_connection.remove_index(:books, column: [:author_id, :name])
2405
2376
 
2406
2377
  original_test_in_order_of_with_enums_values
2407
2378
  ensure
2408
2379
  Book.where(author_id: nil, name: nil).delete_all
2409
- Book.connection.add_index(:books, [:author_id, :name], unique: true)
2380
+ Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
2410
2381
  end
2411
2382
 
2412
2383
  # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
2413
2384
  coerce_tests! :test_in_order_of_with_string_column
2414
2385
  def test_in_order_of_with_string_column_coerced
2415
- Book.connection.remove_index(:books, column: [:author_id, :name])
2386
+ Book.lease_connection.remove_index(:books, column: [:author_id, :name])
2416
2387
 
2417
2388
  original_test_in_order_of_with_string_column
2418
2389
  ensure
2419
2390
  Book.where(author_id: nil, name: nil).delete_all
2420
- Book.connection.add_index(:books, [:author_id, :name], unique: true)
2391
+ Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
2421
2392
  end
2422
2393
 
2423
2394
  # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
2424
2395
  coerce_tests! :test_in_order_of_with_enums_keys
2425
2396
  def test_in_order_of_with_enums_keys_coerced
2426
- Book.connection.remove_index(:books, column: [:author_id, :name])
2397
+ Book.lease_connection.remove_index(:books, column: [:author_id, :name])
2427
2398
 
2428
2399
  original_test_in_order_of_with_enums_keys
2429
2400
  ensure
2430
2401
  Book.where(author_id: nil, name: nil).delete_all
2431
- Book.connection.add_index(:books, [:author_id, :name], unique: true)
2402
+ Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
2432
2403
  end
2433
2404
 
2434
2405
  # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
2435
2406
  coerce_tests! :test_in_order_of_with_nil
2436
2407
  def test_in_order_of_with_nil_coerced
2437
- Book.connection.remove_index(:books, column: [:author_id, :name])
2408
+ Book.lease_connection.remove_index(:books, column: [:author_id, :name])
2438
2409
 
2439
2410
  original_test_in_order_of_with_nil
2440
2411
  ensure
2441
2412
  Book.where(author_id: nil, name: nil).delete_all
2442
- Book.connection.add_index(:books, [:author_id, :name], unique: true)
2413
+ Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
2443
2414
  end
2444
2415
  end
2445
2416
 
@@ -2449,7 +2420,7 @@ class QueryLogsTest < ActiveRecord::TestCase
2449
2420
  coerce_tests! :test_sql_commenter_format
2450
2421
  def test_sql_commenter_format_coerced
2451
2422
  ActiveRecord::QueryLogs.update_formatter(:sqlcommenter)
2452
- assert_sql(%r{/\*application=''active_record''\*/}) do
2423
+ assert_queries_match(%r{/\*application=''active_record''\*/}) do
2453
2424
  Dashboard.first
2454
2425
  end
2455
2426
  end
@@ -2464,7 +2435,7 @@ class QueryLogsTest < ActiveRecord::TestCase
2464
2435
  { tracestate: "congo=t61rcWkgMzE,rojo=00f067aa0ba902b7", custom_proc: -> { "Joe's Shack" } },
2465
2436
  ]
2466
2437
 
2467
- assert_sql(%r{custom_proc=''Joe%27s%20Shack'',tracestate=''congo%3Dt61rcWkgMzE%2Crojo%3D00f067aa0ba902b7''\*/}) do
2438
+ assert_queries_match(%r{custom_proc=''Joe%27s%20Shack'',tracestate=''congo%3Dt61rcWkgMzE%2Crojo%3D00f067aa0ba902b7''\*/}) do
2468
2439
  Dashboard.first
2469
2440
  end
2470
2441
  end
@@ -2479,7 +2450,26 @@ class QueryLogsTest < ActiveRecord::TestCase
2479
2450
  { custom_proc: -> { 1234 } },
2480
2451
  ]
2481
2452
 
2482
- assert_sql(%r{custom_proc=''1234''\*/}) do
2453
+ assert_queries_match(%r{custom_proc=''1234''\*/}) do
2454
+ Dashboard.first
2455
+ end
2456
+ end
2457
+
2458
+ # SQL requires double single-quotes.
2459
+ coerce_tests! :test_sqlcommenter_format_allows_string_keys
2460
+ def test_sqlcommenter_format_allows_string_keys_coerced
2461
+ ActiveRecord::QueryLogs.update_formatter(:sqlcommenter)
2462
+
2463
+ ActiveRecord::QueryLogs.tags = [
2464
+ :application,
2465
+ {
2466
+ "string" => "value",
2467
+ tracestate: "congo=t61rcWkgMzE,rojo=00f067aa0ba902b7",
2468
+ custom_proc: -> { "Joe's Shack" }
2469
+ },
2470
+ ]
2471
+
2472
+ assert_queries_match(%r{custom_proc=''Joe%27s%20Shack'',string=''value'',tracestate=''congo%3Dt61rcWkgMzE%2Crojo%3D00f067aa0ba902b7''\*/}) do
2483
2473
  Dashboard.first
2484
2474
  end
2485
2475
  end
@@ -2489,7 +2479,7 @@ class QueryLogsTest < ActiveRecord::TestCase
2489
2479
  def test_invalid_encoding_query_coerced
2490
2480
  ActiveRecord::QueryLogs.tags = [ :application ]
2491
2481
  assert_raises ActiveRecord::StatementInvalid do
2492
- ActiveRecord::Base.connection.execute "select 1 as '\xFF'"
2482
+ ActiveRecord::Base.lease_connection.execute "select 1 as '\xFF'"
2493
2483
  end
2494
2484
  end
2495
2485
  end
@@ -2646,3 +2636,125 @@ module ActiveRecord
2646
2636
  end
2647
2637
  end
2648
2638
  end
2639
+
2640
+ module ActiveRecord
2641
+ module ConnectionAdapters
2642
+ class PoolConfig
2643
+ class ResolverTest < ActiveRecord::TestCase
2644
+ # SQL Server was not included in the list of available adapters in the error message.
2645
+ coerce_tests! :test_url_invalid_adapter
2646
+ def test_url_invalid_adapter_coerced
2647
+ error = assert_raises(AdapterNotFound) do
2648
+ Base.connection_handler.establish_connection "ridiculous://foo?encoding=utf8"
2649
+ end
2650
+
2651
+ assert_match "Database configuration specifies nonexistent 'ridiculous' adapter. Available adapters are: abstract, fake, mysql2, postgresql, sqlite3, sqlserver, trilogy. Ensure that the adapter is spelled correctly in config/database.yml and that you've added the necessary adapter gem to your Gemfile if it's not in the list of available adapters.", error.message
2652
+ end
2653
+ end
2654
+ end
2655
+ end
2656
+ end
2657
+
2658
+ module ActiveRecord
2659
+ class TableMetadataTest < ActiveSupport::TestCase
2660
+ # Adapter returns an object that is subclass of what is expected in the original test.
2661
+ coerce_tests! %r{#associated_table creates the right type caster for joined table with different association name}
2662
+ def associated_table_creates_the_right_type_caster_for_joined_table_with_different_association_name_coerced
2663
+ base_table_metadata = TableMetadata.new(AuditRequiredDeveloper, Arel::Table.new("developers"))
2664
+
2665
+ associated_table_metadata = base_table_metadata.associated_table("audit_logs")
2666
+
2667
+ assert associated_table_metadata.arel_table.type_for_attribute(:message).is_a?(ActiveRecord::Type::String)
2668
+ end
2669
+ end
2670
+ end
2671
+
2672
+ module ActiveRecord
2673
+ module TypeCaster
2674
+ class ConnectionTest < ActiveSupport::TestCase
2675
+ # Adapter returns an object that is subclass of what is expected in the original test.
2676
+ coerce_tests! %r{#type_for_attribute is not aware of custom types}
2677
+ def type_for_attribute_is_not_aware_of_custom_types_coerced
2678
+ type_caster = Connection.new(AttributedDeveloper, "developers")
2679
+
2680
+ type = type_caster.type_for_attribute(:name)
2681
+
2682
+ assert_not_equal DeveloperName, type.class
2683
+ assert type.is_a?(ActiveRecord::Type::String)
2684
+ end
2685
+ end
2686
+ end
2687
+ end
2688
+
2689
+ require "models/car"
2690
+ class ExplainTest < ActiveRecord::TestCase
2691
+ # Expected query slightly different from because of 'sp_executesql' and query parameters.
2692
+ coerce_tests! :test_relation_explain_with_first
2693
+ def test_relation_explain_with_first_coerced
2694
+ expected_query = capture_sql {
2695
+ Car.all.first
2696
+ }.first[/EXEC sp_executesql N'(.*?) NEXT/, 1]
2697
+ message = Car.all.explain.first
2698
+ assert_match(/^EXPLAIN/, message)
2699
+ assert_match(expected_query, message)
2700
+ end
2701
+
2702
+ # Expected query slightly different from because of 'sp_executesql' and query parameters.
2703
+ coerce_tests! :test_relation_explain_with_last
2704
+ def test_relation_explain_with_last_coerced
2705
+ expected_query = capture_sql {
2706
+ Car.all.last
2707
+ }.first[/EXEC sp_executesql N'(.*?) NEXT/, 1]
2708
+ expected_query = expected_query
2709
+ message = Car.all.explain.last
2710
+
2711
+ assert_match(/^EXPLAIN/, message)
2712
+ assert_match(expected_query, message)
2713
+ end
2714
+ end
2715
+
2716
+ module ActiveRecord
2717
+ module Assertions
2718
+ class QueryAssertionsTest < ActiveSupport::TestCase
2719
+ # Query slightly different in original test.
2720
+ coerce_tests! :test_assert_queries_match
2721
+ def test_assert_queries_match_coerced
2722
+ assert_queries_match(/ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/i, count: 1) { Post.first }
2723
+ assert_queries_match(/ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/i) { Post.first }
2724
+
2725
+ error = assert_raises(Minitest::Assertion) {
2726
+ assert_queries_match(/ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/i, count: 2) { Post.first }
2727
+ }
2728
+ assert_match(/1 instead of 2 queries/, error.message)
2729
+
2730
+ error = assert_raises(Minitest::Assertion) {
2731
+ assert_queries_match(/ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/i, count: 0) { Post.first }
2732
+ }
2733
+ assert_match(/1 instead of 0 queries/, error.message)
2734
+ end
2735
+ end
2736
+ end
2737
+ end
2738
+
2739
+ module ActiveRecord
2740
+ class WithTest < ActiveRecord::TestCase
2741
+ # SQL contains just 'WITH' instead of 'WITH RECURSIVE' as expected by the original test.
2742
+ coerce_tests! :test_with_recursive
2743
+ def test_with_recursive_coerced
2744
+ top_companies = Company.where(firm_id: nil).to_a
2745
+ child_companies = Company.where(firm_id: top_companies).to_a
2746
+ top_companies_and_children = (top_companies.map(&:id) + child_companies.map(&:id)).sort
2747
+
2748
+ relation = Company.with_recursive(
2749
+ top_companies_and_children: [
2750
+ Company.where(firm_id: nil),
2751
+ Company.joins("JOIN top_companies_and_children ON companies.firm_id = top_companies_and_children.id"),
2752
+ ]
2753
+ ).from("top_companies_and_children AS companies")
2754
+
2755
+ assert_equal top_companies_and_children, relation.order(:id).pluck(:id)
2756
+ assert_match "WITH ", relation.to_sql
2757
+ end
2758
+ end
2759
+ end
2760
+