activerecord-sqlserver-adapter 7.1.7 → 7.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
+