activerecord-sqlserver-adapter 7.1.9 → 7.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.devcontainer/Dockerfile +3 -3
  3. data/.github/workflows/ci.yml +11 -5
  4. data/CHANGELOG.md +5 -111
  5. data/Gemfile +4 -4
  6. data/README.md +40 -17
  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 +49 -34
  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 -2
  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 +22 -9
  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 +262 -190
  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/schema_test_sqlserver.rb +0 -4
  39. data/test/cases/showplan_test_sqlserver.rb +7 -7
  40. data/test/cases/specific_schema_test_sqlserver.rb +17 -13
  41. data/test/cases/view_test_sqlserver.rb +1 -9
  42. data/test/schema/sqlserver_specific_schema.rb +4 -4
  43. data/test/support/connection_reflection.rb +1 -1
  44. data/test/support/core_ext/query_cache.rb +2 -2
  45. data/test/support/query_assertions.rb +49 -0
  46. data/test/support/table_definition_sqlserver.rb +24 -0
  47. data/test/support/test_in_memory_oltp.rb +2 -2
  48. metadata +12 -13
  49. data/lib/active_record/sqlserver_base.rb +0 -13
  50. data/test/cases/scratchpad_test_sqlserver.rb +0 -8
  51. 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
@@ -702,6 +723,7 @@ module ActiveRecord
702
723
  ActiveRecord::Migrator.new(:up, [migration], @schema_migration, @internal_metadata).migrate
703
724
  assert connection.table_exists?(long_table_name[0...-1])
704
725
  assert_not connection.table_exists?(:more_testings)
726
+ assert connection.table_exists?(long_table_name[0...-1])
705
727
  ensure
706
728
  connection.drop_table(:more_testings) rescue nil
707
729
  connection.drop_table(long_table_name[0...-1]) rescue nil
@@ -726,28 +748,6 @@ module ActiveRecord
726
748
  assert_match(/Index name \'#{long_index_name}\' on table \'testings\' is too long/i, error.message)
727
749
  end
728
750
 
729
- # SQL Server truncates long table names when renaming.
730
- coerce_tests! :test_rename_table_errors_on_too_long_index_name_7_0
731
- def test_rename_table_errors_on_too_long_index_name_7_0_coerced
732
- long_table_name = "a" * (connection.table_name_length + 1)
733
-
734
- migration = Class.new(ActiveRecord::Migration[7.0]) {
735
- def migrate(x)
736
- add_index :testings, :foo
737
- long_table_name = "a" * (connection.table_name_length + 1)
738
- rename_table :testings, long_table_name
739
- end
740
- }.new
741
-
742
- ActiveRecord::Migrator.new(:up, [migration], @schema_migration, @internal_metadata).migrate
743
-
744
- assert_not connection.table_exists?(:testings)
745
- assert connection.table_exists?(long_table_name[0...-1])
746
- assert connection.index_exists?(long_table_name[0...-1], :foo)
747
- ensure
748
- connection.drop_table(long_table_name[0...-1], if_exists: true)
749
- end
750
-
751
751
  # SQL Server has a different maximum index name length.
752
752
  coerce_tests! :test_create_table_add_index_errors_on_too_long_name_7_0
753
753
  def test_create_table_add_index_errors_on_too_long_name_7_0_coerced
@@ -968,9 +968,9 @@ class FinderTest < ActiveRecord::TestCase
968
968
  # Assert SQL Server limit implementation
969
969
  coerce_tests! :test_take_and_first_and_last_with_integer_should_use_sql_limit
970
970
  def test_take_and_first_and_last_with_integer_should_use_sql_limit_coerced
971
- assert_sql(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.* @0 = 3/) { Topic.take(3).entries }
972
- assert_sql(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.* @0 = 2/) { Topic.first(2).entries }
973
- 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 }
974
974
  end
975
975
 
976
976
  # This fails only when run in the full test suite task. Just taking it out of the mix.
@@ -1001,7 +1001,7 @@ class FinderTest < ActiveRecord::TestCase
1001
1001
  # Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
1002
1002
  coerce_tests! :test_include_on_unloaded_relation_with_match
1003
1003
  def test_include_on_unloaded_relation_with_match_coerced
1004
- 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
1005
1005
  assert_equal true, Customer.where(name: "David").include?(customers(:david))
1006
1006
  end
1007
1007
  end
@@ -1009,7 +1009,7 @@ class FinderTest < ActiveRecord::TestCase
1009
1009
  # Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
1010
1010
  coerce_tests! :test_include_on_unloaded_relation_without_match
1011
1011
  def test_include_on_unloaded_relation_without_match_coerced
1012
- 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
1013
1013
  assert_equal false, Customer.where(name: "David").include?(customers(:mary))
1014
1014
  end
1015
1015
  end
@@ -1017,7 +1017,7 @@ class FinderTest < ActiveRecord::TestCase
1017
1017
  # Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
1018
1018
  coerce_tests! :test_member_on_unloaded_relation_with_match
1019
1019
  def test_member_on_unloaded_relation_with_match_coerced
1020
- 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
1021
1021
  assert_equal true, Customer.where(name: "David").member?(customers(:david))
1022
1022
  end
1023
1023
  end
@@ -1025,7 +1025,7 @@ class FinderTest < ActiveRecord::TestCase
1025
1025
  # Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
1026
1026
  coerce_tests! :test_member_on_unloaded_relation_without_match
1027
1027
  def test_member_on_unloaded_relation_without_match_coerced
1028
- 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
1029
1029
  assert_equal false, Customer.where(name: "David").member?(customers(:mary))
1030
1030
  end
1031
1031
  end
@@ -1039,8 +1039,8 @@ class FinderTest < ActiveRecord::TestCase
1039
1039
  assert_equal topics(:fifth), Topic.first
1040
1040
  assert_equal topics(:third), Topic.last
1041
1041
 
1042
- c = Topic.connection
1043
- 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) {
1044
1044
  Topic.last
1045
1045
  }
1046
1046
  ensure
@@ -1053,8 +1053,8 @@ class FinderTest < ActiveRecord::TestCase
1053
1053
  old_implicit_order_column = Topic.implicit_order_column
1054
1054
  Topic.implicit_order_column = "id"
1055
1055
 
1056
- c = Topic.connection
1057
- 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) {
1058
1058
  Topic.last
1059
1059
  }
1060
1060
  ensure
@@ -1067,9 +1067,9 @@ class FinderTest < ActiveRecord::TestCase
1067
1067
  old_implicit_order_column = NonPrimaryKey.implicit_order_column
1068
1068
  NonPrimaryKey.implicit_order_column = "created_at"
1069
1069
 
1070
- c = NonPrimaryKey.connection
1070
+ c = NonPrimaryKey.lease_connection
1071
1071
 
1072
- 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) {
1073
1073
  NonPrimaryKey.last
1074
1074
  }
1075
1075
  ensure
@@ -1079,7 +1079,7 @@ class FinderTest < ActiveRecord::TestCase
1079
1079
  # Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
1080
1080
  coerce_tests! :test_member_on_unloaded_relation_with_composite_primary_key
1081
1081
  def test_member_on_unloaded_relation_with_composite_primary_key_coerced
1082
- 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
1083
1083
  book = cpk_books(:cpk_great_author_first_book)
1084
1084
  assert Cpk::Book.where(title: "The first book").member?(book)
1085
1085
  end
@@ -1088,13 +1088,13 @@ class FinderTest < ActiveRecord::TestCase
1088
1088
  # Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
1089
1089
  coerce_tests! :test_implicit_order_column_prepends_query_constraints
1090
1090
  def test_implicit_order_column_prepends_query_constraints_coerced
1091
- c = ClothingItem.connection
1091
+ c = ClothingItem.lease_connection
1092
1092
  ClothingItem.implicit_order_column = "description"
1093
1093
  quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type"))
1094
1094
  quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color"))
1095
1095
  quoted_descrption = Regexp.escape(c.quote_table_name("clothing_items.description"))
1096
1096
 
1097
- 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
1098
1098
  assert_kind_of ClothingItem, ClothingItem.first
1099
1099
  end
1100
1100
  ensure
@@ -1104,11 +1104,11 @@ class FinderTest < ActiveRecord::TestCase
1104
1104
  # Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
1105
1105
  coerce_tests! %r{#last for a model with composite query constraints}
1106
1106
  test "#last for a model with composite query constraints coerced" do
1107
- c = ClothingItem.connection
1107
+ c = ClothingItem.lease_connection
1108
1108
  quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type"))
1109
1109
  quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color"))
1110
1110
 
1111
- 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
1112
1112
  assert_kind_of ClothingItem, ClothingItem.last
1113
1113
  end
1114
1114
  end
@@ -1116,11 +1116,11 @@ class FinderTest < ActiveRecord::TestCase
1116
1116
  # Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
1117
1117
  coerce_tests! %r{#first for a model with composite query constraints}
1118
1118
  test "#first for a model with composite query constraints coerced" do
1119
- c = ClothingItem.connection
1119
+ c = ClothingItem.lease_connection
1120
1120
  quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type"))
1121
1121
  quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color"))
1122
1122
 
1123
- 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
1124
1124
  assert_kind_of ClothingItem, ClothingItem.first
1125
1125
  end
1126
1126
  end
@@ -1128,12 +1128,12 @@ class FinderTest < ActiveRecord::TestCase
1128
1128
  # Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
1129
1129
  coerce_tests! :test_implicit_order_column_reorders_query_constraints
1130
1130
  def test_implicit_order_column_reorders_query_constraints_coerced
1131
- c = ClothingItem.connection
1131
+ c = ClothingItem.lease_connection
1132
1132
  ClothingItem.implicit_order_column = "color"
1133
1133
  quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type"))
1134
1134
  quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color"))
1135
1135
 
1136
- 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
1137
1137
  assert_kind_of ClothingItem, ClothingItem.first
1138
1138
  end
1139
1139
  ensure
@@ -1143,7 +1143,7 @@ class FinderTest < ActiveRecord::TestCase
1143
1143
  # Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
1144
1144
  coerce_tests! :test_include_on_unloaded_relation_with_composite_primary_key
1145
1145
  def test_include_on_unloaded_relation_with_composite_primary_key_coerced
1146
- 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
1147
1147
  book = cpk_books(:cpk_great_author_first_book)
1148
1148
  assert Cpk::Book.where(title: "The first book").include?(book)
1149
1149
  end
@@ -1152,12 +1152,12 @@ class FinderTest < ActiveRecord::TestCase
1152
1152
  # Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
1153
1153
  coerce_tests! :test_nth_to_last_with_order_uses_limit
1154
1154
  def test_nth_to_last_with_order_uses_limit_coerced
1155
- c = Topic.connection
1156
- 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
1157
1157
  Topic.second_to_last
1158
1158
  end
1159
1159
 
1160
- 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
1161
1161
  Topic.order(:updated_at).second_to_last
1162
1162
  end
1163
1163
  end
@@ -1205,7 +1205,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
1205
1205
  def test_has_one_coerced
1206
1206
  firm = companies(:first_firm)
1207
1207
  first_account = Account.find(1)
1208
- assert_sql(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do
1208
+ assert_queries_match(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do
1209
1209
  assert_equal first_account, firm.account
1210
1210
  assert_equal first_account.credit_limit, firm.account.credit_limit
1211
1211
  end
@@ -1217,7 +1217,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
1217
1217
  coerce_tests! :test_has_one_through_executes_limited_query
1218
1218
  def test_has_one_through_executes_limited_query_coerced
1219
1219
  boring_club = clubs(:boring_club)
1220
- assert_sql(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do
1220
+ assert_queries_match(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do
1221
1221
  assert_equal boring_club, @member.general_club
1222
1222
  end
1223
1223
  end
@@ -1267,6 +1267,9 @@ class PersistenceTest < ActiveRecord::TestCase
1267
1267
  assert_not_predicate topic, :approved?
1268
1268
  assert_equal "The First Topic", topic.title
1269
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
1270
1273
  end
1271
1274
 
1272
1275
  require "models/author"
@@ -1278,7 +1281,7 @@ class UpdateAllTest < ActiveRecord::TestCase
1278
1281
  _(david.id).must_equal 1
1279
1282
  _(mary.id).must_equal 2
1280
1283
  _(david.name).wont_equal mary.name
1281
- 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
1282
1285
  Author.where("[id] > 1").order(:id).update_all(name: "Test")
1283
1286
  end
1284
1287
  _(david.reload.name).must_equal "David"
@@ -1337,49 +1340,13 @@ module ActiveRecord
1337
1340
  end
1338
1341
  end
1339
1342
 
1340
- class PrimaryKeysTest < ActiveRecord::TestCase
1341
- # SQL Server does not have query for release_savepoint
1342
- coerce_tests! :test_create_without_primary_key_no_extra_query
1343
- def test_create_without_primary_key_no_extra_query_coerced
1344
- klass = Class.new(ActiveRecord::Base) do
1345
- self.table_name = "dashboards"
1346
- end
1347
- klass.create! # warmup schema cache
1348
- assert_queries(2, ignore_none: true) { klass.create! }
1349
- end
1350
- end
1351
-
1352
1343
  require "models/task"
1353
1344
  class QueryCacheTest < ActiveRecord::TestCase
1354
1345
  # SQL Server adapter not in list of supported adapters in original test.
1355
1346
  coerce_tests! :test_cache_does_not_wrap_results_in_arrays
1356
1347
  def test_cache_does_not_wrap_results_in_arrays_coerced
1357
1348
  Task.cache do
1358
- assert_equal 2, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
1359
- end
1360
- end
1361
-
1362
- # Same as original test except that we expect one query to be performed to retrieve the table's primary key
1363
- # and we don't call `reload_type_map` because SQL Server adapter doesn't support it.
1364
- # When we generate the SQL for the `find` it includes ordering on the primary key. If we reset the column
1365
- # information then the primary key needs to be retrieved from the database again to generate the SQL causing the
1366
- # original test's `assert_no_queries` assertion to fail. Assert that the query was to get the primary key.
1367
- coerce_tests! :test_query_cached_even_when_types_are_reset
1368
- def test_query_cached_even_when_types_are_reset_coerced
1369
- Task.cache do
1370
- # Warm the cache
1371
- Task.find(1)
1372
-
1373
- # Clear places where type information is cached
1374
- Task.reset_column_information
1375
- Task.initialize_find_by_cache
1376
- Task.define_attribute_methods
1377
-
1378
- assert_queries(1, ignore_none: true) do
1379
- Task.find(1)
1380
- end
1381
-
1382
- 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")
1383
1350
  end
1384
1351
  end
1385
1352
  end
@@ -1457,7 +1424,7 @@ class RelationTest < ActiveRecord::TestCase
1457
1424
  # Find any limit via our expression.
1458
1425
  coerce_tests! %r{relations don't load all records in #inspect}
1459
1426
  def test_relations_dont_load_all_records_in_inspect_coerced
1460
- assert_sql(/NEXT @0 ROWS.*@0 = \d+/) do
1427
+ assert_queries_match(/NEXT @0 ROWS.*@0 = \d+/) do
1461
1428
  Post.all.inspect
1462
1429
  end
1463
1430
  end
@@ -1465,7 +1432,7 @@ class RelationTest < ActiveRecord::TestCase
1465
1432
  # Find any limit via our expression.
1466
1433
  coerce_tests! %r{relations don't load all records in #pretty_print}
1467
1434
  def test_relations_dont_load_all_records_in_pretty_print_coerced
1468
- assert_sql(/FETCH NEXT @(\d) ROWS ONLY/) do
1435
+ assert_queries_match(/FETCH NEXT @(\d) ROWS ONLY/) do
1469
1436
  PP.pp Post.all, StringIO.new # avoid outputting.
1470
1437
  end
1471
1438
  end
@@ -1475,11 +1442,11 @@ class RelationTest < ActiveRecord::TestCase
1475
1442
  def test_empty_complex_chained_relations_coerced
1476
1443
  posts = Post.select("comments_count").where("id is not null").group("author_id", "id").where("legacy_comments_count > 0")
1477
1444
 
1478
- assert_queries(1) { assert_equal false, posts.empty? }
1445
+ assert_queries_count(1) { assert_equal false, posts.empty? }
1479
1446
  assert_not_predicate posts, :loaded?
1480
1447
 
1481
1448
  no_posts = posts.where(title: "")
1482
- assert_queries(1) { assert_equal true, no_posts.empty? }
1449
+ assert_queries_count(1) { assert_equal true, no_posts.empty? }
1483
1450
  assert_not_predicate no_posts, :loaded?
1484
1451
  end
1485
1452
 
@@ -1514,7 +1481,7 @@ module ActiveRecord
1514
1481
 
1515
1482
  coerce_tests! :test_does_not_duplicate_optimizer_hints_on_merge
1516
1483
  def test_does_not_duplicate_optimizer_hints_on_merge_coerced
1517
- escaped_table = Post.connection.quote_table_name("posts")
1484
+ escaped_table = Post.lease_connection.quote_table_name("posts")
1518
1485
  expected = "SELECT #{escaped_table}.* FROM #{escaped_table} OPTION (OMGHINT)"
1519
1486
  query = Post.optimizer_hints("OMGHINT").merge(Post.optimizer_hints("OMGHINT")).to_sql
1520
1487
  assert_equal expected, query
@@ -1537,11 +1504,11 @@ class SanitizeTest < ActiveRecord::TestCase
1537
1504
  }
1538
1505
  end
1539
1506
 
1540
- assert_sql(/LIKE N'20!% !_reduction!_!!'/) do
1507
+ assert_queries_match(/LIKE @0/) do
1541
1508
  searchable_post.search_as_method("20% _reduction_!").to_a
1542
1509
  end
1543
1510
 
1544
- assert_sql(/LIKE N'20!% !_reduction!_!!'/) do
1511
+ assert_queries_match(/LIKE @0/) do
1545
1512
  searchable_post.search_as_scope("20% _reduction_!").to_a
1546
1513
  end
1547
1514
  end
@@ -1563,9 +1530,9 @@ class SchemaDumperTest < ActiveRecord::TestCase
1563
1530
  @schema_migration.create_version(v)
1564
1531
  end
1565
1532
 
1566
- schema_info = ActiveRecord::Base.connection.dump_schema_information
1533
+ schema_info = ActiveRecord::Base.lease_connection.dump_schema_information
1567
1534
  expected = <<~STR
1568
- 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
1569
1536
  (N'20100301010101'),
1570
1537
  (N'20100201010101'),
1571
1538
  (N'20100101010101');
@@ -1623,7 +1590,7 @@ class SchemaDumperDefaultsCoerceTest < ActiveRecord::TestCase
1623
1590
  include SchemaDumpingHelper
1624
1591
 
1625
1592
  setup do
1626
- @connection = ActiveRecord::Base.connection
1593
+ @connection = ActiveRecord::Base.lease_connection
1627
1594
  @connection.create_table :dump_defaults, force: true do |t|
1628
1595
  t.string :string_with_default, default: "Hello!"
1629
1596
  t.date :date_with_default, default: "2014-06-05"
@@ -1655,21 +1622,24 @@ class TransactionTest < ActiveRecord::TestCase
1655
1622
  coerce_tests! :test_releasing_named_savepoints
1656
1623
  def test_releasing_named_savepoints_coerced
1657
1624
  Topic.transaction do
1658
- Topic.connection.materialize_transactions
1625
+ Topic.lease_connection.materialize_transactions
1659
1626
 
1660
- Topic.connection.create_savepoint("another")
1661
- Topic.connection.release_savepoint("another")
1662
- # We do not have a notion of releasing, so this does nothing vs raise an error.
1663
- Topic.connection.release_savepoint("another")
1627
+ Topic.lease_connection.create_savepoint("another")
1628
+ Topic.lease_connection.release_savepoint("another")
1629
+
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
1664
1634
  end
1665
1635
  end
1666
1636
 
1667
1637
  # SQL Server does not have query for release_savepoint.
1668
1638
  coerce_tests! :test_nested_transactions_after_disable_lazy_transactions
1669
1639
  def test_nested_transactions_after_disable_lazy_transactions_coerced
1670
- Topic.connection.disable_lazy_transactions!
1640
+ Topic.lease_connection.disable_lazy_transactions!
1671
1641
 
1672
- capture_sql do
1642
+ actual_queries = capture_sql(include_schema: true) do
1673
1643
  # RealTransaction (begin..commit)
1674
1644
  Topic.transaction(requires_new: true) do
1675
1645
  # ResetParentTransaction (no queries)
@@ -1687,8 +1657,6 @@ class TransactionTest < ActiveRecord::TestCase
1687
1657
  end
1688
1658
  end
1689
1659
 
1690
- actual_queries = ActiveRecord::SQLCounter.log_all
1691
-
1692
1660
  expected_queries = [
1693
1661
  /BEGIN/i,
1694
1662
  /DELETE/i,
@@ -1706,7 +1674,7 @@ class TransactionTest < ActiveRecord::TestCase
1706
1674
  # SQL Server does not have query for release_savepoint.
1707
1675
  coerce_tests! :test_nested_transactions_skip_excess_savepoints
1708
1676
  def test_nested_transactions_skip_excess_savepoints_coerced
1709
- capture_sql do
1677
+ actual_queries = capture_sql(include_schema: true) do
1710
1678
  # RealTransaction (begin..commit)
1711
1679
  Topic.transaction(requires_new: true) do
1712
1680
  # ResetParentTransaction (no queries)
@@ -1724,8 +1692,6 @@ class TransactionTest < ActiveRecord::TestCase
1724
1692
  end
1725
1693
  end
1726
1694
 
1727
- actual_queries = ActiveRecord::SQLCounter.log_all
1728
-
1729
1695
  expected_queries = [
1730
1696
  /BEGIN/i,
1731
1697
  /DELETE/i,
@@ -1910,12 +1876,12 @@ module ActiveRecord
1910
1876
  # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
1911
1877
  coerce_tests! :test_statement_cache_values_differ
1912
1878
  def test_statement_cache_values_differ_coerced
1913
- Book.connection.remove_index(:books, column: [:author_id, :name])
1879
+ Book.lease_connection.remove_index(:books, column: [:author_id, :name])
1914
1880
 
1915
1881
  original_test_statement_cache_values_differ
1916
1882
  ensure
1917
1883
  Book.where(author_id: nil, name: 'my book').delete_all
1918
- Book.connection.add_index(:books, [:author_id, :name], unique: true)
1884
+ Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
1919
1885
  end
1920
1886
  end
1921
1887
  end
@@ -2145,14 +2111,14 @@ class RelationMergingTest < ActiveRecord::TestCase
2145
2111
 
2146
2112
  non_mary_and_bob = Author.where.not(id: [mary, bob])
2147
2113
 
2148
- author_id = Author.connection.quote_table_name("authors.id")
2149
- 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
2150
2116
  assert_equal [david], non_mary_and_bob.merge(non_mary_and_bob)
2151
2117
  end
2152
2118
 
2153
2119
  only_david = Author.where("#{author_id} IN (?)", david)
2154
2120
 
2155
- assert_sql(/WHERE \(#{Regexp.escape(author_id)} IN \(1\)\)\z/) do
2121
+ assert_queries_match(/WHERE \(#{Regexp.escape(author_id)} IN \(@\d\)\)/) do
2156
2122
  assert_equal [david], only_david.merge(only_david)
2157
2123
  end
2158
2124
  end
@@ -2172,56 +2138,56 @@ class EnumTest < ActiveRecord::TestCase
2172
2138
  # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
2173
2139
  coerce_tests! %r{enums are distinct per class}
2174
2140
  test "enums are distinct per class coerced" do
2175
- Book.connection.remove_index(:books, column: [:author_id, :name])
2141
+ Book.lease_connection.remove_index(:books, column: [:author_id, :name])
2176
2142
 
2177
2143
  send(:'original_enums are distinct per class')
2178
2144
  ensure
2179
2145
  Book.where(author_id: nil, name: nil).delete_all
2180
- Book.connection.add_index(:books, [:author_id, :name], unique: true)
2146
+ Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
2181
2147
  end
2182
2148
 
2183
2149
  # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
2184
2150
  coerce_tests! %r{creating new objects with enum scopes}
2185
2151
  test "creating new objects with enum scopes coerced" do
2186
- Book.connection.remove_index(:books, column: [:author_id, :name])
2152
+ Book.lease_connection.remove_index(:books, column: [:author_id, :name])
2187
2153
 
2188
2154
  send(:'original_creating new objects with enum scopes')
2189
2155
  ensure
2190
2156
  Book.where(author_id: nil, name: nil).delete_all
2191
- Book.connection.add_index(:books, [:author_id, :name], unique: true)
2157
+ Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
2192
2158
  end
2193
2159
 
2194
2160
  # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
2195
2161
  coerce_tests! %r{enums are inheritable}
2196
2162
  test "enums are inheritable coerced" do
2197
- Book.connection.remove_index(:books, column: [:author_id, :name])
2163
+ Book.lease_connection.remove_index(:books, column: [:author_id, :name])
2198
2164
 
2199
2165
  send(:'original_enums are inheritable')
2200
2166
  ensure
2201
2167
  Book.where(author_id: nil, name: nil).delete_all
2202
- Book.connection.add_index(:books, [:author_id, :name], unique: true)
2168
+ Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
2203
2169
  end
2204
2170
 
2205
2171
  # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
2206
2172
  coerce_tests! %r{declare multiple enums at a time}
2207
2173
  test "declare multiple enums at a time coerced" do
2208
- Book.connection.remove_index(:books, column: [:author_id, :name])
2174
+ Book.lease_connection.remove_index(:books, column: [:author_id, :name])
2209
2175
 
2210
2176
  send(:'original_declare multiple enums at a time')
2211
2177
  ensure
2212
2178
  Book.where(author_id: nil, name: nil).delete_all
2213
- Book.connection.add_index(:books, [:author_id, :name], unique: true)
2179
+ Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
2214
2180
  end
2215
2181
 
2216
2182
  # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
2217
2183
  coerce_tests! %r{serializable\? with large number label}
2218
2184
  test "serializable? with large number label coerced" do
2219
- Book.connection.remove_index(:books, column: [:author_id, :name])
2185
+ Book.lease_connection.remove_index(:books, column: [:author_id, :name])
2220
2186
 
2221
2187
  send(:'original_serializable\? with large number label')
2222
2188
  ensure
2223
2189
  Book.where(author_id: nil, name: nil).delete_all
2224
- Book.connection.add_index(:books, [:author_id, :name], unique: true)
2190
+ Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
2225
2191
  end
2226
2192
  end
2227
2193
 
@@ -2234,18 +2200,6 @@ class QueryCacheExpiryTest < ActiveRecord::TestCase
2234
2200
  Task.cache { Task.insert({ starting: Time.now }) }
2235
2201
  end
2236
2202
 
2237
- assert_called(ActiveRecord::Base.connection, :clear_query_cache, times: 2) do
2238
- Task.cache { Task.insert_all!([{ starting: Time.now }]) }
2239
- end
2240
-
2241
- assert_called(ActiveRecord::Base.connection, :clear_query_cache, times: 2) do
2242
- Task.cache { Task.insert!({ starting: Time.now }) }
2243
- end
2244
-
2245
- assert_called(ActiveRecord::Base.connection, :clear_query_cache, times: 2) do
2246
- Task.cache { Task.insert_all!([{ starting: Time.now }]) }
2247
- end
2248
-
2249
2203
  assert_raises(ArgumentError, /does not support upsert/) do
2250
2204
  Task.cache { Task.upsert({ starting: Time.now }) }
2251
2205
  end
@@ -2253,6 +2207,16 @@ class QueryCacheExpiryTest < ActiveRecord::TestCase
2253
2207
  assert_raises(ArgumentError, /does not support upsert/) do
2254
2208
  Task.cache { Task.upsert_all([{ starting: Time.now }]) }
2255
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
2256
2220
  end
2257
2221
  end
2258
2222
 
@@ -2274,7 +2238,7 @@ class EagerLoadingTooManyIdsTest < ActiveRecord::TestCase
2274
2238
 
2275
2239
  # Perform test
2276
2240
  citation_count = Citation.count
2277
- assert_sql(/WHERE \[citations\]\.\[id\] IN \(0, 1/) do
2241
+ assert_queries_match(/WHERE \[citations\]\.\[id\] IN \(0, 1/) do
2278
2242
  assert_equal citation_count, Citation.eager_load(:citations).offset(0).size
2279
2243
  end
2280
2244
  end
@@ -2308,7 +2272,7 @@ class MarshalSerializationTest < ActiveRecord::TestCase
2308
2272
  undef_method :marshal_fixture_path
2309
2273
  def marshal_fixture_path(file_name)
2310
2274
  File.expand_path(
2311
- "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",
2312
2276
  ARTest::SQLServer.test_root_sqlserver
2313
2277
  )
2314
2278
  end
@@ -2359,7 +2323,7 @@ class PreloaderTest < ActiveRecord::TestCase
2359
2323
  assert_equal 2, sql.size
2360
2324
  preload_sql = sql.last
2361
2325
 
2362
- c = Cpk::OrderAgreement.connection
2326
+ c = Cpk::OrderAgreement.lease_connection
2363
2327
  order_id_column = Regexp.escape(c.quote_table_name("cpk_order_agreements.order_id"))
2364
2328
  order_id_constraint = /#{order_id_column} = @0.*@0 = \d+$/
2365
2329
  expectation = /SELECT.*WHERE.* #{order_id_constraint}/
@@ -2383,7 +2347,7 @@ class PreloaderTest < ActiveRecord::TestCase
2383
2347
  assert_equal 2, sql.size
2384
2348
  preload_sql = sql.last
2385
2349
 
2386
- c = Cpk::Order.connection
2350
+ c = Cpk::Order.lease_connection
2387
2351
  order_id = Regexp.escape(c.quote_table_name("cpk_orders.id"))
2388
2352
  order_constraint = /#{order_id} = @0.*@0 = \d+$/
2389
2353
  expectation = /SELECT.*WHERE.* #{order_constraint}/
@@ -2393,20 +2357,6 @@ class PreloaderTest < ActiveRecord::TestCase
2393
2357
  end
2394
2358
  end
2395
2359
 
2396
- class BasePreventWritesTest < ActiveRecord::TestCase
2397
- # SQL Server does not have query for release_savepoint
2398
- coerce_tests! %r{an empty transaction does not raise if preventing writes}
2399
- test "an empty transaction does not raise if preventing writes coerced" do
2400
- ActiveRecord::Base.while_preventing_writes do
2401
- assert_queries(1, ignore_none: true) do
2402
- Bird.transaction do
2403
- ActiveRecord::Base.connection.materialize_transactions
2404
- end
2405
- end
2406
- end
2407
- end
2408
- end
2409
-
2410
2360
  class MigratorTest < ActiveRecord::TestCase
2411
2361
  # Test fails on Windows AppVeyor CI for unknown reason.
2412
2362
  coerce_tests! :test_migrator_db_has_no_schema_migrations_table if RbConfig::CONFIG["host_os"] =~ /mswin|mingw/
@@ -2422,45 +2372,45 @@ class FieldOrderedValuesTest < ActiveRecord::TestCase
2422
2372
  # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
2423
2373
  coerce_tests! :test_in_order_of_with_enums_values
2424
2374
  def test_in_order_of_with_enums_values_coerced
2425
- Book.connection.remove_index(:books, column: [:author_id, :name])
2375
+ Book.lease_connection.remove_index(:books, column: [:author_id, :name])
2426
2376
 
2427
2377
  original_test_in_order_of_with_enums_values
2428
2378
  ensure
2429
2379
  Book.where(author_id: nil, name: nil).delete_all
2430
- Book.connection.add_index(:books, [:author_id, :name], unique: true)
2380
+ Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
2431
2381
  end
2432
2382
 
2433
2383
  # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
2434
2384
  coerce_tests! :test_in_order_of_with_string_column
2435
2385
  def test_in_order_of_with_string_column_coerced
2436
- Book.connection.remove_index(:books, column: [:author_id, :name])
2386
+ Book.lease_connection.remove_index(:books, column: [:author_id, :name])
2437
2387
 
2438
2388
  original_test_in_order_of_with_string_column
2439
2389
  ensure
2440
2390
  Book.where(author_id: nil, name: nil).delete_all
2441
- Book.connection.add_index(:books, [:author_id, :name], unique: true)
2391
+ Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
2442
2392
  end
2443
2393
 
2444
2394
  # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
2445
2395
  coerce_tests! :test_in_order_of_with_enums_keys
2446
2396
  def test_in_order_of_with_enums_keys_coerced
2447
- Book.connection.remove_index(:books, column: [:author_id, :name])
2397
+ Book.lease_connection.remove_index(:books, column: [:author_id, :name])
2448
2398
 
2449
2399
  original_test_in_order_of_with_enums_keys
2450
2400
  ensure
2451
2401
  Book.where(author_id: nil, name: nil).delete_all
2452
- Book.connection.add_index(:books, [:author_id, :name], unique: true)
2402
+ Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
2453
2403
  end
2454
2404
 
2455
2405
  # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
2456
2406
  coerce_tests! :test_in_order_of_with_nil
2457
2407
  def test_in_order_of_with_nil_coerced
2458
- Book.connection.remove_index(:books, column: [:author_id, :name])
2408
+ Book.lease_connection.remove_index(:books, column: [:author_id, :name])
2459
2409
 
2460
2410
  original_test_in_order_of_with_nil
2461
2411
  ensure
2462
2412
  Book.where(author_id: nil, name: nil).delete_all
2463
- Book.connection.add_index(:books, [:author_id, :name], unique: true)
2413
+ Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
2464
2414
  end
2465
2415
  end
2466
2416
 
@@ -2470,7 +2420,7 @@ class QueryLogsTest < ActiveRecord::TestCase
2470
2420
  coerce_tests! :test_sql_commenter_format
2471
2421
  def test_sql_commenter_format_coerced
2472
2422
  ActiveRecord::QueryLogs.update_formatter(:sqlcommenter)
2473
- assert_sql(%r{/\*application=''active_record''\*/}) do
2423
+ assert_queries_match(%r{/\*application=''active_record''\*/}) do
2474
2424
  Dashboard.first
2475
2425
  end
2476
2426
  end
@@ -2485,7 +2435,7 @@ class QueryLogsTest < ActiveRecord::TestCase
2485
2435
  { tracestate: "congo=t61rcWkgMzE,rojo=00f067aa0ba902b7", custom_proc: -> { "Joe's Shack" } },
2486
2436
  ]
2487
2437
 
2488
- 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
2489
2439
  Dashboard.first
2490
2440
  end
2491
2441
  end
@@ -2500,7 +2450,7 @@ class QueryLogsTest < ActiveRecord::TestCase
2500
2450
  { custom_proc: -> { 1234 } },
2501
2451
  ]
2502
2452
 
2503
- assert_sql(%r{custom_proc=''1234''\*/}) do
2453
+ assert_queries_match(%r{custom_proc=''1234''\*/}) do
2504
2454
  Dashboard.first
2505
2455
  end
2506
2456
  end
@@ -2519,7 +2469,7 @@ class QueryLogsTest < ActiveRecord::TestCase
2519
2469
  },
2520
2470
  ]
2521
2471
 
2522
- assert_sql(%r{custom_proc=''Joe%27s%20Shack'',string=''value'',tracestate=''congo%3Dt61rcWkgMzE%2Crojo%3D00f067aa0ba902b7''\*/}) do
2472
+ assert_queries_match(%r{custom_proc=''Joe%27s%20Shack'',string=''value'',tracestate=''congo%3Dt61rcWkgMzE%2Crojo%3D00f067aa0ba902b7''\*/}) do
2523
2473
  Dashboard.first
2524
2474
  end
2525
2475
  end
@@ -2529,7 +2479,7 @@ class QueryLogsTest < ActiveRecord::TestCase
2529
2479
  def test_invalid_encoding_query_coerced
2530
2480
  ActiveRecord::QueryLogs.tags = [ :application ]
2531
2481
  assert_raises ActiveRecord::StatementInvalid do
2532
- ActiveRecord::Base.connection.execute "select 1 as '\xFF'"
2482
+ ActiveRecord::Base.lease_connection.execute "select 1 as '\xFF'"
2533
2483
  end
2534
2484
  end
2535
2485
  end
@@ -2686,3 +2636,125 @@ module ActiveRecord
2686
2636
  end
2687
2637
  end
2688
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
+