activerecord-sqlserver-adapter 7.1.11 → 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 (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 -123
  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 +5 -10
  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 -27
  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 -10
  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
+