activerecord-sqlserver-adapter 7.1.11 → 7.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.devcontainer/Dockerfile +3 -3
- data/.github/workflows/ci.yml +11 -5
- data/CHANGELOG.md +5 -123
- 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 +5 -10
- 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 -27
- 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 -10
- 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
|
+
|