activerecord-sqlserver-adapter 7.0.4.0 → 7.1.0.beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +3 -3
- data/.gitignore +3 -1
- data/CHANGELOG.md +2 -69
- data/Gemfile +3 -0
- data/README.md +16 -11
- data/Rakefile +2 -6
- data/VERSION +1 -1
- data/activerecord-sqlserver-adapter.gemspec +1 -1
- data/lib/active_record/connection_adapters/sqlserver/core_ext/abstract_adapter.rb +20 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +29 -6
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +4 -4
- data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +10 -2
- data/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb +15 -3
- data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +0 -31
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +87 -131
- data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +5 -5
- data/lib/active_record/connection_adapters/sqlserver/quoting.rb +3 -2
- data/lib/active_record/connection_adapters/sqlserver/savepoints.rb +24 -0
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +71 -32
- data/lib/active_record/connection_adapters/sqlserver/showplan.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +6 -0
- data/lib/active_record/connection_adapters/sqlserver/transaction.rb +4 -6
- data/lib/active_record/connection_adapters/sqlserver/type/data.rb +10 -0
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +81 -114
- data/lib/active_record/connection_adapters/sqlserver_column.rb +1 -0
- data/lib/active_record/sqlserver_base.rb +1 -10
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +5 -2
- data/lib/arel/visitors/sqlserver.rb +0 -33
- data/test/cases/adapter_test_sqlserver.rb +8 -7
- data/test/cases/coerced_tests.rb +573 -208
- data/test/cases/column_test_sqlserver.rb +6 -6
- data/test/cases/connection_test_sqlserver.rb +3 -6
- data/test/cases/disconnected_test_sqlserver.rb +5 -8
- data/test/cases/execute_procedure_test_sqlserver.rb +1 -1
- data/test/cases/rake_test_sqlserver.rb +1 -1
- data/test/cases/schema_dumper_test_sqlserver.rb +2 -2
- data/test/cases/view_test_sqlserver.rb +46 -0
- data/test/config.yml +1 -2
- data/test/support/connection_reflection.rb +2 -8
- data/test/support/core_ext/query_cache.rb +7 -1
- data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic_associations.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/SQLServer/rails_7_1_topic.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/SQLServer/rails_7_1_topic_associations.dump +0 -0
- metadata +20 -12
data/test/cases/coerced_tests.rb
CHANGED
@@ -47,6 +47,21 @@ class UniquenessValidationTest < ActiveRecord::TestCase
|
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
50
|
+
class UniquenessValidationWithIndexTest < ActiveRecord::TestCase
|
51
|
+
# Need to explicitly set the WHERE clause to truthy.
|
52
|
+
coerce_tests! :test_partial_index
|
53
|
+
def test_partial_index_coerced
|
54
|
+
Topic.validates_uniqueness_of(:title)
|
55
|
+
@connection.add_index(:topics, :title, unique: true, where: "approved=1", name: :topics_index)
|
56
|
+
|
57
|
+
t = Topic.create!(title: "abc")
|
58
|
+
t.author_name = "John"
|
59
|
+
assert_queries(1) do
|
60
|
+
t.valid?
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
50
65
|
require "models/event"
|
51
66
|
module ActiveRecord
|
52
67
|
class AdapterTest < ActiveRecord::TestCase
|
@@ -92,12 +107,12 @@ module ActiveRecord
|
|
92
107
|
original_test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_is_called_while_preventing_writes
|
93
108
|
end
|
94
109
|
|
110
|
+
# Invalid character encoding causes `ActiveRecord::StatementInvalid` error similar to Postgres.
|
95
111
|
coerce_tests! :test_doesnt_error_when_a_select_query_has_encoding_errors
|
96
112
|
def test_doesnt_error_when_a_select_query_has_encoding_errors_coerced
|
97
113
|
ActiveRecord::Base.while_preventing_writes do
|
98
114
|
# TinyTDS fail on encoding errors.
|
99
|
-
# But at least we can assert it fails in the client and not before when trying to
|
100
|
-
# match the query.
|
115
|
+
# But at least we can assert it fails in the client and not before when trying to match the query.
|
101
116
|
assert_raises ActiveRecord::StatementInvalid do
|
102
117
|
@connection.select_all("SELECT '\xC8'")
|
103
118
|
end
|
@@ -106,38 +121,6 @@ module ActiveRecord
|
|
106
121
|
end
|
107
122
|
end
|
108
123
|
|
109
|
-
module ActiveRecord
|
110
|
-
class AdapterPreventWritesLegacyTest < ActiveRecord::TestCase
|
111
|
-
# Fix randomly failing test. The loading of the model's schema was affecting the test.
|
112
|
-
coerce_tests! :test_errors_when_an_insert_query_is_called_while_preventing_writes
|
113
|
-
def test_errors_when_an_insert_query_is_called_while_preventing_writes_coerced
|
114
|
-
Subscriber.send(:load_schema!)
|
115
|
-
original_test_errors_when_an_insert_query_is_called_while_preventing_writes
|
116
|
-
end
|
117
|
-
|
118
|
-
# Fix randomly failing test. The loading of the model's schema was affecting the test.
|
119
|
-
coerce_tests! :test_errors_when_an_insert_query_prefixed_by_a_slash_star_comment_is_called_while_preventing_writes
|
120
|
-
def test_errors_when_an_insert_query_prefixed_by_a_slash_star_comment_is_called_while_preventing_writes_coerced
|
121
|
-
Subscriber.send(:load_schema!)
|
122
|
-
original_test_errors_when_an_insert_query_prefixed_by_a_slash_star_comment_is_called_while_preventing_writes
|
123
|
-
end
|
124
|
-
|
125
|
-
# Fix randomly failing test. The loading of the model's schema was affecting the test.
|
126
|
-
coerce_tests! :test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_is_called_while_preventing_writes
|
127
|
-
def test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_is_called_while_preventing_writes_coerced
|
128
|
-
Subscriber.send(:load_schema!)
|
129
|
-
original_test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_is_called_while_preventing_writes
|
130
|
-
end
|
131
|
-
|
132
|
-
# Fix randomly failing test. The loading of the model's schema was affecting the test.
|
133
|
-
coerce_tests! :test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_containing_read_command_is_called_while_preventing_writes
|
134
|
-
def test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_containing_read_command_is_called_while_preventing_writes_coerced
|
135
|
-
Subscriber.send(:load_schema!)
|
136
|
-
original_test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_containing_read_command_is_called_while_preventing_writes
|
137
|
-
end
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
124
|
module ActiveRecord
|
142
125
|
class AdapterTestWithoutTransaction < ActiveRecord::TestCase
|
143
126
|
# SQL Server does not allow truncation of tables that are referenced by foreign key
|
@@ -513,6 +496,9 @@ class CalculationsTest < ActiveRecord::TestCase
|
|
513
496
|
|
514
497
|
# Leave it up to users to format selects/functions so HAVING works correctly.
|
515
498
|
coerce_tests! :test_having_with_strong_parameters
|
499
|
+
|
500
|
+
# SELECT columns must be in the GROUP clause. Since since `ids` only selects the primary key you cannot perform this query in SQL Server.
|
501
|
+
coerce_tests! :test_ids_with_includes_and_non_primary_key_order
|
516
502
|
end
|
517
503
|
|
518
504
|
module ActiveRecord
|
@@ -662,11 +648,11 @@ class MigrationTest < ActiveRecord::TestCase
|
|
662
648
|
end
|
663
649
|
}.new
|
664
650
|
|
665
|
-
ActiveRecord::Migrator.new(:up, [migration_a], @schema_migration, 100).migrate
|
651
|
+
ActiveRecord::Migrator.new(:up, [migration_a], @schema_migration, @internal_metadata, 100).migrate
|
666
652
|
assert_column Person, :last_name, "migration_a should have created the last_name column on people"
|
667
653
|
|
668
654
|
assert_nothing_raised do
|
669
|
-
ActiveRecord::Migrator.new(:up, [migration_b], @schema_migration, 101).migrate
|
655
|
+
ActiveRecord::Migrator.new(:up, [migration_b], @schema_migration, @internal_metadata, 101).migrate
|
670
656
|
end
|
671
657
|
ensure
|
672
658
|
Person.reset_column_information
|
@@ -676,6 +662,99 @@ class MigrationTest < ActiveRecord::TestCase
|
|
676
662
|
end
|
677
663
|
end
|
678
664
|
|
665
|
+
module ActiveRecord
|
666
|
+
class Migration
|
667
|
+
class CompatibilityTest < ActiveRecord::TestCase
|
668
|
+
# Error message depends on the database adapter.
|
669
|
+
coerce_tests! :test_create_table_on_7_0
|
670
|
+
def test_create_table_on_7_0_coerced
|
671
|
+
long_table_name = "a" * (connection.table_name_length + 1)
|
672
|
+
migration = Class.new(ActiveRecord::Migration[7.0]) {
|
673
|
+
@@long_table_name = long_table_name
|
674
|
+
def version; 100 end
|
675
|
+
def migrate(x)
|
676
|
+
create_table @@long_table_name
|
677
|
+
end
|
678
|
+
}.new
|
679
|
+
|
680
|
+
error = assert_raises(StandardError) do
|
681
|
+
ActiveRecord::Migrator.new(:up, [migration], @schema_migration, @internal_metadata).migrate
|
682
|
+
end
|
683
|
+
assert_match(/The identifier that starts with '#{long_table_name[0...-1]}' is too long/i, error.message)
|
684
|
+
ensure
|
685
|
+
connection.drop_table(long_table_name) rescue nil
|
686
|
+
end
|
687
|
+
|
688
|
+
# SQL Server truncates long table names when renaming (https://learn.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-rename-transact-sql?view=sql-server-ver16).
|
689
|
+
coerce_tests! :test_rename_table_on_7_0
|
690
|
+
def test_rename_table_on_7_0_coerced
|
691
|
+
long_table_name = "a" * (connection.table_name_length + 1)
|
692
|
+
connection.create_table(:more_testings)
|
693
|
+
|
694
|
+
migration = Class.new(ActiveRecord::Migration[7.0]) {
|
695
|
+
@@long_table_name = long_table_name
|
696
|
+
def version; 100 end
|
697
|
+
def migrate(x)
|
698
|
+
rename_table :more_testings, @@long_table_name
|
699
|
+
end
|
700
|
+
}.new
|
701
|
+
|
702
|
+
ActiveRecord::Migrator.new(:up, [migration], @schema_migration, @internal_metadata).migrate
|
703
|
+
assert connection.table_exists?(long_table_name[0...-1])
|
704
|
+
assert_not connection.table_exists?(:more_testings)
|
705
|
+
assert connection.table_exists?(long_table_name[0...-1])
|
706
|
+
ensure
|
707
|
+
connection.drop_table(:more_testings) rescue nil
|
708
|
+
connection.drop_table(long_table_name[0...-1]) rescue nil
|
709
|
+
end
|
710
|
+
|
711
|
+
# SQL Server has a different maximum index name length.
|
712
|
+
coerce_tests! :test_add_index_errors_on_too_long_name_7_0
|
713
|
+
def test_add_index_errors_on_too_long_name_7_0_coerced
|
714
|
+
long_index_name = 'a' * (connection.index_name_length + 1)
|
715
|
+
|
716
|
+
migration = Class.new(ActiveRecord::Migration[7.0]) {
|
717
|
+
@@long_index_name = long_index_name
|
718
|
+
def migrate(x)
|
719
|
+
add_column :testings, :very_long_column_name_to_test_with, :string
|
720
|
+
add_index :testings, [:foo, :bar, :very_long_column_name_to_test_with], name: @@long_index_name
|
721
|
+
end
|
722
|
+
}.new
|
723
|
+
|
724
|
+
error = assert_raises(StandardError) do
|
725
|
+
ActiveRecord::Migrator.new(:up, [migration], @schema_migration, @internal_metadata).migrate
|
726
|
+
end
|
727
|
+
assert_match(/Index name \'#{long_index_name}\' on table \'testings\' is too long/i, error.message)
|
728
|
+
end
|
729
|
+
|
730
|
+
# SQL Server has a different maximum index name length.
|
731
|
+
coerce_tests! :test_create_table_add_index_errors_on_too_long_name_7_0
|
732
|
+
def test_create_table_add_index_errors_on_too_long_name_7_0_coerced
|
733
|
+
long_index_name = 'a' * (connection.index_name_length + 1)
|
734
|
+
|
735
|
+
migration = Class.new(ActiveRecord::Migration[7.0]) {
|
736
|
+
@@long_index_name = long_index_name
|
737
|
+
def migrate(x)
|
738
|
+
create_table :more_testings do |t|
|
739
|
+
t.integer :foo
|
740
|
+
t.integer :bar
|
741
|
+
t.integer :very_long_column_name_to_test_with
|
742
|
+
t.index [:foo, :bar, :very_long_column_name_to_test_with], name: @@long_index_name
|
743
|
+
end
|
744
|
+
end
|
745
|
+
}.new
|
746
|
+
|
747
|
+
error = assert_raises(StandardError) do
|
748
|
+
ActiveRecord::Migrator.new(:up, [migration], @schema_migration, @internal_metadata).migrate
|
749
|
+
end
|
750
|
+
assert_match(/Index name \'#{long_index_name}\' on table \'more_testings\' is too long/i, error.message)
|
751
|
+
ensure
|
752
|
+
connection.drop_table :more_testings rescue nil
|
753
|
+
end
|
754
|
+
end
|
755
|
+
end
|
756
|
+
end
|
757
|
+
|
679
758
|
class CoreTest < ActiveRecord::TestCase
|
680
759
|
# I think fixtures are using the wrong time zone and the `:first`
|
681
760
|
# `topics`.`bonus_time` attribute of 2005-01-30t15:28:00.00+01:00 is
|
@@ -699,6 +778,7 @@ end
|
|
699
778
|
module ActiveRecord
|
700
779
|
# The original module is hardcoded for PostgreSQL/SQLite/MySQL tests.
|
701
780
|
module DatabaseTasksSetupper
|
781
|
+
undef_method :setup
|
702
782
|
def setup
|
703
783
|
@sqlserver_tasks =
|
704
784
|
Class.new do
|
@@ -721,6 +801,7 @@ module ActiveRecord
|
|
721
801
|
$stderr, @original_stderr = StringIO.new, $stderr
|
722
802
|
end
|
723
803
|
|
804
|
+
undef_method :with_stubbed_new
|
724
805
|
def with_stubbed_new
|
725
806
|
ActiveRecord::Tasks::SQLServerDatabaseTasks.stub(:new, @sqlserver_tasks) do
|
726
807
|
yield
|
@@ -840,36 +921,28 @@ end
|
|
840
921
|
class DefaultScopingTest < ActiveRecord::TestCase
|
841
922
|
# We are not doing order duplicate removal anymore.
|
842
923
|
coerce_tests! :test_order_in_default_scope_should_not_prevail
|
843
|
-
|
844
|
-
# Use our escaped format in assertion.
|
845
|
-
coerce_tests! :test_with_abstract_class_scope_should_be_executed_in_correct_context
|
846
|
-
def test_with_abstract_class_scope_should_be_executed_in_correct_context_coerced
|
847
|
-
vegetarian_pattern, gender_pattern = [/[lions].[is_vegetarian]/, /[lions].[gender]/]
|
848
|
-
assert_match vegetarian_pattern, Lion.all.to_sql
|
849
|
-
assert_match gender_pattern, Lion.female.to_sql
|
850
|
-
end
|
851
924
|
end
|
852
925
|
|
853
926
|
require "models/post"
|
854
927
|
require "models/subscriber"
|
855
928
|
class EachTest < ActiveRecord::TestCase
|
856
929
|
# Quoting in tests does not cope with bracket quoting.
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
end
|
930
|
+
# TODO: Remove coerced test when https://github.com/rails/rails/pull/49269 merged.
|
931
|
+
coerce_tests! :test_in_batches_no_subqueries_for_whole_tables_batching
|
932
|
+
def test_in_batches_no_subqueries_for_whole_tables_batching_coerced
|
933
|
+
c = Post.connection
|
934
|
+
quoted_posts_id = Regexp.escape(c.quote_table_name("posts.id"))
|
935
|
+
assert_sql(/DELETE FROM #{Regexp.escape(c.quote_table_name("posts"))} WHERE #{quoted_posts_id} > .+ AND #{quoted_posts_id} <=/i) do
|
936
|
+
Post.in_batches(of: 2).delete_all
|
865
937
|
end
|
866
938
|
end
|
867
939
|
|
868
940
|
# Quoting in tests does not cope with bracket quoting.
|
941
|
+
# TODO: Remove coerced test when https://github.com/rails/rails/pull/49269 merged.
|
869
942
|
coerce_tests! :test_in_batches_should_quote_batch_order
|
870
943
|
def test_in_batches_should_quote_batch_order_coerced
|
871
|
-
Post.connection
|
872
|
-
assert_sql(/ORDER BY
|
944
|
+
c = Post.connection
|
945
|
+
assert_sql(/ORDER BY #{Regexp.escape(c.quote_table_name('posts'))}\.#{Regexp.escape(c.quote_column_name('id'))}/) do
|
873
946
|
Post.in_batches(of: 1) do |relation|
|
874
947
|
assert_kind_of ActiveRecord::Relation, relation
|
875
948
|
assert_kind_of Post, relation.first
|
@@ -879,13 +952,13 @@ class EachTest < ActiveRecord::TestCase
|
|
879
952
|
end
|
880
953
|
|
881
954
|
class EagerAssociationTest < ActiveRecord::TestCase
|
882
|
-
# Use LEN()
|
955
|
+
# Use LEN() instead of LENGTH() function.
|
883
956
|
coerce_tests! :test_count_with_include
|
884
957
|
def test_count_with_include_coerced
|
885
958
|
assert_equal 3, authors(:david).posts_with_comments.where("LEN(comments.body) > 15").references(:comments).count
|
886
959
|
end
|
887
960
|
|
888
|
-
#
|
961
|
+
# The raw SQL in the scope uses `limit 1`.
|
889
962
|
coerce_tests! %r{including association based on sql condition and no database column}
|
890
963
|
end
|
891
964
|
|
@@ -899,14 +972,6 @@ class FinderTest < ActiveRecord::TestCase
|
|
899
972
|
coerce_tests! %r{doesn't have implicit ordering},
|
900
973
|
:test_find_doesnt_have_implicit_ordering
|
901
974
|
|
902
|
-
# Square brackets around column name
|
903
|
-
coerce_tests! :test_exists_does_not_select_columns_without_alias
|
904
|
-
def test_exists_does_not_select_columns_without_alias_coerced
|
905
|
-
assert_sql(/SELECT\s+1 AS one FROM \[topics\].*OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) do
|
906
|
-
Topic.exists?
|
907
|
-
end
|
908
|
-
end
|
909
|
-
|
910
975
|
# Assert SQL Server limit implementation
|
911
976
|
coerce_tests! :test_take_and_first_and_last_with_integer_should_use_sql_limit
|
912
977
|
def test_take_and_first_and_last_with_integer_should_use_sql_limit_coerced
|
@@ -1018,6 +1083,92 @@ class FinderTest < ActiveRecord::TestCase
|
|
1018
1083
|
NonPrimaryKey.implicit_order_column = old_implicit_order_column
|
1019
1084
|
end
|
1020
1085
|
|
1086
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
1087
|
+
coerce_tests! :test_member_on_unloaded_relation_with_composite_primary_key
|
1088
|
+
def test_member_on_unloaded_relation_with_composite_primary_key_coerced
|
1089
|
+
assert_sql(/1 AS one.* FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/) do
|
1090
|
+
book = cpk_books(:cpk_great_author_first_book)
|
1091
|
+
assert Cpk::Book.where(title: "The first book").member?(book)
|
1092
|
+
end
|
1093
|
+
end
|
1094
|
+
|
1095
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
1096
|
+
coerce_tests! :test_implicit_order_column_prepends_query_constraints
|
1097
|
+
def test_implicit_order_column_prepends_query_constraints_coerced
|
1098
|
+
c = ClothingItem.connection
|
1099
|
+
ClothingItem.implicit_order_column = "description"
|
1100
|
+
quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type"))
|
1101
|
+
quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color"))
|
1102
|
+
quoted_descrption = Regexp.escape(c.quote_table_name("clothing_items.description"))
|
1103
|
+
|
1104
|
+
assert_sql(/ORDER BY #{quoted_descrption} ASC, #{quoted_type} ASC, #{quoted_color} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do
|
1105
|
+
assert_kind_of ClothingItem, ClothingItem.first
|
1106
|
+
end
|
1107
|
+
ensure
|
1108
|
+
ClothingItem.implicit_order_column = nil
|
1109
|
+
end
|
1110
|
+
|
1111
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
1112
|
+
coerce_tests! %r{#last for a model with composite query constraints}
|
1113
|
+
test "#last for a model with composite query constraints coerced" do
|
1114
|
+
c = ClothingItem.connection
|
1115
|
+
quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type"))
|
1116
|
+
quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color"))
|
1117
|
+
|
1118
|
+
assert_sql(/ORDER BY #{quoted_type} DESC, #{quoted_color} DESC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do
|
1119
|
+
assert_kind_of ClothingItem, ClothingItem.last
|
1120
|
+
end
|
1121
|
+
end
|
1122
|
+
|
1123
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
1124
|
+
coerce_tests! %r{#first for a model with composite query constraints}
|
1125
|
+
test "#first for a model with composite query constraints coerced" do
|
1126
|
+
c = ClothingItem.connection
|
1127
|
+
quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type"))
|
1128
|
+
quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color"))
|
1129
|
+
|
1130
|
+
assert_sql(/ORDER BY #{quoted_type} ASC, #{quoted_color} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do
|
1131
|
+
assert_kind_of ClothingItem, ClothingItem.first
|
1132
|
+
end
|
1133
|
+
end
|
1134
|
+
|
1135
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
1136
|
+
coerce_tests! :test_implicit_order_column_reorders_query_constraints
|
1137
|
+
def test_implicit_order_column_reorders_query_constraints_coerced
|
1138
|
+
c = ClothingItem.connection
|
1139
|
+
ClothingItem.implicit_order_column = "color"
|
1140
|
+
quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type"))
|
1141
|
+
quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color"))
|
1142
|
+
|
1143
|
+
assert_sql(/ORDER BY #{quoted_color} ASC, #{quoted_type} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do
|
1144
|
+
assert_kind_of ClothingItem, ClothingItem.first
|
1145
|
+
end
|
1146
|
+
ensure
|
1147
|
+
ClothingItem.implicit_order_column = nil
|
1148
|
+
end
|
1149
|
+
|
1150
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
1151
|
+
coerce_tests! :test_include_on_unloaded_relation_with_composite_primary_key
|
1152
|
+
def test_include_on_unloaded_relation_with_composite_primary_key_coerced
|
1153
|
+
assert_sql(/1 AS one.*OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/) do
|
1154
|
+
book = cpk_books(:cpk_great_author_first_book)
|
1155
|
+
assert Cpk::Book.where(title: "The first book").include?(book)
|
1156
|
+
end
|
1157
|
+
end
|
1158
|
+
|
1159
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
1160
|
+
coerce_tests! :test_nth_to_last_with_order_uses_limit
|
1161
|
+
def test_nth_to_last_with_order_uses_limit_coerced
|
1162
|
+
c = Topic.connection
|
1163
|
+
assert_sql(/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
|
1164
|
+
Topic.second_to_last
|
1165
|
+
end
|
1166
|
+
|
1167
|
+
assert_sql(/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
|
1168
|
+
Topic.order(:updated_at).second_to_last
|
1169
|
+
end
|
1170
|
+
end
|
1171
|
+
|
1021
1172
|
# SQL Server is unable to use aliased SELECT in the HAVING clause.
|
1022
1173
|
coerce_tests! :test_include_on_unloaded_relation_with_having_referencing_aliased_select
|
1023
1174
|
end
|
@@ -1025,7 +1176,7 @@ end
|
|
1025
1176
|
module ActiveRecord
|
1026
1177
|
class Migration
|
1027
1178
|
class ForeignKeyTest < ActiveRecord::TestCase
|
1028
|
-
#
|
1179
|
+
# SQL Server does not support 'restrict' for 'on_update' or 'on_delete'.
|
1029
1180
|
coerce_tests! :test_add_on_delete_restrict_foreign_key
|
1030
1181
|
def test_add_on_delete_restrict_foreign_key_coerced
|
1031
1182
|
assert_raises ArgumentError do
|
@@ -1079,27 +1230,6 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
|
|
1079
1230
|
end
|
1080
1231
|
end
|
1081
1232
|
|
1082
|
-
require "models/company"
|
1083
|
-
class InheritanceTest < ActiveRecord::TestCase
|
1084
|
-
# Rails test required inserting to a identity column.
|
1085
|
-
coerce_tests! :test_a_bad_type_column
|
1086
|
-
def test_a_bad_type_column_coerced
|
1087
|
-
Company.connection.with_identity_insert_enabled("companies") do
|
1088
|
-
Company.connection.insert "INSERT INTO companies (id, #{QUOTED_TYPE}, name) VALUES(100, 'bad_class!', 'Not happening')"
|
1089
|
-
end
|
1090
|
-
assert_raise(ActiveRecord::SubclassNotFound) { Company.find(100) }
|
1091
|
-
end
|
1092
|
-
|
1093
|
-
# Use Square brackets around column name
|
1094
|
-
coerce_tests! :test_eager_load_belongs_to_primary_key_quoting
|
1095
|
-
def test_eager_load_belongs_to_primary_key_quoting_coerced
|
1096
|
-
Account.connection
|
1097
|
-
assert_sql(/\[companies\]\.\[id\] = @0.* @0 = 1/) do
|
1098
|
-
Account.all.merge!(:includes => :firm).find(1)
|
1099
|
-
end
|
1100
|
-
end
|
1101
|
-
end
|
1102
|
-
|
1103
1233
|
class LeftOuterJoinAssociationTest < ActiveRecord::TestCase
|
1104
1234
|
# Uses || operator in SQL. Just trust core gets value out of this test.
|
1105
1235
|
coerce_tests! :test_does_not_override_select
|
@@ -1263,14 +1393,14 @@ end
|
|
1263
1393
|
|
1264
1394
|
require "models/post"
|
1265
1395
|
class RelationTest < ActiveRecord::TestCase
|
1266
|
-
# Use LEN
|
1396
|
+
# Use LEN() instead of LENGTH() function.
|
1267
1397
|
coerce_tests! :test_reverse_order_with_function
|
1268
1398
|
def test_reverse_order_with_function_coerced
|
1269
1399
|
topics = Topic.order(Arel.sql("LEN(title)")).reverse_order
|
1270
1400
|
assert_equal topics(:second).title, topics.first.title
|
1271
1401
|
end
|
1272
1402
|
|
1273
|
-
# Use LEN
|
1403
|
+
# Use LEN() instead of LENGTH() function.
|
1274
1404
|
coerce_tests! :test_reverse_order_with_function_other_predicates
|
1275
1405
|
def test_reverse_order_with_function_other_predicates_coerced
|
1276
1406
|
topics = Topic.order(Arel.sql("author_name, LEN(title), id")).reverse_order
|
@@ -1288,7 +1418,7 @@ class RelationTest < ActiveRecord::TestCase
|
|
1288
1418
|
sql_log = capture_sql do
|
1289
1419
|
assert Post.order(:title).reorder(nil).take
|
1290
1420
|
end
|
1291
|
-
assert sql_log.none? { |sql| /order by [posts]
|
1421
|
+
assert sql_log.none? { |sql| /order by \[posts\]\.\[title\]/i.match?(sql) }, "ORDER BY title was used in the query: #{sql_log}"
|
1292
1422
|
assert sql_log.all? { |sql| /order by \[posts\]\.\[id\]/i.match?(sql) }, "default ORDER BY ID was not used in the query: #{sql_log}"
|
1293
1423
|
end
|
1294
1424
|
|
@@ -1300,7 +1430,7 @@ class RelationTest < ActiveRecord::TestCase
|
|
1300
1430
|
post = Post.order(:title).reorder(nil).first
|
1301
1431
|
end
|
1302
1432
|
assert_equal posts(:welcome), post
|
1303
|
-
assert sql_log.none? { |sql| /order by [posts]
|
1433
|
+
assert sql_log.none? { |sql| /order by \[posts\]\.\[title\]/i.match?(sql) }, "ORDER BY title was used in the query: #{sql_log}"
|
1304
1434
|
assert sql_log.all? { |sql| /order by \[posts\]\.\[id\]/i.match?(sql) }, "default ORDER BY ID was not used in the query: #{sql_log}"
|
1305
1435
|
end
|
1306
1436
|
|
@@ -1339,6 +1469,14 @@ class RelationTest < ActiveRecord::TestCase
|
|
1339
1469
|
end
|
1340
1470
|
end
|
1341
1471
|
|
1472
|
+
# Find any limit via our expression.
|
1473
|
+
coerce_tests! %r{relations don't load all records in #pretty_print}
|
1474
|
+
def test_relations_dont_load_all_records_in_pretty_print_coerced
|
1475
|
+
assert_sql(/FETCH NEXT @(\d) ROWS ONLY/) do
|
1476
|
+
PP.pp Post.all, StringIO.new # avoid outputting.
|
1477
|
+
end
|
1478
|
+
end
|
1479
|
+
|
1342
1480
|
# Order column must be in the GROUP clause.
|
1343
1481
|
coerce_tests! :test_empty_complex_chained_relations
|
1344
1482
|
def test_empty_complex_chained_relations_coerced
|
@@ -1368,7 +1506,7 @@ class RelationTest < ActiveRecord::TestCase
|
|
1368
1506
|
assert_equal post, custom_post_relation.joins(:author).where!(title: post.title).order(:id).take
|
1369
1507
|
end
|
1370
1508
|
|
1371
|
-
# Use LEN()
|
1509
|
+
# Use LEN() instead of LENGTH() function.
|
1372
1510
|
coerce_tests! :test_reverse_arel_assoc_order_with_function
|
1373
1511
|
def test_reverse_arel_assoc_order_with_function_coerced
|
1374
1512
|
topics = Topic.order(Arel.sql("LEN(title)") => :asc).reverse_order
|
@@ -1388,15 +1526,6 @@ module ActiveRecord
|
|
1388
1526
|
query = Post.optimizer_hints("OMGHINT").merge(Post.optimizer_hints("OMGHINT")).to_sql
|
1389
1527
|
assert_equal expected, query
|
1390
1528
|
end
|
1391
|
-
|
1392
|
-
# Workaround for randomly failing test. Ordering of results not guaranteed.
|
1393
|
-
# TODO: Remove coerced test when https://github.com/rails/rails/pull/44168 merged.
|
1394
|
-
coerce_tests! :test_select_quotes_when_using_from_clause
|
1395
|
-
def test_select_quotes_when_using_from_clause_coerced
|
1396
|
-
quoted_join = ActiveRecord::Base.connection.quote_table_name("join")
|
1397
|
-
selected = Post.select(:join).from(Post.select("id as #{quoted_join}")).map(&:join)
|
1398
|
-
assert_equal Post.pluck(:id).sort, selected.sort
|
1399
|
-
end
|
1400
1529
|
end
|
1401
1530
|
end
|
1402
1531
|
|
@@ -1423,9 +1552,36 @@ class SanitizeTest < ActiveRecord::TestCase
|
|
1423
1552
|
searchable_post.search_as_scope("20% _reduction_!").to_a
|
1424
1553
|
end
|
1425
1554
|
end
|
1555
|
+
|
1556
|
+
# Use nvarchar string (N'') in assert
|
1557
|
+
coerce_tests! :test_named_bind_with_literal_colons
|
1558
|
+
def test_named_bind_with_literal_colons_coerced
|
1559
|
+
assert_equal "TO_TIMESTAMP(N'2017/08/02 10:59:00', 'YYYY/MM/DD HH12:MI:SS')", bind("TO_TIMESTAMP(:date, 'YYYY/MM/DD HH12\\:MI\\:SS')", date: "2017/08/02 10:59:00")
|
1560
|
+
assert_raise(ActiveRecord::PreparedStatementInvalid) { bind "TO_TIMESTAMP(:date, 'YYYY/MM/DD HH12:MI:SS')", date: "2017/08/02 10:59:00" }
|
1561
|
+
end
|
1426
1562
|
end
|
1427
1563
|
|
1428
1564
|
class SchemaDumperTest < ActiveRecord::TestCase
|
1565
|
+
# Use nvarchar string (N'') in assert
|
1566
|
+
coerce_tests! :test_dump_schema_information_outputs_lexically_reverse_ordered_versions_regardless_of_database_order
|
1567
|
+
def test_dump_schema_information_outputs_lexically_reverse_ordered_versions_regardless_of_database_order_coerced
|
1568
|
+
versions = %w{ 20100101010101 20100201010101 20100301010101 }
|
1569
|
+
versions.shuffle.each do |v|
|
1570
|
+
@schema_migration.create_version(v)
|
1571
|
+
end
|
1572
|
+
|
1573
|
+
schema_info = ActiveRecord::Base.connection.dump_schema_information
|
1574
|
+
expected = <<~STR
|
1575
|
+
INSERT INTO #{ActiveRecord::Base.connection.quote_table_name("schema_migrations")} (version) VALUES
|
1576
|
+
(N'20100301010101'),
|
1577
|
+
(N'20100201010101'),
|
1578
|
+
(N'20100101010101');
|
1579
|
+
STR
|
1580
|
+
assert_equal expected.strip, schema_info
|
1581
|
+
ensure
|
1582
|
+
@schema_migration.delete_all_versions
|
1583
|
+
end
|
1584
|
+
|
1429
1585
|
# We have precision to 38.
|
1430
1586
|
coerce_tests! :test_schema_dump_keeps_large_precision_integer_columns_as_decimal
|
1431
1587
|
def test_schema_dump_keeps_large_precision_integer_columns_as_decimal_coerced
|
@@ -1449,6 +1605,9 @@ class SchemaDumperTest < ActiveRecord::TestCase
|
|
1449
1605
|
output = dump_all_table_schema([/^[^n]/])
|
1450
1606
|
assert_match %r{precision: 3,[[:space:]]+scale: 2,[[:space:]]+default: 2\.78}, output
|
1451
1607
|
end
|
1608
|
+
|
1609
|
+
# Tests are not about a specific adapter.
|
1610
|
+
coerce_tests! :test_do_not_dump_foreign_keys_when_bypassed_by_config
|
1452
1611
|
end
|
1453
1612
|
|
1454
1613
|
class SchemaDumperDefaultsTest < ActiveRecord::TestCase
|
@@ -1492,16 +1651,95 @@ end
|
|
1492
1651
|
|
1493
1652
|
require "models/topic"
|
1494
1653
|
class TransactionTest < ActiveRecord::TestCase
|
1495
|
-
# SQL Server does not have query for release_savepoint
|
1654
|
+
# SQL Server does not have query for release_savepoint.
|
1496
1655
|
coerce_tests! :test_releasing_named_savepoints
|
1497
1656
|
def test_releasing_named_savepoints_coerced
|
1498
1657
|
Topic.transaction do
|
1658
|
+
Topic.connection.materialize_transactions
|
1659
|
+
|
1499
1660
|
Topic.connection.create_savepoint("another")
|
1500
1661
|
Topic.connection.release_savepoint("another")
|
1501
1662
|
# We do not have a notion of releasing, so this does nothing vs raise an error.
|
1502
1663
|
Topic.connection.release_savepoint("another")
|
1503
1664
|
end
|
1504
1665
|
end
|
1666
|
+
|
1667
|
+
# SQL Server does not have query for release_savepoint.
|
1668
|
+
coerce_tests! :test_nested_transactions_after_disable_lazy_transactions
|
1669
|
+
def test_nested_transactions_after_disable_lazy_transactions_coerced
|
1670
|
+
Topic.connection.disable_lazy_transactions!
|
1671
|
+
|
1672
|
+
capture_sql do
|
1673
|
+
# RealTransaction (begin..commit)
|
1674
|
+
Topic.transaction(requires_new: true) do
|
1675
|
+
# ResetParentTransaction (no queries)
|
1676
|
+
Topic.transaction(requires_new: true) do
|
1677
|
+
Topic.delete_all
|
1678
|
+
# SavepointTransaction (savepoint..release)
|
1679
|
+
Topic.transaction(requires_new: true) do
|
1680
|
+
# ResetParentTransaction (no queries)
|
1681
|
+
Topic.transaction(requires_new: true) do
|
1682
|
+
# no-op
|
1683
|
+
end
|
1684
|
+
end
|
1685
|
+
end
|
1686
|
+
Topic.delete_all
|
1687
|
+
end
|
1688
|
+
end
|
1689
|
+
|
1690
|
+
actual_queries = ActiveRecord::SQLCounter.log_all
|
1691
|
+
|
1692
|
+
expected_queries = [
|
1693
|
+
/BEGIN/i,
|
1694
|
+
/DELETE/i,
|
1695
|
+
/^SAVE TRANSACTION/i,
|
1696
|
+
/DELETE/i,
|
1697
|
+
/COMMIT/i,
|
1698
|
+
]
|
1699
|
+
|
1700
|
+
assert_equal expected_queries.size, actual_queries.size
|
1701
|
+
expected_queries.zip(actual_queries) do |expected, actual|
|
1702
|
+
assert_match expected, actual
|
1703
|
+
end
|
1704
|
+
end
|
1705
|
+
|
1706
|
+
# SQL Server does not have query for release_savepoint.
|
1707
|
+
coerce_tests! :test_nested_transactions_skip_excess_savepoints
|
1708
|
+
def test_nested_transactions_skip_excess_savepoints_coerced
|
1709
|
+
capture_sql do
|
1710
|
+
# RealTransaction (begin..commit)
|
1711
|
+
Topic.transaction(requires_new: true) do
|
1712
|
+
# ResetParentTransaction (no queries)
|
1713
|
+
Topic.transaction(requires_new: true) do
|
1714
|
+
Topic.delete_all
|
1715
|
+
# SavepointTransaction (savepoint..release)
|
1716
|
+
Topic.transaction(requires_new: true) do
|
1717
|
+
# ResetParentTransaction (no queries)
|
1718
|
+
Topic.transaction(requires_new: true) do
|
1719
|
+
Topic.delete_all
|
1720
|
+
end
|
1721
|
+
end
|
1722
|
+
end
|
1723
|
+
Topic.delete_all
|
1724
|
+
end
|
1725
|
+
end
|
1726
|
+
|
1727
|
+
actual_queries = ActiveRecord::SQLCounter.log_all
|
1728
|
+
|
1729
|
+
expected_queries = [
|
1730
|
+
/BEGIN/i,
|
1731
|
+
/DELETE/i,
|
1732
|
+
/^SAVE TRANSACTION/i,
|
1733
|
+
/DELETE/i,
|
1734
|
+
/DELETE/i,
|
1735
|
+
/COMMIT/i,
|
1736
|
+
]
|
1737
|
+
|
1738
|
+
assert_equal expected_queries.size, actual_queries.size
|
1739
|
+
expected_queries.zip(actual_queries) do |expected, actual|
|
1740
|
+
assert_match expected, actual
|
1741
|
+
end
|
1742
|
+
end
|
1505
1743
|
end
|
1506
1744
|
|
1507
1745
|
require "models/tag"
|
@@ -1630,8 +1868,8 @@ class DefaultNumbersTest < ActiveRecord::TestCase
|
|
1630
1868
|
coerce_tests! :test_default_negative_integer
|
1631
1869
|
def test_default_negative_integer_coerced
|
1632
1870
|
record = DefaultNumber.new
|
1633
|
-
assert_equal -5, record.negative_integer
|
1634
|
-
assert_equal -5, record.negative_integer_before_type_cast
|
1871
|
+
assert_equal (-5), record.negative_integer
|
1872
|
+
assert_equal (-5), record.negative_integer_before_type_cast
|
1635
1873
|
end
|
1636
1874
|
|
1637
1875
|
# We do better with native types and do not return strings for everything.
|
@@ -1720,8 +1958,9 @@ module ActiveRecord
|
|
1720
1958
|
end
|
1721
1959
|
|
1722
1960
|
# We need to give the full path for this to work.
|
1961
|
+
undef_method :schema_dump_path
|
1723
1962
|
def schema_dump_path
|
1724
|
-
File.join
|
1963
|
+
File.join(ARTest::SQLServer.root_activerecord, "test/assets/schema_dump_5_1.yml")
|
1725
1964
|
end
|
1726
1965
|
end
|
1727
1966
|
end
|
@@ -1732,7 +1971,7 @@ require "models/comment"
|
|
1732
1971
|
class UnsafeRawSqlTest < ActiveRecord::TestCase
|
1733
1972
|
fixtures :posts
|
1734
1973
|
|
1735
|
-
# Use LEN()
|
1974
|
+
# Use LEN() instead of LENGTH() function.
|
1736
1975
|
coerce_tests! %r{order: always allows Arel}
|
1737
1976
|
test "order: always allows Arel" do
|
1738
1977
|
titles = Post.order(Arel.sql("len(title)")).pluck(:title)
|
@@ -1740,7 +1979,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
|
|
1740
1979
|
assert_not_empty titles
|
1741
1980
|
end
|
1742
1981
|
|
1743
|
-
# Use LEN()
|
1982
|
+
# Use LEN() instead of LENGTH() function.
|
1744
1983
|
coerce_tests! %r{pluck: always allows Arel}
|
1745
1984
|
test "pluck: always allows Arel" do
|
1746
1985
|
excepted_values = Post.includes(:comments).pluck(:title).map { |title| [title, title.size] }
|
@@ -1749,7 +1988,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
|
|
1749
1988
|
assert_equal excepted_values, values
|
1750
1989
|
end
|
1751
1990
|
|
1752
|
-
# Use LEN()
|
1991
|
+
# Use LEN() instead of LENGTH() function.
|
1753
1992
|
coerce_tests! %r{order: allows valid Array arguments}
|
1754
1993
|
test "order: allows valid Array arguments" do
|
1755
1994
|
ids_expected = Post.order(Arel.sql("author_id, len(title)")).pluck(:id)
|
@@ -1759,6 +1998,27 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
|
|
1759
1998
|
assert_equal ids_expected, ids
|
1760
1999
|
end
|
1761
2000
|
|
2001
|
+
# Use LEN() instead of LENGTH() function.
|
2002
|
+
coerce_tests! %r{order: allows nested functions}
|
2003
|
+
test "order: allows nested functions" do
|
2004
|
+
ids_expected = Post.order(Arel.sql("author_id, len(trim(title))")).pluck(:id)
|
2005
|
+
|
2006
|
+
# $DEBUG = true
|
2007
|
+
ids = Post.order("author_id, len(trim(title))").pluck(:id)
|
2008
|
+
|
2009
|
+
assert_equal ids_expected, ids
|
2010
|
+
end
|
2011
|
+
|
2012
|
+
# Use LEN() instead of LENGTH() function.
|
2013
|
+
coerce_tests! %r{pluck: allows nested functions}
|
2014
|
+
test "pluck: allows nested functions" do
|
2015
|
+
title_lengths_expected = Post.pluck(Arel.sql("len(trim(title))"))
|
2016
|
+
|
2017
|
+
title_lengths = Post.pluck("len(trim(title))")
|
2018
|
+
|
2019
|
+
assert_equal title_lengths_expected, title_lengths
|
2020
|
+
end
|
2021
|
+
|
1762
2022
|
test "order: allows string column names that are quoted" do
|
1763
2023
|
ids_expected = Post.order(Arel.sql("id")).pluck(:id)
|
1764
2024
|
|
@@ -1822,6 +2082,18 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
|
|
1822
2082
|
|
1823
2083
|
assert_equal titles_expected, titles
|
1824
2084
|
end
|
2085
|
+
|
2086
|
+
# Collation name should not be quoted. Hardcoded values for different adapters.
|
2087
|
+
coerce_tests! %r{order: allows valid arguments with COLLATE}
|
2088
|
+
test "order: allows valid arguments with COLLATE" do
|
2089
|
+
collation_name = "Latin1_General_CS_AS_WS"
|
2090
|
+
|
2091
|
+
ids_expected = Post.order(Arel.sql(%Q'author_id, title COLLATE #{collation_name} DESC')).pluck(:id)
|
2092
|
+
|
2093
|
+
ids = Post.order(["author_id", %Q'title COLLATE #{collation_name} DESC']).pluck(:id)
|
2094
|
+
|
2095
|
+
assert_equal ids_expected, ids
|
2096
|
+
end
|
1825
2097
|
end
|
1826
2098
|
|
1827
2099
|
class ReservedWordTest < ActiveRecord::TestCase
|
@@ -2021,14 +2293,15 @@ class LogSubscriberTest < ActiveRecord::TestCase
|
|
2021
2293
|
end
|
2022
2294
|
end
|
2023
2295
|
|
2024
|
-
|
2025
|
-
|
2026
|
-
|
2027
|
-
|
2028
|
-
|
2029
|
-
|
2030
|
-
|
2031
|
-
end
|
2296
|
+
# TODO: Method `reset_column_information` does not exist. Comment out the test for the time being.
|
2297
|
+
# class ActiveRecordSchemaTest < ActiveRecord::TestCase
|
2298
|
+
# # Workaround for randomly failing test.
|
2299
|
+
# coerce_tests! :test_has_primary_key
|
2300
|
+
# def test_has_primary_key_coerced
|
2301
|
+
# @schema_migration.reset_column_information
|
2302
|
+
# original_test_has_primary_key
|
2303
|
+
# end
|
2304
|
+
# end
|
2032
2305
|
|
2033
2306
|
class ReloadModelsTest < ActiveRecord::TestCase
|
2034
2307
|
# Skip test on Windows. The number of arguments passed to `IO.popen` in
|
@@ -2039,6 +2312,7 @@ end
|
|
2039
2312
|
class MarshalSerializationTest < ActiveRecord::TestCase
|
2040
2313
|
private
|
2041
2314
|
|
2315
|
+
undef_method :marshal_fixture_path
|
2042
2316
|
def marshal_fixture_path(file_name)
|
2043
2317
|
File.expand_path(
|
2044
2318
|
"support/marshal_compatibility_fixtures/#{ActiveRecord::Base.connection.adapter_name}/#{file_name}.dump",
|
@@ -2073,6 +2347,59 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
|
|
2073
2347
|
end
|
2074
2348
|
end
|
2075
2349
|
|
2350
|
+
class PreloaderTest < ActiveRecord::TestCase
|
2351
|
+
# Need to handle query parameters in SQL regex.
|
2352
|
+
coerce_tests! :test_preloads_has_many_on_model_with_a_composite_primary_key_through_id_attribute
|
2353
|
+
def test_preloads_has_many_on_model_with_a_composite_primary_key_through_id_attribute_coerced
|
2354
|
+
order = cpk_orders(:cpk_groceries_order_2)
|
2355
|
+
_shop_id, order_id = order.id
|
2356
|
+
order_agreements = Cpk::OrderAgreement.where(order_id: order_id).to_a
|
2357
|
+
|
2358
|
+
assert_not_empty order_agreements
|
2359
|
+
assert_equal order_agreements.sort, order.order_agreements.sort
|
2360
|
+
|
2361
|
+
loaded_order = nil
|
2362
|
+
sql = capture_sql do
|
2363
|
+
loaded_order = Cpk::Order.where(id: order_id).includes(:order_agreements).to_a.first
|
2364
|
+
end
|
2365
|
+
|
2366
|
+
assert_equal 2, sql.size
|
2367
|
+
preload_sql = sql.last
|
2368
|
+
|
2369
|
+
c = Cpk::OrderAgreement.connection
|
2370
|
+
order_id_column = Regexp.escape(c.quote_table_name("cpk_order_agreements.order_id"))
|
2371
|
+
order_id_constraint = /#{order_id_column} = @0.*@0 = \d+$/
|
2372
|
+
expectation = /SELECT.*WHERE.* #{order_id_constraint}/
|
2373
|
+
|
2374
|
+
assert_match(expectation, preload_sql)
|
2375
|
+
assert_equal order_agreements.sort, loaded_order.order_agreements.sort
|
2376
|
+
end
|
2377
|
+
|
2378
|
+
# Need to handle query parameters in SQL regex.
|
2379
|
+
coerce_tests! :test_preloads_belongs_to_a_composite_primary_key_model_through_id_attribute
|
2380
|
+
def test_preloads_belongs_to_a_composite_primary_key_model_through_id_attribute_coerced
|
2381
|
+
order_agreement = cpk_order_agreements(:order_agreement_three)
|
2382
|
+
order = cpk_orders(:cpk_groceries_order_2)
|
2383
|
+
assert_equal order, order_agreement.order
|
2384
|
+
|
2385
|
+
loaded_order_agreement = nil
|
2386
|
+
sql = capture_sql do
|
2387
|
+
loaded_order_agreement = Cpk::OrderAgreement.where(id: order_agreement.id).includes(:order).to_a.first
|
2388
|
+
end
|
2389
|
+
|
2390
|
+
assert_equal 2, sql.size
|
2391
|
+
preload_sql = sql.last
|
2392
|
+
|
2393
|
+
c = Cpk::Order.connection
|
2394
|
+
order_id = Regexp.escape(c.quote_table_name("cpk_orders.id"))
|
2395
|
+
order_constraint = /#{order_id} = @0.*@0 = \d+$/
|
2396
|
+
expectation = /SELECT.*WHERE.* #{order_constraint}/
|
2397
|
+
|
2398
|
+
assert_match(expectation, preload_sql)
|
2399
|
+
assert_equal order, loaded_order_agreement.order
|
2400
|
+
end
|
2401
|
+
end
|
2402
|
+
|
2076
2403
|
class BasePreventWritesTest < ActiveRecord::TestCase
|
2077
2404
|
# SQL Server does not have query for release_savepoint
|
2078
2405
|
coerce_tests! %r{an empty transaction does not raise if preventing writes}
|
@@ -2085,20 +2412,6 @@ class BasePreventWritesTest < ActiveRecord::TestCase
|
|
2085
2412
|
end
|
2086
2413
|
end
|
2087
2414
|
end
|
2088
|
-
|
2089
|
-
class BasePreventWritesLegacyTest < ActiveRecord::TestCase
|
2090
|
-
# SQL Server does not have query for release_savepoint
|
2091
|
-
coerce_tests! %r{an empty transaction does not raise if preventing writes}
|
2092
|
-
test "an empty transaction does not raise if preventing writes coerced" do
|
2093
|
-
ActiveRecord::Base.connection_handler.while_preventing_writes do
|
2094
|
-
assert_queries(1, ignore_none: true) do
|
2095
|
-
Bird.transaction do
|
2096
|
-
ActiveRecord::Base.connection.materialize_transactions
|
2097
|
-
end
|
2098
|
-
end
|
2099
|
-
end
|
2100
|
-
end
|
2101
|
-
end
|
2102
2415
|
end
|
2103
2416
|
|
2104
2417
|
class MigratorTest < ActiveRecord::TestCase
|
@@ -2150,7 +2463,7 @@ class FieldOrderedValuesTest < ActiveRecord::TestCase
|
|
2150
2463
|
coerce_tests! :test_in_order_of_with_nil
|
2151
2464
|
def test_in_order_of_with_nil_coerced
|
2152
2465
|
Book.connection.remove_index(:books, column: [:author_id, :name])
|
2153
|
-
|
2466
|
+
|
2154
2467
|
original_test_in_order_of_with_nil
|
2155
2468
|
ensure
|
2156
2469
|
Book.where(author_id: nil, name: nil).delete_all
|
@@ -2160,96 +2473,63 @@ end
|
|
2160
2473
|
|
2161
2474
|
require "models/dashboard"
|
2162
2475
|
class QueryLogsTest < ActiveRecord::TestCase
|
2163
|
-
#
|
2164
|
-
|
2165
|
-
|
2166
|
-
|
2167
|
-
|
2168
|
-
|
2169
|
-
assert_sql(%r{/\*application:active_record,custom_string:test content\*/}) do
|
2476
|
+
# SQL requires double single-quotes.
|
2477
|
+
coerce_tests! :test_sql_commenter_format
|
2478
|
+
def test_sql_commenter_format_coerced
|
2479
|
+
ActiveRecord::QueryLogs.update_formatter(:sqlcommenter)
|
2480
|
+
assert_sql(%r{/\*application=''active_record''\*/}) do
|
2170
2481
|
Dashboard.first
|
2171
2482
|
end
|
2172
2483
|
end
|
2173
2484
|
|
2174
|
-
|
2175
|
-
|
2485
|
+
# SQL requires double single-quotes.
|
2486
|
+
coerce_tests! :test_sqlcommenter_format_value
|
2487
|
+
def test_sqlcommenter_format_value_coerced
|
2488
|
+
ActiveRecord::QueryLogs.update_formatter(:sqlcommenter)
|
2489
|
+
|
2490
|
+
ActiveRecord::QueryLogs.tags = [
|
2491
|
+
:application,
|
2492
|
+
{ tracestate: "congo=t61rcWkgMzE,rojo=00f067aa0ba902b7", custom_proc: -> { "Joe's Shack" } },
|
2493
|
+
]
|
2176
2494
|
|
2177
|
-
assert_sql(%r{
|
2495
|
+
assert_sql(%r{custom_proc=''Joe%27s%20Shack'',tracestate=''congo%3Dt61rcWkgMzE%2Crojo%3D00f067aa0ba902b7''\*/}) do
|
2178
2496
|
Dashboard.first
|
2179
2497
|
end
|
2180
2498
|
end
|
2181
2499
|
|
2182
|
-
|
2500
|
+
# SQL requires double single-quotes.
|
2501
|
+
coerce_tests! :test_sqlcommenter_format_value_string_coercible
|
2502
|
+
def test_sqlcommenter_format_value_string_coercible_coerced
|
2503
|
+
ActiveRecord::QueryLogs.update_formatter(:sqlcommenter)
|
2504
|
+
|
2183
2505
|
ActiveRecord::QueryLogs.tags = [
|
2184
2506
|
:application,
|
2185
|
-
{ custom_proc: -> {
|
2507
|
+
{ custom_proc: -> { 1234 } },
|
2186
2508
|
]
|
2187
2509
|
|
2188
|
-
assert_sql(%r{
|
2510
|
+
assert_sql(%r{custom_proc=''1234''\*/}) do
|
2189
2511
|
Dashboard.first
|
2190
2512
|
end
|
2191
2513
|
end
|
2192
2514
|
|
2193
|
-
|
2194
|
-
|
2195
|
-
|
2196
|
-
|
2197
|
-
|
2198
|
-
|
2515
|
+
# Invalid character encoding causes `ActiveRecord::StatementInvalid` error similar to Postgres.
|
2516
|
+
coerce_tests! :test_invalid_encoding_query
|
2517
|
+
def test_invalid_encoding_query_coerced
|
2518
|
+
ActiveRecord::QueryLogs.tags = [ :application ]
|
2519
|
+
assert_raises ActiveRecord::StatementInvalid do
|
2520
|
+
ActiveRecord::Base.connection.execute "select 1 as '\xFF'"
|
2199
2521
|
end
|
2200
2522
|
end
|
2201
2523
|
end
|
2202
2524
|
|
2203
|
-
# SQL Server does not support upsert yet
|
2204
|
-
# TODO: Remove coerce after Rails 7.1.0 (see https://github.com/rails/rails/pull/44050)
|
2205
2525
|
class InsertAllTest < ActiveRecord::TestCase
|
2206
|
-
|
2207
|
-
|
2208
|
-
|
2209
|
-
|
2210
|
-
end
|
2211
|
-
end
|
2212
|
-
|
2213
|
-
coerce_tests! :test_upsert_all_only_updates_the_list_of_columns_provided_via_update_only
|
2214
|
-
def test_upsert_all_only_updates_the_list_of_columns_provided_via_update_only_coerced
|
2215
|
-
assert_raises(ArgumentError, /does not support upsert/) do
|
2216
|
-
original_test_upsert_all_only_updates_the_list_of_columns_provided_via_update_only
|
2217
|
-
end
|
2218
|
-
end
|
2219
|
-
|
2220
|
-
coerce_tests! :test_upsert_all_implicitly_sets_timestamps_on_create_when_model_record_timestamps_is_true
|
2221
|
-
def test_upsert_all_implicitly_sets_timestamps_on_create_when_model_record_timestamps_is_true_coerced
|
2222
|
-
assert_raises(ArgumentError, /does not support upsert/) do
|
2223
|
-
original_test_upsert_all_implicitly_sets_timestamps_on_create_when_model_record_timestamps_is_true
|
2224
|
-
end
|
2225
|
-
end
|
2226
|
-
|
2227
|
-
coerce_tests! :test_upsert_all_respects_created_at_precision_when_touched_implicitly
|
2228
|
-
def test_upsert_all_respects_created_at_precision_when_touched_implicitly_coerced
|
2229
|
-
assert_raises(ArgumentError, /does not support upsert/) do
|
2230
|
-
original_test_upsert_all_respects_created_at_precision_when_touched_implicitly
|
2231
|
-
end
|
2232
|
-
end
|
2233
|
-
|
2234
|
-
coerce_tests! :test_upsert_all_does_not_implicitly_set_timestamps_on_create_when_model_record_timestamps_is_true_but_overridden
|
2235
|
-
def test_upsert_all_does_not_implicitly_set_timestamps_on_create_when_model_record_timestamps_is_true_but_overridden_coerced
|
2236
|
-
assert_raises(ArgumentError, /does not support upsert/) do
|
2237
|
-
original_test_upsert_all_does_not_implicitly_set_timestamps_on_create_when_model_record_timestamps_is_true_but_overridden
|
2238
|
-
end
|
2239
|
-
end
|
2240
|
-
|
2241
|
-
coerce_tests! :test_upsert_all_does_not_implicitly_set_timestamps_on_create_when_model_record_timestamps_is_false
|
2242
|
-
def test_upsert_all_does_not_implicitly_set_timestamps_on_create_when_model_record_timestamps_is_false_coerced
|
2243
|
-
assert_raises(ArgumentError, /does not support upsert/) do
|
2244
|
-
original_test_upsert_all_does_not_implicitly_set_timestamps_on_create_when_model_record_timestamps_is_false
|
2245
|
-
end
|
2246
|
-
end
|
2526
|
+
# Same as original but using INSERTED.name as UPPER argument
|
2527
|
+
coerce_tests! :test_insert_all_returns_requested_sql_fields
|
2528
|
+
def test_insert_all_returns_requested_sql_fields_coerced
|
2529
|
+
skip unless supports_insert_returning?
|
2247
2530
|
|
2248
|
-
|
2249
|
-
|
2250
|
-
assert_raises(ArgumentError, /does not support upsert/) do
|
2251
|
-
original_test_upsert_all_implicitly_sets_timestamps_on_create_when_model_record_timestamps_is_false_but_overridden
|
2252
|
-
end
|
2531
|
+
result = Book.insert_all! [{ name: "Rework", author_id: 1 }], returning: Arel.sql("UPPER(INSERTED.name) as name")
|
2532
|
+
assert_equal %w[ REWORK ], result.pluck("name")
|
2253
2533
|
end
|
2254
2534
|
end
|
2255
2535
|
|
@@ -2272,17 +2552,6 @@ class HasOneThroughDisableJoinsAssociationsTest < ActiveRecord::TestCase
|
|
2272
2552
|
end
|
2273
2553
|
end
|
2274
2554
|
|
2275
|
-
class InsertAllTest < ActiveRecord::TestCase
|
2276
|
-
coerce_tests! :test_insert_all_returns_requested_sql_fields
|
2277
|
-
# Same as original but using INSERTED.name as UPPER argument
|
2278
|
-
def test_insert_all_returns_requested_sql_fields_coerced
|
2279
|
-
skip unless supports_insert_returning?
|
2280
|
-
|
2281
|
-
result = Book.insert_all! [{ name: "Rework", author_id: 1 }], returning: Arel.sql("UPPER(INSERTED.name) as name")
|
2282
|
-
assert_equal %w[ REWORK ], result.pluck("name")
|
2283
|
-
end
|
2284
|
-
end
|
2285
|
-
|
2286
2555
|
class ActiveRecord::Encryption::EncryptableRecordTest < ActiveRecord::EncryptionTestCase
|
2287
2556
|
# TODO: Remove coerce after Rails 7.1.0 (see https://github.com/rails/rails/pull/44052)
|
2288
2557
|
# Same as original but SQL Server string is varchar(4000), not varchar(255) as other adapters. Produce invalid strings with 4001 characters
|
@@ -2294,3 +2563,99 @@ class ActiveRecord::Encryption::EncryptableRecordTest < ActiveRecord::Encryption
|
|
2294
2563
|
assert_not author.valid?
|
2295
2564
|
end
|
2296
2565
|
end
|
2566
|
+
|
2567
|
+
module ActiveRecord
|
2568
|
+
class Migration
|
2569
|
+
class InvalidOptionsTest < ActiveRecord::TestCase
|
2570
|
+
# Include the additional SQL Server migration options.
|
2571
|
+
undef_method :invalid_add_column_option_exception_message
|
2572
|
+
def invalid_add_column_option_exception_message(key)
|
2573
|
+
default_keys = [":limit", ":precision", ":scale", ":default", ":null", ":collation", ":comment", ":primary_key", ":if_exists", ":if_not_exists"]
|
2574
|
+
default_keys.concat([":is_identity"]) # SQL Server additional valid keys
|
2575
|
+
|
2576
|
+
"Unknown key: :#{key}. Valid keys are: #{default_keys.join(", ")}"
|
2577
|
+
end
|
2578
|
+
end
|
2579
|
+
end
|
2580
|
+
end
|
2581
|
+
|
2582
|
+
# SQL Server does not support upsert. Removed dependency on `insert_all` that uses upsert.
|
2583
|
+
class ActiveRecord::Encryption::ConcurrencyTest < ActiveRecord::EncryptionTestCase
|
2584
|
+
undef_method :thread_encrypting_and_decrypting
|
2585
|
+
def thread_encrypting_and_decrypting(thread_label)
|
2586
|
+
posts = 100.times.collect { |index| EncryptedPost.create! title: "Article #{index} (#{thread_label})", body: "Body #{index} (#{thread_label})" }
|
2587
|
+
|
2588
|
+
Thread.new do
|
2589
|
+
posts.each.with_index do |article, index|
|
2590
|
+
assert_encrypted_attribute article, :title, "Article #{index} (#{thread_label})"
|
2591
|
+
article.decrypt
|
2592
|
+
assert_not_encrypted_attribute article, :title, "Article #{index} (#{thread_label})"
|
2593
|
+
end
|
2594
|
+
end
|
2595
|
+
end
|
2596
|
+
end
|
2597
|
+
|
2598
|
+
# Need to use `install_unregistered_type_fallback` instead of `install_unregistered_type_error` so that message-pack
|
2599
|
+
# can read and write `ActiveRecord::ConnectionAdapters::SQLServer::Type::Data` objects.
|
2600
|
+
class ActiveRecordMessagePackTest < ActiveRecord::TestCase
|
2601
|
+
private
|
2602
|
+
undef_method :serializer
|
2603
|
+
def serializer
|
2604
|
+
@serializer ||= ::MessagePack::Factory.new.tap do |factory|
|
2605
|
+
ActiveRecord::MessagePack::Extensions.install(factory)
|
2606
|
+
ActiveSupport::MessagePack::Extensions.install(factory)
|
2607
|
+
ActiveSupport::MessagePack::Extensions.install_unregistered_type_fallback(factory)
|
2608
|
+
end
|
2609
|
+
end
|
2610
|
+
end
|
2611
|
+
|
2612
|
+
class StoreTest < ActiveRecord::TestCase
|
2613
|
+
# Set the attribute as JSON type for the `StoreTest#saved changes tracking for accessors with json column` test.
|
2614
|
+
Admin::User.attribute :json_options, ActiveRecord::Type::SQLServer::Json.new
|
2615
|
+
end
|
2616
|
+
|
2617
|
+
class TestDatabasesTest < ActiveRecord::TestCase
|
2618
|
+
# Tests are not about a specific adapter.
|
2619
|
+
coerce_all_tests!
|
2620
|
+
end
|
2621
|
+
|
2622
|
+
module ActiveRecord
|
2623
|
+
module ConnectionAdapters
|
2624
|
+
class ConnectionHandlersShardingDbTest < ActiveRecord::TestCase
|
2625
|
+
# Tests are not about a specific adapter.
|
2626
|
+
coerce_all_tests!
|
2627
|
+
end
|
2628
|
+
end
|
2629
|
+
end
|
2630
|
+
|
2631
|
+
module ActiveRecord
|
2632
|
+
module ConnectionAdapters
|
2633
|
+
class ConnectionSwappingNestedTest < ActiveRecord::TestCase
|
2634
|
+
# Tests are not about a specific adapter.
|
2635
|
+
coerce_all_tests!
|
2636
|
+
end
|
2637
|
+
end
|
2638
|
+
end
|
2639
|
+
|
2640
|
+
module ActiveRecord
|
2641
|
+
module ConnectionAdapters
|
2642
|
+
class ConnectionHandlersMultiDbTest < ActiveRecord::TestCase
|
2643
|
+
# Tests are not about a specific adapter.
|
2644
|
+
coerce_tests! :test_switching_connections_via_handler
|
2645
|
+
end
|
2646
|
+
end
|
2647
|
+
end
|
2648
|
+
|
2649
|
+
module ActiveRecord
|
2650
|
+
module ConnectionAdapters
|
2651
|
+
class ConnectionHandlersMultiPoolConfigTest < ActiveRecord::TestCase
|
2652
|
+
# Tests are not about a specific adapter.
|
2653
|
+
coerce_all_tests!
|
2654
|
+
end
|
2655
|
+
end
|
2656
|
+
end
|
2657
|
+
|
2658
|
+
# TODO: Need to uncoerce the 'SerializedAttributeTest' tests before releasing adapter for Rails 7.1
|
2659
|
+
class SerializedAttributeTest < ActiveRecord::TestCase
|
2660
|
+
coerce_all_tests!
|
2661
|
+
end
|