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