activerecord-sqlserver-adapter 5.2.1 → 7.0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.editorconfig +9 -0
- data/.github/issue_template.md +23 -0
- data/.github/workflows/ci.yml +29 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +29 -0
- data/CHANGELOG.md +17 -27
- data/{Dockerfile → Dockerfile.ci} +1 -1
- data/Gemfile +49 -41
- data/Guardfile +9 -8
- data/MIT-LICENSE +1 -1
- data/README.md +65 -42
- data/RUNNING_UNIT_TESTS.md +3 -0
- data/Rakefile +14 -16
- data/VERSION +1 -1
- data/activerecord-sqlserver-adapter.gemspec +25 -14
- data/appveyor.yml +22 -17
- data/docker-compose.ci.yml +7 -5
- data/guides/RELEASING.md +11 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +2 -4
- data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +5 -4
- data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +10 -14
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +12 -5
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +2 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +10 -7
- data/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb +30 -0
- data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +9 -4
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +117 -52
- data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +9 -12
- data/lib/active_record/connection_adapters/sqlserver/errors.rb +2 -3
- data/lib/active_record/connection_adapters/sqlserver/quoting.rb +51 -14
- data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +40 -6
- data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +18 -10
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +235 -167
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +4 -2
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +3 -1
- data/lib/active_record/connection_adapters/sqlserver/showplan.rb +8 -8
- data/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +36 -7
- data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +43 -45
- data/lib/active_record/connection_adapters/sqlserver/transaction.rb +8 -10
- data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/binary.rb +5 -4
- data/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/char.rb +7 -4
- data/lib/active_record/connection_adapters/sqlserver/type/data.rb +5 -3
- data/lib/active_record/connection_adapters/sqlserver/type/date.rb +7 -5
- data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +8 -8
- data/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +5 -4
- data/lib/active_record/connection_adapters/sqlserver/type/decimal_without_scale.rb +22 -0
- data/lib/active_record/connection_adapters/sqlserver/type/float.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/integer.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/json.rb +2 -1
- data/lib/active_record/connection_adapters/sqlserver/type/money.rb +4 -4
- data/lib/active_record/connection_adapters/sqlserver/type/real.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +4 -4
- data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/string.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver/type/text.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/time.rb +6 -6
- data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +8 -9
- data/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +5 -4
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +6 -5
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +4 -4
- data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +4 -3
- data/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +6 -5
- data/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +4 -4
- data/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +6 -5
- data/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +4 -4
- data/lib/active_record/connection_adapters/sqlserver/type.rb +38 -35
- data/lib/active_record/connection_adapters/sqlserver/utils.rb +26 -12
- data/lib/active_record/connection_adapters/sqlserver/version.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +271 -180
- data/lib/active_record/connection_adapters/sqlserver_column.rb +76 -16
- data/lib/active_record/sqlserver_base.rb +11 -9
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +38 -39
- data/lib/activerecord-sqlserver-adapter.rb +3 -1
- data/lib/arel/visitors/sqlserver.rb +177 -56
- data/lib/arel_sqlserver.rb +4 -2
- data/test/appveyor/dbsetup.ps1 +4 -4
- data/test/cases/active_schema_test_sqlserver.rb +55 -0
- data/test/cases/adapter_test_sqlserver.rb +258 -173
- data/test/cases/change_column_collation_test_sqlserver.rb +33 -0
- data/test/cases/change_column_null_test_sqlserver.rb +14 -12
- data/test/cases/coerced_tests.rb +1421 -397
- data/test/cases/column_test_sqlserver.rb +321 -315
- data/test/cases/connection_test_sqlserver.rb +17 -20
- data/test/cases/disconnected_test_sqlserver.rb +39 -0
- data/test/cases/eager_load_too_many_ids_test_sqlserver.rb +18 -0
- data/test/cases/execute_procedure_test_sqlserver.rb +28 -19
- data/test/cases/fetch_test_sqlserver.rb +33 -21
- data/test/cases/fully_qualified_identifier_test_sqlserver.rb +15 -19
- data/test/cases/helper_sqlserver.rb +15 -15
- data/test/cases/in_clause_test_sqlserver.rb +63 -0
- data/test/cases/index_test_sqlserver.rb +15 -15
- data/test/cases/json_test_sqlserver.rb +25 -25
- data/test/cases/lateral_test_sqlserver.rb +35 -0
- data/test/cases/migration_test_sqlserver.rb +74 -27
- data/test/cases/optimizer_hints_test_sqlserver.rb +72 -0
- data/test/cases/order_test_sqlserver.rb +59 -53
- data/test/cases/pessimistic_locking_test_sqlserver.rb +27 -33
- data/test/cases/primary_keys_test_sqlserver.rb +103 -0
- data/test/cases/rake_test_sqlserver.rb +70 -45
- data/test/cases/schema_dumper_test_sqlserver.rb +124 -109
- data/test/cases/schema_test_sqlserver.rb +20 -26
- data/test/cases/scratchpad_test_sqlserver.rb +4 -4
- data/test/cases/showplan_test_sqlserver.rb +28 -35
- data/test/cases/specific_schema_test_sqlserver.rb +68 -65
- data/test/cases/transaction_test_sqlserver.rb +18 -20
- data/test/cases/trigger_test_sqlserver.rb +14 -13
- data/test/cases/utils_test_sqlserver.rb +70 -70
- data/test/cases/uuid_test_sqlserver.rb +13 -14
- data/test/debug.rb +8 -6
- data/test/migrations/create_clients_and_change_column_collation.rb +19 -0
- data/test/migrations/create_clients_and_change_column_null.rb +3 -1
- data/test/migrations/transaction_table/1_table_will_never_be_created.rb +4 -4
- data/test/models/sqlserver/booking.rb +3 -1
- data/test/models/sqlserver/composite_pk.rb +9 -0
- data/test/models/sqlserver/customers_view.rb +3 -1
- data/test/models/sqlserver/datatype.rb +2 -0
- data/test/models/sqlserver/datatype_migration.rb +2 -0
- data/test/models/sqlserver/dollar_table_name.rb +3 -1
- data/test/models/sqlserver/edge_schema.rb +3 -3
- data/test/models/sqlserver/fk_has_fk.rb +3 -1
- data/test/models/sqlserver/fk_has_pk.rb +3 -1
- data/test/models/sqlserver/natural_pk_data.rb +4 -2
- data/test/models/sqlserver/natural_pk_int_data.rb +3 -1
- data/test/models/sqlserver/no_pk_data.rb +3 -1
- data/test/models/sqlserver/object_default.rb +3 -1
- data/test/models/sqlserver/quoted_table.rb +4 -2
- data/test/models/sqlserver/quoted_view_1.rb +3 -1
- data/test/models/sqlserver/quoted_view_2.rb +3 -1
- data/test/models/sqlserver/sst_memory.rb +3 -1
- data/test/models/sqlserver/sst_string_collation.rb +3 -0
- data/test/models/sqlserver/string_default.rb +3 -1
- data/test/models/sqlserver/string_defaults_big_view.rb +3 -1
- data/test/models/sqlserver/string_defaults_view.rb +3 -1
- data/test/models/sqlserver/tinyint_pk.rb +3 -1
- data/test/models/sqlserver/trigger.rb +4 -2
- data/test/models/sqlserver/trigger_history.rb +3 -1
- data/test/models/sqlserver/upper.rb +3 -1
- data/test/models/sqlserver/uppered.rb +3 -1
- data/test/models/sqlserver/uuid.rb +3 -1
- data/test/schema/sqlserver_specific_schema.rb +56 -21
- data/test/support/coerceable_test_sqlserver.rb +19 -13
- data/test/support/connection_reflection.rb +3 -2
- data/test/support/core_ext/query_cache.rb +4 -1
- data/test/support/load_schema_sqlserver.rb +5 -5
- data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic_associations.dump +0 -0
- data/test/support/minitest_sqlserver.rb +3 -1
- data/test/support/paths_sqlserver.rb +11 -11
- data/test/support/rake_helpers.rb +15 -10
- data/test/support/sql_counter_sqlserver.rb +16 -15
- data/test/support/test_in_memory_oltp.rb +9 -7
- metadata +47 -13
- data/.travis.yml +0 -25
- data/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb +0 -26
data/test/cases/coerced_tests.rb
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "cases/helper_sqlserver"
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
require 'models/event'
|
|
5
|
+
require "models/event"
|
|
6
6
|
class UniquenessValidationTest < ActiveRecord::TestCase
|
|
7
|
-
# So sp_executesql swallows this exception. Run without
|
|
7
|
+
# So sp_executesql swallows this exception. Run without prepared to see it.
|
|
8
8
|
coerce_tests! :test_validate_uniqueness_with_limit
|
|
9
9
|
def test_validate_uniqueness_with_limit_coerced
|
|
10
10
|
connection.unprepared_statement do
|
|
@@ -14,7 +14,7 @@ class UniquenessValidationTest < ActiveRecord::TestCase
|
|
|
14
14
|
end
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
# So sp_executesql swallows this exception. Run without
|
|
17
|
+
# So sp_executesql swallows this exception. Run without prepared to see it.
|
|
18
18
|
coerce_tests! :test_validate_uniqueness_with_limit_and_utf8
|
|
19
19
|
def test_validate_uniqueness_with_limit_and_utf8_coerced
|
|
20
20
|
connection.unprepared_statement do
|
|
@@ -23,27 +23,45 @@ class UniquenessValidationTest < ActiveRecord::TestCase
|
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
25
|
end
|
|
26
|
-
end
|
|
27
26
|
|
|
27
|
+
# Same as original coerced test except that it handles default SQL Server case-insensitive collation.
|
|
28
|
+
coerce_tests! :test_validate_uniqueness_by_default_database_collation
|
|
29
|
+
def test_validate_uniqueness_by_default_database_collation_coerced
|
|
30
|
+
Topic.validates_uniqueness_of(:author_email_address)
|
|
31
|
+
|
|
32
|
+
topic1 = Topic.new(author_email_address: "david@loudthinking.com")
|
|
33
|
+
topic2 = Topic.new(author_email_address: "David@loudthinking.com")
|
|
34
|
+
|
|
35
|
+
assert_equal 1, Topic.where(author_email_address: "david@loudthinking.com").count
|
|
28
36
|
|
|
37
|
+
assert_not topic1.valid?
|
|
38
|
+
assert_not topic1.save
|
|
29
39
|
|
|
40
|
+
# Case insensitive collation (SQL_Latin1_General_CP1_CI_AS) by default.
|
|
41
|
+
# Should not allow "David" if "david" exists.
|
|
42
|
+
assert_not topic2.valid?
|
|
43
|
+
assert_not topic2.save
|
|
30
44
|
|
|
31
|
-
|
|
45
|
+
assert_equal 1, Topic.where(author_email_address: "david@loudthinking.com").count
|
|
46
|
+
assert_equal 1, Topic.where(author_email_address: "David@loudthinking.com").count
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
require "models/event"
|
|
32
51
|
module ActiveRecord
|
|
33
52
|
class AdapterTest < ActiveRecord::TestCase
|
|
34
|
-
#
|
|
35
|
-
coerce_tests! :
|
|
36
|
-
coerce_tests! :test_insert_update_delete_with_legacy_binds
|
|
53
|
+
# Legacy binds are not supported.
|
|
54
|
+
coerce_tests! :test_select_all_insert_update_delete_with_casted_binds
|
|
37
55
|
|
|
38
56
|
# As far as I can tell, SQL Server does not support null bytes in strings.
|
|
39
57
|
coerce_tests! :test_update_prepared_statement
|
|
40
58
|
|
|
41
|
-
# So sp_executesql swallows this exception. Run without
|
|
59
|
+
# So sp_executesql swallows this exception. Run without prepared to see it.
|
|
42
60
|
coerce_tests! :test_value_limit_violations_are_translated_to_specific_exception
|
|
43
61
|
def test_value_limit_violations_are_translated_to_specific_exception_coerced
|
|
44
62
|
connection.unprepared_statement do
|
|
45
63
|
error = assert_raises(ActiveRecord::ValueTooLong) do
|
|
46
|
-
Event.create(title:
|
|
64
|
+
Event.create(title: "abcdefgh")
|
|
47
65
|
end
|
|
48
66
|
assert_not_nil error.cause
|
|
49
67
|
end
|
|
@@ -51,38 +69,153 @@ module ActiveRecord
|
|
|
51
69
|
end
|
|
52
70
|
end
|
|
53
71
|
|
|
72
|
+
module ActiveRecord
|
|
73
|
+
class AdapterPreventWritesTest < ActiveRecord::TestCase
|
|
74
|
+
# Fix randomly failing test. The loading of the model's schema was affecting the test.
|
|
75
|
+
coerce_tests! :test_errors_when_an_insert_query_is_called_while_preventing_writes
|
|
76
|
+
def test_errors_when_an_insert_query_is_called_while_preventing_writes_coerced
|
|
77
|
+
Subscriber.send(:load_schema!)
|
|
78
|
+
original_test_errors_when_an_insert_query_is_called_while_preventing_writes
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Fix randomly failing test. The loading of the model's schema was affecting the test.
|
|
82
|
+
coerce_tests! :test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_containing_read_command_is_called_while_preventing_writes
|
|
83
|
+
def test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_containing_read_command_is_called_while_preventing_writes_coerced
|
|
84
|
+
Subscriber.send(:load_schema!)
|
|
85
|
+
original_test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_containing_read_command_is_called_while_preventing_writes
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Fix randomly failing test. The loading of the model's schema was affecting the test.
|
|
89
|
+
coerce_tests! :test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_is_called_while_preventing_writes
|
|
90
|
+
def test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_is_called_while_preventing_writes_coerced
|
|
91
|
+
Subscriber.send(:load_schema!)
|
|
92
|
+
original_test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_is_called_while_preventing_writes
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
coerce_tests! :test_doesnt_error_when_a_select_query_has_encoding_errors
|
|
96
|
+
def test_doesnt_error_when_a_select_query_has_encoding_errors_coerced
|
|
97
|
+
ActiveRecord::Base.while_preventing_writes do
|
|
98
|
+
# 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.
|
|
101
|
+
assert_raises ActiveRecord::StatementInvalid do
|
|
102
|
+
@connection.select_all("SELECT '\xC8'")
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
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
|
+
module ActiveRecord
|
|
142
|
+
class AdapterTestWithoutTransaction < ActiveRecord::TestCase
|
|
143
|
+
# SQL Server does not allow truncation of tables that are referenced by foreign key
|
|
144
|
+
# constraints. So manually remove/add foreign keys in test.
|
|
145
|
+
coerce_tests! :test_truncate_tables
|
|
146
|
+
def test_truncate_tables_coerced
|
|
147
|
+
# Remove foreign key constraint to allow truncation.
|
|
148
|
+
@connection.remove_foreign_key :authors, :author_addresses
|
|
149
|
+
|
|
150
|
+
assert_operator Post.count, :>, 0
|
|
151
|
+
assert_operator Author.count, :>, 0
|
|
152
|
+
assert_operator AuthorAddress.count, :>, 0
|
|
153
|
+
|
|
154
|
+
@connection.truncate_tables("author_addresses", "authors", "posts")
|
|
155
|
+
|
|
156
|
+
assert_equal 0, Post.count
|
|
157
|
+
assert_equal 0, Author.count
|
|
158
|
+
assert_equal 0, AuthorAddress.count
|
|
159
|
+
ensure
|
|
160
|
+
reset_fixtures("posts", "authors", "author_addresses")
|
|
161
|
+
|
|
162
|
+
# Restore foreign key constraint.
|
|
163
|
+
@connection.add_foreign_key :authors, :author_addresses
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# SQL Server does not allow truncation of tables that are referenced by foreign key
|
|
167
|
+
# constraints. So manually remove/add foreign keys in test.
|
|
168
|
+
coerce_tests! :test_truncate_tables_with_query_cache
|
|
169
|
+
def test_truncate_tables_with_query_cache
|
|
170
|
+
# Remove foreign key constraint to allow truncation.
|
|
171
|
+
@connection.remove_foreign_key :authors, :author_addresses
|
|
54
172
|
|
|
173
|
+
@connection.enable_query_cache!
|
|
174
|
+
|
|
175
|
+
assert_operator Post.count, :>, 0
|
|
176
|
+
assert_operator Author.count, :>, 0
|
|
177
|
+
assert_operator AuthorAddress.count, :>, 0
|
|
178
|
+
|
|
179
|
+
@connection.truncate_tables("author_addresses", "authors", "posts")
|
|
180
|
+
|
|
181
|
+
assert_equal 0, Post.count
|
|
182
|
+
assert_equal 0, Author.count
|
|
183
|
+
assert_equal 0, AuthorAddress.count
|
|
184
|
+
ensure
|
|
185
|
+
reset_fixtures("posts", "authors", "author_addresses")
|
|
186
|
+
@connection.disable_query_cache!
|
|
55
187
|
|
|
188
|
+
# Restore foreign key constraint.
|
|
189
|
+
@connection.add_foreign_key :authors, :author_addresses
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
56
193
|
|
|
57
|
-
require
|
|
194
|
+
require "models/topic"
|
|
58
195
|
class AttributeMethodsTest < ActiveRecord::TestCase
|
|
196
|
+
# Use IFF for boolean statement in SELECT
|
|
59
197
|
coerce_tests! %r{typecast attribute from select to false}
|
|
60
198
|
def test_typecast_attribute_from_select_to_false_coerced
|
|
61
|
-
Topic.create(:title =>
|
|
199
|
+
Topic.create(:title => "Budget")
|
|
62
200
|
topic = Topic.all.merge!(:select => "topics.*, IIF (1 = 2, 1, 0) as is_test").first
|
|
63
|
-
|
|
201
|
+
assert_not_predicate topic, :is_test?
|
|
64
202
|
end
|
|
65
203
|
|
|
204
|
+
# Use IFF for boolean statement in SELECT
|
|
66
205
|
coerce_tests! %r{typecast attribute from select to true}
|
|
67
206
|
def test_typecast_attribute_from_select_to_true_coerced
|
|
68
|
-
Topic.create(:title =>
|
|
207
|
+
Topic.create(:title => "Budget")
|
|
69
208
|
topic = Topic.all.merge!(:select => "topics.*, IIF (1 = 1, 1, 0) as is_test").first
|
|
70
|
-
|
|
209
|
+
assert_predicate topic, :is_test?
|
|
71
210
|
end
|
|
72
211
|
end
|
|
73
212
|
|
|
74
|
-
|
|
75
|
-
class NumericDataTest < ActiveRecord::TestCase
|
|
76
|
-
# We do not have do the DecimalWithoutScale type.
|
|
77
|
-
coerce_tests! :test_numeric_fields
|
|
78
|
-
coerce_tests! :test_numeric_fields_with_scale
|
|
79
|
-
end
|
|
80
|
-
|
|
81
213
|
class BasicsTest < ActiveRecord::TestCase
|
|
214
|
+
# Use square brackets as SQL Server escaped character
|
|
82
215
|
coerce_tests! :test_column_names_are_escaped
|
|
83
216
|
def test_column_names_are_escaped_coerced
|
|
84
217
|
conn = ActiveRecord::Base.connection
|
|
85
|
-
assert_equal
|
|
218
|
+
assert_equal "[t]]]", conn.quote_column_name("t]")
|
|
86
219
|
end
|
|
87
220
|
|
|
88
221
|
# Just like PostgreSQLAdapter does.
|
|
@@ -96,52 +229,60 @@ class BasicsTest < ActiveRecord::TestCase
|
|
|
96
229
|
Time.use_zone("Eastern Time (US & Canada)") do
|
|
97
230
|
topic = Topic.find(1)
|
|
98
231
|
time = Time.zone.parse("2017-07-17 10:56")
|
|
99
|
-
topic.
|
|
232
|
+
topic.update!(written_on: time)
|
|
100
233
|
assert_equal(time, topic.written_on)
|
|
101
234
|
end
|
|
102
235
|
end
|
|
103
236
|
|
|
104
237
|
def test_update_date_time_attributes_with_default_timezone_local
|
|
105
|
-
with_env_tz
|
|
238
|
+
with_env_tz "America/New_York" do
|
|
106
239
|
with_timezone_config default: :local do
|
|
107
240
|
Time.use_zone("Eastern Time (US & Canada)") do
|
|
108
241
|
topic = Topic.find(1)
|
|
109
242
|
time = Time.zone.parse("2017-07-17 10:56")
|
|
110
|
-
topic.
|
|
243
|
+
topic.update!(written_on: time)
|
|
111
244
|
assert_equal(time, topic.written_on)
|
|
112
245
|
end
|
|
113
246
|
end
|
|
114
247
|
end
|
|
115
248
|
end
|
|
116
249
|
|
|
117
|
-
#
|
|
118
|
-
coerce_tests! %r{
|
|
119
|
-
test "
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
250
|
+
# SQL Server does not have query for release_savepoint
|
|
251
|
+
coerce_tests! %r{an empty transaction does not raise if preventing writes}
|
|
252
|
+
test "an empty transaction does not raise if preventing writes coerced" do
|
|
253
|
+
ActiveRecord::Base.while_preventing_writes do
|
|
254
|
+
assert_queries(1, ignore_none: true) do
|
|
255
|
+
Bird.transaction do
|
|
256
|
+
ActiveRecord::Base.connection.materialize_transactions
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
end
|
|
125
260
|
end
|
|
126
261
|
end
|
|
127
262
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
263
|
class BelongsToAssociationsTest < ActiveRecord::TestCase
|
|
132
264
|
# Since @client.firm is a single first/top, and we use FETCH the order clause is used.
|
|
133
265
|
coerce_tests! :test_belongs_to_does_not_use_order_by
|
|
134
266
|
|
|
267
|
+
# Square brackets around column name
|
|
135
268
|
coerce_tests! :test_belongs_to_with_primary_key_joins_on_correct_column
|
|
136
269
|
def test_belongs_to_with_primary_key_joins_on_correct_column_coerced
|
|
137
270
|
sql = Client.joins(:firm_with_primary_key).to_sql
|
|
138
271
|
assert_no_match(/\[firm_with_primary_keys_companies\]\.\[id\]/, sql)
|
|
139
272
|
assert_match(/\[firm_with_primary_keys_companies\]\.\[name\]/, sql)
|
|
140
273
|
end
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
|
|
144
274
|
|
|
275
|
+
# Asserted SQL to get one row different from original test.
|
|
276
|
+
coerce_tests! :test_belongs_to
|
|
277
|
+
def test_belongs_to_coerced
|
|
278
|
+
client = Client.find(3)
|
|
279
|
+
first_firm = companies(:first_firm)
|
|
280
|
+
assert_sql(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do
|
|
281
|
+
assert_equal first_firm, client.firm
|
|
282
|
+
assert_equal first_firm.name, client.firm.name
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
end
|
|
145
286
|
|
|
146
287
|
module ActiveRecord
|
|
147
288
|
class BindParameterTest < ActiveRecord::TestCase
|
|
@@ -163,62 +304,203 @@ module ActiveRecord
|
|
|
163
304
|
# SQL Server adapter does not use a statement cache as query plans are already reused using `EXEC sp_executesql`.
|
|
164
305
|
coerce_tests! :test_statement_cache
|
|
165
306
|
coerce_tests! :test_statement_cache_with_query_cache
|
|
307
|
+
coerce_tests! :test_statement_cache_with_find
|
|
166
308
|
coerce_tests! :test_statement_cache_with_find_by
|
|
167
309
|
coerce_tests! :test_statement_cache_with_in_clause
|
|
168
310
|
coerce_tests! :test_statement_cache_with_sql_string_literal
|
|
311
|
+
|
|
312
|
+
# Same as original coerced test except prepared statements include `EXEC sp_executesql` wrapper.
|
|
313
|
+
coerce_tests! :test_bind_params_to_sql_with_prepared_statements, :test_bind_params_to_sql_with_unprepared_statements
|
|
314
|
+
def test_bind_params_to_sql_with_prepared_statements_coerced
|
|
315
|
+
assert_bind_params_to_sql_coerced(prepared: true)
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
def test_bind_params_to_sql_with_unprepared_statements_coerced
|
|
319
|
+
@connection.unprepared_statement do
|
|
320
|
+
assert_bind_params_to_sql_coerced(prepared: false)
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
private
|
|
325
|
+
|
|
326
|
+
def assert_bind_params_to_sql_coerced(prepared:)
|
|
327
|
+
table = Author.quoted_table_name
|
|
328
|
+
pk = "#{table}.#{Author.quoted_primary_key}"
|
|
329
|
+
|
|
330
|
+
# prepared_statements: true
|
|
331
|
+
#
|
|
332
|
+
# EXEC sp_executesql N'SELECT [authors].* FROM [authors] WHERE [authors].[id] IN (@0, @1, @2) OR [authors].[id] IS NULL)', N'@0 bigint, @1 bigint, @2 bigint', @0 = 1, @1 = 2, @2 = 3
|
|
333
|
+
#
|
|
334
|
+
# prepared_statements: false
|
|
335
|
+
#
|
|
336
|
+
# SELECT [authors].* FROM [authors] WHERE ([authors].[id] IN (1, 2, 3) OR [authors].[id] IS NULL)
|
|
337
|
+
#
|
|
338
|
+
sql_unprepared = "SELECT #{table}.* FROM #{table} WHERE (#{pk} IN (#{bind_params(1..3)}) OR #{pk} IS NULL)"
|
|
339
|
+
sql_prepared = "EXEC sp_executesql N'SELECT #{table}.* FROM #{table} WHERE (#{pk} IN (#{bind_params(1..3)}) OR #{pk} IS NULL)', N'@0 bigint, @1 bigint, @2 bigint', @0 = 1, @1 = 2, @2 = 3"
|
|
340
|
+
|
|
341
|
+
authors = Author.where(id: [1, 2, 3, nil])
|
|
342
|
+
assert_equal sql_unprepared, @connection.to_sql(authors.arel)
|
|
343
|
+
assert_sql(prepared ? sql_prepared : sql_unprepared) { assert_equal 3, authors.length }
|
|
344
|
+
|
|
345
|
+
# prepared_statements: true
|
|
346
|
+
#
|
|
347
|
+
# EXEC sp_executesql N'SELECT [authors].* FROM [authors] WHERE [authors].[id] IN (@0, @1, @2)', N'@0 bigint, @1 bigint, @2 bigint', @0 = 1, @1 = 2, @2 = 3
|
|
348
|
+
#
|
|
349
|
+
# prepared_statements: false
|
|
350
|
+
#
|
|
351
|
+
# SELECT [authors].* FROM [authors] WHERE [authors].[id] IN (1, 2, 3)
|
|
352
|
+
#
|
|
353
|
+
sql_unprepared = "SELECT #{table}.* FROM #{table} WHERE #{pk} IN (#{bind_params(1..3)})"
|
|
354
|
+
sql_prepared = "EXEC sp_executesql N'SELECT #{table}.* FROM #{table} WHERE #{pk} IN (#{bind_params(1..3)})', N'@0 bigint, @1 bigint, @2 bigint', @0 = 1, @1 = 2, @2 = 3"
|
|
355
|
+
|
|
356
|
+
authors = Author.where(id: [1, 2, 3, 9223372036854775808])
|
|
357
|
+
assert_equal sql_unprepared, @connection.to_sql(authors.arel)
|
|
358
|
+
assert_sql(prepared ? sql_prepared : sql_unprepared) { assert_equal 3, authors.length }
|
|
359
|
+
end
|
|
169
360
|
end
|
|
170
361
|
end
|
|
171
362
|
|
|
172
|
-
|
|
173
363
|
module ActiveRecord
|
|
174
364
|
class InstrumentationTest < ActiveRecord::TestCase
|
|
175
|
-
#
|
|
365
|
+
# Fix randomly failing test. The loading of the model's schema was affecting the test.
|
|
176
366
|
coerce_tests! :test_payload_name_on_load
|
|
177
367
|
def test_payload_name_on_load_coerced
|
|
178
|
-
Book.
|
|
179
|
-
|
|
180
|
-
subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |*args|
|
|
181
|
-
event = ActiveSupport::Notifications::Event.new(*args)
|
|
182
|
-
if event.payload[:sql].match "SELECT"
|
|
183
|
-
assert_equal "Book Load", event.payload[:name]
|
|
184
|
-
end
|
|
185
|
-
end
|
|
186
|
-
Book.first
|
|
187
|
-
ensure
|
|
188
|
-
ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber
|
|
368
|
+
Book.send(:load_schema!)
|
|
369
|
+
original_test_payload_name_on_load
|
|
189
370
|
end
|
|
190
371
|
end
|
|
191
372
|
end
|
|
192
373
|
|
|
193
374
|
class CalculationsTest < ActiveRecord::TestCase
|
|
194
|
-
#
|
|
375
|
+
# Fix randomly failing test. The loading of the model's schema was affecting the test.
|
|
195
376
|
coerce_tests! :test_offset_is_kept
|
|
196
377
|
def test_offset_is_kept_coerced
|
|
197
|
-
Account.
|
|
198
|
-
|
|
199
|
-
assert_equal 1, queries.length
|
|
200
|
-
assert_match(/OFFSET/, queries.first)
|
|
378
|
+
Account.send(:load_schema!)
|
|
379
|
+
original_test_offset_is_kept
|
|
201
380
|
end
|
|
202
381
|
|
|
203
|
-
#
|
|
382
|
+
# The SQL Server `AVG()` function for a list of integers returns an integer (not a decimal).
|
|
204
383
|
coerce_tests! :test_should_return_decimal_average_of_integer_field
|
|
205
384
|
def test_should_return_decimal_average_of_integer_field_coerced
|
|
206
385
|
value = Account.average(:id)
|
|
207
|
-
assert_equal
|
|
386
|
+
assert_equal 3, value
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
# In SQL Server the `AVG()` function for a list of integers returns an integer so need to cast values as decimals before averaging.
|
|
390
|
+
# Match SQL Server limit implementation.
|
|
391
|
+
coerce_tests! :test_select_avg_with_group_by_as_virtual_attribute_with_sql
|
|
392
|
+
def test_select_avg_with_group_by_as_virtual_attribute_with_sql_coerced
|
|
393
|
+
rails_core = companies(:rails_core)
|
|
394
|
+
|
|
395
|
+
sql = <<~SQL
|
|
396
|
+
SELECT firm_id, AVG(CAST(credit_limit AS DECIMAL)) AS avg_credit_limit
|
|
397
|
+
FROM accounts
|
|
398
|
+
WHERE firm_id = ?
|
|
399
|
+
GROUP BY firm_id
|
|
400
|
+
ORDER BY firm_id
|
|
401
|
+
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY
|
|
402
|
+
SQL
|
|
403
|
+
|
|
404
|
+
account = Account.find_by_sql([sql, rails_core]).first
|
|
405
|
+
|
|
406
|
+
# id was not selected, so it should be nil
|
|
407
|
+
# (cannot select id because it wasn't used in the GROUP BY clause)
|
|
408
|
+
assert_nil account.id
|
|
409
|
+
|
|
410
|
+
# firm_id was explicitly selected, so it should be present
|
|
411
|
+
assert_equal(rails_core, account.firm)
|
|
412
|
+
|
|
413
|
+
# avg_credit_limit should be present as a virtual attribute
|
|
414
|
+
assert_equal(52.5, account.avg_credit_limit)
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
# In SQL Server the `AVG()` function for a list of integers returns an integer so need to cast values as decimals before averaging.
|
|
418
|
+
# Order column must be in the GROUP clause.
|
|
419
|
+
coerce_tests! :test_select_avg_with_group_by_as_virtual_attribute_with_ar
|
|
420
|
+
def test_select_avg_with_group_by_as_virtual_attribute_with_ar_coerced
|
|
421
|
+
rails_core = companies(:rails_core)
|
|
422
|
+
|
|
423
|
+
account = Account
|
|
424
|
+
.select(:firm_id, "AVG(CAST(credit_limit AS DECIMAL)) AS avg_credit_limit")
|
|
425
|
+
.where(firm: rails_core)
|
|
426
|
+
.group(:firm_id)
|
|
427
|
+
.order(:firm_id)
|
|
428
|
+
.take!
|
|
429
|
+
|
|
430
|
+
# id was not selected, so it should be nil
|
|
431
|
+
# (cannot select id because it wasn't used in the GROUP BY clause)
|
|
432
|
+
assert_nil account.id
|
|
433
|
+
|
|
434
|
+
# firm_id was explicitly selected, so it should be present
|
|
435
|
+
assert_equal(rails_core, account.firm)
|
|
436
|
+
|
|
437
|
+
# avg_credit_limit should be present as a virtual attribute
|
|
438
|
+
assert_equal(52.5, account.avg_credit_limit)
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
# In SQL Server the `AVG()` function for a list of integers returns an integer so need to cast values as decimals before averaging.
|
|
442
|
+
# SELECT columns must be in the GROUP clause.
|
|
443
|
+
# Match SQL Server limit implementation.
|
|
444
|
+
coerce_tests! :test_select_avg_with_joins_and_group_by_as_virtual_attribute_with_sql
|
|
445
|
+
def test_select_avg_with_joins_and_group_by_as_virtual_attribute_with_sql_coerced
|
|
446
|
+
rails_core = companies(:rails_core)
|
|
447
|
+
|
|
448
|
+
sql = <<~SQL
|
|
449
|
+
SELECT companies.*, AVG(CAST(accounts.credit_limit AS DECIMAL)) AS avg_credit_limit
|
|
450
|
+
FROM companies
|
|
451
|
+
INNER JOIN accounts ON companies.id = accounts.firm_id
|
|
452
|
+
WHERE companies.id = ?
|
|
453
|
+
GROUP BY companies.id, companies.type, companies.firm_id, companies.firm_name, companies.name, companies.client_of, companies.rating, companies.account_id, companies.description
|
|
454
|
+
ORDER BY companies.id
|
|
455
|
+
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY
|
|
456
|
+
SQL
|
|
457
|
+
|
|
458
|
+
firm = DependentFirm.find_by_sql([sql, rails_core]).first
|
|
459
|
+
|
|
460
|
+
# all the DependentFirm attributes should be present
|
|
461
|
+
assert_equal rails_core, firm
|
|
462
|
+
assert_equal rails_core.name, firm.name
|
|
463
|
+
|
|
464
|
+
# avg_credit_limit should be present as a virtual attribute
|
|
465
|
+
assert_equal(52.5, firm.avg_credit_limit)
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
# In SQL Server the `AVG()` function for a list of integers returns an integer so need to cast values as decimals before averaging.
|
|
470
|
+
# SELECT columns must be in the GROUP clause.
|
|
471
|
+
coerce_tests! :test_select_avg_with_joins_and_group_by_as_virtual_attribute_with_ar
|
|
472
|
+
def test_select_avg_with_joins_and_group_by_as_virtual_attribute_with_ar_coerced
|
|
473
|
+
rails_core = companies(:rails_core)
|
|
474
|
+
|
|
475
|
+
firm = DependentFirm
|
|
476
|
+
.select("companies.*", "AVG(CAST(accounts.credit_limit AS DECIMAL)) AS avg_credit_limit")
|
|
477
|
+
.where(id: rails_core)
|
|
478
|
+
.joins(:account)
|
|
479
|
+
.group(:id, :type, :firm_id, :firm_name, :name, :client_of, :rating, :account_id, :description)
|
|
480
|
+
.take!
|
|
481
|
+
|
|
482
|
+
# all the DependentFirm attributes should be present
|
|
483
|
+
assert_equal rails_core, firm
|
|
484
|
+
assert_equal rails_core.name, firm.name
|
|
485
|
+
|
|
486
|
+
# avg_credit_limit should be present as a virtual attribute
|
|
487
|
+
assert_equal(52.5, firm.avg_credit_limit)
|
|
208
488
|
end
|
|
209
489
|
|
|
490
|
+
# Match SQL Server limit implementation
|
|
210
491
|
coerce_tests! :test_limit_is_kept
|
|
211
492
|
def test_limit_is_kept_coerced
|
|
212
493
|
queries = capture_sql_ss { Account.limit(1).count }
|
|
213
494
|
assert_equal 1, queries.length
|
|
214
|
-
|
|
495
|
+
assert_match(/ORDER BY \[accounts\]\.\[id\] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/, queries.first)
|
|
215
496
|
end
|
|
216
497
|
|
|
498
|
+
# Match SQL Server limit implementation
|
|
217
499
|
coerce_tests! :test_limit_with_offset_is_kept
|
|
218
500
|
def test_limit_with_offset_is_kept_coerced
|
|
219
501
|
queries = capture_sql_ss { Account.limit(1).offset(1).count }
|
|
220
502
|
assert_equal 1, queries.length
|
|
221
|
-
|
|
503
|
+
assert_match(/ORDER BY \[accounts\]\.\[id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY.*@0 = 1, @1 = 1/, queries.first)
|
|
222
504
|
end
|
|
223
505
|
|
|
224
506
|
# SQL Server needs an alias for the calculated column
|
|
@@ -233,49 +515,65 @@ class CalculationsTest < ActiveRecord::TestCase
|
|
|
233
515
|
coerce_tests! :test_having_with_strong_parameters
|
|
234
516
|
end
|
|
235
517
|
|
|
236
|
-
|
|
237
|
-
|
|
238
518
|
module ActiveRecord
|
|
239
519
|
class Migration
|
|
240
520
|
class ChangeSchemaTest < ActiveRecord::TestCase
|
|
241
|
-
|
|
242
|
-
coerce_tests! :
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
end
|
|
521
|
+
# Integer.default is a number and not a string
|
|
522
|
+
coerce_tests! :test_create_table_with_defaults
|
|
523
|
+
def test_create_table_with_defaults_coerce
|
|
524
|
+
connection.create_table :testings do |t|
|
|
525
|
+
t.column :one, :string, default: "hello"
|
|
526
|
+
t.column :two, :boolean, default: true
|
|
527
|
+
t.column :three, :boolean, default: false
|
|
528
|
+
t.column :four, :integer, default: 1
|
|
529
|
+
t.column :five, :text, default: "hello"
|
|
530
|
+
end
|
|
252
531
|
|
|
532
|
+
columns = connection.columns(:testings)
|
|
533
|
+
one = columns.detect { |c| c.name == "one" }
|
|
534
|
+
two = columns.detect { |c| c.name == "two" }
|
|
535
|
+
three = columns.detect { |c| c.name == "three" }
|
|
536
|
+
four = columns.detect { |c| c.name == "four" }
|
|
537
|
+
five = columns.detect { |c| c.name == "five" }
|
|
538
|
+
|
|
539
|
+
assert_equal "hello", one.default
|
|
540
|
+
assert_equal true, connection.lookup_cast_type_from_column(two).deserialize(two.default)
|
|
541
|
+
assert_equal false, connection.lookup_cast_type_from_column(three).deserialize(three.default)
|
|
542
|
+
assert_equal 1, four.default
|
|
543
|
+
assert_equal "hello", five.default
|
|
544
|
+
end
|
|
253
545
|
|
|
546
|
+
# Rails adds precision 6 by default, sql server uses datetime2 for datetimes with precision
|
|
547
|
+
coerce_tests! :test_add_column_with_postgresql_datetime_type
|
|
548
|
+
def test_add_column_with_postgresql_datetime_type_coerced
|
|
549
|
+
connection.create_table :testings do |t|
|
|
550
|
+
t.column :foo, :datetime
|
|
551
|
+
end
|
|
254
552
|
|
|
255
|
-
|
|
256
|
-
module ConnectionAdapters
|
|
257
|
-
class QuoteARBaseTest < ActiveRecord::TestCase
|
|
553
|
+
column = connection.columns(:testings).find { |c| c.name == "foo" }
|
|
258
554
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
def test_quote_ar_object_coerced
|
|
262
|
-
value = DatetimePrimaryKey.new(id: @time)
|
|
263
|
-
assert_equal "'02-14-2017 12:34:56.79'", @connection.quote(value)
|
|
555
|
+
assert_equal :datetime, column.type
|
|
556
|
+
assert_equal "datetime2(6)", column.sql_type
|
|
264
557
|
end
|
|
265
558
|
|
|
266
|
-
#
|
|
267
|
-
coerce_tests! :
|
|
268
|
-
def
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
559
|
+
# timestamp is datetime with default limit
|
|
560
|
+
coerce_tests! :test_change_column_with_timestamp_type
|
|
561
|
+
def test_change_column_with_timestamp_type_coerced
|
|
562
|
+
connection.create_table :testings do |t|
|
|
563
|
+
t.column :foo, :datetime, null: false
|
|
564
|
+
end
|
|
565
|
+
|
|
566
|
+
connection.change_column :testings, :foo, :timestamp
|
|
567
|
+
|
|
568
|
+
column = connection.columns(:testings).find { |c| c.name == "foo" }
|
|
272
569
|
|
|
570
|
+
assert_equal :datetime, column.type
|
|
571
|
+
assert_equal "datetime", column.sql_type
|
|
572
|
+
end
|
|
273
573
|
end
|
|
274
574
|
end
|
|
275
575
|
end
|
|
276
576
|
|
|
277
|
-
|
|
278
|
-
|
|
279
577
|
module ActiveRecord
|
|
280
578
|
class Migration
|
|
281
579
|
class ColumnAttributesTest < ActiveRecord::TestCase
|
|
@@ -290,16 +588,13 @@ module ActiveRecord
|
|
|
290
588
|
end
|
|
291
589
|
end
|
|
292
590
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
591
|
module ActiveRecord
|
|
297
592
|
class Migration
|
|
298
|
-
class ColumnsTest
|
|
593
|
+
class ColumnsTest < ActiveRecord::TestCase
|
|
299
594
|
# Our defaults are real 70000 integers vs '70000' strings.
|
|
300
595
|
coerce_tests! :test_rename_column_preserves_default_value_not_null
|
|
301
596
|
def test_rename_column_preserves_default_value_not_null_coerced
|
|
302
|
-
add_column
|
|
597
|
+
add_column "test_models", "salary", :integer, :default => 70000
|
|
303
598
|
default_before = connection.columns("test_models").find { |c| c.name == "salary" }.default
|
|
304
599
|
assert_equal 70000, default_before
|
|
305
600
|
rename_column "test_models", "salary", "annual_salary"
|
|
@@ -315,9 +610,9 @@ module ActiveRecord
|
|
|
315
610
|
add_column "test_models", :hat_size, :integer
|
|
316
611
|
add_column "test_models", :hat_style, :string, :limit => 100
|
|
317
612
|
add_index "test_models", ["hat_style", "hat_size"], :unique => true
|
|
318
|
-
assert_equal 1, connection.indexes(
|
|
613
|
+
assert_equal 1, connection.indexes("test_models").size
|
|
319
614
|
remove_column("test_models", "hat_size")
|
|
320
|
-
assert_equal [], connection.indexes(
|
|
615
|
+
assert_equal [], connection.indexes("test_models").map(&:name)
|
|
321
616
|
end
|
|
322
617
|
|
|
323
618
|
# Choose `StatementInvalid` vs `ActiveRecordError`.
|
|
@@ -332,61 +627,49 @@ module ActiveRecord
|
|
|
332
627
|
end
|
|
333
628
|
end
|
|
334
629
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
630
|
class MigrationTest < ActiveRecord::TestCase
|
|
339
|
-
# We do not have do the DecimalWithoutScale type.
|
|
340
|
-
coerce_tests! :test_add_table_with_decimals
|
|
341
|
-
def test_add_table_with_decimals_coerced
|
|
342
|
-
Person.connection.drop_table :big_numbers rescue nil
|
|
343
|
-
assert !BigNumber.table_exists?
|
|
344
|
-
GiveMeBigNumbers.up
|
|
345
|
-
BigNumber.reset_column_information
|
|
346
|
-
assert BigNumber.create(
|
|
347
|
-
:bank_balance => 1586.43,
|
|
348
|
-
:big_bank_balance => BigDecimal("1000234000567.95"),
|
|
349
|
-
:world_population => 6000000000,
|
|
350
|
-
:my_house_population => 3,
|
|
351
|
-
:value_of_e => BigDecimal("2.7182818284590452353602875")
|
|
352
|
-
)
|
|
353
|
-
b = BigNumber.first
|
|
354
|
-
assert_not_nil b
|
|
355
|
-
assert_not_nil b.bank_balance
|
|
356
|
-
assert_not_nil b.big_bank_balance
|
|
357
|
-
assert_not_nil b.world_population
|
|
358
|
-
assert_not_nil b.my_house_population
|
|
359
|
-
assert_not_nil b.value_of_e
|
|
360
|
-
assert_kind_of BigDecimal, b.world_population
|
|
361
|
-
assert_equal '6000000000.0', b.world_population.to_s
|
|
362
|
-
assert_kind_of Integer, b.my_house_population
|
|
363
|
-
assert_equal 3, b.my_house_population
|
|
364
|
-
assert_kind_of BigDecimal, b.bank_balance
|
|
365
|
-
assert_equal BigDecimal("1586.43"), b.bank_balance
|
|
366
|
-
assert_kind_of BigDecimal, b.big_bank_balance
|
|
367
|
-
assert_equal BigDecimal("1000234000567.95"), b.big_bank_balance
|
|
368
|
-
GiveMeBigNumbers.down
|
|
369
|
-
assert_raise(ActiveRecord::StatementInvalid) { BigNumber.first }
|
|
370
|
-
end
|
|
371
|
-
|
|
372
631
|
# For some reason our tests set Rails.@_env which breaks test env switching.
|
|
373
632
|
coerce_tests! :test_internal_metadata_stores_environment_when_other_data_exists
|
|
374
633
|
coerce_tests! :test_internal_metadata_stores_environment
|
|
375
|
-
end
|
|
376
634
|
|
|
635
|
+
# Same as original but using binary type instead of blob
|
|
636
|
+
coerce_tests! :test_add_column_with_casted_type_if_not_exists_set_to_true
|
|
637
|
+
def test_add_column_with_casted_type_if_not_exists_set_to_true_coerced
|
|
638
|
+
migration_a = Class.new(ActiveRecord::Migration::Current) {
|
|
639
|
+
def version; 100 end
|
|
640
|
+
def migrate(x)
|
|
641
|
+
add_column "people", "last_name", :binary
|
|
642
|
+
end
|
|
643
|
+
}.new
|
|
644
|
+
|
|
645
|
+
migration_b = Class.new(ActiveRecord::Migration::Current) {
|
|
646
|
+
def version; 101 end
|
|
647
|
+
def migrate(x)
|
|
648
|
+
add_column "people", "last_name", :binary, if_not_exists: true
|
|
649
|
+
end
|
|
650
|
+
}.new
|
|
377
651
|
|
|
652
|
+
ActiveRecord::Migrator.new(:up, [migration_a], @schema_migration, 100).migrate
|
|
653
|
+
assert_column Person, :last_name, "migration_a should have created the last_name column on people"
|
|
378
654
|
|
|
655
|
+
assert_nothing_raised do
|
|
656
|
+
ActiveRecord::Migrator.new(:up, [migration_b], @schema_migration, 101).migrate
|
|
657
|
+
end
|
|
658
|
+
ensure
|
|
659
|
+
Person.reset_column_information
|
|
660
|
+
if Person.column_names.include?("last_name")
|
|
661
|
+
Person.connection.remove_column("people", "last_name")
|
|
662
|
+
end
|
|
663
|
+
end
|
|
664
|
+
end
|
|
379
665
|
|
|
380
666
|
class CoreTest < ActiveRecord::TestCase
|
|
381
|
-
# I think fixtures are
|
|
667
|
+
# I think fixtures are using the wrong time zone and the `:first`
|
|
382
668
|
# `topics`.`bonus_time` attribute of 2005-01-30t15:28:00.00+01:00 is
|
|
383
669
|
# getting local EST time for me and set to "09:28:00.0000000".
|
|
384
670
|
coerce_tests! :test_pretty_print_persisted
|
|
385
671
|
end
|
|
386
672
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
673
|
module ActiveRecord
|
|
391
674
|
module ConnectionAdapters
|
|
392
675
|
# Just like PostgreSQLAdapter does.
|
|
@@ -400,27 +683,147 @@ module ActiveRecord
|
|
|
400
683
|
end
|
|
401
684
|
end
|
|
402
685
|
|
|
686
|
+
module ActiveRecord
|
|
687
|
+
# The original module is hardcoded for PostgreSQL/SQLite/MySQL tests.
|
|
688
|
+
module DatabaseTasksSetupper
|
|
689
|
+
def setup
|
|
690
|
+
@sqlserver_tasks =
|
|
691
|
+
Class.new do
|
|
692
|
+
def create; end
|
|
693
|
+
|
|
694
|
+
def drop; end
|
|
403
695
|
|
|
696
|
+
def purge; end
|
|
404
697
|
|
|
698
|
+
def charset; end
|
|
405
699
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
700
|
+
def collation; end
|
|
701
|
+
|
|
702
|
+
def structure_dump(*); end
|
|
703
|
+
|
|
704
|
+
def structure_load(*); end
|
|
705
|
+
end.new
|
|
706
|
+
|
|
707
|
+
$stdout, @original_stdout = StringIO.new, $stdout
|
|
708
|
+
$stderr, @original_stderr = StringIO.new, $stderr
|
|
709
|
+
end
|
|
710
|
+
|
|
711
|
+
def with_stubbed_new
|
|
712
|
+
ActiveRecord::Tasks::SQLServerDatabaseTasks.stub(:new, @sqlserver_tasks) do
|
|
713
|
+
yield
|
|
714
|
+
end
|
|
715
|
+
end
|
|
716
|
+
end
|
|
717
|
+
|
|
718
|
+
class DatabaseTasksCreateTest < ActiveRecord::TestCase
|
|
719
|
+
# Coerce PostgreSQL/SQLite/MySQL tests.
|
|
720
|
+
coerce_all_tests!
|
|
721
|
+
|
|
722
|
+
def test_sqlserver_create
|
|
723
|
+
with_stubbed_new do
|
|
724
|
+
assert_called(eval("@sqlserver_tasks"), :create) do
|
|
725
|
+
ActiveRecord::Tasks::DatabaseTasks.create "adapter" => :sqlserver
|
|
726
|
+
end
|
|
727
|
+
end
|
|
728
|
+
end
|
|
729
|
+
end
|
|
730
|
+
|
|
731
|
+
class DatabaseTasksDropTest < ActiveRecord::TestCase
|
|
732
|
+
# Coerce PostgreSQL/SQLite/MySQL tests.
|
|
733
|
+
coerce_all_tests!
|
|
734
|
+
|
|
735
|
+
def test_sqlserver_drop
|
|
736
|
+
with_stubbed_new do
|
|
737
|
+
assert_called(eval("@sqlserver_tasks"), :drop) do
|
|
738
|
+
ActiveRecord::Tasks::DatabaseTasks.drop "adapter" => :sqlserver
|
|
739
|
+
end
|
|
740
|
+
end
|
|
741
|
+
end
|
|
742
|
+
end
|
|
743
|
+
|
|
744
|
+
class DatabaseTasksPurgeTest < ActiveRecord::TestCase
|
|
745
|
+
# Coerce PostgreSQL/SQLite/MySQL tests.
|
|
746
|
+
coerce_all_tests!
|
|
747
|
+
|
|
748
|
+
def test_sqlserver_purge
|
|
749
|
+
with_stubbed_new do
|
|
750
|
+
assert_called(eval("@sqlserver_tasks"), :purge) do
|
|
751
|
+
ActiveRecord::Tasks::DatabaseTasks.purge "adapter" => :sqlserver
|
|
752
|
+
end
|
|
753
|
+
end
|
|
754
|
+
end
|
|
755
|
+
end
|
|
756
|
+
|
|
757
|
+
class DatabaseTasksCharsetTest < ActiveRecord::TestCase
|
|
758
|
+
# Coerce PostgreSQL/SQLite/MySQL tests.
|
|
759
|
+
coerce_all_tests!
|
|
760
|
+
|
|
761
|
+
def test_sqlserver_charset
|
|
762
|
+
with_stubbed_new do
|
|
763
|
+
assert_called(eval("@sqlserver_tasks"), :charset) do
|
|
764
|
+
ActiveRecord::Tasks::DatabaseTasks.charset "adapter" => :sqlserver
|
|
765
|
+
end
|
|
766
|
+
end
|
|
767
|
+
end
|
|
768
|
+
end
|
|
769
|
+
|
|
770
|
+
class DatabaseTasksCollationTest < ActiveRecord::TestCase
|
|
771
|
+
# Coerce PostgreSQL/SQLite/MySQL tests.
|
|
772
|
+
coerce_all_tests!
|
|
773
|
+
|
|
774
|
+
def test_sqlserver_collation
|
|
775
|
+
with_stubbed_new do
|
|
776
|
+
assert_called(eval("@sqlserver_tasks"), :collation) do
|
|
777
|
+
ActiveRecord::Tasks::DatabaseTasks.collation "adapter" => :sqlserver
|
|
778
|
+
end
|
|
779
|
+
end
|
|
780
|
+
end
|
|
781
|
+
end
|
|
782
|
+
|
|
783
|
+
class DatabaseTasksStructureDumpTest < ActiveRecord::TestCase
|
|
784
|
+
# Coerce PostgreSQL/SQLite/MySQL tests.
|
|
785
|
+
coerce_all_tests!
|
|
786
|
+
|
|
787
|
+
def test_sqlserver_structure_dump
|
|
788
|
+
with_stubbed_new do
|
|
789
|
+
assert_called_with(
|
|
790
|
+
eval("@sqlserver_tasks"), :structure_dump,
|
|
791
|
+
["awesome-file.sql", nil]
|
|
792
|
+
) do
|
|
793
|
+
ActiveRecord::Tasks::DatabaseTasks.structure_dump({ "adapter" => :sqlserver }, "awesome-file.sql")
|
|
794
|
+
end
|
|
795
|
+
end
|
|
796
|
+
end
|
|
797
|
+
end
|
|
798
|
+
|
|
799
|
+
class DatabaseTasksStructureLoadTest < ActiveRecord::TestCase
|
|
800
|
+
# Coerce PostgreSQL/SQLite/MySQL tests.
|
|
801
|
+
coerce_all_tests!
|
|
802
|
+
|
|
803
|
+
def test_sqlserver_structure_load
|
|
804
|
+
with_stubbed_new do
|
|
805
|
+
assert_called_with(
|
|
806
|
+
eval("@sqlserver_tasks"),
|
|
807
|
+
:structure_load,
|
|
808
|
+
["awesome-file.sql", nil]
|
|
809
|
+
) do
|
|
810
|
+
ActiveRecord::Tasks::DatabaseTasks.structure_load({ "adapter" => :sqlserver }, "awesome-file.sql")
|
|
811
|
+
end
|
|
812
|
+
end
|
|
813
|
+
end
|
|
410
814
|
end
|
|
815
|
+
|
|
411
816
|
class DatabaseTasksCreateAllTest < ActiveRecord::TestCase
|
|
412
817
|
# We extend `local_database?` so that common VM IPs can be used.
|
|
413
818
|
coerce_tests! :test_ignores_remote_databases, :test_warning_for_remote_databases
|
|
414
819
|
end
|
|
820
|
+
|
|
415
821
|
class DatabaseTasksDropAllTest < ActiveRecord::TestCase
|
|
416
822
|
# We extend `local_database?` so that common VM IPs can be used.
|
|
417
823
|
coerce_tests! :test_ignores_remote_databases, :test_warning_for_remote_databases
|
|
418
824
|
end
|
|
419
825
|
end
|
|
420
826
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
827
|
class DefaultScopingTest < ActiveRecord::TestCase
|
|
425
828
|
# We are not doing order duplicate removal anymore.
|
|
426
829
|
coerce_tests! :test_order_in_default_scope_should_not_prevail
|
|
@@ -434,16 +837,13 @@ class DefaultScopingTest < ActiveRecord::TestCase
|
|
|
434
837
|
end
|
|
435
838
|
end
|
|
436
839
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
require 'models/post'
|
|
441
|
-
require 'models/subscriber'
|
|
840
|
+
require "models/post"
|
|
841
|
+
require "models/subscriber"
|
|
442
842
|
class EachTest < ActiveRecord::TestCase
|
|
443
843
|
# Quoting in tests does not cope with bracket quoting.
|
|
444
844
|
coerce_tests! :test_find_in_batches_should_quote_batch_order
|
|
445
845
|
def test_find_in_batches_should_quote_batch_order_coerced
|
|
446
|
-
|
|
846
|
+
Post.connection
|
|
447
847
|
assert_sql(/ORDER BY \[posts\]\.\[id\]/) do
|
|
448
848
|
Post.find_in_batches(:batch_size => 1) do |batch|
|
|
449
849
|
assert_kind_of Array, batch
|
|
@@ -455,7 +855,7 @@ class EachTest < ActiveRecord::TestCase
|
|
|
455
855
|
# Quoting in tests does not cope with bracket quoting.
|
|
456
856
|
coerce_tests! :test_in_batches_should_quote_batch_order
|
|
457
857
|
def test_in_batches_should_quote_batch_order_coerced
|
|
458
|
-
|
|
858
|
+
Post.connection
|
|
459
859
|
assert_sql(/ORDER BY \[posts\]\.\[id\]/) do
|
|
460
860
|
Post.in_batches(of: 1) do |relation|
|
|
461
861
|
assert_kind_of ActiveRecord::Relation, relation
|
|
@@ -465,9 +865,6 @@ class EachTest < ActiveRecord::TestCase
|
|
|
465
865
|
end
|
|
466
866
|
end
|
|
467
867
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
868
|
class EagerAssociationTest < ActiveRecord::TestCase
|
|
472
869
|
# Use LEN() vs length() function.
|
|
473
870
|
coerce_tests! :test_count_with_include
|
|
@@ -479,14 +876,17 @@ class EagerAssociationTest < ActiveRecord::TestCase
|
|
|
479
876
|
coerce_tests! %r{including association based on sql condition and no database column}
|
|
480
877
|
end
|
|
481
878
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
require 'models/topic'
|
|
879
|
+
require "models/topic"
|
|
880
|
+
require "models/customer"
|
|
881
|
+
require "models/non_primary_key"
|
|
486
882
|
class FinderTest < ActiveRecord::TestCase
|
|
883
|
+
fixtures :customers, :topics, :authors
|
|
884
|
+
|
|
885
|
+
# We have implicit ordering, via FETCH.
|
|
487
886
|
coerce_tests! %r{doesn't have implicit ordering},
|
|
488
|
-
:test_find_doesnt_have_implicit_ordering
|
|
887
|
+
:test_find_doesnt_have_implicit_ordering
|
|
489
888
|
|
|
889
|
+
# Square brackets around column name
|
|
490
890
|
coerce_tests! :test_exists_does_not_select_columns_without_alias
|
|
491
891
|
def test_exists_does_not_select_columns_without_alias_coerced
|
|
492
892
|
assert_sql(/SELECT\s+1 AS one FROM \[topics\].*OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) do
|
|
@@ -494,6 +894,7 @@ class FinderTest < ActiveRecord::TestCase
|
|
|
494
894
|
end
|
|
495
895
|
end
|
|
496
896
|
|
|
897
|
+
# Assert SQL Server limit implementation
|
|
497
898
|
coerce_tests! :test_take_and_first_and_last_with_integer_should_use_sql_limit
|
|
498
899
|
def test_take_and_first_and_last_with_integer_should_use_sql_limit_coerced
|
|
499
900
|
assert_sql(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.* @0 = 3/) { Topic.take(3).entries }
|
|
@@ -507,7 +908,7 @@ class FinderTest < ActiveRecord::TestCase
|
|
|
507
908
|
# Can not use array condition due to not finding right type and hence fractional second quoting.
|
|
508
909
|
coerce_tests! :test_condition_utc_time_interpolation_with_default_timezone_local
|
|
509
910
|
def test_condition_utc_time_interpolation_with_default_timezone_local_coerced
|
|
510
|
-
with_env_tz
|
|
911
|
+
with_env_tz "America/New_York" do
|
|
511
912
|
with_timezone_config default: :local do
|
|
512
913
|
topic = Topic.first
|
|
513
914
|
assert_equal topic, Topic.where(written_on: topic.written_on.getutc).first
|
|
@@ -518,17 +919,95 @@ class FinderTest < ActiveRecord::TestCase
|
|
|
518
919
|
# Can not use array condition due to not finding right type and hence fractional second quoting.
|
|
519
920
|
coerce_tests! :test_condition_local_time_interpolation_with_default_timezone_utc
|
|
520
921
|
def test_condition_local_time_interpolation_with_default_timezone_utc_coerced
|
|
521
|
-
with_env_tz
|
|
922
|
+
with_env_tz "America/New_York" do
|
|
522
923
|
with_timezone_config default: :utc do
|
|
523
924
|
topic = Topic.first
|
|
524
925
|
assert_equal topic, Topic.where(written_on: topic.written_on.getlocal).first
|
|
525
926
|
end
|
|
526
927
|
end
|
|
527
928
|
end
|
|
528
|
-
end
|
|
529
929
|
|
|
930
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
|
931
|
+
coerce_tests! :test_include_on_unloaded_relation_with_match
|
|
932
|
+
def test_include_on_unloaded_relation_with_match_coerced
|
|
933
|
+
assert_sql(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do
|
|
934
|
+
assert_equal true, Customer.where(name: "David").include?(customers(:david))
|
|
935
|
+
end
|
|
936
|
+
end
|
|
937
|
+
|
|
938
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
|
939
|
+
coerce_tests! :test_include_on_unloaded_relation_without_match
|
|
940
|
+
def test_include_on_unloaded_relation_without_match_coerced
|
|
941
|
+
assert_sql(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do
|
|
942
|
+
assert_equal false, Customer.where(name: "David").include?(customers(:mary))
|
|
943
|
+
end
|
|
944
|
+
end
|
|
945
|
+
|
|
946
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
|
947
|
+
coerce_tests! :test_member_on_unloaded_relation_with_match
|
|
948
|
+
def test_member_on_unloaded_relation_with_match_coerced
|
|
949
|
+
assert_sql(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do
|
|
950
|
+
assert_equal true, Customer.where(name: "David").member?(customers(:david))
|
|
951
|
+
end
|
|
952
|
+
end
|
|
953
|
+
|
|
954
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
|
955
|
+
coerce_tests! :test_member_on_unloaded_relation_without_match
|
|
956
|
+
def test_member_on_unloaded_relation_without_match_coerced
|
|
957
|
+
assert_sql(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do
|
|
958
|
+
assert_equal false, Customer.where(name: "David").member?(customers(:mary))
|
|
959
|
+
end
|
|
960
|
+
end
|
|
961
|
+
|
|
962
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
|
963
|
+
coerce_tests! :test_implicit_order_column_is_configurable
|
|
964
|
+
def test_implicit_order_column_is_configurable_coerced
|
|
965
|
+
old_implicit_order_column = Topic.implicit_order_column
|
|
966
|
+
Topic.implicit_order_column = "title"
|
|
967
|
+
|
|
968
|
+
assert_equal topics(:fifth), Topic.first
|
|
969
|
+
assert_equal topics(:third), Topic.last
|
|
970
|
+
|
|
971
|
+
c = Topic.connection
|
|
972
|
+
assert_sql(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.title"))} DESC, #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) {
|
|
973
|
+
Topic.last
|
|
974
|
+
}
|
|
975
|
+
ensure
|
|
976
|
+
Topic.implicit_order_column = old_implicit_order_column
|
|
977
|
+
end
|
|
978
|
+
|
|
979
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
|
980
|
+
coerce_tests! :test_implicit_order_set_to_primary_key
|
|
981
|
+
def test_implicit_order_set_to_primary_key_coerced
|
|
982
|
+
old_implicit_order_column = Topic.implicit_order_column
|
|
983
|
+
Topic.implicit_order_column = "id"
|
|
984
|
+
|
|
985
|
+
c = Topic.connection
|
|
986
|
+
assert_sql(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) {
|
|
987
|
+
Topic.last
|
|
988
|
+
}
|
|
989
|
+
ensure
|
|
990
|
+
Topic.implicit_order_column = old_implicit_order_column
|
|
991
|
+
end
|
|
992
|
+
|
|
993
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
|
994
|
+
coerce_tests! :test_implicit_order_for_model_without_primary_key
|
|
995
|
+
def test_implicit_order_for_model_without_primary_key_coerced
|
|
996
|
+
old_implicit_order_column = NonPrimaryKey.implicit_order_column
|
|
997
|
+
NonPrimaryKey.implicit_order_column = "created_at"
|
|
998
|
+
|
|
999
|
+
c = NonPrimaryKey.connection
|
|
530
1000
|
|
|
1001
|
+
assert_sql(/ORDER BY #{Regexp.escape(c.quote_table_name("non_primary_keys.created_at"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) {
|
|
1002
|
+
NonPrimaryKey.last
|
|
1003
|
+
}
|
|
1004
|
+
ensure
|
|
1005
|
+
NonPrimaryKey.implicit_order_column = old_implicit_order_column
|
|
1006
|
+
end
|
|
531
1007
|
|
|
1008
|
+
# SQL Server is unable to use aliased SELECT in the HAVING clause.
|
|
1009
|
+
coerce_tests! :test_include_on_unloaded_relation_with_having_referencing_aliased_select
|
|
1010
|
+
end
|
|
532
1011
|
|
|
533
1012
|
module ActiveRecord
|
|
534
1013
|
class Migration
|
|
@@ -543,94 +1022,120 @@ module ActiveRecord
|
|
|
543
1022
|
@connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", on_update: :restrict
|
|
544
1023
|
end
|
|
545
1024
|
end
|
|
546
|
-
end
|
|
547
|
-
end
|
|
548
|
-
end
|
|
549
1025
|
|
|
1026
|
+
# Error message depends on the database adapter.
|
|
1027
|
+
coerce_tests! :test_add_foreign_key_with_if_not_exists_not_set
|
|
1028
|
+
def test_add_foreign_key_with_if_not_exists_not_set_coerced
|
|
1029
|
+
@connection.add_foreign_key :astronauts, :rockets
|
|
1030
|
+
assert_equal 1, @connection.foreign_keys("astronauts").size
|
|
550
1031
|
|
|
1032
|
+
error = assert_raises do
|
|
1033
|
+
@connection.add_foreign_key :astronauts, :rockets
|
|
1034
|
+
end
|
|
551
1035
|
|
|
1036
|
+
assert_match(/TinyTds::Error: There is already an object named '.*' in the database/, error.message)
|
|
1037
|
+
end
|
|
1038
|
+
end
|
|
1039
|
+
end
|
|
1040
|
+
end
|
|
552
1041
|
|
|
553
1042
|
class HasOneAssociationsTest < ActiveRecord::TestCase
|
|
554
1043
|
# We use OFFSET/FETCH vs TOP. So we always have an order.
|
|
555
1044
|
coerce_tests! :test_has_one_does_not_use_order_by
|
|
556
|
-
end
|
|
557
|
-
|
|
558
1045
|
|
|
1046
|
+
# Asserted SQL to get one row different from original test.
|
|
1047
|
+
coerce_tests! :test_has_one
|
|
1048
|
+
def test_has_one_coerced
|
|
1049
|
+
firm = companies(:first_firm)
|
|
1050
|
+
first_account = Account.find(1)
|
|
1051
|
+
assert_sql(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do
|
|
1052
|
+
assert_equal first_account, firm.account
|
|
1053
|
+
assert_equal first_account.credit_limit, firm.account.credit_limit
|
|
1054
|
+
end
|
|
1055
|
+
end
|
|
1056
|
+
end
|
|
559
1057
|
|
|
1058
|
+
class HasOneThroughAssociationsTest < ActiveRecord::TestCase
|
|
1059
|
+
# Asserted SQL to get one row different from original test.
|
|
1060
|
+
coerce_tests! :test_has_one_through_executes_limited_query
|
|
1061
|
+
def test_has_one_through_executes_limited_query_coerced
|
|
1062
|
+
boring_club = clubs(:boring_club)
|
|
1063
|
+
assert_sql(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do
|
|
1064
|
+
assert_equal boring_club, @member.general_club
|
|
1065
|
+
end
|
|
1066
|
+
end
|
|
1067
|
+
end
|
|
560
1068
|
|
|
561
|
-
require
|
|
1069
|
+
require "models/company"
|
|
562
1070
|
class InheritanceTest < ActiveRecord::TestCase
|
|
1071
|
+
# Rails test required inserting to a identity column.
|
|
563
1072
|
coerce_tests! :test_a_bad_type_column
|
|
564
1073
|
def test_a_bad_type_column_coerced
|
|
565
|
-
Company.connection.with_identity_insert_enabled(
|
|
1074
|
+
Company.connection.with_identity_insert_enabled("companies") do
|
|
566
1075
|
Company.connection.insert "INSERT INTO companies (id, #{QUOTED_TYPE}, name) VALUES(100, 'bad_class!', 'Not happening')"
|
|
567
1076
|
end
|
|
568
1077
|
assert_raise(ActiveRecord::SubclassNotFound) { Company.find(100) }
|
|
569
1078
|
end
|
|
570
1079
|
|
|
1080
|
+
# Use Square brackets around column name
|
|
571
1081
|
coerce_tests! :test_eager_load_belongs_to_primary_key_quoting
|
|
572
1082
|
def test_eager_load_belongs_to_primary_key_quoting_coerced
|
|
573
|
-
|
|
1083
|
+
Account.connection
|
|
574
1084
|
assert_sql(/\[companies\]\.\[id\] = @0.* @0 = 1/) do
|
|
575
1085
|
Account.all.merge!(:includes => :firm).find(1)
|
|
576
1086
|
end
|
|
577
1087
|
end
|
|
578
1088
|
end
|
|
579
1089
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
1090
|
class LeftOuterJoinAssociationTest < ActiveRecord::TestCase
|
|
584
1091
|
# Uses || operator in SQL. Just trust core gets value out of this test.
|
|
585
1092
|
coerce_tests! :test_does_not_override_select
|
|
586
1093
|
end
|
|
587
1094
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
class NamedScopingTest < ActiveRecord::TestCase
|
|
592
|
-
# This works now because we add an `order(:id)` sort to break the order tie for deterministic results.
|
|
593
|
-
coerce_tests! :test_scopes_honor_current_scopes_from_when_defined
|
|
594
|
-
def test_scopes_honor_current_scopes_from_when_defined_coerced
|
|
595
|
-
assert !Post.ranked_by_comments.order(:id).limit_by(5).empty?
|
|
596
|
-
assert !authors(:david).posts.ranked_by_comments.order(:id).limit_by(5).empty?
|
|
597
|
-
assert_not_equal Post.ranked_by_comments.order(:id).limit_by(5), authors(:david).posts.ranked_by_comments.order(:id).limit_by(5)
|
|
598
|
-
assert_not_equal Post.order(:id).top(5), authors(:david).posts.order(:id).top(5)
|
|
599
|
-
# Oracle sometimes sorts differently if WHERE condition is changed
|
|
600
|
-
assert_equal authors(:david).posts.ranked_by_comments.limit_by(5).to_a.sort_by(&:id), authors(:david).posts.top(5).to_a.sort_by(&:id)
|
|
601
|
-
assert_equal Post.ranked_by_comments.limit_by(5), Post.top(5)
|
|
602
|
-
end
|
|
603
|
-
end
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
require 'models/developer'
|
|
609
|
-
require 'models/computer'
|
|
1095
|
+
require "models/developer"
|
|
1096
|
+
require "models/computer"
|
|
610
1097
|
class NestedRelationScopingTest < ActiveRecord::TestCase
|
|
1098
|
+
# Assert SQL Server limit implementation
|
|
611
1099
|
coerce_tests! :test_merge_options
|
|
612
1100
|
def test_merge_options_coerced
|
|
613
|
-
Developer.where(
|
|
1101
|
+
Developer.where("salary = 80000").scoping do
|
|
614
1102
|
Developer.limit(10).scoping do
|
|
615
1103
|
devs = Developer.all
|
|
616
1104
|
sql = devs.to_sql
|
|
617
|
-
assert_match
|
|
618
|
-
assert_match
|
|
1105
|
+
assert_match "(salary = 80000)", sql
|
|
1106
|
+
assert_match "FETCH NEXT 10 ROWS ONLY", sql
|
|
619
1107
|
end
|
|
620
1108
|
end
|
|
621
1109
|
end
|
|
622
1110
|
end
|
|
623
1111
|
|
|
1112
|
+
require "models/topic"
|
|
1113
|
+
class PersistenceTest < ActiveRecord::TestCase
|
|
1114
|
+
# Rails test required updating a identity column.
|
|
1115
|
+
coerce_tests! :test_update_columns_changing_id
|
|
624
1116
|
|
|
1117
|
+
# Rails test required updating a identity column.
|
|
1118
|
+
coerce_tests! :test_update
|
|
1119
|
+
def test_update_coerced
|
|
1120
|
+
topic = Topic.find(1)
|
|
1121
|
+
assert_not_predicate topic, :approved?
|
|
1122
|
+
assert_equal "The First Topic", topic.title
|
|
625
1123
|
|
|
1124
|
+
topic.update("approved" => true, "title" => "The First Topic Updated")
|
|
1125
|
+
topic.reload
|
|
1126
|
+
assert_predicate topic, :approved?
|
|
1127
|
+
assert_equal "The First Topic Updated", topic.title
|
|
626
1128
|
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
1129
|
+
topic.update(approved: false, title: "The First Topic")
|
|
1130
|
+
topic.reload
|
|
1131
|
+
assert_not_predicate topic, :approved?
|
|
1132
|
+
assert_equal "The First Topic", topic.title
|
|
1133
|
+
end
|
|
1134
|
+
end
|
|
632
1135
|
|
|
633
|
-
|
|
1136
|
+
require "models/author"
|
|
1137
|
+
class UpdateAllTest < ActiveRecord::TestCase
|
|
1138
|
+
# Rails test required updating a identity column.
|
|
634
1139
|
coerce_tests! :test_update_all_doesnt_ignore_order
|
|
635
1140
|
def test_update_all_doesnt_ignore_order_coerced
|
|
636
1141
|
david, mary = authors(:david), authors(:mary)
|
|
@@ -638,81 +1143,112 @@ class PersistenceTest < ActiveRecord::TestCase
|
|
|
638
1143
|
_(mary.id).must_equal 2
|
|
639
1144
|
_(david.name).wont_equal mary.name
|
|
640
1145
|
assert_sql(/UPDATE.*\(SELECT \[authors\].\[id\] FROM \[authors\].*ORDER BY \[authors\].\[id\]/i) do
|
|
641
|
-
Author.where(
|
|
1146
|
+
Author.where("[id] > 1").order(:id).update_all(name: "Test")
|
|
642
1147
|
end
|
|
643
|
-
_(david.reload.name).must_equal
|
|
644
|
-
_(mary.reload.name).must_equal
|
|
1148
|
+
_(david.reload.name).must_equal "David"
|
|
1149
|
+
_(mary.reload.name).must_equal "Test"
|
|
645
1150
|
end
|
|
646
1151
|
|
|
647
|
-
#
|
|
648
|
-
coerce_tests! :
|
|
649
|
-
def
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
# SQLServer: Here is where it breaks down. No exceptions.
|
|
662
|
-
# assert_raise(ActiveRecord::RecordNotUnique, ActiveRecord::StatementInvalid) do
|
|
663
|
-
# topic.update_attributes(id: 3, title: "Hm is it possible?")
|
|
664
|
-
# end
|
|
665
|
-
# assert_not_equal "Hm is it possible?", Topic.find(3).title
|
|
666
|
-
# topic.update_attributes(id: 1234)
|
|
667
|
-
# assert_nothing_raised { topic.reload }
|
|
668
|
-
# assert_equal topic.title, Topic.find(1234).title
|
|
1152
|
+
# SELECT columns must be in the GROUP clause.
|
|
1153
|
+
coerce_tests! :test_update_all_with_group_by
|
|
1154
|
+
def test_update_all_with_group_by_coerced
|
|
1155
|
+
minimum_comments_count = 2
|
|
1156
|
+
|
|
1157
|
+
Post.most_commented(minimum_comments_count).update_all(title: "ig")
|
|
1158
|
+
posts = Post.select(:id, :title).group(:title).most_commented(minimum_comments_count).all.to_a
|
|
1159
|
+
|
|
1160
|
+
assert_operator posts.length, :>, 0
|
|
1161
|
+
assert posts.all? { |post| post.comments.length >= minimum_comments_count }
|
|
1162
|
+
assert posts.all? { |post| "ig" == post.title }
|
|
1163
|
+
|
|
1164
|
+
post = Post.select(:id, :title).group(:title).joins(:comments).group("posts.id").having("count(comments.id) < #{minimum_comments_count}").first
|
|
1165
|
+
assert_not_equal "ig", post.title
|
|
669
1166
|
end
|
|
670
1167
|
end
|
|
671
1168
|
|
|
1169
|
+
class DeleteAllTest < ActiveRecord::TestCase
|
|
1170
|
+
# SELECT columns must be in the GROUP clause.
|
|
1171
|
+
coerce_tests! :test_delete_all_with_group_by_and_having
|
|
1172
|
+
def test_delete_all_with_group_by_and_having_coerced
|
|
1173
|
+
minimum_comments_count = 2
|
|
1174
|
+
posts_to_be_deleted = Post.select(:id).most_commented(minimum_comments_count).all.to_a
|
|
1175
|
+
assert_operator posts_to_be_deleted.length, :>, 0
|
|
672
1176
|
|
|
1177
|
+
assert_difference("Post.count", -posts_to_be_deleted.length) do
|
|
1178
|
+
Post.most_commented(minimum_comments_count).delete_all
|
|
1179
|
+
end
|
|
673
1180
|
|
|
1181
|
+
posts_to_be_deleted.each do |deleted_post|
|
|
1182
|
+
assert_raise(ActiveRecord::RecordNotFound) { deleted_post.reload }
|
|
1183
|
+
end
|
|
1184
|
+
end
|
|
1185
|
+
end
|
|
674
1186
|
|
|
675
|
-
require
|
|
1187
|
+
require "models/topic"
|
|
676
1188
|
module ActiveRecord
|
|
677
1189
|
class PredicateBuilderTest < ActiveRecord::TestCase
|
|
1190
|
+
# Same as original test except string has `N` prefix to indicate unicode string.
|
|
678
1191
|
coerce_tests! :test_registering_new_handlers
|
|
679
1192
|
def test_registering_new_handlers_coerced
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
1193
|
+
assert_match %r{#{Regexp.escape(topic_title)} ~ N'rails'}i, Topic.where(title: /rails/).to_sql
|
|
1194
|
+
end
|
|
1195
|
+
|
|
1196
|
+
# Same as original test except string has `N` prefix to indicate unicode string.
|
|
1197
|
+
coerce_tests! :test_registering_new_handlers_for_association
|
|
1198
|
+
def test_registering_new_handlers_for_association_coerced
|
|
1199
|
+
assert_match %r{#{Regexp.escape(topic_title)} ~ N'rails'}i, Reply.joins(:topic).where(topics: { title: /rails/ }).to_sql
|
|
686
1200
|
end
|
|
687
1201
|
end
|
|
688
1202
|
end
|
|
689
1203
|
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
1204
|
class PrimaryKeysTest < ActiveRecord::TestCase
|
|
694
|
-
#
|
|
695
|
-
# but as far as I can tell, this is only one for us anyway.
|
|
1205
|
+
# SQL Server does not have query for release_savepoint
|
|
696
1206
|
coerce_tests! :test_create_without_primary_key_no_extra_query
|
|
1207
|
+
def test_create_without_primary_key_no_extra_query_coerced
|
|
1208
|
+
klass = Class.new(ActiveRecord::Base) do
|
|
1209
|
+
self.table_name = "dashboards"
|
|
1210
|
+
end
|
|
1211
|
+
klass.create! # warmup schema cache
|
|
1212
|
+
assert_queries(2, ignore_none: true) { klass.create! }
|
|
1213
|
+
end
|
|
697
1214
|
end
|
|
698
1215
|
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
require 'models/task'
|
|
1216
|
+
require "models/task"
|
|
703
1217
|
class QueryCacheTest < ActiveRecord::TestCase
|
|
1218
|
+
# SQL Server adapter not in list of supported adapters in original test.
|
|
704
1219
|
coerce_tests! :test_cache_does_not_wrap_results_in_arrays
|
|
705
1220
|
def test_cache_does_not_wrap_results_in_arrays_coerced
|
|
706
1221
|
Task.cache do
|
|
707
|
-
|
|
1222
|
+
assert_equal 2, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
|
|
708
1223
|
end
|
|
709
1224
|
end
|
|
710
|
-
end
|
|
711
1225
|
|
|
1226
|
+
# Same as original test except that we expect one query to be performed to retrieve the table's primary key
|
|
1227
|
+
# and we don't call `reload_type_map` because SQL Server adapter doesn't support it.
|
|
1228
|
+
# When we generate the SQL for the `find` it includes ordering on the primary key. If we reset the column
|
|
1229
|
+
# information then the primary key needs to be retrieved from the database again to generate the SQL causing the
|
|
1230
|
+
# original test's `assert_no_queries` assertion to fail. Assert that the query was to get the primary key.
|
|
1231
|
+
coerce_tests! :test_query_cached_even_when_types_are_reset
|
|
1232
|
+
def test_query_cached_even_when_types_are_reset_coerced
|
|
1233
|
+
Task.cache do
|
|
1234
|
+
# Warm the cache
|
|
1235
|
+
Task.find(1)
|
|
712
1236
|
|
|
1237
|
+
# Clear places where type information is cached
|
|
1238
|
+
Task.reset_column_information
|
|
1239
|
+
Task.initialize_find_by_cache
|
|
1240
|
+
Task.define_attribute_methods
|
|
713
1241
|
|
|
1242
|
+
assert_queries(1, ignore_none: true) do
|
|
1243
|
+
Task.find(1)
|
|
1244
|
+
end
|
|
714
1245
|
|
|
715
|
-
|
|
1246
|
+
assert_includes ActiveRecord::SQLCounter.log_all.first, "TC.CONSTRAINT_TYPE = N''PRIMARY KEY''"
|
|
1247
|
+
end
|
|
1248
|
+
end
|
|
1249
|
+
end
|
|
1250
|
+
|
|
1251
|
+
require "models/post"
|
|
716
1252
|
class RelationTest < ActiveRecord::TestCase
|
|
717
1253
|
# Use LEN vs LENGTH function.
|
|
718
1254
|
coerce_tests! :test_reverse_order_with_function
|
|
@@ -733,6 +1269,28 @@ class RelationTest < ActiveRecord::TestCase
|
|
|
733
1269
|
# We have implicit ordering, via FETCH.
|
|
734
1270
|
coerce_tests! %r{doesn't have implicit ordering}
|
|
735
1271
|
|
|
1272
|
+
# We have implicit ordering, via FETCH.
|
|
1273
|
+
coerce_tests! :test_reorder_with_take
|
|
1274
|
+
def test_reorder_with_take_coerced
|
|
1275
|
+
sql_log = capture_sql do
|
|
1276
|
+
assert Post.order(:title).reorder(nil).take
|
|
1277
|
+
end
|
|
1278
|
+
assert sql_log.none? { |sql| /order by [posts].[title]/i.match?(sql) }, "ORDER BY title was used in the query: #{sql_log}"
|
|
1279
|
+
assert sql_log.all? { |sql| /order by \[posts\]\.\[id\]/i.match?(sql) }, "default ORDER BY ID was not used in the query: #{sql_log}"
|
|
1280
|
+
end
|
|
1281
|
+
|
|
1282
|
+
# We have implicit ordering, via FETCH.
|
|
1283
|
+
coerce_tests! :test_reorder_with_first
|
|
1284
|
+
def test_reorder_with_first_coerced
|
|
1285
|
+
post = nil
|
|
1286
|
+
sql_log = capture_sql do
|
|
1287
|
+
post = Post.order(:title).reorder(nil).first
|
|
1288
|
+
end
|
|
1289
|
+
assert_equal posts(:welcome), post
|
|
1290
|
+
assert sql_log.none? { |sql| /order by [posts].[title]/i.match?(sql) }, "ORDER BY title was used in the query: #{sql_log}"
|
|
1291
|
+
assert sql_log.all? { |sql| /order by \[posts\]\.\[id\]/i.match?(sql) }, "default ORDER BY ID was not used in the query: #{sql_log}"
|
|
1292
|
+
end
|
|
1293
|
+
|
|
736
1294
|
# We are not doing order duplicate removal anymore.
|
|
737
1295
|
coerce_tests! :test_order_using_scoping
|
|
738
1296
|
|
|
@@ -756,17 +1314,17 @@ class RelationTest < ActiveRecord::TestCase
|
|
|
756
1314
|
# so we are skipping all together.
|
|
757
1315
|
coerce_tests! :test_empty_complex_chained_relations
|
|
758
1316
|
|
|
759
|
-
# Can't apply offset
|
|
1317
|
+
# Can't apply offset without ORDER
|
|
760
1318
|
coerce_tests! %r{using a custom table affects the wheres}
|
|
761
|
-
test
|
|
1319
|
+
test "using a custom table affects the wheres coerced" do
|
|
762
1320
|
post = posts(:welcome)
|
|
763
1321
|
|
|
764
1322
|
assert_equal post, custom_post_relation.where!(title: post.title).order(:id).take
|
|
765
1323
|
end
|
|
766
1324
|
|
|
767
|
-
# Can't apply offset
|
|
1325
|
+
# Can't apply offset without ORDER
|
|
768
1326
|
coerce_tests! %r{using a custom table with joins affects the joins}
|
|
769
|
-
test
|
|
1327
|
+
test "using a custom table with joins affects the joins coerced" do
|
|
770
1328
|
post = posts(:welcome)
|
|
771
1329
|
|
|
772
1330
|
assert_equal post, custom_post_relation.joins(:author).where!(title: post.title).order(:id).take
|
|
@@ -780,45 +1338,55 @@ class RelationTest < ActiveRecord::TestCase
|
|
|
780
1338
|
end
|
|
781
1339
|
end
|
|
782
1340
|
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
%r[INNER\s+JOIN\s+#{Regexp.escape(Author.quoted_table_name)}\s+\Wauthors_categorizations\W]i.match?(sql)
|
|
1341
|
+
module ActiveRecord
|
|
1342
|
+
class RelationTest < ActiveRecord::TestCase
|
|
1343
|
+
# Skipping this test. SQL Server doesn't support optimizer hint as comments
|
|
1344
|
+
coerce_tests! :test_relation_with_optimizer_hints_filters_sql_comment_delimiters
|
|
1345
|
+
|
|
1346
|
+
coerce_tests! :test_does_not_duplicate_optimizer_hints_on_merge
|
|
1347
|
+
def test_does_not_duplicate_optimizer_hints_on_merge_coerced
|
|
1348
|
+
escaped_table = Post.connection.quote_table_name("posts")
|
|
1349
|
+
expected = "SELECT #{escaped_table}.* FROM #{escaped_table} OPTION (OMGHINT)"
|
|
1350
|
+
query = Post.optimizer_hints("OMGHINT").merge(Post.optimizer_hints("OMGHINT")).to_sql
|
|
1351
|
+
assert_equal expected, query
|
|
795
1352
|
end
|
|
796
1353
|
|
|
797
|
-
|
|
1354
|
+
# Workaround for randomly failing test. Ordering of results not guaranteed.
|
|
1355
|
+
# TODO: Remove coerced test when https://github.com/rails/rails/pull/44168 merged.
|
|
1356
|
+
coerce_tests! :test_select_quotes_when_using_from_clause
|
|
1357
|
+
def test_select_quotes_when_using_from_clause_coerced
|
|
1358
|
+
quoted_join = ActiveRecord::Base.connection.quote_table_name("join")
|
|
1359
|
+
selected = Post.select(:join).from(Post.select("id as #{quoted_join}")).map(&:join)
|
|
1360
|
+
assert_equal Post.pluck(:id).sort, selected.sort
|
|
1361
|
+
end
|
|
798
1362
|
end
|
|
799
1363
|
end
|
|
800
1364
|
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
require 'models/post'
|
|
1365
|
+
require "models/post"
|
|
805
1366
|
class SanitizeTest < ActiveRecord::TestCase
|
|
1367
|
+
# Use nvarchar string (N'') in assert
|
|
806
1368
|
coerce_tests! :test_sanitize_sql_like_example_use_case
|
|
807
1369
|
def test_sanitize_sql_like_example_use_case_coerced
|
|
808
1370
|
searchable_post = Class.new(Post) do
|
|
809
|
-
def self.
|
|
810
|
-
where("title LIKE ?", sanitize_sql_like(term,
|
|
1371
|
+
def self.search_as_method(term)
|
|
1372
|
+
where("title LIKE ?", sanitize_sql_like(term, "!"))
|
|
811
1373
|
end
|
|
1374
|
+
|
|
1375
|
+
scope :search_as_scope, ->(term) {
|
|
1376
|
+
where("title LIKE ?", sanitize_sql_like(term, "!"))
|
|
1377
|
+
}
|
|
1378
|
+
end
|
|
1379
|
+
|
|
1380
|
+
assert_sql(/LIKE N'20!% !_reduction!_!!'/) do
|
|
1381
|
+
searchable_post.search_as_method("20% _reduction_!").to_a
|
|
812
1382
|
end
|
|
813
|
-
|
|
814
|
-
|
|
1383
|
+
|
|
1384
|
+
assert_sql(/LIKE N'20!% !_reduction!_!!'/) do
|
|
1385
|
+
searchable_post.search_as_scope("20% _reduction_!").to_a
|
|
815
1386
|
end
|
|
816
1387
|
end
|
|
817
1388
|
end
|
|
818
1389
|
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
1390
|
class SchemaDumperTest < ActiveRecord::TestCase
|
|
823
1391
|
# We have precision to 38.
|
|
824
1392
|
coerce_tests! :test_schema_dump_keeps_large_precision_integer_columns_as_decimal
|
|
@@ -850,19 +1418,14 @@ class SchemaDumperDefaultsTest < ActiveRecord::TestCase
|
|
|
850
1418
|
coerce_tests! :test_schema_dump_defaults_with_universally_supported_types
|
|
851
1419
|
end
|
|
852
1420
|
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
1421
|
class TestAdapterWithInvalidConnection < ActiveRecord::TestCase
|
|
857
1422
|
# We trust Rails on this since we do not want to install mysql.
|
|
858
1423
|
coerce_tests! %r{inspect on Model class does not raise}
|
|
859
1424
|
end
|
|
860
1425
|
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
require 'models/topic'
|
|
1426
|
+
require "models/topic"
|
|
865
1427
|
class TransactionTest < ActiveRecord::TestCase
|
|
1428
|
+
# SQL Server does not have query for release_savepoint
|
|
866
1429
|
coerce_tests! :test_releasing_named_savepoints
|
|
867
1430
|
def test_releasing_named_savepoints_coerced
|
|
868
1431
|
Topic.transaction do
|
|
@@ -874,10 +1437,7 @@ class TransactionTest < ActiveRecord::TestCase
|
|
|
874
1437
|
end
|
|
875
1438
|
end
|
|
876
1439
|
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
require 'models/tag'
|
|
1440
|
+
require "models/tag"
|
|
881
1441
|
class TransactionIsolationTest < ActiveRecord::TestCase
|
|
882
1442
|
# SQL Server will lock the table for counts even when both
|
|
883
1443
|
# connections are `READ COMMITTED`. So we bypass with `READPAST`.
|
|
@@ -887,7 +1447,7 @@ class TransactionIsolationTest < ActiveRecord::TestCase
|
|
|
887
1447
|
assert_equal 0, Tag.count
|
|
888
1448
|
Tag2.transaction do
|
|
889
1449
|
Tag2.create
|
|
890
|
-
assert_equal 0, Tag.lock(
|
|
1450
|
+
assert_equal 0, Tag.lock("WITH(READPAST)").count
|
|
891
1451
|
end
|
|
892
1452
|
end
|
|
893
1453
|
assert_equal 1, Tag.count
|
|
@@ -897,10 +1457,7 @@ class TransactionIsolationTest < ActiveRecord::TestCase
|
|
|
897
1457
|
coerce_tests! %r{repeatable read}
|
|
898
1458
|
end
|
|
899
1459
|
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
require 'models/book'
|
|
1460
|
+
require "models/book"
|
|
904
1461
|
class ViewWithPrimaryKeyTest < ActiveRecord::TestCase
|
|
905
1462
|
# We have a few view tables. use includes vs equality.
|
|
906
1463
|
coerce_tests! :test_views
|
|
@@ -912,9 +1469,10 @@ class ViewWithPrimaryKeyTest < ActiveRecord::TestCase
|
|
|
912
1469
|
coerce_tests! :test_does_not_assume_id_column_as_primary_key
|
|
913
1470
|
def test_does_not_assume_id_column_as_primary_key_coerced
|
|
914
1471
|
model = Class.new(ActiveRecord::Base) { self.table_name = "ebooks" }
|
|
915
|
-
assert_equal
|
|
1472
|
+
assert_equal "id", model.primary_key
|
|
916
1473
|
end
|
|
917
1474
|
end
|
|
1475
|
+
|
|
918
1476
|
class ViewWithoutPrimaryKeyTest < ActiveRecord::TestCase
|
|
919
1477
|
# We have a few view tables. use includes vs equality.
|
|
920
1478
|
coerce_tests! :test_views
|
|
@@ -923,23 +1481,18 @@ class ViewWithoutPrimaryKeyTest < ActiveRecord::TestCase
|
|
|
923
1481
|
end
|
|
924
1482
|
end
|
|
925
1483
|
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
require 'models/author'
|
|
1484
|
+
require "models/author"
|
|
930
1485
|
class YamlSerializationTest < ActiveRecord::TestCase
|
|
931
1486
|
coerce_tests! :test_types_of_virtual_columns_are_not_changed_on_round_trip
|
|
932
1487
|
def test_types_of_virtual_columns_are_not_changed_on_round_trip_coerced
|
|
933
|
-
author = Author.select(
|
|
934
|
-
|
|
1488
|
+
author = Author.select("authors.*, 5 as posts_count").first
|
|
1489
|
+
dumped_author = YAML.dump(author)
|
|
1490
|
+
dumped = YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(dumped_author) : YAML.load(dumped_author)
|
|
935
1491
|
assert_equal 5, author.posts_count
|
|
936
1492
|
assert_equal 5, dumped.posts_count
|
|
937
1493
|
end
|
|
938
1494
|
end
|
|
939
1495
|
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
1496
|
class DateTimePrecisionTest < ActiveRecord::TestCase
|
|
944
1497
|
# Original test had `7` which we support vs `8` which we use.
|
|
945
1498
|
coerce_tests! :test_invalid_datetime_precision_raises_error
|
|
@@ -955,7 +1508,7 @@ class DateTimePrecisionTest < ActiveRecord::TestCase
|
|
|
955
1508
|
coerce_tests! :test_datetime_precision_is_truncated_on_assignment
|
|
956
1509
|
def test_datetime_precision_is_truncated_on_assignment_coerced
|
|
957
1510
|
@connection.create_table(:foos, force: true)
|
|
958
|
-
@connection.add_column :foos, :created_at,
|
|
1511
|
+
@connection.add_column :foos, :created_at, :datetime, precision: 0
|
|
959
1512
|
@connection.add_column :foos, :updated_at, :datetime, precision: 6
|
|
960
1513
|
|
|
961
1514
|
time = ::Time.now.change(nsec: 123456789)
|
|
@@ -972,8 +1525,6 @@ class DateTimePrecisionTest < ActiveRecord::TestCase
|
|
|
972
1525
|
end
|
|
973
1526
|
end
|
|
974
1527
|
|
|
975
|
-
|
|
976
|
-
|
|
977
1528
|
class TimePrecisionTest < ActiveRecord::TestCase
|
|
978
1529
|
# datetime is rounded to increments of .000, .003, or .007 seconds
|
|
979
1530
|
coerce_tests! :test_time_precision_is_truncated_on_assignment
|
|
@@ -994,9 +1545,10 @@ class TimePrecisionTest < ActiveRecord::TestCase
|
|
|
994
1545
|
assert_equal 0, foo.start.nsec
|
|
995
1546
|
assert_equal 123457000, foo.finish.nsec
|
|
996
1547
|
end
|
|
997
|
-
end
|
|
998
|
-
|
|
999
1548
|
|
|
1549
|
+
# SQL Server uses default precision for time.
|
|
1550
|
+
coerce_tests! :test_no_time_precision_isnt_truncated_on_assignment
|
|
1551
|
+
end
|
|
1000
1552
|
|
|
1001
1553
|
class DefaultNumbersTest < ActiveRecord::TestCase
|
|
1002
1554
|
# We do better with native types and do not return strings for everything.
|
|
@@ -1006,16 +1558,23 @@ class DefaultNumbersTest < ActiveRecord::TestCase
|
|
|
1006
1558
|
assert_equal 7, record.positive_integer
|
|
1007
1559
|
assert_equal 7, record.positive_integer_before_type_cast
|
|
1008
1560
|
end
|
|
1561
|
+
|
|
1562
|
+
# We do better with native types and do not return strings for everything.
|
|
1009
1563
|
coerce_tests! :test_default_negative_integer
|
|
1010
1564
|
def test_default_negative_integer_coerced
|
|
1011
1565
|
record = DefaultNumber.new
|
|
1012
1566
|
assert_equal -5, record.negative_integer
|
|
1013
1567
|
assert_equal -5, record.negative_integer_before_type_cast
|
|
1014
1568
|
end
|
|
1015
|
-
end
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
1569
|
|
|
1570
|
+
# We do better with native types and do not return strings for everything.
|
|
1571
|
+
coerce_tests! :test_default_decimal_number
|
|
1572
|
+
def test_default_decimal_number_coerced
|
|
1573
|
+
record = DefaultNumber.new
|
|
1574
|
+
assert_equal BigDecimal("2.78"), record.decimal_number
|
|
1575
|
+
assert_equal 2.78, record.decimal_number_before_type_cast
|
|
1576
|
+
end
|
|
1577
|
+
end
|
|
1019
1578
|
|
|
1020
1579
|
module ActiveRecord
|
|
1021
1580
|
class CollectionCacheKeyTest < ActiveRecord::TestCase
|
|
@@ -1024,114 +1583,179 @@ module ActiveRecord
|
|
|
1024
1583
|
end
|
|
1025
1584
|
end
|
|
1026
1585
|
|
|
1586
|
+
module ActiveRecord
|
|
1587
|
+
class CacheKeyTest < ActiveRecord::TestCase
|
|
1588
|
+
# Like Mysql2 and PostgreSQL, SQL Server doesn't return a string value for updated_at. In the Rails tests
|
|
1589
|
+
# the tests are skipped if adapter is Mysql2 or PostgreSQL.
|
|
1590
|
+
coerce_tests! %r{cache_version is the same when it comes from the DB or from the user}
|
|
1591
|
+
coerce_tests! %r{cache_version does NOT call updated_at when value is from the database}
|
|
1592
|
+
coerce_tests! %r{cache_version does not truncate zeros when timestamp ends in zeros}
|
|
1593
|
+
end
|
|
1594
|
+
end
|
|
1027
1595
|
|
|
1028
|
-
|
|
1029
|
-
|
|
1596
|
+
require "models/book"
|
|
1030
1597
|
module ActiveRecord
|
|
1031
1598
|
class StatementCacheTest < ActiveRecord::TestCase
|
|
1032
1599
|
# Getting random failures.
|
|
1033
1600
|
coerce_tests! :test_find_does_not_use_statement_cache_if_table_name_is_changed
|
|
1034
|
-
end
|
|
1035
|
-
end
|
|
1036
|
-
|
|
1037
1601
|
|
|
1602
|
+
# Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
|
|
1603
|
+
coerce_tests! :test_statement_cache_values_differ
|
|
1604
|
+
def test_statement_cache_values_differ_coerced
|
|
1605
|
+
Book.connection.remove_index(:books, column: [:author_id, :name])
|
|
1038
1606
|
|
|
1607
|
+
original_test_statement_cache_values_differ
|
|
1608
|
+
ensure
|
|
1609
|
+
Book.where(author_id: nil, name: 'my book').delete_all
|
|
1610
|
+
Book.connection.add_index(:books, [:author_id, :name], unique: true)
|
|
1611
|
+
end
|
|
1612
|
+
end
|
|
1613
|
+
end
|
|
1039
1614
|
|
|
1040
1615
|
module ActiveRecord
|
|
1041
1616
|
module ConnectionAdapters
|
|
1042
1617
|
class SchemaCacheTest < ActiveRecord::TestCase
|
|
1618
|
+
# Tests fail on Windows AppVeyor CI with 'Permission denied' error when renaming file during `File.atomic_write` call.
|
|
1619
|
+
coerce_tests! :test_yaml_dump_and_load, :test_yaml_dump_and_load_with_gzip if RbConfig::CONFIG["host_os"] =~ /mswin|mingw/
|
|
1620
|
+
|
|
1621
|
+
# Ruby 2.5 and 2.6 have issues to marshal Time before 1900. 2012.sql has one column with default value 1753
|
|
1622
|
+
coerce_tests! :test_marshal_dump_and_load_with_gzip, :test_marshal_dump_and_load_via_disk
|
|
1623
|
+
|
|
1624
|
+
# Tests fail on Windows AppVeyor CI with 'Permission denied' error when renaming file during `File.atomic_write` call.
|
|
1625
|
+
unless RbConfig::CONFIG["host_os"] =~ /mswin|mingw/
|
|
1626
|
+
def test_marshal_dump_and_load_with_gzip_coerced
|
|
1627
|
+
with_marshable_time_defaults { original_test_marshal_dump_and_load_with_gzip }
|
|
1628
|
+
end
|
|
1629
|
+
def test_marshal_dump_and_load_via_disk_coerced
|
|
1630
|
+
with_marshable_time_defaults { original_test_marshal_dump_and_load_via_disk }
|
|
1631
|
+
end
|
|
1632
|
+
end
|
|
1633
|
+
|
|
1043
1634
|
private
|
|
1635
|
+
|
|
1636
|
+
def with_marshable_time_defaults
|
|
1637
|
+
# Detect problems
|
|
1638
|
+
if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.7")
|
|
1639
|
+
column = @connection.columns(:sst_datatypes).find { |c| c.name == "datetime" }
|
|
1640
|
+
current_default = column.default if column.default.is_a?(Time) && column.default.year < 1900
|
|
1641
|
+
end
|
|
1642
|
+
|
|
1643
|
+
# Correct problems
|
|
1644
|
+
if current_default.present?
|
|
1645
|
+
@connection.change_column_default(:sst_datatypes, :datetime, current_default.dup.change(year: 1900))
|
|
1646
|
+
end
|
|
1647
|
+
|
|
1648
|
+
# Run original test
|
|
1649
|
+
yield
|
|
1650
|
+
ensure
|
|
1651
|
+
# Revert changes
|
|
1652
|
+
@connection.change_column_default(:sst_datatypes, :datetime, current_default) if current_default.present?
|
|
1653
|
+
end
|
|
1654
|
+
|
|
1044
1655
|
# We need to give the full path for this to work.
|
|
1045
1656
|
def schema_dump_path
|
|
1046
|
-
File.join ARTest::SQLServer.root_activerecord,
|
|
1657
|
+
File.join ARTest::SQLServer.root_activerecord, "test/assets/schema_dump_5_1.yml"
|
|
1047
1658
|
end
|
|
1048
1659
|
end
|
|
1049
1660
|
end
|
|
1050
1661
|
end
|
|
1051
1662
|
|
|
1663
|
+
require "models/post"
|
|
1664
|
+
require "models/comment"
|
|
1052
1665
|
class UnsafeRawSqlTest < ActiveRecord::TestCase
|
|
1053
|
-
|
|
1054
|
-
test 'order: always allows Arel' do
|
|
1055
|
-
ids_depr = with_unsafe_raw_sql_deprecated { Post.order(Arel.sql("len(title)")).pluck(:title) }
|
|
1056
|
-
ids_disabled = with_unsafe_raw_sql_disabled { Post.order(Arel.sql("len(title)")).pluck(:title) }
|
|
1666
|
+
fixtures :posts
|
|
1057
1667
|
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
values_depr = with_unsafe_raw_sql_deprecated { Post.includes(:comments).pluck(:title, Arel.sql("len(title)")) }
|
|
1063
|
-
values_disabled = with_unsafe_raw_sql_disabled { Post.includes(:comments).pluck(:title, Arel.sql("len(title)")) }
|
|
1668
|
+
# Use LEN() vs length() function.
|
|
1669
|
+
coerce_tests! %r{order: always allows Arel}
|
|
1670
|
+
test "order: always allows Arel" do
|
|
1671
|
+
titles = Post.order(Arel.sql("len(title)")).pluck(:title)
|
|
1064
1672
|
|
|
1065
|
-
|
|
1673
|
+
assert_not_empty titles
|
|
1066
1674
|
end
|
|
1067
1675
|
|
|
1676
|
+
# Use LEN() vs length() function.
|
|
1677
|
+
coerce_tests! %r{pluck: always allows Arel}
|
|
1678
|
+
test "pluck: always allows Arel" do
|
|
1679
|
+
excepted_values = Post.includes(:comments).pluck(:title).map { |title| [title, title.size] }
|
|
1680
|
+
values = Post.includes(:comments).pluck(:title, Arel.sql("len(title)"))
|
|
1068
1681
|
|
|
1069
|
-
|
|
1070
|
-
test "order: disallows invalid Array arguments" do
|
|
1071
|
-
with_unsafe_raw_sql_disabled do
|
|
1072
|
-
assert_raises(ActiveRecord::UnknownAttributeReference) do
|
|
1073
|
-
Post.order(["author_id", "len(title)"]).pluck(:id)
|
|
1074
|
-
end
|
|
1075
|
-
end
|
|
1682
|
+
assert_equal excepted_values, values
|
|
1076
1683
|
end
|
|
1077
1684
|
|
|
1685
|
+
# Use LEN() vs length() function.
|
|
1078
1686
|
coerce_tests! %r{order: allows valid Array arguments}
|
|
1079
1687
|
test "order: allows valid Array arguments" do
|
|
1080
1688
|
ids_expected = Post.order(Arel.sql("author_id, len(title)")).pluck(:id)
|
|
1081
1689
|
|
|
1082
|
-
|
|
1083
|
-
ids_disabled = with_unsafe_raw_sql_disabled { Post.order(["author_id", Arel.sql("len(title)")]).pluck(:id) }
|
|
1690
|
+
ids = Post.order(["author_id", "len(title)"]).pluck(:id)
|
|
1084
1691
|
|
|
1085
|
-
assert_equal ids_expected,
|
|
1086
|
-
assert_equal ids_expected, ids_disabled
|
|
1692
|
+
assert_equal ids_expected, ids
|
|
1087
1693
|
end
|
|
1088
1694
|
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
end
|
|
1695
|
+
test "order: allows string column names that are quoted" do
|
|
1696
|
+
ids_expected = Post.order(Arel.sql("id")).pluck(:id)
|
|
1697
|
+
|
|
1698
|
+
ids = Post.order("[id]").pluck(:id)
|
|
1699
|
+
|
|
1700
|
+
assert_equal ids_expected, ids
|
|
1096
1701
|
end
|
|
1097
1702
|
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1703
|
+
test "order: allows string column names that are quoted with table" do
|
|
1704
|
+
ids_expected = Post.order(Arel.sql("id")).pluck(:id)
|
|
1705
|
+
|
|
1706
|
+
ids = Post.order("[posts].[id]").pluck(:id)
|
|
1707
|
+
|
|
1708
|
+
assert_equal ids_expected, ids
|
|
1709
|
+
end
|
|
1710
|
+
|
|
1711
|
+
test "order: allows string column names that are quoted with table and user" do
|
|
1712
|
+
ids_expected = Post.order(Arel.sql("id")).pluck(:id)
|
|
1713
|
+
|
|
1714
|
+
ids = Post.order("[dbo].[posts].[id]").pluck(:id)
|
|
1715
|
+
|
|
1716
|
+
assert_equal ids_expected, ids
|
|
1717
|
+
end
|
|
1718
|
+
|
|
1719
|
+
test "order: allows string column names that are quoted with table, user and database" do
|
|
1720
|
+
ids_expected = Post.order(Arel.sql("id")).pluck(:id)
|
|
1721
|
+
|
|
1722
|
+
ids = Post.order("[activerecord_unittest].[dbo].[posts].[id]").pluck(:id)
|
|
1723
|
+
|
|
1724
|
+
assert_equal ids_expected, ids
|
|
1725
|
+
end
|
|
1726
|
+
|
|
1727
|
+
test "pluck: allows string column name that are quoted" do
|
|
1728
|
+
titles_expected = Post.pluck(Arel.sql("title"))
|
|
1729
|
+
|
|
1730
|
+
titles = Post.pluck("[title]")
|
|
1731
|
+
|
|
1732
|
+
assert_equal titles_expected, titles
|
|
1733
|
+
end
|
|
1734
|
+
|
|
1735
|
+
test "pluck: allows string column name that are quoted with table" do
|
|
1736
|
+
titles_expected = Post.pluck(Arel.sql("title"))
|
|
1737
|
+
|
|
1738
|
+
titles = Post.pluck("[posts].[title]")
|
|
1739
|
+
|
|
1740
|
+
assert_equal titles_expected, titles
|
|
1741
|
+
end
|
|
1134
1742
|
|
|
1743
|
+
test "pluck: allows string column name that are quoted with table and user" do
|
|
1744
|
+
titles_expected = Post.pluck(Arel.sql("title"))
|
|
1745
|
+
|
|
1746
|
+
titles = Post.pluck("[dbo].[posts].[title]")
|
|
1747
|
+
|
|
1748
|
+
assert_equal titles_expected, titles
|
|
1749
|
+
end
|
|
1750
|
+
|
|
1751
|
+
test "pluck: allows string column name that are quoted with table, user and database" do
|
|
1752
|
+
titles_expected = Post.pluck(Arel.sql("title"))
|
|
1753
|
+
|
|
1754
|
+
titles = Post.pluck("[activerecord_unittest].[dbo].[posts].[title]")
|
|
1755
|
+
|
|
1756
|
+
assert_equal titles_expected, titles
|
|
1757
|
+
end
|
|
1758
|
+
end
|
|
1135
1759
|
|
|
1136
1760
|
class ReservedWordTest < ActiveRecord::TestCase
|
|
1137
1761
|
coerce_tests! :test_change_columns
|
|
@@ -1143,41 +1767,441 @@ class ReservedWordTest < ActiveRecord::TestCase
|
|
|
1143
1767
|
end
|
|
1144
1768
|
end
|
|
1145
1769
|
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
1770
|
class OptimisticLockingTest < ActiveRecord::TestCase
|
|
1149
1771
|
# We do not allow updating identities, but we can test using a non-identity key
|
|
1150
1772
|
coerce_tests! :test_update_with_dirty_primary_key
|
|
1151
1773
|
def test_update_with_dirty_primary_key_coerced
|
|
1152
1774
|
assert_raises(ActiveRecord::RecordNotUnique) do
|
|
1153
|
-
record = StringKeyObject.find(
|
|
1154
|
-
record.id =
|
|
1775
|
+
record = StringKeyObject.find("record1")
|
|
1776
|
+
record.id = "record2"
|
|
1155
1777
|
record.save!
|
|
1156
1778
|
end
|
|
1157
1779
|
|
|
1158
|
-
record = StringKeyObject.find(
|
|
1159
|
-
record.id =
|
|
1780
|
+
record = StringKeyObject.find("record1")
|
|
1781
|
+
record.id = "record42"
|
|
1160
1782
|
record.save!
|
|
1161
1783
|
|
|
1162
|
-
assert StringKeyObject.find(
|
|
1784
|
+
assert StringKeyObject.find("record42")
|
|
1163
1785
|
assert_raises(ActiveRecord::RecordNotFound) do
|
|
1164
|
-
StringKeyObject.find(
|
|
1786
|
+
StringKeyObject.find("record1")
|
|
1165
1787
|
end
|
|
1166
1788
|
end
|
|
1167
1789
|
end
|
|
1168
1790
|
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
1791
|
class RelationMergingTest < ActiveRecord::TestCase
|
|
1792
|
+
# Use nvarchar string (N'') in assert
|
|
1172
1793
|
coerce_tests! :test_merging_with_order_with_binds
|
|
1173
1794
|
def test_merging_with_order_with_binds_coerced
|
|
1174
1795
|
relation = Post.all.merge(Post.order([Arel.sql("title LIKE ?"), "%suffix"]))
|
|
1175
1796
|
assert_equal ["title LIKE N'%suffix'"], relation.order_values
|
|
1176
1797
|
end
|
|
1798
|
+
|
|
1799
|
+
# Same as original but change first regexp to match sp_executesql binding syntax
|
|
1800
|
+
coerce_tests! :test_merge_doesnt_duplicate_same_clauses
|
|
1801
|
+
def test_merge_doesnt_duplicate_same_clauses_coerced
|
|
1802
|
+
david, mary, bob = authors(:david, :mary, :bob)
|
|
1803
|
+
|
|
1804
|
+
non_mary_and_bob = Author.where.not(id: [mary, bob])
|
|
1805
|
+
|
|
1806
|
+
author_id = Author.connection.quote_table_name("authors.id")
|
|
1807
|
+
assert_sql(/WHERE #{Regexp.escape(author_id)} NOT IN \((@\d), \g<1>\)'/) do
|
|
1808
|
+
assert_equal [david], non_mary_and_bob.merge(non_mary_and_bob)
|
|
1809
|
+
end
|
|
1810
|
+
|
|
1811
|
+
only_david = Author.where("#{author_id} IN (?)", david)
|
|
1812
|
+
|
|
1813
|
+
assert_sql(/WHERE \(#{Regexp.escape(author_id)} IN \(1\)\)\z/) do
|
|
1814
|
+
assert_equal [david], only_david.merge(only_david)
|
|
1815
|
+
end
|
|
1816
|
+
end
|
|
1177
1817
|
end
|
|
1178
1818
|
|
|
1819
|
+
module ActiveRecord
|
|
1820
|
+
class DatabaseTasksTruncateAllTest < ActiveRecord::TestCase
|
|
1821
|
+
# SQL Server does not allow truncation of tables that are referenced by foreign key
|
|
1822
|
+
# constraints. As this test truncates all tables we would need to remove all foreign
|
|
1823
|
+
# key constraints and then restore them afterwards to get this test to pass.
|
|
1824
|
+
coerce_tests! :test_truncate_tables
|
|
1825
|
+
end
|
|
1826
|
+
end
|
|
1827
|
+
|
|
1828
|
+
require "models/book"
|
|
1829
|
+
class EnumTest < ActiveRecord::TestCase
|
|
1830
|
+
# Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
|
|
1831
|
+
coerce_tests! %r{enums are distinct per class}
|
|
1832
|
+
test "enums are distinct per class coerced" do
|
|
1833
|
+
Book.connection.remove_index(:books, column: [:author_id, :name])
|
|
1834
|
+
|
|
1835
|
+
send(:'original_enums are distinct per class')
|
|
1836
|
+
ensure
|
|
1837
|
+
Book.where(author_id: nil, name: nil).delete_all
|
|
1838
|
+
Book.connection.add_index(:books, [:author_id, :name], unique: true)
|
|
1839
|
+
end
|
|
1840
|
+
|
|
1841
|
+
# Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
|
|
1842
|
+
coerce_tests! %r{creating new objects with enum scopes}
|
|
1843
|
+
test "creating new objects with enum scopes coerced" do
|
|
1844
|
+
Book.connection.remove_index(:books, column: [:author_id, :name])
|
|
1845
|
+
|
|
1846
|
+
send(:'original_creating new objects with enum scopes')
|
|
1847
|
+
ensure
|
|
1848
|
+
Book.where(author_id: nil, name: nil).delete_all
|
|
1849
|
+
Book.connection.add_index(:books, [:author_id, :name], unique: true)
|
|
1850
|
+
end
|
|
1851
|
+
|
|
1852
|
+
# Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
|
|
1853
|
+
coerce_tests! %r{enums are inheritable}
|
|
1854
|
+
test "enums are inheritable coerced" do
|
|
1855
|
+
Book.connection.remove_index(:books, column: [:author_id, :name])
|
|
1856
|
+
|
|
1857
|
+
send(:'original_enums are inheritable')
|
|
1858
|
+
ensure
|
|
1859
|
+
Book.where(author_id: nil, name: nil).delete_all
|
|
1860
|
+
Book.connection.add_index(:books, [:author_id, :name], unique: true)
|
|
1861
|
+
end
|
|
1862
|
+
|
|
1863
|
+
# Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
|
|
1864
|
+
coerce_tests! %r{declare multiple enums at a time}
|
|
1865
|
+
test "declare multiple enums at a time coerced" do
|
|
1866
|
+
Book.connection.remove_index(:books, column: [:author_id, :name])
|
|
1867
|
+
|
|
1868
|
+
send(:'original_declare multiple enums at a time')
|
|
1869
|
+
ensure
|
|
1870
|
+
Book.where(author_id: nil, name: nil).delete_all
|
|
1871
|
+
Book.connection.add_index(:books, [:author_id, :name], unique: true)
|
|
1872
|
+
end
|
|
1873
|
+
|
|
1874
|
+
# Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
|
|
1875
|
+
coerce_tests! %r{serializable\? with large number label}
|
|
1876
|
+
test "serializable? with large number label coerced" do
|
|
1877
|
+
Book.connection.remove_index(:books, column: [:author_id, :name])
|
|
1878
|
+
|
|
1879
|
+
send(:'original_serializable\? with large number label')
|
|
1880
|
+
ensure
|
|
1881
|
+
Book.where(author_id: nil, name: nil).delete_all
|
|
1882
|
+
Book.connection.add_index(:books, [:author_id, :name], unique: true)
|
|
1883
|
+
end
|
|
1884
|
+
end
|
|
1179
1885
|
|
|
1886
|
+
require "models/task"
|
|
1887
|
+
class QueryCacheExpiryTest < ActiveRecord::TestCase
|
|
1888
|
+
# SQL Server does not support skipping or upserting duplicates.
|
|
1889
|
+
coerce_tests! :test_insert_all
|
|
1890
|
+
def test_insert_all_coerced
|
|
1891
|
+
assert_raises(ArgumentError, /does not support skipping duplicates/) do
|
|
1892
|
+
Task.cache { Task.insert({ starting: Time.now }) }
|
|
1893
|
+
end
|
|
1894
|
+
|
|
1895
|
+
assert_called(ActiveRecord::Base.connection, :clear_query_cache, times: 2) do
|
|
1896
|
+
Task.cache { Task.insert_all!([{ starting: Time.now }]) }
|
|
1897
|
+
end
|
|
1898
|
+
|
|
1899
|
+
assert_called(ActiveRecord::Base.connection, :clear_query_cache, times: 2) do
|
|
1900
|
+
Task.cache { Task.insert!({ starting: Time.now }) }
|
|
1901
|
+
end
|
|
1902
|
+
|
|
1903
|
+
assert_called(ActiveRecord::Base.connection, :clear_query_cache, times: 2) do
|
|
1904
|
+
Task.cache { Task.insert_all!([{ starting: Time.now }]) }
|
|
1905
|
+
end
|
|
1906
|
+
|
|
1907
|
+
assert_raises(ArgumentError, /does not support upsert/) do
|
|
1908
|
+
Task.cache { Task.upsert({ starting: Time.now }) }
|
|
1909
|
+
end
|
|
1910
|
+
|
|
1911
|
+
assert_raises(ArgumentError, /does not support upsert/) do
|
|
1912
|
+
Task.cache { Task.upsert_all([{ starting: Time.now }]) }
|
|
1913
|
+
end
|
|
1914
|
+
end
|
|
1915
|
+
end
|
|
1916
|
+
|
|
1917
|
+
require "models/citation"
|
|
1180
1918
|
class EagerLoadingTooManyIdsTest < ActiveRecord::TestCase
|
|
1181
|
-
|
|
1182
|
-
|
|
1919
|
+
fixtures :citations
|
|
1920
|
+
|
|
1921
|
+
# Original Rails test fails with SQL Server error message "The query processor ran out of internal resources and
|
|
1922
|
+
# could not produce a query plan". This error goes away if you change database compatibility level to 110 (SQL 2012)
|
|
1923
|
+
# (see https://www.mssqltips.com/sqlservertip/5279/sql-server-error-query-processor-ran-out-of-internal-resources-and-could-not-produce-a-query-plan/).
|
|
1924
|
+
# However, you cannot change the compatibility level during a test. The purpose of the test is to ensure that an
|
|
1925
|
+
# unprepared statement is used if the number of values exceeds the adapter's `bind_params_length`. The coerced test
|
|
1926
|
+
# still does this as there will be 32,768 remaining citation records in the database and the `bind_params_length` of
|
|
1927
|
+
# adapter is 2,098.
|
|
1928
|
+
coerce_tests! :test_eager_loading_too_many_ids
|
|
1929
|
+
def test_eager_loading_too_many_ids_coerced
|
|
1930
|
+
# Remove excess records.
|
|
1931
|
+
Citation.limit(32768).order(id: :desc).delete_all
|
|
1932
|
+
|
|
1933
|
+
# Perform test
|
|
1934
|
+
citation_count = Citation.count
|
|
1935
|
+
assert_sql(/WHERE \[citations\]\.\[id\] IN \(0, 1/) do
|
|
1936
|
+
assert_equal citation_count, Citation.eager_load(:citations).offset(0).size
|
|
1937
|
+
end
|
|
1938
|
+
end
|
|
1939
|
+
end
|
|
1940
|
+
|
|
1941
|
+
class LogSubscriberTest < ActiveRecord::TestCase
|
|
1942
|
+
# Call original test from coerced test. Fixes issue on CI with Rails installed as a gem.
|
|
1943
|
+
coerce_tests! :test_verbose_query_logs
|
|
1944
|
+
def test_verbose_query_logs_coerced
|
|
1945
|
+
original_test_verbose_query_logs
|
|
1946
|
+
end
|
|
1947
|
+
|
|
1948
|
+
# Bindings logged slightly differently.
|
|
1949
|
+
coerce_tests! :test_where_in_binds_logging_include_attribute_names
|
|
1950
|
+
def test_where_in_binds_logging_include_attribute_names_coerced
|
|
1951
|
+
Developer.where(id: [1, 2, 3, 4, 5]).load
|
|
1952
|
+
wait
|
|
1953
|
+
assert_match(%{@0 = 1, @1 = 2, @2 = 3, @3 = 4, @4 = 5 [["id", nil], ["id", nil], ["id", nil], ["id", nil], ["id", nil]]}, @logger.logged(:debug).last)
|
|
1954
|
+
end
|
|
1955
|
+
end
|
|
1956
|
+
|
|
1957
|
+
class ActiveRecordSchemaTest < ActiveRecord::TestCase
|
|
1958
|
+
# Workaround for randomly failing test.
|
|
1959
|
+
coerce_tests! :test_has_primary_key
|
|
1960
|
+
def test_has_primary_key_coerced
|
|
1961
|
+
@schema_migration.reset_column_information
|
|
1962
|
+
original_test_has_primary_key
|
|
1963
|
+
end
|
|
1964
|
+
end
|
|
1965
|
+
|
|
1966
|
+
class ReloadModelsTest < ActiveRecord::TestCase
|
|
1967
|
+
# Skip test on Windows. The number of arguments passed to `IO.popen` in
|
|
1968
|
+
# `activesupport/lib/active_support/testing/isolation.rb` exceeds what Windows can handle.
|
|
1969
|
+
coerce_tests! :test_has_one_with_reload if RbConfig::CONFIG["host_os"] =~ /mswin|mingw/
|
|
1970
|
+
end
|
|
1971
|
+
|
|
1972
|
+
class MarshalSerializationTest < ActiveRecord::TestCase
|
|
1973
|
+
private
|
|
1974
|
+
|
|
1975
|
+
def marshal_fixture_path(file_name)
|
|
1976
|
+
File.expand_path(
|
|
1977
|
+
"support/marshal_compatibility_fixtures/#{ActiveRecord::Base.connection.adapter_name}/#{file_name}.dump",
|
|
1978
|
+
ARTest::SQLServer.test_root_sqlserver
|
|
1979
|
+
)
|
|
1980
|
+
end
|
|
1981
|
+
end
|
|
1982
|
+
|
|
1983
|
+
class NestedThroughAssociationsTest < ActiveRecord::TestCase
|
|
1984
|
+
# Same as original but replace order with "order(:id)" to ensure that assert_includes_and_joins_equal doesn't raise
|
|
1985
|
+
# "A column has been specified more than once in the order by list"
|
|
1986
|
+
# Example: original test generate queries like "ORDER BY authors.id, [authors].[id]". We don't support duplicate columns in the order list
|
|
1987
|
+
coerce_tests! :test_has_many_through_has_many_with_has_many_through_habtm_source_reflection_preload_via_joins, :test_has_many_through_has_and_belongs_to_many_with_has_many_source_reflection_preload_via_joins
|
|
1988
|
+
def test_has_many_through_has_many_with_has_many_through_habtm_source_reflection_preload_via_joins_coerced
|
|
1989
|
+
# preload table schemas
|
|
1990
|
+
Author.joins(:category_post_comments).first
|
|
1991
|
+
|
|
1992
|
+
assert_includes_and_joins_equal(
|
|
1993
|
+
Author.where("comments.id" => comments(:does_it_hurt).id).order(:id),
|
|
1994
|
+
[authors(:david), authors(:mary)], :category_post_comments
|
|
1995
|
+
)
|
|
1996
|
+
end
|
|
1997
|
+
|
|
1998
|
+
def test_has_many_through_has_and_belongs_to_many_with_has_many_source_reflection_preload_via_joins_coerced
|
|
1999
|
+
# preload table schemas
|
|
2000
|
+
Category.joins(:post_comments).first
|
|
2001
|
+
|
|
2002
|
+
assert_includes_and_joins_equal(
|
|
2003
|
+
Category.where("comments.id" => comments(:more_greetings).id).order(:id),
|
|
2004
|
+
[categories(:general), categories(:technology)], :post_comments
|
|
2005
|
+
)
|
|
2006
|
+
end
|
|
2007
|
+
end
|
|
2008
|
+
|
|
2009
|
+
class BasePreventWritesTest < ActiveRecord::TestCase
|
|
2010
|
+
# SQL Server does not have query for release_savepoint
|
|
2011
|
+
coerce_tests! %r{an empty transaction does not raise if preventing writes}
|
|
2012
|
+
test "an empty transaction does not raise if preventing writes coerced" do
|
|
2013
|
+
ActiveRecord::Base.while_preventing_writes do
|
|
2014
|
+
assert_queries(1, ignore_none: true) do
|
|
2015
|
+
Bird.transaction do
|
|
2016
|
+
ActiveRecord::Base.connection.materialize_transactions
|
|
2017
|
+
end
|
|
2018
|
+
end
|
|
2019
|
+
end
|
|
2020
|
+
end
|
|
2021
|
+
|
|
2022
|
+
class BasePreventWritesLegacyTest < ActiveRecord::TestCase
|
|
2023
|
+
# SQL Server does not have query for release_savepoint
|
|
2024
|
+
coerce_tests! %r{an empty transaction does not raise if preventing writes}
|
|
2025
|
+
test "an empty transaction does not raise if preventing writes coerced" do
|
|
2026
|
+
ActiveRecord::Base.connection_handler.while_preventing_writes do
|
|
2027
|
+
assert_queries(1, ignore_none: true) do
|
|
2028
|
+
Bird.transaction do
|
|
2029
|
+
ActiveRecord::Base.connection.materialize_transactions
|
|
2030
|
+
end
|
|
2031
|
+
end
|
|
2032
|
+
end
|
|
2033
|
+
end
|
|
2034
|
+
end
|
|
2035
|
+
end
|
|
2036
|
+
|
|
2037
|
+
class MigratorTest < ActiveRecord::TestCase
|
|
2038
|
+
# Test fails on Windows AppVeyor CI for unknown reason.
|
|
2039
|
+
coerce_tests! :test_migrator_db_has_no_schema_migrations_table if RbConfig::CONFIG["host_os"] =~ /mswin|mingw/
|
|
2040
|
+
end
|
|
2041
|
+
|
|
2042
|
+
class MultiDbMigratorTest < ActiveRecord::TestCase
|
|
2043
|
+
# Test fails on Windows AppVeyor CI for unknown reason.
|
|
2044
|
+
coerce_tests! :test_migrator_db_has_no_schema_migrations_table if RbConfig::CONFIG["host_os"] =~ /mswin|mingw/
|
|
2045
|
+
end
|
|
2046
|
+
|
|
2047
|
+
require "models/book"
|
|
2048
|
+
class FieldOrderedValuesTest < ActiveRecord::TestCase
|
|
2049
|
+
# Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
|
|
2050
|
+
coerce_tests! :test_in_order_of_with_enums_values
|
|
2051
|
+
def test_in_order_of_with_enums_values_coerced
|
|
2052
|
+
Book.connection.remove_index(:books, column: [:author_id, :name])
|
|
2053
|
+
|
|
2054
|
+
original_test_in_order_of_with_enums_values
|
|
2055
|
+
ensure
|
|
2056
|
+
Book.where(author_id: nil, name: nil).delete_all
|
|
2057
|
+
Book.connection.add_index(:books, [:author_id, :name], unique: true)
|
|
2058
|
+
end
|
|
2059
|
+
|
|
2060
|
+
# Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
|
|
2061
|
+
coerce_tests! :test_in_order_of_with_enums_keys
|
|
2062
|
+
def test_in_order_of_with_enums_keys_coerced
|
|
2063
|
+
Book.connection.remove_index(:books, column: [:author_id, :name])
|
|
2064
|
+
|
|
2065
|
+
original_test_in_order_of_with_enums_keys
|
|
2066
|
+
ensure
|
|
2067
|
+
Book.where(author_id: nil, name: nil).delete_all
|
|
2068
|
+
Book.connection.add_index(:books, [:author_id, :name], unique: true)
|
|
2069
|
+
end
|
|
2070
|
+
end
|
|
2071
|
+
|
|
2072
|
+
require "models/dashboard"
|
|
2073
|
+
class QueryLogsTest < ActiveRecord::TestCase
|
|
2074
|
+
# Same as original coerced test except our SQL ends with binding.
|
|
2075
|
+
# TODO: Remove coerce after Rails 7.1.0 (see https://github.com/rails/rails/pull/44053)
|
|
2076
|
+
coerce_tests! :test_custom_basic_tags, :test_custom_proc_tags, :test_multiple_custom_tags, :test_custom_proc_context_tags
|
|
2077
|
+
def test_custom_basic_tags_coerced
|
|
2078
|
+
ActiveRecord::QueryLogs.tags = [ :application, { custom_string: "test content" } ]
|
|
2079
|
+
|
|
2080
|
+
assert_sql(%r{/\*application:active_record,custom_string:test content\*/}) do
|
|
2081
|
+
Dashboard.first
|
|
2082
|
+
end
|
|
2083
|
+
end
|
|
2084
|
+
|
|
2085
|
+
def test_custom_proc_tags_coerced
|
|
2086
|
+
ActiveRecord::QueryLogs.tags = [ :application, { custom_proc: -> { "test content" } } ]
|
|
2087
|
+
|
|
2088
|
+
assert_sql(%r{/\*application:active_record,custom_proc:test content\*/}) do
|
|
2089
|
+
Dashboard.first
|
|
2090
|
+
end
|
|
2091
|
+
end
|
|
2092
|
+
|
|
2093
|
+
def test_multiple_custom_tags_coerced
|
|
2094
|
+
ActiveRecord::QueryLogs.tags = [
|
|
2095
|
+
:application,
|
|
2096
|
+
{ custom_proc: -> { "test content" }, another_proc: -> { "more test content" } },
|
|
2097
|
+
]
|
|
2098
|
+
|
|
2099
|
+
assert_sql(%r{/\*application:active_record,custom_proc:test content,another_proc:more test content\*/}) do
|
|
2100
|
+
Dashboard.first
|
|
2101
|
+
end
|
|
2102
|
+
end
|
|
2103
|
+
|
|
2104
|
+
def test_custom_proc_context_tags_coerced
|
|
2105
|
+
ActiveSupport::ExecutionContext[:foo] = "bar"
|
|
2106
|
+
ActiveRecord::QueryLogs.tags = [ :application, { custom_context_proc: ->(context) { context[:foo] } } ]
|
|
2107
|
+
|
|
2108
|
+
assert_sql(%r{/\*application:active_record,custom_context_proc:bar\*/}) do
|
|
2109
|
+
Dashboard.first
|
|
2110
|
+
end
|
|
2111
|
+
end
|
|
2112
|
+
end
|
|
2113
|
+
|
|
2114
|
+
# SQL Server does not support upsert yet
|
|
2115
|
+
# TODO: Remove coerce after Rails 7.1.0 (see https://github.com/rails/rails/pull/44050)
|
|
2116
|
+
class InsertAllTest < ActiveRecord::TestCase
|
|
2117
|
+
coerce_tests! :test_upsert_all_only_updates_the_column_provided_via_update_only
|
|
2118
|
+
def test_upsert_all_only_updates_the_column_provided_via_update_only_coerced
|
|
2119
|
+
assert_raises(ArgumentError, /does not support upsert/) do
|
|
2120
|
+
original_test_upsert_all_only_updates_the_column_provided_via_update_only
|
|
2121
|
+
end
|
|
2122
|
+
end
|
|
2123
|
+
|
|
2124
|
+
coerce_tests! :test_upsert_all_only_updates_the_list_of_columns_provided_via_update_only
|
|
2125
|
+
def test_upsert_all_only_updates_the_list_of_columns_provided_via_update_only_coerced
|
|
2126
|
+
assert_raises(ArgumentError, /does not support upsert/) do
|
|
2127
|
+
original_test_upsert_all_only_updates_the_list_of_columns_provided_via_update_only
|
|
2128
|
+
end
|
|
2129
|
+
end
|
|
2130
|
+
|
|
2131
|
+
coerce_tests! :test_upsert_all_implicitly_sets_timestamps_on_create_when_model_record_timestamps_is_true
|
|
2132
|
+
def test_upsert_all_implicitly_sets_timestamps_on_create_when_model_record_timestamps_is_true_coerced
|
|
2133
|
+
assert_raises(ArgumentError, /does not support upsert/) do
|
|
2134
|
+
original_test_upsert_all_implicitly_sets_timestamps_on_create_when_model_record_timestamps_is_true
|
|
2135
|
+
end
|
|
2136
|
+
end
|
|
2137
|
+
|
|
2138
|
+
coerce_tests! :test_upsert_all_respects_created_at_precision_when_touched_implicitly
|
|
2139
|
+
def test_upsert_all_respects_created_at_precision_when_touched_implicitly_coerced
|
|
2140
|
+
assert_raises(ArgumentError, /does not support upsert/) do
|
|
2141
|
+
original_test_upsert_all_respects_created_at_precision_when_touched_implicitly
|
|
2142
|
+
end
|
|
2143
|
+
end
|
|
2144
|
+
|
|
2145
|
+
coerce_tests! :test_upsert_all_does_not_implicitly_set_timestamps_on_create_when_model_record_timestamps_is_true_but_overridden
|
|
2146
|
+
def test_upsert_all_does_not_implicitly_set_timestamps_on_create_when_model_record_timestamps_is_true_but_overridden_coerced
|
|
2147
|
+
assert_raises(ArgumentError, /does not support upsert/) do
|
|
2148
|
+
original_test_upsert_all_does_not_implicitly_set_timestamps_on_create_when_model_record_timestamps_is_true_but_overridden
|
|
2149
|
+
end
|
|
2150
|
+
end
|
|
2151
|
+
|
|
2152
|
+
coerce_tests! :test_upsert_all_does_not_implicitly_set_timestamps_on_create_when_model_record_timestamps_is_false
|
|
2153
|
+
def test_upsert_all_does_not_implicitly_set_timestamps_on_create_when_model_record_timestamps_is_false_coerced
|
|
2154
|
+
assert_raises(ArgumentError, /does not support upsert/) do
|
|
2155
|
+
original_test_upsert_all_does_not_implicitly_set_timestamps_on_create_when_model_record_timestamps_is_false
|
|
2156
|
+
end
|
|
2157
|
+
end
|
|
2158
|
+
|
|
2159
|
+
coerce_tests! :test_upsert_all_implicitly_sets_timestamps_on_create_when_model_record_timestamps_is_false_but_overridden
|
|
2160
|
+
def test_upsert_all_implicitly_sets_timestamps_on_create_when_model_record_timestamps_is_false_but_overridden_coerced
|
|
2161
|
+
assert_raises(ArgumentError, /does not support upsert/) do
|
|
2162
|
+
original_test_upsert_all_implicitly_sets_timestamps_on_create_when_model_record_timestamps_is_false_but_overridden
|
|
2163
|
+
end
|
|
2164
|
+
end
|
|
2165
|
+
end
|
|
2166
|
+
|
|
2167
|
+
class HasOneThroughDisableJoinsAssociationsTest < ActiveRecord::TestCase
|
|
2168
|
+
# TODO: Remove coerce after Rails 7.1.0 (see https://github.com/rails/rails/pull/44051)
|
|
2169
|
+
coerce_tests! :test_disable_joins_through_with_enum_type
|
|
2170
|
+
def test_disable_joins_through_with_enum_type_coerced
|
|
2171
|
+
joins = capture_sql { @member.club }
|
|
2172
|
+
no_joins = capture_sql { @member.club_without_joins }
|
|
2173
|
+
|
|
2174
|
+
assert_equal 1, joins.size
|
|
2175
|
+
assert_equal 2, no_joins.size
|
|
2176
|
+
|
|
2177
|
+
assert_match(/INNER JOIN/, joins.first)
|
|
2178
|
+
no_joins.each do |nj|
|
|
2179
|
+
assert_no_match(/INNER JOIN/, nj)
|
|
2180
|
+
end
|
|
2181
|
+
|
|
2182
|
+
assert_match(/\[memberships\]\.\[type\]/, no_joins.first)
|
|
2183
|
+
end
|
|
2184
|
+
end
|
|
2185
|
+
|
|
2186
|
+
class InsertAllTest < ActiveRecord::TestCase
|
|
2187
|
+
coerce_tests! :test_insert_all_returns_requested_sql_fields
|
|
2188
|
+
# Same as original but using INSERTED.name as UPPER argument
|
|
2189
|
+
def test_insert_all_returns_requested_sql_fields_coerced
|
|
2190
|
+
skip unless supports_insert_returning?
|
|
2191
|
+
|
|
2192
|
+
result = Book.insert_all! [{ name: "Rework", author_id: 1 }], returning: Arel.sql("UPPER(INSERTED.name) as name")
|
|
2193
|
+
assert_equal %w[ REWORK ], result.pluck("name")
|
|
2194
|
+
end
|
|
2195
|
+
end
|
|
2196
|
+
|
|
2197
|
+
class ActiveRecord::Encryption::EncryptableRecordTest < ActiveRecord::EncryptionTestCase
|
|
2198
|
+
# TODO: Remove coerce after Rails 7.1.0 (see https://github.com/rails/rails/pull/44052)
|
|
2199
|
+
# Same as original but SQL Server string is varchar(4000), not varchar(255) as other adapters. Produce invalid strings with 4001 characters
|
|
2200
|
+
coerce_tests! %r{validate column sizes}
|
|
2201
|
+
test "validate column sizes coerced" do
|
|
2202
|
+
assert EncryptedAuthor.new(name: "jorge").valid?
|
|
2203
|
+
assert_not EncryptedAuthor.new(name: "a" * 4001).valid?
|
|
2204
|
+
author = EncryptedAuthor.create(name: "a" * 4001)
|
|
2205
|
+
assert_not author.valid?
|
|
2206
|
+
end
|
|
1183
2207
|
end
|