activerecord-sqlserver-adapter 7.0.5.1 → 7.1.0.rc1
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 +38 -83
- data/Gemfile +3 -0
- data/README.md +16 -11
- data/RUNNING_UNIT_TESTS.md +24 -10
- 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 +86 -133
- 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 +68 -29
- 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 +526 -235
- 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/transaction_test_sqlserver.rb +13 -8
- 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
- data/test/support/sql_counter_sqlserver.rb +0 -15
- metadata +14 -8
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,52 +921,16 @@ 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
|
-
end
|
|
852
|
-
|
|
853
|
-
require "models/post"
|
|
854
|
-
require "models/subscriber"
|
|
855
|
-
class EachTest < ActiveRecord::TestCase
|
|
856
|
-
# Quoting in tests does not cope with bracket quoting.
|
|
857
|
-
coerce_tests! :test_find_in_batches_should_quote_batch_order
|
|
858
|
-
def test_find_in_batches_should_quote_batch_order_coerced
|
|
859
|
-
Post.connection
|
|
860
|
-
assert_sql(/ORDER BY \[posts\]\.\[id\]/) do
|
|
861
|
-
Post.find_in_batches(:batch_size => 1) do |batch|
|
|
862
|
-
assert_kind_of Array, batch
|
|
863
|
-
assert_kind_of Post, batch.first
|
|
864
|
-
end
|
|
865
|
-
end
|
|
866
|
-
end
|
|
867
|
-
|
|
868
|
-
# Quoting in tests does not cope with bracket quoting.
|
|
869
|
-
coerce_tests! :test_in_batches_should_quote_batch_order
|
|
870
|
-
def test_in_batches_should_quote_batch_order_coerced
|
|
871
|
-
Post.connection
|
|
872
|
-
assert_sql(/ORDER BY \[posts\]\.\[id\]/) do
|
|
873
|
-
Post.in_batches(of: 1) do |relation|
|
|
874
|
-
assert_kind_of ActiveRecord::Relation, relation
|
|
875
|
-
assert_kind_of Post, relation.first
|
|
876
|
-
end
|
|
877
|
-
end
|
|
878
|
-
end
|
|
879
924
|
end
|
|
880
925
|
|
|
881
926
|
class EagerAssociationTest < ActiveRecord::TestCase
|
|
882
|
-
# Use LEN()
|
|
927
|
+
# Use LEN() instead of LENGTH() function.
|
|
883
928
|
coerce_tests! :test_count_with_include
|
|
884
929
|
def test_count_with_include_coerced
|
|
885
930
|
assert_equal 3, authors(:david).posts_with_comments.where("LEN(comments.body) > 15").references(:comments).count
|
|
886
931
|
end
|
|
887
932
|
|
|
888
|
-
#
|
|
933
|
+
# The raw SQL in the scope uses `limit 1`.
|
|
889
934
|
coerce_tests! %r{including association based on sql condition and no database column}
|
|
890
935
|
end
|
|
891
936
|
|
|
@@ -899,14 +944,6 @@ class FinderTest < ActiveRecord::TestCase
|
|
|
899
944
|
coerce_tests! %r{doesn't have implicit ordering},
|
|
900
945
|
:test_find_doesnt_have_implicit_ordering
|
|
901
946
|
|
|
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
947
|
# Assert SQL Server limit implementation
|
|
911
948
|
coerce_tests! :test_take_and_first_and_last_with_integer_should_use_sql_limit
|
|
912
949
|
def test_take_and_first_and_last_with_integer_should_use_sql_limit_coerced
|
|
@@ -1018,6 +1055,92 @@ class FinderTest < ActiveRecord::TestCase
|
|
|
1018
1055
|
NonPrimaryKey.implicit_order_column = old_implicit_order_column
|
|
1019
1056
|
end
|
|
1020
1057
|
|
|
1058
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
|
1059
|
+
coerce_tests! :test_member_on_unloaded_relation_with_composite_primary_key
|
|
1060
|
+
def test_member_on_unloaded_relation_with_composite_primary_key_coerced
|
|
1061
|
+
assert_sql(/1 AS one.* FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/) do
|
|
1062
|
+
book = cpk_books(:cpk_great_author_first_book)
|
|
1063
|
+
assert Cpk::Book.where(title: "The first book").member?(book)
|
|
1064
|
+
end
|
|
1065
|
+
end
|
|
1066
|
+
|
|
1067
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
|
1068
|
+
coerce_tests! :test_implicit_order_column_prepends_query_constraints
|
|
1069
|
+
def test_implicit_order_column_prepends_query_constraints_coerced
|
|
1070
|
+
c = ClothingItem.connection
|
|
1071
|
+
ClothingItem.implicit_order_column = "description"
|
|
1072
|
+
quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type"))
|
|
1073
|
+
quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color"))
|
|
1074
|
+
quoted_descrption = Regexp.escape(c.quote_table_name("clothing_items.description"))
|
|
1075
|
+
|
|
1076
|
+
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
|
|
1077
|
+
assert_kind_of ClothingItem, ClothingItem.first
|
|
1078
|
+
end
|
|
1079
|
+
ensure
|
|
1080
|
+
ClothingItem.implicit_order_column = nil
|
|
1081
|
+
end
|
|
1082
|
+
|
|
1083
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
|
1084
|
+
coerce_tests! %r{#last for a model with composite query constraints}
|
|
1085
|
+
test "#last for a model with composite query constraints coerced" do
|
|
1086
|
+
c = ClothingItem.connection
|
|
1087
|
+
quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type"))
|
|
1088
|
+
quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color"))
|
|
1089
|
+
|
|
1090
|
+
assert_sql(/ORDER BY #{quoted_type} DESC, #{quoted_color} DESC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do
|
|
1091
|
+
assert_kind_of ClothingItem, ClothingItem.last
|
|
1092
|
+
end
|
|
1093
|
+
end
|
|
1094
|
+
|
|
1095
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
|
1096
|
+
coerce_tests! %r{#first for a model with composite query constraints}
|
|
1097
|
+
test "#first for a model with composite query constraints coerced" do
|
|
1098
|
+
c = ClothingItem.connection
|
|
1099
|
+
quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type"))
|
|
1100
|
+
quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color"))
|
|
1101
|
+
|
|
1102
|
+
assert_sql(/ORDER BY #{quoted_type} ASC, #{quoted_color} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do
|
|
1103
|
+
assert_kind_of ClothingItem, ClothingItem.first
|
|
1104
|
+
end
|
|
1105
|
+
end
|
|
1106
|
+
|
|
1107
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
|
1108
|
+
coerce_tests! :test_implicit_order_column_reorders_query_constraints
|
|
1109
|
+
def test_implicit_order_column_reorders_query_constraints_coerced
|
|
1110
|
+
c = ClothingItem.connection
|
|
1111
|
+
ClothingItem.implicit_order_column = "color"
|
|
1112
|
+
quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type"))
|
|
1113
|
+
quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color"))
|
|
1114
|
+
|
|
1115
|
+
assert_sql(/ORDER BY #{quoted_color} ASC, #{quoted_type} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do
|
|
1116
|
+
assert_kind_of ClothingItem, ClothingItem.first
|
|
1117
|
+
end
|
|
1118
|
+
ensure
|
|
1119
|
+
ClothingItem.implicit_order_column = nil
|
|
1120
|
+
end
|
|
1121
|
+
|
|
1122
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
|
1123
|
+
coerce_tests! :test_include_on_unloaded_relation_with_composite_primary_key
|
|
1124
|
+
def test_include_on_unloaded_relation_with_composite_primary_key_coerced
|
|
1125
|
+
assert_sql(/1 AS one.*OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/) do
|
|
1126
|
+
book = cpk_books(:cpk_great_author_first_book)
|
|
1127
|
+
assert Cpk::Book.where(title: "The first book").include?(book)
|
|
1128
|
+
end
|
|
1129
|
+
end
|
|
1130
|
+
|
|
1131
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
|
1132
|
+
coerce_tests! :test_nth_to_last_with_order_uses_limit
|
|
1133
|
+
def test_nth_to_last_with_order_uses_limit_coerced
|
|
1134
|
+
c = Topic.connection
|
|
1135
|
+
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
|
|
1136
|
+
Topic.second_to_last
|
|
1137
|
+
end
|
|
1138
|
+
|
|
1139
|
+
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
|
|
1140
|
+
Topic.order(:updated_at).second_to_last
|
|
1141
|
+
end
|
|
1142
|
+
end
|
|
1143
|
+
|
|
1021
1144
|
# SQL Server is unable to use aliased SELECT in the HAVING clause.
|
|
1022
1145
|
coerce_tests! :test_include_on_unloaded_relation_with_having_referencing_aliased_select
|
|
1023
1146
|
end
|
|
@@ -1025,7 +1148,7 @@ end
|
|
|
1025
1148
|
module ActiveRecord
|
|
1026
1149
|
class Migration
|
|
1027
1150
|
class ForeignKeyTest < ActiveRecord::TestCase
|
|
1028
|
-
#
|
|
1151
|
+
# SQL Server does not support 'restrict' for 'on_update' or 'on_delete'.
|
|
1029
1152
|
coerce_tests! :test_add_on_delete_restrict_foreign_key
|
|
1030
1153
|
def test_add_on_delete_restrict_foreign_key_coerced
|
|
1031
1154
|
assert_raises ArgumentError do
|
|
@@ -1079,27 +1202,6 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
|
|
|
1079
1202
|
end
|
|
1080
1203
|
end
|
|
1081
1204
|
|
|
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
1205
|
class LeftOuterJoinAssociationTest < ActiveRecord::TestCase
|
|
1104
1206
|
# Uses || operator in SQL. Just trust core gets value out of this test.
|
|
1105
1207
|
coerce_tests! :test_does_not_override_select
|
|
@@ -1263,14 +1365,14 @@ end
|
|
|
1263
1365
|
|
|
1264
1366
|
require "models/post"
|
|
1265
1367
|
class RelationTest < ActiveRecord::TestCase
|
|
1266
|
-
# Use LEN
|
|
1368
|
+
# Use LEN() instead of LENGTH() function.
|
|
1267
1369
|
coerce_tests! :test_reverse_order_with_function
|
|
1268
1370
|
def test_reverse_order_with_function_coerced
|
|
1269
1371
|
topics = Topic.order(Arel.sql("LEN(title)")).reverse_order
|
|
1270
1372
|
assert_equal topics(:second).title, topics.first.title
|
|
1271
1373
|
end
|
|
1272
1374
|
|
|
1273
|
-
# Use LEN
|
|
1375
|
+
# Use LEN() instead of LENGTH() function.
|
|
1274
1376
|
coerce_tests! :test_reverse_order_with_function_other_predicates
|
|
1275
1377
|
def test_reverse_order_with_function_other_predicates_coerced
|
|
1276
1378
|
topics = Topic.order(Arel.sql("author_name, LEN(title), id")).reverse_order
|
|
@@ -1288,7 +1390,7 @@ class RelationTest < ActiveRecord::TestCase
|
|
|
1288
1390
|
sql_log = capture_sql do
|
|
1289
1391
|
assert Post.order(:title).reorder(nil).take
|
|
1290
1392
|
end
|
|
1291
|
-
assert sql_log.none? { |sql| /order by [posts]
|
|
1393
|
+
assert sql_log.none? { |sql| /order by \[posts\]\.\[title\]/i.match?(sql) }, "ORDER BY title was used in the query: #{sql_log}"
|
|
1292
1394
|
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
1395
|
end
|
|
1294
1396
|
|
|
@@ -1300,7 +1402,7 @@ class RelationTest < ActiveRecord::TestCase
|
|
|
1300
1402
|
post = Post.order(:title).reorder(nil).first
|
|
1301
1403
|
end
|
|
1302
1404
|
assert_equal posts(:welcome), post
|
|
1303
|
-
assert sql_log.none? { |sql| /order by [posts]
|
|
1405
|
+
assert sql_log.none? { |sql| /order by \[posts\]\.\[title\]/i.match?(sql) }, "ORDER BY title was used in the query: #{sql_log}"
|
|
1304
1406
|
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
1407
|
end
|
|
1306
1408
|
|
|
@@ -1339,6 +1441,14 @@ class RelationTest < ActiveRecord::TestCase
|
|
|
1339
1441
|
end
|
|
1340
1442
|
end
|
|
1341
1443
|
|
|
1444
|
+
# Find any limit via our expression.
|
|
1445
|
+
coerce_tests! %r{relations don't load all records in #pretty_print}
|
|
1446
|
+
def test_relations_dont_load_all_records_in_pretty_print_coerced
|
|
1447
|
+
assert_sql(/FETCH NEXT @(\d) ROWS ONLY/) do
|
|
1448
|
+
PP.pp Post.all, StringIO.new # avoid outputting.
|
|
1449
|
+
end
|
|
1450
|
+
end
|
|
1451
|
+
|
|
1342
1452
|
# Order column must be in the GROUP clause.
|
|
1343
1453
|
coerce_tests! :test_empty_complex_chained_relations
|
|
1344
1454
|
def test_empty_complex_chained_relations_coerced
|
|
@@ -1368,7 +1478,7 @@ class RelationTest < ActiveRecord::TestCase
|
|
|
1368
1478
|
assert_equal post, custom_post_relation.joins(:author).where!(title: post.title).order(:id).take
|
|
1369
1479
|
end
|
|
1370
1480
|
|
|
1371
|
-
# Use LEN()
|
|
1481
|
+
# Use LEN() instead of LENGTH() function.
|
|
1372
1482
|
coerce_tests! :test_reverse_arel_assoc_order_with_function
|
|
1373
1483
|
def test_reverse_arel_assoc_order_with_function_coerced
|
|
1374
1484
|
topics = Topic.order(Arel.sql("LEN(title)") => :asc).reverse_order
|
|
@@ -1388,15 +1498,6 @@ module ActiveRecord
|
|
|
1388
1498
|
query = Post.optimizer_hints("OMGHINT").merge(Post.optimizer_hints("OMGHINT")).to_sql
|
|
1389
1499
|
assert_equal expected, query
|
|
1390
1500
|
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
1501
|
end
|
|
1401
1502
|
end
|
|
1402
1503
|
|
|
@@ -1423,9 +1524,36 @@ class SanitizeTest < ActiveRecord::TestCase
|
|
|
1423
1524
|
searchable_post.search_as_scope("20% _reduction_!").to_a
|
|
1424
1525
|
end
|
|
1425
1526
|
end
|
|
1527
|
+
|
|
1528
|
+
# Use nvarchar string (N'') in assert
|
|
1529
|
+
coerce_tests! :test_named_bind_with_literal_colons
|
|
1530
|
+
def test_named_bind_with_literal_colons_coerced
|
|
1531
|
+
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")
|
|
1532
|
+
assert_raise(ActiveRecord::PreparedStatementInvalid) { bind "TO_TIMESTAMP(:date, 'YYYY/MM/DD HH12:MI:SS')", date: "2017/08/02 10:59:00" }
|
|
1533
|
+
end
|
|
1426
1534
|
end
|
|
1427
1535
|
|
|
1428
1536
|
class SchemaDumperTest < ActiveRecord::TestCase
|
|
1537
|
+
# Use nvarchar string (N'') in assert
|
|
1538
|
+
coerce_tests! :test_dump_schema_information_outputs_lexically_reverse_ordered_versions_regardless_of_database_order
|
|
1539
|
+
def test_dump_schema_information_outputs_lexically_reverse_ordered_versions_regardless_of_database_order_coerced
|
|
1540
|
+
versions = %w{ 20100101010101 20100201010101 20100301010101 }
|
|
1541
|
+
versions.shuffle.each do |v|
|
|
1542
|
+
@schema_migration.create_version(v)
|
|
1543
|
+
end
|
|
1544
|
+
|
|
1545
|
+
schema_info = ActiveRecord::Base.connection.dump_schema_information
|
|
1546
|
+
expected = <<~STR
|
|
1547
|
+
INSERT INTO #{ActiveRecord::Base.connection.quote_table_name("schema_migrations")} (version) VALUES
|
|
1548
|
+
(N'20100301010101'),
|
|
1549
|
+
(N'20100201010101'),
|
|
1550
|
+
(N'20100101010101');
|
|
1551
|
+
STR
|
|
1552
|
+
assert_equal expected.strip, schema_info
|
|
1553
|
+
ensure
|
|
1554
|
+
@schema_migration.delete_all_versions
|
|
1555
|
+
end
|
|
1556
|
+
|
|
1429
1557
|
# We have precision to 38.
|
|
1430
1558
|
coerce_tests! :test_schema_dump_keeps_large_precision_integer_columns_as_decimal
|
|
1431
1559
|
def test_schema_dump_keeps_large_precision_integer_columns_as_decimal_coerced
|
|
@@ -1449,6 +1577,9 @@ class SchemaDumperTest < ActiveRecord::TestCase
|
|
|
1449
1577
|
output = dump_all_table_schema([/^[^n]/])
|
|
1450
1578
|
assert_match %r{precision: 3,[[:space:]]+scale: 2,[[:space:]]+default: 2\.78}, output
|
|
1451
1579
|
end
|
|
1580
|
+
|
|
1581
|
+
# Tests are not about a specific adapter.
|
|
1582
|
+
coerce_tests! :test_do_not_dump_foreign_keys_when_bypassed_by_config
|
|
1452
1583
|
end
|
|
1453
1584
|
|
|
1454
1585
|
class SchemaDumperDefaultsTest < ActiveRecord::TestCase
|
|
@@ -1492,16 +1623,95 @@ end
|
|
|
1492
1623
|
|
|
1493
1624
|
require "models/topic"
|
|
1494
1625
|
class TransactionTest < ActiveRecord::TestCase
|
|
1495
|
-
# SQL Server does not have query for release_savepoint
|
|
1626
|
+
# SQL Server does not have query for release_savepoint.
|
|
1496
1627
|
coerce_tests! :test_releasing_named_savepoints
|
|
1497
1628
|
def test_releasing_named_savepoints_coerced
|
|
1498
1629
|
Topic.transaction do
|
|
1630
|
+
Topic.connection.materialize_transactions
|
|
1631
|
+
|
|
1499
1632
|
Topic.connection.create_savepoint("another")
|
|
1500
1633
|
Topic.connection.release_savepoint("another")
|
|
1501
1634
|
# We do not have a notion of releasing, so this does nothing vs raise an error.
|
|
1502
1635
|
Topic.connection.release_savepoint("another")
|
|
1503
1636
|
end
|
|
1504
1637
|
end
|
|
1638
|
+
|
|
1639
|
+
# SQL Server does not have query for release_savepoint.
|
|
1640
|
+
coerce_tests! :test_nested_transactions_after_disable_lazy_transactions
|
|
1641
|
+
def test_nested_transactions_after_disable_lazy_transactions_coerced
|
|
1642
|
+
Topic.connection.disable_lazy_transactions!
|
|
1643
|
+
|
|
1644
|
+
capture_sql do
|
|
1645
|
+
# RealTransaction (begin..commit)
|
|
1646
|
+
Topic.transaction(requires_new: true) do
|
|
1647
|
+
# ResetParentTransaction (no queries)
|
|
1648
|
+
Topic.transaction(requires_new: true) do
|
|
1649
|
+
Topic.delete_all
|
|
1650
|
+
# SavepointTransaction (savepoint..release)
|
|
1651
|
+
Topic.transaction(requires_new: true) do
|
|
1652
|
+
# ResetParentTransaction (no queries)
|
|
1653
|
+
Topic.transaction(requires_new: true) do
|
|
1654
|
+
# no-op
|
|
1655
|
+
end
|
|
1656
|
+
end
|
|
1657
|
+
end
|
|
1658
|
+
Topic.delete_all
|
|
1659
|
+
end
|
|
1660
|
+
end
|
|
1661
|
+
|
|
1662
|
+
actual_queries = ActiveRecord::SQLCounter.log_all
|
|
1663
|
+
|
|
1664
|
+
expected_queries = [
|
|
1665
|
+
/BEGIN/i,
|
|
1666
|
+
/DELETE/i,
|
|
1667
|
+
/^SAVE TRANSACTION/i,
|
|
1668
|
+
/DELETE/i,
|
|
1669
|
+
/COMMIT/i,
|
|
1670
|
+
]
|
|
1671
|
+
|
|
1672
|
+
assert_equal expected_queries.size, actual_queries.size
|
|
1673
|
+
expected_queries.zip(actual_queries) do |expected, actual|
|
|
1674
|
+
assert_match expected, actual
|
|
1675
|
+
end
|
|
1676
|
+
end
|
|
1677
|
+
|
|
1678
|
+
# SQL Server does not have query for release_savepoint.
|
|
1679
|
+
coerce_tests! :test_nested_transactions_skip_excess_savepoints
|
|
1680
|
+
def test_nested_transactions_skip_excess_savepoints_coerced
|
|
1681
|
+
capture_sql do
|
|
1682
|
+
# RealTransaction (begin..commit)
|
|
1683
|
+
Topic.transaction(requires_new: true) do
|
|
1684
|
+
# ResetParentTransaction (no queries)
|
|
1685
|
+
Topic.transaction(requires_new: true) do
|
|
1686
|
+
Topic.delete_all
|
|
1687
|
+
# SavepointTransaction (savepoint..release)
|
|
1688
|
+
Topic.transaction(requires_new: true) do
|
|
1689
|
+
# ResetParentTransaction (no queries)
|
|
1690
|
+
Topic.transaction(requires_new: true) do
|
|
1691
|
+
Topic.delete_all
|
|
1692
|
+
end
|
|
1693
|
+
end
|
|
1694
|
+
end
|
|
1695
|
+
Topic.delete_all
|
|
1696
|
+
end
|
|
1697
|
+
end
|
|
1698
|
+
|
|
1699
|
+
actual_queries = ActiveRecord::SQLCounter.log_all
|
|
1700
|
+
|
|
1701
|
+
expected_queries = [
|
|
1702
|
+
/BEGIN/i,
|
|
1703
|
+
/DELETE/i,
|
|
1704
|
+
/^SAVE TRANSACTION/i,
|
|
1705
|
+
/DELETE/i,
|
|
1706
|
+
/DELETE/i,
|
|
1707
|
+
/COMMIT/i,
|
|
1708
|
+
]
|
|
1709
|
+
|
|
1710
|
+
assert_equal expected_queries.size, actual_queries.size
|
|
1711
|
+
expected_queries.zip(actual_queries) do |expected, actual|
|
|
1712
|
+
assert_match expected, actual
|
|
1713
|
+
end
|
|
1714
|
+
end
|
|
1505
1715
|
end
|
|
1506
1716
|
|
|
1507
1717
|
require "models/tag"
|
|
@@ -1630,8 +1840,8 @@ class DefaultNumbersTest < ActiveRecord::TestCase
|
|
|
1630
1840
|
coerce_tests! :test_default_negative_integer
|
|
1631
1841
|
def test_default_negative_integer_coerced
|
|
1632
1842
|
record = DefaultNumber.new
|
|
1633
|
-
assert_equal -5, record.negative_integer
|
|
1634
|
-
assert_equal -5, record.negative_integer_before_type_cast
|
|
1843
|
+
assert_equal (-5), record.negative_integer
|
|
1844
|
+
assert_equal (-5), record.negative_integer_before_type_cast
|
|
1635
1845
|
end
|
|
1636
1846
|
|
|
1637
1847
|
# We do better with native types and do not return strings for everything.
|
|
@@ -1720,8 +1930,9 @@ module ActiveRecord
|
|
|
1720
1930
|
end
|
|
1721
1931
|
|
|
1722
1932
|
# We need to give the full path for this to work.
|
|
1933
|
+
undef_method :schema_dump_path
|
|
1723
1934
|
def schema_dump_path
|
|
1724
|
-
File.join
|
|
1935
|
+
File.join(ARTest::SQLServer.root_activerecord, "test/assets/schema_dump_5_1.yml")
|
|
1725
1936
|
end
|
|
1726
1937
|
end
|
|
1727
1938
|
end
|
|
@@ -1732,7 +1943,7 @@ require "models/comment"
|
|
|
1732
1943
|
class UnsafeRawSqlTest < ActiveRecord::TestCase
|
|
1733
1944
|
fixtures :posts
|
|
1734
1945
|
|
|
1735
|
-
# Use LEN()
|
|
1946
|
+
# Use LEN() instead of LENGTH() function.
|
|
1736
1947
|
coerce_tests! %r{order: always allows Arel}
|
|
1737
1948
|
test "order: always allows Arel" do
|
|
1738
1949
|
titles = Post.order(Arel.sql("len(title)")).pluck(:title)
|
|
@@ -1740,7 +1951,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
|
|
|
1740
1951
|
assert_not_empty titles
|
|
1741
1952
|
end
|
|
1742
1953
|
|
|
1743
|
-
# Use LEN()
|
|
1954
|
+
# Use LEN() instead of LENGTH() function.
|
|
1744
1955
|
coerce_tests! %r{pluck: always allows Arel}
|
|
1745
1956
|
test "pluck: always allows Arel" do
|
|
1746
1957
|
excepted_values = Post.includes(:comments).pluck(:title).map { |title| [title, title.size] }
|
|
@@ -1749,7 +1960,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
|
|
|
1749
1960
|
assert_equal excepted_values, values
|
|
1750
1961
|
end
|
|
1751
1962
|
|
|
1752
|
-
# Use LEN()
|
|
1963
|
+
# Use LEN() instead of LENGTH() function.
|
|
1753
1964
|
coerce_tests! %r{order: allows valid Array arguments}
|
|
1754
1965
|
test "order: allows valid Array arguments" do
|
|
1755
1966
|
ids_expected = Post.order(Arel.sql("author_id, len(title)")).pluck(:id)
|
|
@@ -1759,6 +1970,27 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
|
|
|
1759
1970
|
assert_equal ids_expected, ids
|
|
1760
1971
|
end
|
|
1761
1972
|
|
|
1973
|
+
# Use LEN() instead of LENGTH() function.
|
|
1974
|
+
coerce_tests! %r{order: allows nested functions}
|
|
1975
|
+
test "order: allows nested functions" do
|
|
1976
|
+
ids_expected = Post.order(Arel.sql("author_id, len(trim(title))")).pluck(:id)
|
|
1977
|
+
|
|
1978
|
+
# $DEBUG = true
|
|
1979
|
+
ids = Post.order("author_id, len(trim(title))").pluck(:id)
|
|
1980
|
+
|
|
1981
|
+
assert_equal ids_expected, ids
|
|
1982
|
+
end
|
|
1983
|
+
|
|
1984
|
+
# Use LEN() instead of LENGTH() function.
|
|
1985
|
+
coerce_tests! %r{pluck: allows nested functions}
|
|
1986
|
+
test "pluck: allows nested functions" do
|
|
1987
|
+
title_lengths_expected = Post.pluck(Arel.sql("len(trim(title))"))
|
|
1988
|
+
|
|
1989
|
+
title_lengths = Post.pluck("len(trim(title))")
|
|
1990
|
+
|
|
1991
|
+
assert_equal title_lengths_expected, title_lengths
|
|
1992
|
+
end
|
|
1993
|
+
|
|
1762
1994
|
test "order: allows string column names that are quoted" do
|
|
1763
1995
|
ids_expected = Post.order(Arel.sql("id")).pluck(:id)
|
|
1764
1996
|
|
|
@@ -1822,6 +2054,18 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
|
|
|
1822
2054
|
|
|
1823
2055
|
assert_equal titles_expected, titles
|
|
1824
2056
|
end
|
|
2057
|
+
|
|
2058
|
+
# Collation name should not be quoted. Hardcoded values for different adapters.
|
|
2059
|
+
coerce_tests! %r{order: allows valid arguments with COLLATE}
|
|
2060
|
+
test "order: allows valid arguments with COLLATE" do
|
|
2061
|
+
collation_name = "Latin1_General_CS_AS_WS"
|
|
2062
|
+
|
|
2063
|
+
ids_expected = Post.order(Arel.sql(%Q'author_id, title COLLATE #{collation_name} DESC')).pluck(:id)
|
|
2064
|
+
|
|
2065
|
+
ids = Post.order(["author_id", %Q'title COLLATE #{collation_name} DESC']).pluck(:id)
|
|
2066
|
+
|
|
2067
|
+
assert_equal ids_expected, ids
|
|
2068
|
+
end
|
|
1825
2069
|
end
|
|
1826
2070
|
|
|
1827
2071
|
class ReservedWordTest < ActiveRecord::TestCase
|
|
@@ -2021,15 +2265,6 @@ class LogSubscriberTest < ActiveRecord::TestCase
|
|
|
2021
2265
|
end
|
|
2022
2266
|
end
|
|
2023
2267
|
|
|
2024
|
-
class ActiveRecordSchemaTest < ActiveRecord::TestCase
|
|
2025
|
-
# Workaround for randomly failing test.
|
|
2026
|
-
coerce_tests! :test_has_primary_key
|
|
2027
|
-
def test_has_primary_key_coerced
|
|
2028
|
-
@schema_migration.reset_column_information
|
|
2029
|
-
original_test_has_primary_key
|
|
2030
|
-
end
|
|
2031
|
-
end
|
|
2032
|
-
|
|
2033
2268
|
class ReloadModelsTest < ActiveRecord::TestCase
|
|
2034
2269
|
# Skip test on Windows. The number of arguments passed to `IO.popen` in
|
|
2035
2270
|
# `activesupport/lib/active_support/testing/isolation.rb` exceeds what Windows can handle.
|
|
@@ -2039,6 +2274,7 @@ end
|
|
|
2039
2274
|
class MarshalSerializationTest < ActiveRecord::TestCase
|
|
2040
2275
|
private
|
|
2041
2276
|
|
|
2277
|
+
undef_method :marshal_fixture_path
|
|
2042
2278
|
def marshal_fixture_path(file_name)
|
|
2043
2279
|
File.expand_path(
|
|
2044
2280
|
"support/marshal_compatibility_fixtures/#{ActiveRecord::Base.connection.adapter_name}/#{file_name}.dump",
|
|
@@ -2073,6 +2309,59 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
|
|
|
2073
2309
|
end
|
|
2074
2310
|
end
|
|
2075
2311
|
|
|
2312
|
+
class PreloaderTest < ActiveRecord::TestCase
|
|
2313
|
+
# Need to handle query parameters in SQL regex.
|
|
2314
|
+
coerce_tests! :test_preloads_has_many_on_model_with_a_composite_primary_key_through_id_attribute
|
|
2315
|
+
def test_preloads_has_many_on_model_with_a_composite_primary_key_through_id_attribute_coerced
|
|
2316
|
+
order = cpk_orders(:cpk_groceries_order_2)
|
|
2317
|
+
_shop_id, order_id = order.id
|
|
2318
|
+
order_agreements = Cpk::OrderAgreement.where(order_id: order_id).to_a
|
|
2319
|
+
|
|
2320
|
+
assert_not_empty order_agreements
|
|
2321
|
+
assert_equal order_agreements.sort, order.order_agreements.sort
|
|
2322
|
+
|
|
2323
|
+
loaded_order = nil
|
|
2324
|
+
sql = capture_sql do
|
|
2325
|
+
loaded_order = Cpk::Order.where(id: order_id).includes(:order_agreements).to_a.first
|
|
2326
|
+
end
|
|
2327
|
+
|
|
2328
|
+
assert_equal 2, sql.size
|
|
2329
|
+
preload_sql = sql.last
|
|
2330
|
+
|
|
2331
|
+
c = Cpk::OrderAgreement.connection
|
|
2332
|
+
order_id_column = Regexp.escape(c.quote_table_name("cpk_order_agreements.order_id"))
|
|
2333
|
+
order_id_constraint = /#{order_id_column} = @0.*@0 = \d+$/
|
|
2334
|
+
expectation = /SELECT.*WHERE.* #{order_id_constraint}/
|
|
2335
|
+
|
|
2336
|
+
assert_match(expectation, preload_sql)
|
|
2337
|
+
assert_equal order_agreements.sort, loaded_order.order_agreements.sort
|
|
2338
|
+
end
|
|
2339
|
+
|
|
2340
|
+
# Need to handle query parameters in SQL regex.
|
|
2341
|
+
coerce_tests! :test_preloads_belongs_to_a_composite_primary_key_model_through_id_attribute
|
|
2342
|
+
def test_preloads_belongs_to_a_composite_primary_key_model_through_id_attribute_coerced
|
|
2343
|
+
order_agreement = cpk_order_agreements(:order_agreement_three)
|
|
2344
|
+
order = cpk_orders(:cpk_groceries_order_2)
|
|
2345
|
+
assert_equal order, order_agreement.order
|
|
2346
|
+
|
|
2347
|
+
loaded_order_agreement = nil
|
|
2348
|
+
sql = capture_sql do
|
|
2349
|
+
loaded_order_agreement = Cpk::OrderAgreement.where(id: order_agreement.id).includes(:order).to_a.first
|
|
2350
|
+
end
|
|
2351
|
+
|
|
2352
|
+
assert_equal 2, sql.size
|
|
2353
|
+
preload_sql = sql.last
|
|
2354
|
+
|
|
2355
|
+
c = Cpk::Order.connection
|
|
2356
|
+
order_id = Regexp.escape(c.quote_table_name("cpk_orders.id"))
|
|
2357
|
+
order_constraint = /#{order_id} = @0.*@0 = \d+$/
|
|
2358
|
+
expectation = /SELECT.*WHERE.* #{order_constraint}/
|
|
2359
|
+
|
|
2360
|
+
assert_match(expectation, preload_sql)
|
|
2361
|
+
assert_equal order, loaded_order_agreement.order
|
|
2362
|
+
end
|
|
2363
|
+
end
|
|
2364
|
+
|
|
2076
2365
|
class BasePreventWritesTest < ActiveRecord::TestCase
|
|
2077
2366
|
# SQL Server does not have query for release_savepoint
|
|
2078
2367
|
coerce_tests! %r{an empty transaction does not raise if preventing writes}
|
|
@@ -2085,20 +2374,6 @@ class BasePreventWritesTest < ActiveRecord::TestCase
|
|
|
2085
2374
|
end
|
|
2086
2375
|
end
|
|
2087
2376
|
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
2377
|
end
|
|
2103
2378
|
|
|
2104
2379
|
class MigratorTest < ActiveRecord::TestCase
|
|
@@ -2150,7 +2425,7 @@ class FieldOrderedValuesTest < ActiveRecord::TestCase
|
|
|
2150
2425
|
coerce_tests! :test_in_order_of_with_nil
|
|
2151
2426
|
def test_in_order_of_with_nil_coerced
|
|
2152
2427
|
Book.connection.remove_index(:books, column: [:author_id, :name])
|
|
2153
|
-
|
|
2428
|
+
|
|
2154
2429
|
original_test_in_order_of_with_nil
|
|
2155
2430
|
ensure
|
|
2156
2431
|
Book.where(author_id: nil, name: nil).delete_all
|
|
@@ -2160,137 +2435,153 @@ end
|
|
|
2160
2435
|
|
|
2161
2436
|
require "models/dashboard"
|
|
2162
2437
|
class QueryLogsTest < ActiveRecord::TestCase
|
|
2163
|
-
#
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
assert_sql(%r{/\*application:active_record,custom_string:test content\*/}) do
|
|
2438
|
+
# SQL requires double single-quotes.
|
|
2439
|
+
coerce_tests! :test_sql_commenter_format
|
|
2440
|
+
def test_sql_commenter_format_coerced
|
|
2441
|
+
ActiveRecord::QueryLogs.update_formatter(:sqlcommenter)
|
|
2442
|
+
assert_sql(%r{/\*application=''active_record''\*/}) do
|
|
2170
2443
|
Dashboard.first
|
|
2171
2444
|
end
|
|
2172
2445
|
end
|
|
2173
2446
|
|
|
2174
|
-
|
|
2175
|
-
|
|
2447
|
+
# SQL requires double single-quotes.
|
|
2448
|
+
coerce_tests! :test_sqlcommenter_format_value
|
|
2449
|
+
def test_sqlcommenter_format_value_coerced
|
|
2450
|
+
ActiveRecord::QueryLogs.update_formatter(:sqlcommenter)
|
|
2451
|
+
|
|
2452
|
+
ActiveRecord::QueryLogs.tags = [
|
|
2453
|
+
:application,
|
|
2454
|
+
{ tracestate: "congo=t61rcWkgMzE,rojo=00f067aa0ba902b7", custom_proc: -> { "Joe's Shack" } },
|
|
2455
|
+
]
|
|
2176
2456
|
|
|
2177
|
-
assert_sql(%r{
|
|
2457
|
+
assert_sql(%r{custom_proc=''Joe%27s%20Shack'',tracestate=''congo%3Dt61rcWkgMzE%2Crojo%3D00f067aa0ba902b7''\*/}) do
|
|
2178
2458
|
Dashboard.first
|
|
2179
2459
|
end
|
|
2180
2460
|
end
|
|
2181
2461
|
|
|
2182
|
-
|
|
2462
|
+
# SQL requires double single-quotes.
|
|
2463
|
+
coerce_tests! :test_sqlcommenter_format_value_string_coercible
|
|
2464
|
+
def test_sqlcommenter_format_value_string_coercible_coerced
|
|
2465
|
+
ActiveRecord::QueryLogs.update_formatter(:sqlcommenter)
|
|
2466
|
+
|
|
2183
2467
|
ActiveRecord::QueryLogs.tags = [
|
|
2184
2468
|
:application,
|
|
2185
|
-
{ custom_proc: -> {
|
|
2469
|
+
{ custom_proc: -> { 1234 } },
|
|
2186
2470
|
]
|
|
2187
2471
|
|
|
2188
|
-
assert_sql(%r{
|
|
2472
|
+
assert_sql(%r{custom_proc=''1234''\*/}) do
|
|
2189
2473
|
Dashboard.first
|
|
2190
2474
|
end
|
|
2191
2475
|
end
|
|
2192
2476
|
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2477
|
+
# Invalid character encoding causes `ActiveRecord::StatementInvalid` error similar to Postgres.
|
|
2478
|
+
coerce_tests! :test_invalid_encoding_query
|
|
2479
|
+
def test_invalid_encoding_query_coerced
|
|
2480
|
+
ActiveRecord::QueryLogs.tags = [ :application ]
|
|
2481
|
+
assert_raises ActiveRecord::StatementInvalid do
|
|
2482
|
+
ActiveRecord::Base.connection.execute "select 1 as '\xFF'"
|
|
2199
2483
|
end
|
|
2200
2484
|
end
|
|
2201
2485
|
end
|
|
2202
2486
|
|
|
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
2487
|
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
|
|
2488
|
+
# Same as original but using INSERTED.name as UPPER argument
|
|
2489
|
+
coerce_tests! :test_insert_all_returns_requested_sql_fields
|
|
2490
|
+
def test_insert_all_returns_requested_sql_fields_coerced
|
|
2491
|
+
skip unless supports_insert_returning?
|
|
2219
2492
|
|
|
2220
|
-
|
|
2221
|
-
|
|
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
|
|
2493
|
+
result = Book.insert_all! [{ name: "Rework", author_id: 1 }], returning: Arel.sql("UPPER(INSERTED.name) as name")
|
|
2494
|
+
assert_equal %w[ REWORK ], result.pluck("name")
|
|
2225
2495
|
end
|
|
2496
|
+
end
|
|
2226
2497
|
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2498
|
+
module ActiveRecord
|
|
2499
|
+
class Migration
|
|
2500
|
+
class InvalidOptionsTest < ActiveRecord::TestCase
|
|
2501
|
+
# Include the additional SQL Server migration options.
|
|
2502
|
+
undef_method :invalid_add_column_option_exception_message
|
|
2503
|
+
def invalid_add_column_option_exception_message(key)
|
|
2504
|
+
default_keys = [":limit", ":precision", ":scale", ":default", ":null", ":collation", ":comment", ":primary_key", ":if_exists", ":if_not_exists"]
|
|
2505
|
+
default_keys.concat([":is_identity"]) # SQL Server additional valid keys
|
|
2506
|
+
|
|
2507
|
+
"Unknown key: :#{key}. Valid keys are: #{default_keys.join(", ")}"
|
|
2508
|
+
end
|
|
2231
2509
|
end
|
|
2232
2510
|
end
|
|
2511
|
+
end
|
|
2233
2512
|
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
end
|
|
2513
|
+
# SQL Server does not support upsert. Removed dependency on `insert_all` that uses upsert.
|
|
2514
|
+
class ActiveRecord::Encryption::ConcurrencyTest < ActiveRecord::EncryptionTestCase
|
|
2515
|
+
undef_method :thread_encrypting_and_decrypting
|
|
2516
|
+
def thread_encrypting_and_decrypting(thread_label)
|
|
2517
|
+
posts = 100.times.collect { |index| EncryptedPost.create! title: "Article #{index} (#{thread_label})", body: "Body #{index} (#{thread_label})" }
|
|
2240
2518
|
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2519
|
+
Thread.new do
|
|
2520
|
+
posts.each.with_index do |article, index|
|
|
2521
|
+
assert_encrypted_attribute article, :title, "Article #{index} (#{thread_label})"
|
|
2522
|
+
article.decrypt
|
|
2523
|
+
assert_not_encrypted_attribute article, :title, "Article #{index} (#{thread_label})"
|
|
2524
|
+
end
|
|
2245
2525
|
end
|
|
2246
2526
|
end
|
|
2527
|
+
end
|
|
2247
2528
|
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2529
|
+
# Need to use `install_unregistered_type_fallback` instead of `install_unregistered_type_error` so that message-pack
|
|
2530
|
+
# can read and write `ActiveRecord::ConnectionAdapters::SQLServer::Type::Data` objects.
|
|
2531
|
+
class ActiveRecordMessagePackTest < ActiveRecord::TestCase
|
|
2532
|
+
private
|
|
2533
|
+
undef_method :serializer
|
|
2534
|
+
def serializer
|
|
2535
|
+
@serializer ||= ::MessagePack::Factory.new.tap do |factory|
|
|
2536
|
+
ActiveRecord::MessagePack::Extensions.install(factory)
|
|
2537
|
+
ActiveSupport::MessagePack::Extensions.install(factory)
|
|
2538
|
+
ActiveSupport::MessagePack::Extensions.install_unregistered_type_fallback(factory)
|
|
2252
2539
|
end
|
|
2253
2540
|
end
|
|
2254
2541
|
end
|
|
2255
2542
|
|
|
2256
|
-
class
|
|
2257
|
-
#
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
joins = capture_sql { @member.club }
|
|
2261
|
-
no_joins = capture_sql { @member.club_without_joins }
|
|
2543
|
+
class StoreTest < ActiveRecord::TestCase
|
|
2544
|
+
# Set the attribute as JSON type for the `StoreTest#saved changes tracking for accessors with json column` test.
|
|
2545
|
+
Admin::User.attribute :json_options, ActiveRecord::Type::SQLServer::Json.new
|
|
2546
|
+
end
|
|
2262
2547
|
|
|
2263
|
-
|
|
2264
|
-
|
|
2548
|
+
class TestDatabasesTest < ActiveRecord::TestCase
|
|
2549
|
+
# Tests are not about a specific adapter.
|
|
2550
|
+
coerce_all_tests!
|
|
2551
|
+
end
|
|
2265
2552
|
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2553
|
+
module ActiveRecord
|
|
2554
|
+
module ConnectionAdapters
|
|
2555
|
+
class ConnectionHandlersShardingDbTest < ActiveRecord::TestCase
|
|
2556
|
+
# Tests are not about a specific adapter.
|
|
2557
|
+
coerce_all_tests!
|
|
2269
2558
|
end
|
|
2270
|
-
|
|
2271
|
-
assert_match(/\[memberships\]\.\[type\]/, no_joins.first)
|
|
2272
2559
|
end
|
|
2273
2560
|
end
|
|
2274
2561
|
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2562
|
+
module ActiveRecord
|
|
2563
|
+
module ConnectionAdapters
|
|
2564
|
+
class ConnectionSwappingNestedTest < ActiveRecord::TestCase
|
|
2565
|
+
# Tests are not about a specific adapter.
|
|
2566
|
+
coerce_all_tests!
|
|
2567
|
+
end
|
|
2568
|
+
end
|
|
2569
|
+
end
|
|
2280
2570
|
|
|
2281
|
-
|
|
2282
|
-
|
|
2571
|
+
module ActiveRecord
|
|
2572
|
+
module ConnectionAdapters
|
|
2573
|
+
class ConnectionHandlersMultiDbTest < ActiveRecord::TestCase
|
|
2574
|
+
# Tests are not about a specific adapter.
|
|
2575
|
+
coerce_tests! :test_switching_connections_via_handler
|
|
2576
|
+
end
|
|
2283
2577
|
end
|
|
2284
2578
|
end
|
|
2285
2579
|
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
assert_not EncryptedAuthor.new(name: "a" * 4001).valid?
|
|
2293
|
-
author = EncryptedAuthor.create(name: "a" * 4001)
|
|
2294
|
-
assert_not author.valid?
|
|
2580
|
+
module ActiveRecord
|
|
2581
|
+
module ConnectionAdapters
|
|
2582
|
+
class ConnectionHandlersMultiPoolConfigTest < ActiveRecord::TestCase
|
|
2583
|
+
# Tests are not about a specific adapter.
|
|
2584
|
+
coerce_all_tests!
|
|
2585
|
+
end
|
|
2295
2586
|
end
|
|
2296
2587
|
end
|