activerecord-sqlserver-adapter 7.2.9 → 8.0.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/.github/workflows/ci.yml +10 -6
- data/CHANGELOG.md +5 -67
- data/Dockerfile.ci +1 -1
- data/Gemfile +2 -0
- data/README.md +16 -17
- data/VERSION +1 -1
- data/activerecord-sqlserver-adapter.gemspec +2 -2
- data/docker-compose.ci.yml +0 -1
- data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +50 -56
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +129 -118
- data/lib/active_record/connection_adapters/sqlserver/showplan.rb +1 -0
- data/lib/active_record/connection_adapters/sqlserver/type/time.rb +3 -2
- data/lib/active_record/connection_adapters/sqlserver/utils.rb +0 -4
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +17 -27
- data/test/cases/adapter_test_sqlserver.rb +36 -38
- data/test/cases/coerced_tests.rb +153 -60
- data/test/cases/helper_sqlserver.rb +0 -8
- data/test/cases/optimizer_hints_test_sqlserver.rb +1 -2
- data/test/cases/schema_test_sqlserver.rb +0 -6
- data/test/cases/showplan_test_sqlserver.rb +2 -2
- data/test/cases/specific_schema_test_sqlserver.rb +6 -6
- data/test/cases/view_test_sqlserver.rb +3 -9
- data/test/support/query_assertions.rb +0 -22
- metadata +11 -15
- data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +0 -29
- data/test/cases/temp_test_sqlserver.rb +0 -9
- data/test/cases/temporary_table_test_sqlserver.rb +0 -19
- data/test/fixtures/sst_customers_view.yml +0 -6
data/test/cases/coerced_tests.rb
CHANGED
|
@@ -248,7 +248,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
|
|
|
248
248
|
def test_belongs_to_coerced
|
|
249
249
|
client = Client.find(3)
|
|
250
250
|
first_firm = companies(:first_firm)
|
|
251
|
-
assert_queries_match(/FETCH NEXT @(\d) ROWS ONLY/) do
|
|
251
|
+
assert_queries_match(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do
|
|
252
252
|
assert_equal first_firm, client.firm
|
|
253
253
|
assert_equal first_firm.name, client.firm.name
|
|
254
254
|
end
|
|
@@ -257,6 +257,21 @@ end
|
|
|
257
257
|
|
|
258
258
|
module ActiveRecord
|
|
259
259
|
class BindParameterTest < ActiveRecord::TestCase
|
|
260
|
+
# Same as original coerced test except log is found using `EXEC sp_executesql` wrapper.
|
|
261
|
+
coerce_tests! :test_binds_are_logged
|
|
262
|
+
def test_binds_are_logged_coerced
|
|
263
|
+
sub = Arel::Nodes::BindParam.new(1)
|
|
264
|
+
binds = [Relation::QueryAttribute.new("id", 1, Type::Value.new)]
|
|
265
|
+
sql = "select * from topics where id = #{sub.to_sql}"
|
|
266
|
+
|
|
267
|
+
@connection.exec_query(sql, "SQL", binds)
|
|
268
|
+
|
|
269
|
+
logged_sql = "EXEC sp_executesql N'#{sql}', N'#{sub.to_sql} int', #{sub.to_sql} = 1"
|
|
270
|
+
message = @subscriber.calls.find { |args| args[4][:sql] == logged_sql }
|
|
271
|
+
|
|
272
|
+
assert_equal binds, message[4][:binds]
|
|
273
|
+
end
|
|
274
|
+
|
|
260
275
|
# SQL Server adapter does not use a statement cache as query plans are already reused using `EXEC sp_executesql`.
|
|
261
276
|
coerce_tests! :test_statement_cache
|
|
262
277
|
coerce_tests! :test_statement_cache_with_query_cache
|
|
@@ -264,6 +279,55 @@ module ActiveRecord
|
|
|
264
279
|
coerce_tests! :test_statement_cache_with_find_by
|
|
265
280
|
coerce_tests! :test_statement_cache_with_in_clause
|
|
266
281
|
coerce_tests! :test_statement_cache_with_sql_string_literal
|
|
282
|
+
|
|
283
|
+
# Same as original coerced test except prepared statements include `EXEC sp_executesql` wrapper.
|
|
284
|
+
coerce_tests! :test_bind_params_to_sql_with_prepared_statements, :test_bind_params_to_sql_with_unprepared_statements
|
|
285
|
+
def test_bind_params_to_sql_with_prepared_statements_coerced
|
|
286
|
+
assert_bind_params_to_sql_coerced(prepared: true)
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def test_bind_params_to_sql_with_unprepared_statements_coerced
|
|
290
|
+
@connection.unprepared_statement do
|
|
291
|
+
assert_bind_params_to_sql_coerced(prepared: false)
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
private
|
|
296
|
+
|
|
297
|
+
def assert_bind_params_to_sql_coerced(prepared:)
|
|
298
|
+
table = Author.quoted_table_name
|
|
299
|
+
pk = "#{table}.#{Author.quoted_primary_key}"
|
|
300
|
+
|
|
301
|
+
# prepared_statements: true
|
|
302
|
+
#
|
|
303
|
+
# EXEC sp_executesql N'SELECT [authors].* FROM [authors] WHERE [authors].[id] IN (@0, @1, @2) OR [authors].[id] IS NULL)', N'@0 bigint, @1 bigint, @2 bigint', @0 = 1, @1 = 2, @2 = 3
|
|
304
|
+
#
|
|
305
|
+
# prepared_statements: false
|
|
306
|
+
#
|
|
307
|
+
# SELECT [authors].* FROM [authors] WHERE ([authors].[id] IN (1, 2, 3) OR [authors].[id] IS NULL)
|
|
308
|
+
#
|
|
309
|
+
sql_unprepared = "SELECT #{table}.* FROM #{table} WHERE (#{pk} IN (#{bind_params(1..3)}) OR #{pk} IS NULL)"
|
|
310
|
+
sql_prepared = "EXEC sp_executesql N'SELECT #{table}.* FROM #{table} WHERE (#{pk} IN (#{bind_params(1..3)}) OR #{pk} IS NULL)', N'@0 bigint, @1 bigint, @2 bigint', @0 = 1, @1 = 2, @2 = 3"
|
|
311
|
+
|
|
312
|
+
authors = Author.where(id: [1, 2, 3, nil])
|
|
313
|
+
assert_equal sql_unprepared, @connection.to_sql(authors.arel)
|
|
314
|
+
assert_queries_match(prepared ? sql_prepared : sql_unprepared) { assert_equal 3, authors.length }
|
|
315
|
+
|
|
316
|
+
# prepared_statements: true
|
|
317
|
+
#
|
|
318
|
+
# EXEC sp_executesql N'SELECT [authors].* FROM [authors] WHERE [authors].[id] IN (@0, @1, @2)', N'@0 bigint, @1 bigint, @2 bigint', @0 = 1, @1 = 2, @2 = 3
|
|
319
|
+
#
|
|
320
|
+
# prepared_statements: false
|
|
321
|
+
#
|
|
322
|
+
# SELECT [authors].* FROM [authors] WHERE [authors].[id] IN (1, 2, 3)
|
|
323
|
+
#
|
|
324
|
+
sql_unprepared = "SELECT #{table}.* FROM #{table} WHERE #{pk} IN (#{bind_params(1..3)})"
|
|
325
|
+
sql_prepared = "EXEC sp_executesql N'SELECT #{table}.* FROM #{table} WHERE #{pk} IN (#{bind_params(1..3)})', N'@0 bigint, @1 bigint, @2 bigint', @0 = 1, @1 = 2, @2 = 3"
|
|
326
|
+
|
|
327
|
+
authors = Author.where(id: [1, 2, 3, 9223372036854775808])
|
|
328
|
+
assert_equal sql_unprepared, @connection.to_sql(authors.arel)
|
|
329
|
+
assert_queries_match(prepared ? sql_prepared : sql_unprepared) { assert_equal 3, authors.length }
|
|
330
|
+
end
|
|
267
331
|
end
|
|
268
332
|
end
|
|
269
333
|
|
|
@@ -312,18 +376,6 @@ module ActiveRecord
|
|
|
312
376
|
end
|
|
313
377
|
|
|
314
378
|
class CalculationsTest < ActiveRecord::TestCase
|
|
315
|
-
# SELECT columns must be in the GROUP clause.
|
|
316
|
-
coerce_tests! :test_should_count_with_group_by_qualified_name_on_loaded
|
|
317
|
-
def test_should_count_with_group_by_qualified_name_on_loaded_coerced
|
|
318
|
-
accounts = Account.group("accounts.id").select("accounts.id")
|
|
319
|
-
expected = { 1 => 1, 2 => 1, 3 => 1, 4 => 1, 5 => 1, 6 => 1 }
|
|
320
|
-
assert_not_predicate accounts, :loaded?
|
|
321
|
-
assert_equal expected, accounts.count
|
|
322
|
-
accounts.load
|
|
323
|
-
assert_predicate accounts, :loaded?
|
|
324
|
-
assert_equal expected, accounts.count(:id)
|
|
325
|
-
end
|
|
326
|
-
|
|
327
379
|
# Fix randomly failing test. The loading of the model's schema was affecting the test.
|
|
328
380
|
coerce_tests! :test_offset_is_kept
|
|
329
381
|
def test_offset_is_kept_coerced
|
|
@@ -444,7 +496,7 @@ class CalculationsTest < ActiveRecord::TestCase
|
|
|
444
496
|
def test_limit_is_kept_coerced
|
|
445
497
|
queries = capture_sql { Account.limit(1).count }
|
|
446
498
|
assert_equal 1, queries.length
|
|
447
|
-
assert_match(/ORDER BY \[accounts\]\.\[id\] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/, queries.first)
|
|
499
|
+
assert_match(/ORDER BY \[accounts\]\.\[id\] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/, queries.first)
|
|
448
500
|
end
|
|
449
501
|
|
|
450
502
|
# Match SQL Server limit implementation
|
|
@@ -452,7 +504,7 @@ class CalculationsTest < ActiveRecord::TestCase
|
|
|
452
504
|
def test_limit_with_offset_is_kept_coerced
|
|
453
505
|
queries = capture_sql { Account.limit(1).offset(1).count }
|
|
454
506
|
assert_equal 1, queries.length
|
|
455
|
-
assert_match(/ORDER BY \[accounts\]\.\[id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY/, queries.first)
|
|
507
|
+
assert_match(/ORDER BY \[accounts\]\.\[id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY.*@0 = 1, @1 = 1/, queries.first)
|
|
456
508
|
end
|
|
457
509
|
|
|
458
510
|
# SQL Server needs an alias for the calculated column
|
|
@@ -468,6 +520,9 @@ class CalculationsTest < ActiveRecord::TestCase
|
|
|
468
520
|
|
|
469
521
|
# SELECT columns must be in the GROUP clause. Since since `ids` only selects the primary key you cannot perform this query in SQL Server.
|
|
470
522
|
coerce_tests! :test_ids_with_includes_and_non_primary_key_order
|
|
523
|
+
|
|
524
|
+
# To limit the results in SQL Server we use `FETCH NEXT @0 ROWS ONLY` instead of `LIMIT @0`. To use `FETCH NEXT` an order must be provided.
|
|
525
|
+
coerce_tests! :test_no_order_by_when_counting_all
|
|
471
526
|
end
|
|
472
527
|
|
|
473
528
|
module ActiveRecord
|
|
@@ -916,9 +971,9 @@ class FinderTest < ActiveRecord::TestCase
|
|
|
916
971
|
# Assert SQL Server limit implementation
|
|
917
972
|
coerce_tests! :test_take_and_first_and_last_with_integer_should_use_sql_limit
|
|
918
973
|
def test_take_and_first_and_last_with_integer_should_use_sql_limit_coerced
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
974
|
+
assert_queries_match(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.* @0 = 3/) { Topic.take(3).entries }
|
|
975
|
+
assert_queries_match(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.* @0 = 2/) { Topic.first(2).entries }
|
|
976
|
+
assert_queries_match(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.* @0 = 5/) { Topic.last(5).entries }
|
|
922
977
|
end
|
|
923
978
|
|
|
924
979
|
# This fails only when run in the full test suite task. Just taking it out of the mix.
|
|
@@ -949,7 +1004,7 @@ class FinderTest < ActiveRecord::TestCase
|
|
|
949
1004
|
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
|
950
1005
|
coerce_tests! :test_include_on_unloaded_relation_with_match
|
|
951
1006
|
def test_include_on_unloaded_relation_with_match_coerced
|
|
952
|
-
assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY/) do
|
|
1007
|
+
assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do
|
|
953
1008
|
assert_equal true, Customer.where(name: "David").include?(customers(:david))
|
|
954
1009
|
end
|
|
955
1010
|
end
|
|
@@ -957,7 +1012,7 @@ class FinderTest < ActiveRecord::TestCase
|
|
|
957
1012
|
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
|
958
1013
|
coerce_tests! :test_include_on_unloaded_relation_without_match
|
|
959
1014
|
def test_include_on_unloaded_relation_without_match_coerced
|
|
960
|
-
assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY/) do
|
|
1015
|
+
assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do
|
|
961
1016
|
assert_equal false, Customer.where(name: "David").include?(customers(:mary))
|
|
962
1017
|
end
|
|
963
1018
|
end
|
|
@@ -965,7 +1020,7 @@ class FinderTest < ActiveRecord::TestCase
|
|
|
965
1020
|
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
|
966
1021
|
coerce_tests! :test_member_on_unloaded_relation_with_match
|
|
967
1022
|
def test_member_on_unloaded_relation_with_match_coerced
|
|
968
|
-
assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY/) do
|
|
1023
|
+
assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do
|
|
969
1024
|
assert_equal true, Customer.where(name: "David").member?(customers(:david))
|
|
970
1025
|
end
|
|
971
1026
|
end
|
|
@@ -973,7 +1028,7 @@ class FinderTest < ActiveRecord::TestCase
|
|
|
973
1028
|
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
|
974
1029
|
coerce_tests! :test_member_on_unloaded_relation_without_match
|
|
975
1030
|
def test_member_on_unloaded_relation_without_match_coerced
|
|
976
|
-
assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY/) do
|
|
1031
|
+
assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do
|
|
977
1032
|
assert_equal false, Customer.where(name: "David").member?(customers(:mary))
|
|
978
1033
|
end
|
|
979
1034
|
end
|
|
@@ -988,7 +1043,7 @@ class FinderTest < ActiveRecord::TestCase
|
|
|
988
1043
|
assert_equal topics(:third), Topic.last
|
|
989
1044
|
|
|
990
1045
|
c = Topic.lease_connection
|
|
991
|
-
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/i) {
|
|
1046
|
+
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) {
|
|
992
1047
|
Topic.last
|
|
993
1048
|
}
|
|
994
1049
|
ensure
|
|
@@ -1002,7 +1057,7 @@ class FinderTest < ActiveRecord::TestCase
|
|
|
1002
1057
|
Topic.implicit_order_column = "id"
|
|
1003
1058
|
|
|
1004
1059
|
c = Topic.lease_connection
|
|
1005
|
-
assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/i) {
|
|
1060
|
+
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) {
|
|
1006
1061
|
Topic.last
|
|
1007
1062
|
}
|
|
1008
1063
|
ensure
|
|
@@ -1017,7 +1072,7 @@ class FinderTest < ActiveRecord::TestCase
|
|
|
1017
1072
|
|
|
1018
1073
|
c = NonPrimaryKey.lease_connection
|
|
1019
1074
|
|
|
1020
|
-
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/i) {
|
|
1075
|
+
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) {
|
|
1021
1076
|
NonPrimaryKey.last
|
|
1022
1077
|
}
|
|
1023
1078
|
ensure
|
|
@@ -1027,7 +1082,7 @@ class FinderTest < ActiveRecord::TestCase
|
|
|
1027
1082
|
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
|
1028
1083
|
coerce_tests! :test_member_on_unloaded_relation_with_composite_primary_key
|
|
1029
1084
|
def test_member_on_unloaded_relation_with_composite_primary_key_coerced
|
|
1030
|
-
assert_queries_match(/1 AS one.* FETCH NEXT @(\d) ROWS ONLY/) do
|
|
1085
|
+
assert_queries_match(/1 AS one.* FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/) do
|
|
1031
1086
|
book = cpk_books(:cpk_great_author_first_book)
|
|
1032
1087
|
assert Cpk::Book.where(title: "The first book").member?(book)
|
|
1033
1088
|
end
|
|
@@ -1042,7 +1097,7 @@ class FinderTest < ActiveRecord::TestCase
|
|
|
1042
1097
|
quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color"))
|
|
1043
1098
|
quoted_descrption = Regexp.escape(c.quote_table_name("clothing_items.description"))
|
|
1044
1099
|
|
|
1045
|
-
assert_queries_match(/ORDER BY #{quoted_descrption} ASC, #{quoted_type} ASC, #{quoted_color} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY/i) do
|
|
1100
|
+
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
|
|
1046
1101
|
assert_kind_of ClothingItem, ClothingItem.first
|
|
1047
1102
|
end
|
|
1048
1103
|
ensure
|
|
@@ -1056,7 +1111,7 @@ class FinderTest < ActiveRecord::TestCase
|
|
|
1056
1111
|
quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type"))
|
|
1057
1112
|
quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color"))
|
|
1058
1113
|
|
|
1059
|
-
assert_queries_match(/ORDER BY #{quoted_type} DESC, #{quoted_color} DESC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY/i) do
|
|
1114
|
+
assert_queries_match(/ORDER BY #{quoted_type} DESC, #{quoted_color} DESC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do
|
|
1060
1115
|
assert_kind_of ClothingItem, ClothingItem.last
|
|
1061
1116
|
end
|
|
1062
1117
|
end
|
|
@@ -1068,7 +1123,7 @@ class FinderTest < ActiveRecord::TestCase
|
|
|
1068
1123
|
quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type"))
|
|
1069
1124
|
quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color"))
|
|
1070
1125
|
|
|
1071
|
-
assert_queries_match(/ORDER BY #{quoted_type} ASC, #{quoted_color} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY/i) do
|
|
1126
|
+
assert_queries_match(/ORDER BY #{quoted_type} ASC, #{quoted_color} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do
|
|
1072
1127
|
assert_kind_of ClothingItem, ClothingItem.first
|
|
1073
1128
|
end
|
|
1074
1129
|
end
|
|
@@ -1081,7 +1136,7 @@ class FinderTest < ActiveRecord::TestCase
|
|
|
1081
1136
|
quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type"))
|
|
1082
1137
|
quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color"))
|
|
1083
1138
|
|
|
1084
|
-
assert_queries_match(/ORDER BY #{quoted_color} ASC, #{quoted_type} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY/i) do
|
|
1139
|
+
assert_queries_match(/ORDER BY #{quoted_color} ASC, #{quoted_type} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do
|
|
1085
1140
|
assert_kind_of ClothingItem, ClothingItem.first
|
|
1086
1141
|
end
|
|
1087
1142
|
ensure
|
|
@@ -1091,7 +1146,7 @@ class FinderTest < ActiveRecord::TestCase
|
|
|
1091
1146
|
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
|
1092
1147
|
coerce_tests! :test_include_on_unloaded_relation_with_composite_primary_key
|
|
1093
1148
|
def test_include_on_unloaded_relation_with_composite_primary_key_coerced
|
|
1094
|
-
assert_queries_match(/1 AS one.*OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY/) do
|
|
1149
|
+
assert_queries_match(/1 AS one.*OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/) do
|
|
1095
1150
|
book = cpk_books(:cpk_great_author_first_book)
|
|
1096
1151
|
assert Cpk::Book.where(title: "The first book").include?(book)
|
|
1097
1152
|
end
|
|
@@ -1101,11 +1156,11 @@ class FinderTest < ActiveRecord::TestCase
|
|
|
1101
1156
|
coerce_tests! :test_nth_to_last_with_order_uses_limit
|
|
1102
1157
|
def test_nth_to_last_with_order_uses_limit_coerced
|
|
1103
1158
|
c = Topic.lease_connection
|
|
1104
|
-
assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET @(\d) ROWS FETCH NEXT @(\d) ROWS ONLY/i) do
|
|
1159
|
+
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
|
|
1105
1160
|
Topic.second_to_last
|
|
1106
1161
|
end
|
|
1107
1162
|
|
|
1108
|
-
assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.updated_at"))} DESC OFFSET @(\d) ROWS FETCH NEXT @(\d) ROWS ONLY/i) do
|
|
1163
|
+
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
|
|
1109
1164
|
Topic.order(:updated_at).second_to_last
|
|
1110
1165
|
end
|
|
1111
1166
|
end
|
|
@@ -1153,7 +1208,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
|
|
|
1153
1208
|
def test_has_one_coerced
|
|
1154
1209
|
firm = companies(:first_firm)
|
|
1155
1210
|
first_account = Account.find(1)
|
|
1156
|
-
assert_queries_match(/FETCH NEXT @(\d) ROWS ONLY/) do
|
|
1211
|
+
assert_queries_match(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do
|
|
1157
1212
|
assert_equal first_account, firm.account
|
|
1158
1213
|
assert_equal first_account.credit_limit, firm.account.credit_limit
|
|
1159
1214
|
end
|
|
@@ -1165,7 +1220,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
|
|
|
1165
1220
|
coerce_tests! :test_has_one_through_executes_limited_query
|
|
1166
1221
|
def test_has_one_through_executes_limited_query_coerced
|
|
1167
1222
|
boring_club = clubs(:boring_club)
|
|
1168
|
-
assert_queries_match(/FETCH NEXT @(\d) ROWS ONLY/) do
|
|
1223
|
+
assert_queries_match(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do
|
|
1169
1224
|
assert_equal boring_club, @member.general_club
|
|
1170
1225
|
end
|
|
1171
1226
|
end
|
|
@@ -1285,6 +1340,20 @@ module ActiveRecord
|
|
|
1285
1340
|
def test_registering_new_handlers_for_association_coerced
|
|
1286
1341
|
assert_match %r{#{Regexp.escape(topic_title)} ~ N'rails'}i, Reply.joins(:topic).where(topics: { title: /rails/ }).to_sql
|
|
1287
1342
|
end
|
|
1343
|
+
|
|
1344
|
+
# Same as original test except string has `N` prefix to indicate unicode string.
|
|
1345
|
+
coerce_tests! :test_registering_new_handlers_for_joins
|
|
1346
|
+
def test_registering_new_handlers_for_joins_coerced
|
|
1347
|
+
Reply.belongs_to :regexp_topic, -> { where(title: /rails/) }, class_name: "Topic", foreign_key: "parent_id"
|
|
1348
|
+
|
|
1349
|
+
assert_match %r{#{Regexp.escape(quote_table_name("regexp_topic.title"))} ~ N'rails'}i, Reply.joins(:regexp_topic).references(Arel.sql("regexp_topic")).to_sql
|
|
1350
|
+
end
|
|
1351
|
+
|
|
1352
|
+
private
|
|
1353
|
+
|
|
1354
|
+
def topic_title
|
|
1355
|
+
Topic.lease_connection.quote_table_name("topics.title")
|
|
1356
|
+
end
|
|
1288
1357
|
end
|
|
1289
1358
|
end
|
|
1290
1359
|
|
|
@@ -1372,7 +1441,7 @@ class RelationTest < ActiveRecord::TestCase
|
|
|
1372
1441
|
# Find any limit via our expression.
|
|
1373
1442
|
coerce_tests! %r{relations don't load all records in #inspect}
|
|
1374
1443
|
def test_relations_dont_load_all_records_in_inspect_coerced
|
|
1375
|
-
assert_queries_match(/NEXT @0 ROWS
|
|
1444
|
+
assert_queries_match(/NEXT @0 ROWS.*@0 = \d+/) do
|
|
1376
1445
|
Post.all.inspect
|
|
1377
1446
|
end
|
|
1378
1447
|
end
|
|
@@ -2060,7 +2129,7 @@ class RelationMergingTest < ActiveRecord::TestCase
|
|
|
2060
2129
|
non_mary_and_bob = Author.where.not(id: [mary, bob])
|
|
2061
2130
|
|
|
2062
2131
|
author_id = Author.lease_connection.quote_table_name("authors.id")
|
|
2063
|
-
assert_queries_match(/WHERE #{Regexp.escape(author_id)} NOT IN \((@\d), \g<1>\)/) do
|
|
2132
|
+
assert_queries_match(/WHERE #{Regexp.escape(author_id)} NOT IN \((@\d), \g<1>\)'/) do
|
|
2064
2133
|
assert_equal [david], non_mary_and_bob.merge(non_mary_and_bob)
|
|
2065
2134
|
end
|
|
2066
2135
|
|
|
@@ -2116,17 +2185,6 @@ class EnumTest < ActiveRecord::TestCase
|
|
|
2116
2185
|
Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
|
|
2117
2186
|
end
|
|
2118
2187
|
|
|
2119
|
-
# Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
|
|
2120
|
-
coerce_tests! %r{declare multiple enums at a time}
|
|
2121
|
-
test "declare multiple enums at a time coerced" do
|
|
2122
|
-
Book.lease_connection.remove_index(:books, column: [:author_id, :name])
|
|
2123
|
-
|
|
2124
|
-
send(:'original_declare multiple enums at a time')
|
|
2125
|
-
ensure
|
|
2126
|
-
Book.where(author_id: nil, name: nil).delete_all
|
|
2127
|
-
Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
|
|
2128
|
-
end
|
|
2129
|
-
|
|
2130
2188
|
# Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
|
|
2131
2189
|
coerce_tests! %r{serializable\? with large number label}
|
|
2132
2190
|
test "serializable? with large number label coerced" do
|
|
@@ -2265,7 +2323,7 @@ class PreloaderTest < ActiveRecord::TestCase
|
|
|
2265
2323
|
|
|
2266
2324
|
c = Cpk::OrderAgreement.lease_connection
|
|
2267
2325
|
order_id_column = Regexp.escape(c.quote_table_name("cpk_order_agreements.order_id"))
|
|
2268
|
-
order_id_constraint = /#{order_id_column} = @0
|
|
2326
|
+
order_id_constraint = /#{order_id_column} = @0.*@0 = \d+$/
|
|
2269
2327
|
expectation = /SELECT.*WHERE.* #{order_id_constraint}/
|
|
2270
2328
|
|
|
2271
2329
|
assert_match(expectation, preload_sql)
|
|
@@ -2289,7 +2347,7 @@ class PreloaderTest < ActiveRecord::TestCase
|
|
|
2289
2347
|
|
|
2290
2348
|
c = Cpk::Order.lease_connection
|
|
2291
2349
|
order_id = Regexp.escape(c.quote_table_name("cpk_orders.id"))
|
|
2292
|
-
order_constraint = /#{order_id} = @0
|
|
2350
|
+
order_constraint = /#{order_id} = @0.*@0 = \d+$/
|
|
2293
2351
|
expectation = /SELECT.*WHERE.* #{order_constraint}/
|
|
2294
2352
|
|
|
2295
2353
|
assert_match(expectation, preload_sql)
|
|
@@ -2359,8 +2417,10 @@ class QueryLogsTest < ActiveRecord::TestCase
|
|
|
2359
2417
|
# SQL requires double single-quotes.
|
|
2360
2418
|
coerce_tests! :test_sql_commenter_format
|
|
2361
2419
|
def test_sql_commenter_format_coerced
|
|
2362
|
-
ActiveRecord::QueryLogs.
|
|
2363
|
-
|
|
2420
|
+
ActiveRecord::QueryLogs.tags_formatter = :sqlcommenter
|
|
2421
|
+
ActiveRecord::QueryLogs.tags = [:application]
|
|
2422
|
+
|
|
2423
|
+
assert_queries_match(%r{/\*application=''active_record''\*/}) do
|
|
2364
2424
|
Dashboard.first
|
|
2365
2425
|
end
|
|
2366
2426
|
end
|
|
@@ -2368,14 +2428,14 @@ class QueryLogsTest < ActiveRecord::TestCase
|
|
|
2368
2428
|
# SQL requires double single-quotes.
|
|
2369
2429
|
coerce_tests! :test_sqlcommenter_format_value
|
|
2370
2430
|
def test_sqlcommenter_format_value_coerced
|
|
2371
|
-
ActiveRecord::QueryLogs.
|
|
2431
|
+
ActiveRecord::QueryLogs.tags_formatter = :sqlcommenter
|
|
2372
2432
|
|
|
2373
2433
|
ActiveRecord::QueryLogs.tags = [
|
|
2374
2434
|
:application,
|
|
2375
2435
|
{ tracestate: "congo=t61rcWkgMzE,rojo=00f067aa0ba902b7", custom_proc: -> { "Joe's Shack" } },
|
|
2376
2436
|
]
|
|
2377
2437
|
|
|
2378
|
-
assert_queries_match(%r{custom_proc='Joe%27s%20Shack',tracestate='congo%3Dt61rcWkgMzE%2Crojo%3D00f067aa0ba902b7'\*/}) do
|
|
2438
|
+
assert_queries_match(%r{custom_proc=''Joe%27s%20Shack'',tracestate=''congo%3Dt61rcWkgMzE%2Crojo%3D00f067aa0ba902b7''\*/}) do
|
|
2379
2439
|
Dashboard.first
|
|
2380
2440
|
end
|
|
2381
2441
|
end
|
|
@@ -2383,14 +2443,14 @@ class QueryLogsTest < ActiveRecord::TestCase
|
|
|
2383
2443
|
# SQL requires double single-quotes.
|
|
2384
2444
|
coerce_tests! :test_sqlcommenter_format_value_string_coercible
|
|
2385
2445
|
def test_sqlcommenter_format_value_string_coercible_coerced
|
|
2386
|
-
ActiveRecord::QueryLogs.
|
|
2446
|
+
ActiveRecord::QueryLogs.tags_formatter = :sqlcommenter
|
|
2387
2447
|
|
|
2388
2448
|
ActiveRecord::QueryLogs.tags = [
|
|
2389
2449
|
:application,
|
|
2390
2450
|
{ custom_proc: -> { 1234 } },
|
|
2391
2451
|
]
|
|
2392
2452
|
|
|
2393
|
-
assert_queries_match(%r{custom_proc='1234'\*/}) do
|
|
2453
|
+
assert_queries_match(%r{custom_proc=''1234''\*/}) do
|
|
2394
2454
|
Dashboard.first
|
|
2395
2455
|
end
|
|
2396
2456
|
end
|
|
@@ -2398,7 +2458,7 @@ class QueryLogsTest < ActiveRecord::TestCase
|
|
|
2398
2458
|
# SQL requires double single-quotes.
|
|
2399
2459
|
coerce_tests! :test_sqlcommenter_format_allows_string_keys
|
|
2400
2460
|
def test_sqlcommenter_format_allows_string_keys_coerced
|
|
2401
|
-
ActiveRecord::QueryLogs.
|
|
2461
|
+
ActiveRecord::QueryLogs.tags_formatter = :sqlcommenter
|
|
2402
2462
|
|
|
2403
2463
|
ActiveRecord::QueryLogs.tags = [
|
|
2404
2464
|
:application,
|
|
@@ -2409,7 +2469,7 @@ class QueryLogsTest < ActiveRecord::TestCase
|
|
|
2409
2469
|
},
|
|
2410
2470
|
]
|
|
2411
2471
|
|
|
2412
|
-
assert_queries_match(%r{custom_proc='Joe%27s%20Shack',string='value',tracestate='congo%3Dt61rcWkgMzE%2Crojo%3D00f067aa0ba902b7'\*/}) do
|
|
2472
|
+
assert_queries_match(%r{custom_proc=''Joe%27s%20Shack'',string=''value'',tracestate=''congo%3Dt61rcWkgMzE%2Crojo%3D00f067aa0ba902b7''\*/}) do
|
|
2413
2473
|
Dashboard.first
|
|
2414
2474
|
end
|
|
2415
2475
|
end
|
|
@@ -2433,6 +2493,17 @@ class InsertAllTest < ActiveRecord::TestCase
|
|
|
2433
2493
|
result = Book.insert_all! [{ name: "Rework", author_id: 1 }], returning: Arel.sql("UPPER(INSERTED.name) as name")
|
|
2434
2494
|
assert_equal %w[ REWORK ], result.pluck("name")
|
|
2435
2495
|
end
|
|
2496
|
+
|
|
2497
|
+
# Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
|
|
2498
|
+
coerce_tests! :test_insert_with_type_casting_and_serialize_is_consistent
|
|
2499
|
+
def test_insert_with_type_casting_and_serialize_is_consistent_coerced
|
|
2500
|
+
connection.remove_index(:books, column: [:author_id, :name])
|
|
2501
|
+
|
|
2502
|
+
original_test_insert_with_type_casting_and_serialize_is_consistent
|
|
2503
|
+
ensure
|
|
2504
|
+
Book.where(author_id: nil, name: '["Array"]').delete_all
|
|
2505
|
+
Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
|
|
2506
|
+
end
|
|
2436
2507
|
end
|
|
2437
2508
|
|
|
2438
2509
|
module ActiveRecord
|
|
@@ -2595,6 +2666,28 @@ module ActiveRecord
|
|
|
2595
2666
|
end
|
|
2596
2667
|
end
|
|
2597
2668
|
|
|
2669
|
+
module ActiveRecord
|
|
2670
|
+
module ConnectionAdapters
|
|
2671
|
+
class RegistrationIsolatedTest < ActiveRecord::TestCase
|
|
2672
|
+
# SQL Server was not included in the list of available adapters in the error message.
|
|
2673
|
+
coerce_tests! %r{resolve raises if the adapter is using the pre 7.2 adapter registration API}
|
|
2674
|
+
def resolve_raises_if_the_adapter_is_using_the_pre_7_2_adapter_registration_API
|
|
2675
|
+
exception = assert_raises(ActiveRecord::AdapterNotFound) do
|
|
2676
|
+
ActiveRecord::ConnectionAdapters.resolve("fake_legacy")
|
|
2677
|
+
end
|
|
2678
|
+
|
|
2679
|
+
assert_equal(
|
|
2680
|
+
"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.",
|
|
2681
|
+
exception.message
|
|
2682
|
+
)
|
|
2683
|
+
ensure
|
|
2684
|
+
ActiveRecord::ConnectionAdapters.instance_variable_get(:@adapters).delete("fake_legacy")
|
|
2685
|
+
end
|
|
2686
|
+
end
|
|
2687
|
+
end
|
|
2688
|
+
end
|
|
2689
|
+
|
|
2690
|
+
|
|
2598
2691
|
module ActiveRecord
|
|
2599
2692
|
class TableMetadataTest < ActiveSupport::TestCase
|
|
2600
2693
|
# Adapter returns an object that is subclass of what is expected in the original test.
|
|
@@ -2633,7 +2726,7 @@ class ExplainTest < ActiveRecord::TestCase
|
|
|
2633
2726
|
def test_relation_explain_with_first_coerced
|
|
2634
2727
|
expected_query = capture_sql {
|
|
2635
2728
|
Car.all.first
|
|
2636
|
-
}.first[/(.*?) NEXT/, 1]
|
|
2729
|
+
}.first[/EXEC sp_executesql N'(.*?) NEXT/, 1]
|
|
2637
2730
|
message = Car.all.explain.first
|
|
2638
2731
|
assert_match(/^EXPLAIN/, message)
|
|
2639
2732
|
assert_match(expected_query, message)
|
|
@@ -2644,7 +2737,7 @@ class ExplainTest < ActiveRecord::TestCase
|
|
|
2644
2737
|
def test_relation_explain_with_last_coerced
|
|
2645
2738
|
expected_query = capture_sql {
|
|
2646
2739
|
Car.all.last
|
|
2647
|
-
}.first[/(.*?) NEXT/, 1]
|
|
2740
|
+
}.first[/EXEC sp_executesql N'(.*?) NEXT/, 1]
|
|
2648
2741
|
expected_query = expected_query
|
|
2649
2742
|
message = Car.all.explain.last
|
|
2650
2743
|
|
|
@@ -15,14 +15,6 @@ require "support/connection_reflection"
|
|
|
15
15
|
require "support/query_assertions"
|
|
16
16
|
require "mocha/minitest"
|
|
17
17
|
|
|
18
|
-
Minitest.after_run do
|
|
19
|
-
puts "\n\n"
|
|
20
|
-
puts "=" * 80
|
|
21
|
-
puts ActiveRecord::Base.lease_connection.send(:sqlserver_version)
|
|
22
|
-
puts "\nSQL Server Version Year: #{ActiveRecord::Base.lease_connection.get_database_version}"
|
|
23
|
-
puts "=" * 80
|
|
24
|
-
end
|
|
25
|
-
|
|
26
18
|
module ActiveSupport
|
|
27
19
|
class TestCase < ::Minitest::Test
|
|
28
20
|
include ARTest::SQLServer::CoerceableTest
|
|
@@ -29,14 +29,13 @@ class OptimizerHitsTestSQLServer < ActiveRecord::TestCase
|
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
it "support subqueries" do
|
|
32
|
-
assert_queries_match(%r{SELECT COUNT\(count_column\) FROM \(SELECT .*\) subquery_for_count OPTION \(MAXDOP 2\)}) do
|
|
32
|
+
assert_queries_match(%r{.*'SELECT COUNT\(count_column\) FROM \(SELECT .*\) subquery_for_count OPTION \(MAXDOP 2\)'.*}) do
|
|
33
33
|
companies = Company.optimizer_hints("MAXDOP 2")
|
|
34
34
|
companies = companies.select(:id).where(firm_id: [0, 1]).limit(3)
|
|
35
35
|
assert_equal 3, companies.count
|
|
36
36
|
end
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
-
|
|
40
39
|
it "support order" do
|
|
41
40
|
assert_queries_match(%r{\ASELECT .+ FROM .+ ORDER .+ OPTION .+\z}) do
|
|
42
41
|
companies = Company.optimizer_hints("LABEL='FindCompanies'")
|
|
@@ -101,11 +101,5 @@ class SchemaTestSQLServer < ActiveRecord::TestCase
|
|
|
101
101
|
assert_equal "[with].[select notation]", connection.send(:get_raw_table_name, "INSERT INTO [with].[select notation] SELECT * FROM [table_name]")
|
|
102
102
|
end
|
|
103
103
|
end
|
|
104
|
-
|
|
105
|
-
describe 'CREATE VIEW statements' do
|
|
106
|
-
it do
|
|
107
|
-
assert_equal "test_table_as", connection.send(:get_raw_table_name, "CREATE VIEW test_views ( test_table_a_id, test_table_b_id ) AS SELECT test_table_as.id as test_table_a_id, test_table_bs.id as test_table_b_id FROM (test_table_as with(nolock) LEFT JOIN test_table_bs with(nolock) ON (test_table_as.id = test_table_bs.test_table_a_id))")
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
104
|
end
|
|
111
105
|
end
|
|
@@ -28,13 +28,13 @@ class ShowplanTestSQLServer < ActiveRecord::TestCase
|
|
|
28
28
|
|
|
29
29
|
it "from array condition using index" do
|
|
30
30
|
plan = Car.where(id: [1, 2]).explain.inspect
|
|
31
|
-
_(plan).must_include "SELECT [cars].* FROM [cars] WHERE [cars].[id]"
|
|
31
|
+
_(plan).must_include "SELECT [cars].* FROM [cars] WHERE [cars].[id] IN (1, 2)"
|
|
32
32
|
_(plan).must_include "Clustered Index Seek", "make sure we do not showplan the sp_executesql"
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
it "from array condition" do
|
|
36
36
|
plan = Car.where(name: ["honda", "zyke"]).explain.inspect
|
|
37
|
-
_(plan).must_include " SELECT [cars].* FROM [cars] WHERE [cars].[name]"
|
|
37
|
+
_(plan).must_include " SELECT [cars].* FROM [cars] WHERE [cars].[name] IN (N'honda', N'zyke')"
|
|
38
38
|
_(plan).must_include "Clustered Index Scan", "make sure we do not showplan the sp_executesql"
|
|
39
39
|
end
|
|
40
40
|
end
|
|
@@ -116,16 +116,16 @@ class SpecificSchemaTestSQLServer < ActiveRecord::TestCase
|
|
|
116
116
|
end
|
|
117
117
|
end
|
|
118
118
|
# Using ActiveRecord's quoted_id feature for objects.
|
|
119
|
-
|
|
120
|
-
|
|
119
|
+
assert_queries_match(/@0 = 'T'/) { SSTestDatatypeMigration.where(char_col: value.new).first }
|
|
120
|
+
assert_queries_match(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: value.new).first }
|
|
121
121
|
# Using our custom char type data.
|
|
122
122
|
type = ActiveRecord::Type::SQLServer::Char
|
|
123
123
|
data = ActiveRecord::Type::SQLServer::Data
|
|
124
|
-
|
|
125
|
-
|
|
124
|
+
assert_queries_match(/@0 = 'T'/) { SSTestDatatypeMigration.where(char_col: data.new("T", type.new)).first }
|
|
125
|
+
assert_queries_match(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: data.new("T", type.new)).first }
|
|
126
126
|
# Taking care of everything.
|
|
127
|
-
|
|
128
|
-
|
|
127
|
+
assert_queries_match(/@0 = 'T'/) { SSTestDatatypeMigration.where(char_col: "T").first }
|
|
128
|
+
assert_queries_match(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: "T").first }
|
|
129
129
|
end
|
|
130
130
|
|
|
131
131
|
it "can update and hence properly quoted non-national char/varchar columns" do
|
|
@@ -48,17 +48,11 @@ class ViewTestSQLServer < ActiveRecord::TestCase
|
|
|
48
48
|
end
|
|
49
49
|
end
|
|
50
50
|
|
|
51
|
-
describe
|
|
52
|
-
it "
|
|
53
|
-
assert_difference("SSTestCustomersView.count",
|
|
51
|
+
describe 'identity insert' do
|
|
52
|
+
it "identity insert works with views" do
|
|
53
|
+
assert_difference("SSTestCustomersView.count", 1) do
|
|
54
54
|
SSTestCustomersView.create!(id: 5, name: "Bob")
|
|
55
|
-
SSTestCustomersView.create!(id: 6, name: "Tim")
|
|
56
55
|
end
|
|
57
56
|
end
|
|
58
|
-
|
|
59
|
-
it "creates table records through a view using fixtures" do
|
|
60
|
-
ActiveRecord::FixtureSet.create_fixtures(File.join(ARTest::SQLServer.test_root_sqlserver, "fixtures"), ["sst_customers_view"])
|
|
61
|
-
assert_equal SSTestCustomersView.all.count, 2
|
|
62
|
-
end
|
|
63
57
|
end
|
|
64
58
|
end
|
|
@@ -22,28 +22,6 @@ module ARTest
|
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
def assert_queries_and_values_match(match, bound_values=[], count: nil, &block)
|
|
26
|
-
ActiveRecord::Base.lease_connection.materialize_transactions
|
|
27
|
-
|
|
28
|
-
counter = ActiveRecord::Assertions::QueryAssertions::SQLCounter.new
|
|
29
|
-
ActiveSupport::Notifications.subscribed(counter, "sql.active_record") do
|
|
30
|
-
result = _assert_nothing_raised_or_warn("assert_queries_match", &block)
|
|
31
|
-
queries = counter.log_full
|
|
32
|
-
matched_queries = queries.select do |query, values|
|
|
33
|
-
values = values.map { |v| v.respond_to?(:quoted) ? v.quoted : v }
|
|
34
|
-
match === query && bound_values === values
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
if count
|
|
38
|
-
assert_equal count, matched_queries.size, "#{matched_queries.size} instead of #{count} queries were executed.#{count.log.empty? ? '' : "\nQueries:\n#{counter.log.join("\n")}"}"
|
|
39
|
-
else
|
|
40
|
-
assert_operator matched_queries.size, :>=, 1, "1 or more queries expected, but none were executed.#{counter.log.empty? ? '' : "\nQueries:\n#{counter.log.join("\n")}"}"
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
result
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
|
|
47
25
|
private
|
|
48
26
|
|
|
49
27
|
# Rails tests expect a save-point to be created and released. SQL Server does not release
|