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