activerecord-sqlserver-adapter 7.1.7 → 7.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|