activerecord-sqlserver-adapter 7.0.7 → 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 -2
- data/CHANGELOG.md +2 -94
- 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 +42 -0
- 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 -58
- 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 -118
- 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 +558 -248
- 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 +6 -10
- 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 +15 -9
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
|
@@ -1450,12 +1606,8 @@ class SchemaDumperTest < ActiveRecord::TestCase
|
|
1450
1606
|
assert_match %r{precision: 3,[[:space:]]+scale: 2,[[:space:]]+default: 2\.78}, output
|
1451
1607
|
end
|
1452
1608
|
|
1453
|
-
#
|
1454
|
-
coerce_tests! :
|
1455
|
-
def test_schema_dumps_check_constraints_coerced
|
1456
|
-
constraint_definition = dump_table_schema("products").split(/\n/).grep(/t.check_constraint.*products_price_check/).first.strip
|
1457
|
-
assert_equal 't.check_constraint "[price]>[discounted_price]", name: "products_price_check"', constraint_definition
|
1458
|
-
end
|
1609
|
+
# Tests are not about a specific adapter.
|
1610
|
+
coerce_tests! :test_do_not_dump_foreign_keys_when_bypassed_by_config
|
1459
1611
|
end
|
1460
1612
|
|
1461
1613
|
class SchemaDumperDefaultsTest < ActiveRecord::TestCase
|
@@ -1499,16 +1651,95 @@ end
|
|
1499
1651
|
|
1500
1652
|
require "models/topic"
|
1501
1653
|
class TransactionTest < ActiveRecord::TestCase
|
1502
|
-
# SQL Server does not have query for release_savepoint
|
1654
|
+
# SQL Server does not have query for release_savepoint.
|
1503
1655
|
coerce_tests! :test_releasing_named_savepoints
|
1504
1656
|
def test_releasing_named_savepoints_coerced
|
1505
1657
|
Topic.transaction do
|
1658
|
+
Topic.connection.materialize_transactions
|
1659
|
+
|
1506
1660
|
Topic.connection.create_savepoint("another")
|
1507
1661
|
Topic.connection.release_savepoint("another")
|
1508
1662
|
# We do not have a notion of releasing, so this does nothing vs raise an error.
|
1509
1663
|
Topic.connection.release_savepoint("another")
|
1510
1664
|
end
|
1511
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
|
1512
1743
|
end
|
1513
1744
|
|
1514
1745
|
require "models/tag"
|
@@ -1637,8 +1868,8 @@ class DefaultNumbersTest < ActiveRecord::TestCase
|
|
1637
1868
|
coerce_tests! :test_default_negative_integer
|
1638
1869
|
def test_default_negative_integer_coerced
|
1639
1870
|
record = DefaultNumber.new
|
1640
|
-
assert_equal -5, record.negative_integer
|
1641
|
-
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
|
1642
1873
|
end
|
1643
1874
|
|
1644
1875
|
# We do better with native types and do not return strings for everything.
|
@@ -1727,8 +1958,9 @@ module ActiveRecord
|
|
1727
1958
|
end
|
1728
1959
|
|
1729
1960
|
# We need to give the full path for this to work.
|
1961
|
+
undef_method :schema_dump_path
|
1730
1962
|
def schema_dump_path
|
1731
|
-
File.join
|
1963
|
+
File.join(ARTest::SQLServer.root_activerecord, "test/assets/schema_dump_5_1.yml")
|
1732
1964
|
end
|
1733
1965
|
end
|
1734
1966
|
end
|
@@ -1739,7 +1971,7 @@ require "models/comment"
|
|
1739
1971
|
class UnsafeRawSqlTest < ActiveRecord::TestCase
|
1740
1972
|
fixtures :posts
|
1741
1973
|
|
1742
|
-
# Use LEN()
|
1974
|
+
# Use LEN() instead of LENGTH() function.
|
1743
1975
|
coerce_tests! %r{order: always allows Arel}
|
1744
1976
|
test "order: always allows Arel" do
|
1745
1977
|
titles = Post.order(Arel.sql("len(title)")).pluck(:title)
|
@@ -1747,7 +1979,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
|
|
1747
1979
|
assert_not_empty titles
|
1748
1980
|
end
|
1749
1981
|
|
1750
|
-
# Use LEN()
|
1982
|
+
# Use LEN() instead of LENGTH() function.
|
1751
1983
|
coerce_tests! %r{pluck: always allows Arel}
|
1752
1984
|
test "pluck: always allows Arel" do
|
1753
1985
|
excepted_values = Post.includes(:comments).pluck(:title).map { |title| [title, title.size] }
|
@@ -1756,7 +1988,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
|
|
1756
1988
|
assert_equal excepted_values, values
|
1757
1989
|
end
|
1758
1990
|
|
1759
|
-
# Use LEN()
|
1991
|
+
# Use LEN() instead of LENGTH() function.
|
1760
1992
|
coerce_tests! %r{order: allows valid Array arguments}
|
1761
1993
|
test "order: allows valid Array arguments" do
|
1762
1994
|
ids_expected = Post.order(Arel.sql("author_id, len(title)")).pluck(:id)
|
@@ -1766,6 +1998,27 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
|
|
1766
1998
|
assert_equal ids_expected, ids
|
1767
1999
|
end
|
1768
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
|
+
|
1769
2022
|
test "order: allows string column names that are quoted" do
|
1770
2023
|
ids_expected = Post.order(Arel.sql("id")).pluck(:id)
|
1771
2024
|
|
@@ -1829,6 +2082,18 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
|
|
1829
2082
|
|
1830
2083
|
assert_equal titles_expected, titles
|
1831
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
|
1832
2097
|
end
|
1833
2098
|
|
1834
2099
|
class ReservedWordTest < ActiveRecord::TestCase
|
@@ -2028,14 +2293,15 @@ class LogSubscriberTest < ActiveRecord::TestCase
|
|
2028
2293
|
end
|
2029
2294
|
end
|
2030
2295
|
|
2031
|
-
|
2032
|
-
|
2033
|
-
|
2034
|
-
|
2035
|
-
|
2036
|
-
|
2037
|
-
|
2038
|
-
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
|
2039
2305
|
|
2040
2306
|
class ReloadModelsTest < ActiveRecord::TestCase
|
2041
2307
|
# Skip test on Windows. The number of arguments passed to `IO.popen` in
|
@@ -2046,6 +2312,7 @@ end
|
|
2046
2312
|
class MarshalSerializationTest < ActiveRecord::TestCase
|
2047
2313
|
private
|
2048
2314
|
|
2315
|
+
undef_method :marshal_fixture_path
|
2049
2316
|
def marshal_fixture_path(file_name)
|
2050
2317
|
File.expand_path(
|
2051
2318
|
"support/marshal_compatibility_fixtures/#{ActiveRecord::Base.connection.adapter_name}/#{file_name}.dump",
|
@@ -2080,6 +2347,59 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
|
|
2080
2347
|
end
|
2081
2348
|
end
|
2082
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
|
+
|
2083
2403
|
class BasePreventWritesTest < ActiveRecord::TestCase
|
2084
2404
|
# SQL Server does not have query for release_savepoint
|
2085
2405
|
coerce_tests! %r{an empty transaction does not raise if preventing writes}
|
@@ -2092,20 +2412,6 @@ class BasePreventWritesTest < ActiveRecord::TestCase
|
|
2092
2412
|
end
|
2093
2413
|
end
|
2094
2414
|
end
|
2095
|
-
|
2096
|
-
class BasePreventWritesLegacyTest < ActiveRecord::TestCase
|
2097
|
-
# SQL Server does not have query for release_savepoint
|
2098
|
-
coerce_tests! %r{an empty transaction does not raise if preventing writes}
|
2099
|
-
test "an empty transaction does not raise if preventing writes coerced" do
|
2100
|
-
ActiveRecord::Base.connection_handler.while_preventing_writes do
|
2101
|
-
assert_queries(1, ignore_none: true) do
|
2102
|
-
Bird.transaction do
|
2103
|
-
ActiveRecord::Base.connection.materialize_transactions
|
2104
|
-
end
|
2105
|
-
end
|
2106
|
-
end
|
2107
|
-
end
|
2108
|
-
end
|
2109
2415
|
end
|
2110
2416
|
|
2111
2417
|
class MigratorTest < ActiveRecord::TestCase
|
@@ -2167,96 +2473,63 @@ end
|
|
2167
2473
|
|
2168
2474
|
require "models/dashboard"
|
2169
2475
|
class QueryLogsTest < ActiveRecord::TestCase
|
2170
|
-
#
|
2171
|
-
|
2172
|
-
|
2173
|
-
|
2174
|
-
|
2175
|
-
|
2176
|
-
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
|
2177
2481
|
Dashboard.first
|
2178
2482
|
end
|
2179
2483
|
end
|
2180
2484
|
|
2181
|
-
|
2182
|
-
|
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
|
+
]
|
2183
2494
|
|
2184
|
-
assert_sql(%r{
|
2495
|
+
assert_sql(%r{custom_proc=''Joe%27s%20Shack'',tracestate=''congo%3Dt61rcWkgMzE%2Crojo%3D00f067aa0ba902b7''\*/}) do
|
2185
2496
|
Dashboard.first
|
2186
2497
|
end
|
2187
2498
|
end
|
2188
2499
|
|
2189
|
-
|
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
|
+
|
2190
2505
|
ActiveRecord::QueryLogs.tags = [
|
2191
2506
|
:application,
|
2192
|
-
{ custom_proc: -> {
|
2507
|
+
{ custom_proc: -> { 1234 } },
|
2193
2508
|
]
|
2194
2509
|
|
2195
|
-
assert_sql(%r{
|
2510
|
+
assert_sql(%r{custom_proc=''1234''\*/}) do
|
2196
2511
|
Dashboard.first
|
2197
2512
|
end
|
2198
2513
|
end
|
2199
2514
|
|
2200
|
-
|
2201
|
-
|
2202
|
-
|
2203
|
-
|
2204
|
-
|
2205
|
-
|
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'"
|
2206
2521
|
end
|
2207
2522
|
end
|
2208
2523
|
end
|
2209
2524
|
|
2210
|
-
# SQL Server does not support upsert yet
|
2211
|
-
# TODO: Remove coerce after Rails 7.1.0 (see https://github.com/rails/rails/pull/44050)
|
2212
2525
|
class InsertAllTest < ActiveRecord::TestCase
|
2213
|
-
|
2214
|
-
|
2215
|
-
|
2216
|
-
|
2217
|
-
end
|
2218
|
-
end
|
2219
|
-
|
2220
|
-
coerce_tests! :test_upsert_all_only_updates_the_list_of_columns_provided_via_update_only
|
2221
|
-
def test_upsert_all_only_updates_the_list_of_columns_provided_via_update_only_coerced
|
2222
|
-
assert_raises(ArgumentError, /does not support upsert/) do
|
2223
|
-
original_test_upsert_all_only_updates_the_list_of_columns_provided_via_update_only
|
2224
|
-
end
|
2225
|
-
end
|
2226
|
-
|
2227
|
-
coerce_tests! :test_upsert_all_implicitly_sets_timestamps_on_create_when_model_record_timestamps_is_true
|
2228
|
-
def test_upsert_all_implicitly_sets_timestamps_on_create_when_model_record_timestamps_is_true_coerced
|
2229
|
-
assert_raises(ArgumentError, /does not support upsert/) do
|
2230
|
-
original_test_upsert_all_implicitly_sets_timestamps_on_create_when_model_record_timestamps_is_true
|
2231
|
-
end
|
2232
|
-
end
|
2233
|
-
|
2234
|
-
coerce_tests! :test_upsert_all_respects_created_at_precision_when_touched_implicitly
|
2235
|
-
def test_upsert_all_respects_created_at_precision_when_touched_implicitly_coerced
|
2236
|
-
assert_raises(ArgumentError, /does not support upsert/) do
|
2237
|
-
original_test_upsert_all_respects_created_at_precision_when_touched_implicitly
|
2238
|
-
end
|
2239
|
-
end
|
2240
|
-
|
2241
|
-
coerce_tests! :test_upsert_all_does_not_implicitly_set_timestamps_on_create_when_model_record_timestamps_is_true_but_overridden
|
2242
|
-
def test_upsert_all_does_not_implicitly_set_timestamps_on_create_when_model_record_timestamps_is_true_but_overridden_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_true_but_overridden
|
2245
|
-
end
|
2246
|
-
end
|
2247
|
-
|
2248
|
-
coerce_tests! :test_upsert_all_does_not_implicitly_set_timestamps_on_create_when_model_record_timestamps_is_false
|
2249
|
-
def test_upsert_all_does_not_implicitly_set_timestamps_on_create_when_model_record_timestamps_is_false_coerced
|
2250
|
-
assert_raises(ArgumentError, /does not support upsert/) do
|
2251
|
-
original_test_upsert_all_does_not_implicitly_set_timestamps_on_create_when_model_record_timestamps_is_false
|
2252
|
-
end
|
2253
|
-
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?
|
2254
2530
|
|
2255
|
-
|
2256
|
-
|
2257
|
-
assert_raises(ArgumentError, /does not support upsert/) do
|
2258
|
-
original_test_upsert_all_implicitly_sets_timestamps_on_create_when_model_record_timestamps_is_false_but_overridden
|
2259
|
-
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")
|
2260
2533
|
end
|
2261
2534
|
end
|
2262
2535
|
|
@@ -2279,17 +2552,6 @@ class HasOneThroughDisableJoinsAssociationsTest < ActiveRecord::TestCase
|
|
2279
2552
|
end
|
2280
2553
|
end
|
2281
2554
|
|
2282
|
-
class InsertAllTest < ActiveRecord::TestCase
|
2283
|
-
coerce_tests! :test_insert_all_returns_requested_sql_fields
|
2284
|
-
# Same as original but using INSERTED.name as UPPER argument
|
2285
|
-
def test_insert_all_returns_requested_sql_fields_coerced
|
2286
|
-
skip unless supports_insert_returning?
|
2287
|
-
|
2288
|
-
result = Book.insert_all! [{ name: "Rework", author_id: 1 }], returning: Arel.sql("UPPER(INSERTED.name) as name")
|
2289
|
-
assert_equal %w[ REWORK ], result.pluck("name")
|
2290
|
-
end
|
2291
|
-
end
|
2292
|
-
|
2293
2555
|
class ActiveRecord::Encryption::EncryptableRecordTest < ActiveRecord::EncryptionTestCase
|
2294
2556
|
# TODO: Remove coerce after Rails 7.1.0 (see https://github.com/rails/rails/pull/44052)
|
2295
2557
|
# Same as original but SQL Server string is varchar(4000), not varchar(255) as other adapters. Produce invalid strings with 4001 characters
|
@@ -2304,48 +2566,96 @@ end
|
|
2304
2566
|
|
2305
2567
|
module ActiveRecord
|
2306
2568
|
class Migration
|
2307
|
-
class
|
2308
|
-
#
|
2309
|
-
|
2310
|
-
def
|
2311
|
-
|
2312
|
-
|
2313
|
-
|
2314
|
-
|
2315
|
-
assert_equal "products", constraint.table_name
|
2316
|
-
assert_equal "products_price_check", constraint.name
|
2317
|
-
assert_equal "[price]>[discounted_price]", constraint.expression
|
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(", ")}"
|
2318
2577
|
end
|
2578
|
+
end
|
2579
|
+
end
|
2580
|
+
end
|
2319
2581
|
|
2320
|
-
|
2321
|
-
|
2322
|
-
|
2323
|
-
|
2324
|
-
|
2325
|
-
check_constraints = @connection.check_constraints("trades")
|
2326
|
-
assert_equal 1, check_constraints.size
|
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})" }
|
2327
2587
|
|
2328
|
-
|
2329
|
-
|
2330
|
-
|
2331
|
-
|
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})"
|
2332
2593
|
end
|
2594
|
+
end
|
2595
|
+
end
|
2596
|
+
end
|
2333
2597
|
|
2334
|
-
|
2335
|
-
|
2336
|
-
|
2337
|
-
|
2338
|
-
|
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
|
2339
2611
|
|
2340
|
-
|
2341
|
-
|
2342
|
-
|
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
|
2343
2616
|
|
2344
|
-
|
2345
|
-
|
2346
|
-
|
2347
|
-
|
2348
|
-
|
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
|
2349
2645
|
end
|
2350
2646
|
end
|
2351
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
|