activerecord-sqlserver-adapter 6.0.0.rc2 → 6.1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +26 -0
- data/CHANGELOG.md +20 -41
- data/README.md +32 -3
- data/RUNNING_UNIT_TESTS.md +1 -1
- data/VERSION +1 -1
- data/activerecord-sqlserver-adapter.gemspec +1 -1
- data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +0 -9
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +7 -2
- data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +0 -4
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +28 -16
- data/lib/active_record/connection_adapters/sqlserver/quoting.rb +7 -7
- data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +22 -1
- data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +9 -3
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +31 -8
- data/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +27 -7
- data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +0 -1
- data/lib/active_record/connection_adapters/sqlserver/transaction.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver/type.rb +1 -0
- data/lib/active_record/connection_adapters/sqlserver/type/decimal_without_scale.rb +22 -0
- data/lib/active_record/connection_adapters/sqlserver/utils.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +100 -68
- data/lib/active_record/connection_adapters/sqlserver_column.rb +17 -0
- data/lib/active_record/sqlserver_base.rb +9 -15
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +17 -14
- data/lib/arel/visitors/sqlserver.rb +111 -39
- data/test/cases/adapter_test_sqlserver.rb +48 -14
- data/test/cases/change_column_collation_test_sqlserver.rb +33 -0
- data/test/cases/coerced_tests.rb +598 -78
- data/test/cases/column_test_sqlserver.rb +5 -2
- data/test/cases/disconnected_test_sqlserver.rb +39 -0
- data/test/cases/execute_procedure_test_sqlserver.rb +9 -0
- data/test/cases/in_clause_test_sqlserver.rb +27 -0
- data/test/cases/lateral_test_sqlserver.rb +35 -0
- data/test/cases/migration_test_sqlserver.rb +51 -0
- data/test/cases/optimizer_hints_test_sqlserver.rb +72 -0
- data/test/cases/order_test_sqlserver.rb +7 -0
- data/test/cases/primary_keys_test_sqlserver.rb +103 -0
- data/test/cases/rake_test_sqlserver.rb +3 -2
- data/test/cases/schema_dumper_test_sqlserver.rb +20 -3
- data/test/migrations/create_clients_and_change_column_collation.rb +19 -0
- data/test/models/sqlserver/sst_string_collation.rb +3 -0
- data/test/schema/sqlserver_specific_schema.rb +17 -0
- data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic_associations.dump +0 -0
- data/test/support/sql_counter_sqlserver.rb +14 -12
- metadata +32 -13
- data/.travis.yml +0 -23
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "cases/helper_sqlserver"
|
4
|
+
require "migrations/create_clients_and_change_column_collation"
|
5
|
+
|
6
|
+
class ChangeColumnCollationTestSqlServer < ActiveRecord::TestCase
|
7
|
+
before do
|
8
|
+
@old_verbose = ActiveRecord::Migration.verbose
|
9
|
+
ActiveRecord::Migration.verbose = false
|
10
|
+
CreateClientsAndChangeColumnCollation.new.up
|
11
|
+
end
|
12
|
+
|
13
|
+
after do
|
14
|
+
CreateClientsAndChangeColumnCollation.new.down
|
15
|
+
ActiveRecord::Migration.verbose = @old_verbose
|
16
|
+
end
|
17
|
+
|
18
|
+
def find_column(table, name)
|
19
|
+
table.find { |column| column.name == name }
|
20
|
+
end
|
21
|
+
|
22
|
+
let(:clients_table) { connection.columns("clients") }
|
23
|
+
let(:name_column) { find_column(clients_table, "name") }
|
24
|
+
let(:code_column) { find_column(clients_table, "code") }
|
25
|
+
|
26
|
+
it "change column collation to other than default" do
|
27
|
+
_(name_column.collation).must_equal "SQL_Latin1_General_CP1_CS_AS"
|
28
|
+
end
|
29
|
+
|
30
|
+
it "change column collation to default" do
|
31
|
+
_(code_column.collation).must_equal "SQL_Latin1_General_CP1_CI_AS"
|
32
|
+
end
|
33
|
+
end
|
data/test/cases/coerced_tests.rb
CHANGED
@@ -24,22 +24,35 @@ class UniquenessValidationTest < ActiveRecord::TestCase
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
-
#
|
28
|
-
coerce_tests! :
|
29
|
-
def
|
30
|
-
|
31
|
-
skip if database_collation.include?("_CI_")
|
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)
|
32
31
|
|
33
|
-
|
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
|
36
|
+
|
37
|
+
assert_not topic1.valid?
|
38
|
+
assert_not topic1.save
|
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
|
44
|
+
|
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
|
34
47
|
end
|
35
48
|
end
|
36
49
|
|
37
50
|
require "models/event"
|
38
51
|
module ActiveRecord
|
39
52
|
class AdapterTest < ActiveRecord::TestCase
|
40
|
-
#
|
41
|
-
coerce_tests! :
|
42
|
-
coerce_tests! :
|
53
|
+
# Legacy binds are not supported.
|
54
|
+
coerce_tests! :test_select_all_insert_update_delete_with_casted_binds
|
55
|
+
coerce_tests! :test_select_all_insert_update_delete_with_legacy_binds
|
43
56
|
|
44
57
|
# As far as I can tell, SQL Server does not support null bytes in strings.
|
45
58
|
coerce_tests! :test_update_prepared_statement
|
@@ -54,13 +67,63 @@ module ActiveRecord
|
|
54
67
|
assert_not_nil error.cause
|
55
68
|
end
|
56
69
|
end
|
70
|
+
end
|
71
|
+
end
|
57
72
|
|
73
|
+
module ActiveRecord
|
74
|
+
class AdapterPreventWritesTest < ActiveRecord::TestCase
|
58
75
|
# Fix randomly failing test. The loading of the model's schema was affecting the test.
|
59
76
|
coerce_tests! :test_errors_when_an_insert_query_is_called_while_preventing_writes
|
60
77
|
def test_errors_when_an_insert_query_is_called_while_preventing_writes_coerced
|
61
78
|
Subscriber.send(:load_schema!)
|
62
79
|
original_test_errors_when_an_insert_query_is_called_while_preventing_writes
|
63
80
|
end
|
81
|
+
|
82
|
+
# Fix randomly failing test. The loading of the model's schema was affecting the test.
|
83
|
+
coerce_tests! :test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_containing_read_command_is_called_while_preventing_writes
|
84
|
+
def test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_containing_read_command_is_called_while_preventing_writes_coerced
|
85
|
+
Subscriber.send(:load_schema!)
|
86
|
+
original_test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_containing_read_command_is_called_while_preventing_writes
|
87
|
+
end
|
88
|
+
|
89
|
+
# Fix randomly failing test. The loading of the model's schema was affecting the test.
|
90
|
+
coerce_tests! :test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_is_called_while_preventing_writes
|
91
|
+
def test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_is_called_while_preventing_writes_coerced
|
92
|
+
Subscriber.send(:load_schema!)
|
93
|
+
original_test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_is_called_while_preventing_writes
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
module ActiveRecord
|
99
|
+
class AdapterPreventWritesLegacyTest < ActiveRecord::TestCase
|
100
|
+
# Fix randomly failing test. The loading of the model's schema was affecting the test.
|
101
|
+
coerce_tests! :test_errors_when_an_insert_query_is_called_while_preventing_writes
|
102
|
+
def test_errors_when_an_insert_query_is_called_while_preventing_writes_coerced
|
103
|
+
Subscriber.send(:load_schema!)
|
104
|
+
original_test_errors_when_an_insert_query_is_called_while_preventing_writes
|
105
|
+
end
|
106
|
+
|
107
|
+
# Fix randomly failing test. The loading of the model's schema was affecting the test.
|
108
|
+
coerce_tests! :test_errors_when_an_insert_query_prefixed_by_a_slash_star_comment_is_called_while_preventing_writes
|
109
|
+
def test_errors_when_an_insert_query_prefixed_by_a_slash_star_comment_is_called_while_preventing_writes_coerced
|
110
|
+
Subscriber.send(:load_schema!)
|
111
|
+
original_test_errors_when_an_insert_query_prefixed_by_a_slash_star_comment_is_called_while_preventing_writes
|
112
|
+
end
|
113
|
+
|
114
|
+
# Fix randomly failing test. The loading of the model's schema was affecting the test.
|
115
|
+
coerce_tests! :test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_is_called_while_preventing_writes
|
116
|
+
def test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_is_called_while_preventing_writes_coerced
|
117
|
+
Subscriber.send(:load_schema!)
|
118
|
+
original_test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_is_called_while_preventing_writes
|
119
|
+
end
|
120
|
+
|
121
|
+
# Fix randomly failing test. The loading of the model's schema was affecting the test.
|
122
|
+
coerce_tests! :test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_containing_read_command_is_called_while_preventing_writes
|
123
|
+
def test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_containing_read_command_is_called_while_preventing_writes_coerced
|
124
|
+
Subscriber.send(:load_schema!)
|
125
|
+
original_test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_containing_read_command_is_called_while_preventing_writes
|
126
|
+
end
|
64
127
|
end
|
65
128
|
end
|
66
129
|
|
@@ -176,7 +239,7 @@ class BasicsTest < ActiveRecord::TestCase
|
|
176
239
|
# SQL Server does not have query for release_savepoint
|
177
240
|
coerce_tests! %r{an empty transaction does not raise if preventing writes}
|
178
241
|
test "an empty transaction does not raise if preventing writes coerced" do
|
179
|
-
ActiveRecord::Base.
|
242
|
+
ActiveRecord::Base.while_preventing_writes do
|
180
243
|
assert_queries(1, ignore_none: true) do
|
181
244
|
Bird.transaction do
|
182
245
|
ActiveRecord::Base.connection.materialize_transactions
|
@@ -234,6 +297,55 @@ module ActiveRecord
|
|
234
297
|
coerce_tests! :test_statement_cache_with_find_by
|
235
298
|
coerce_tests! :test_statement_cache_with_in_clause
|
236
299
|
coerce_tests! :test_statement_cache_with_sql_string_literal
|
300
|
+
|
301
|
+
# Same as original coerced test except prepared statements include `EXEC sp_executesql` wrapper.
|
302
|
+
coerce_tests! :test_bind_params_to_sql_with_prepared_statements, :test_bind_params_to_sql_with_unprepared_statements
|
303
|
+
def test_bind_params_to_sql_with_prepared_statements_coerced
|
304
|
+
assert_bind_params_to_sql_coerced(prepared: true)
|
305
|
+
end
|
306
|
+
|
307
|
+
def test_bind_params_to_sql_with_unprepared_statements_coerced
|
308
|
+
@connection.unprepared_statement do
|
309
|
+
assert_bind_params_to_sql_coerced(prepared: false)
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
private
|
314
|
+
|
315
|
+
def assert_bind_params_to_sql_coerced(prepared:)
|
316
|
+
table = Author.quoted_table_name
|
317
|
+
pk = "#{table}.#{Author.quoted_primary_key}"
|
318
|
+
|
319
|
+
# prepared_statements: true
|
320
|
+
#
|
321
|
+
# 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
|
322
|
+
#
|
323
|
+
# prepared_statements: false
|
324
|
+
#
|
325
|
+
# SELECT [authors].* FROM [authors] WHERE ([authors].[id] IN (1, 2, 3) OR [authors].[id] IS NULL)
|
326
|
+
#
|
327
|
+
sql_unprepared = "SELECT #{table}.* FROM #{table} WHERE (#{pk} IN (#{bind_params(1..3)}) OR #{pk} IS NULL)"
|
328
|
+
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"
|
329
|
+
|
330
|
+
authors = Author.where(id: [1, 2, 3, nil])
|
331
|
+
assert_equal sql_unprepared, @connection.to_sql(authors.arel)
|
332
|
+
assert_sql(prepared ? sql_prepared : sql_unprepared) { assert_equal 3, authors.length }
|
333
|
+
|
334
|
+
# prepared_statements: true
|
335
|
+
#
|
336
|
+
# 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
|
337
|
+
#
|
338
|
+
# prepared_statements: false
|
339
|
+
#
|
340
|
+
# SELECT [authors].* FROM [authors] WHERE [authors].[id] IN (1, 2, 3)
|
341
|
+
#
|
342
|
+
sql_unprepared = "SELECT #{table}.* FROM #{table} WHERE #{pk} IN (#{bind_params(1..3)})"
|
343
|
+
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"
|
344
|
+
|
345
|
+
authors = Author.where(id: [1, 2, 3, 9223372036854775808])
|
346
|
+
assert_equal sql_unprepared, @connection.to_sql(authors.arel)
|
347
|
+
assert_sql(prepared ? sql_prepared : sql_unprepared) { assert_equal 3, authors.length }
|
348
|
+
end
|
237
349
|
end
|
238
350
|
end
|
239
351
|
|
@@ -256,11 +368,112 @@ class CalculationsTest < ActiveRecord::TestCase
|
|
256
368
|
original_test_offset_is_kept
|
257
369
|
end
|
258
370
|
|
259
|
-
#
|
371
|
+
# The SQL Server `AVG()` function for a list of integers returns an integer (not a decimal).
|
260
372
|
coerce_tests! :test_should_return_decimal_average_of_integer_field
|
261
373
|
def test_should_return_decimal_average_of_integer_field_coerced
|
262
374
|
value = Account.average(:id)
|
263
|
-
assert_equal
|
375
|
+
assert_equal 3, value
|
376
|
+
end
|
377
|
+
|
378
|
+
# In SQL Server the `AVG()` function for a list of integers returns an integer so need to cast values as decimals before averaging.
|
379
|
+
# Match SQL Server limit implementation.
|
380
|
+
coerce_tests! :test_select_avg_with_group_by_as_virtual_attribute_with_sql
|
381
|
+
def test_select_avg_with_group_by_as_virtual_attribute_with_sql_coerced
|
382
|
+
rails_core = companies(:rails_core)
|
383
|
+
|
384
|
+
sql = <<~SQL
|
385
|
+
SELECT firm_id, AVG(CAST(credit_limit AS DECIMAL)) AS avg_credit_limit
|
386
|
+
FROM accounts
|
387
|
+
WHERE firm_id = ?
|
388
|
+
GROUP BY firm_id
|
389
|
+
ORDER BY firm_id
|
390
|
+
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY
|
391
|
+
SQL
|
392
|
+
|
393
|
+
account = Account.find_by_sql([sql, rails_core]).first
|
394
|
+
|
395
|
+
# id was not selected, so it should be nil
|
396
|
+
# (cannot select id because it wasn't used in the GROUP BY clause)
|
397
|
+
assert_nil account.id
|
398
|
+
|
399
|
+
# firm_id was explicitly selected, so it should be present
|
400
|
+
assert_equal(rails_core, account.firm)
|
401
|
+
|
402
|
+
# avg_credit_limit should be present as a virtual attribute
|
403
|
+
assert_equal(52.5, account.avg_credit_limit)
|
404
|
+
end
|
405
|
+
|
406
|
+
# In SQL Server the `AVG()` function for a list of integers returns an integer so need to cast values as decimals before averaging.
|
407
|
+
# Order column must be in the GROUP clause.
|
408
|
+
coerce_tests! :test_select_avg_with_group_by_as_virtual_attribute_with_ar
|
409
|
+
def test_select_avg_with_group_by_as_virtual_attribute_with_ar_coerced
|
410
|
+
rails_core = companies(:rails_core)
|
411
|
+
|
412
|
+
account = Account
|
413
|
+
.select(:firm_id, "AVG(CAST(credit_limit AS DECIMAL)) AS avg_credit_limit")
|
414
|
+
.where(firm: rails_core)
|
415
|
+
.group(:firm_id)
|
416
|
+
.order(:firm_id)
|
417
|
+
.take!
|
418
|
+
|
419
|
+
# id was not selected, so it should be nil
|
420
|
+
# (cannot select id because it wasn't used in the GROUP BY clause)
|
421
|
+
assert_nil account.id
|
422
|
+
|
423
|
+
# firm_id was explicitly selected, so it should be present
|
424
|
+
assert_equal(rails_core, account.firm)
|
425
|
+
|
426
|
+
# avg_credit_limit should be present as a virtual attribute
|
427
|
+
assert_equal(52.5, account.avg_credit_limit)
|
428
|
+
end
|
429
|
+
|
430
|
+
# In SQL Server the `AVG()` function for a list of integers returns an integer so need to cast values as decimals before averaging.
|
431
|
+
# SELECT columns must be in the GROUP clause.
|
432
|
+
# Match SQL Server limit implementation.
|
433
|
+
coerce_tests! :test_select_avg_with_joins_and_group_by_as_virtual_attribute_with_sql
|
434
|
+
def test_select_avg_with_joins_and_group_by_as_virtual_attribute_with_sql_coerced
|
435
|
+
rails_core = companies(:rails_core)
|
436
|
+
|
437
|
+
sql = <<~SQL
|
438
|
+
SELECT companies.*, AVG(CAST(accounts.credit_limit AS DECIMAL)) AS avg_credit_limit
|
439
|
+
FROM companies
|
440
|
+
INNER JOIN accounts ON companies.id = accounts.firm_id
|
441
|
+
WHERE companies.id = ?
|
442
|
+
GROUP BY companies.id, companies.type, companies.firm_id, companies.firm_name, companies.name, companies.client_of, companies.rating, companies.account_id, companies.description
|
443
|
+
ORDER BY companies.id
|
444
|
+
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY
|
445
|
+
SQL
|
446
|
+
|
447
|
+
firm = DependentFirm.find_by_sql([sql, rails_core]).first
|
448
|
+
|
449
|
+
# all the DependentFirm attributes should be present
|
450
|
+
assert_equal rails_core, firm
|
451
|
+
assert_equal rails_core.name, firm.name
|
452
|
+
|
453
|
+
# avg_credit_limit should be present as a virtual attribute
|
454
|
+
assert_equal(52.5, firm.avg_credit_limit)
|
455
|
+
end
|
456
|
+
|
457
|
+
|
458
|
+
# In SQL Server the `AVG()` function for a list of integers returns an integer so need to cast values as decimals before averaging.
|
459
|
+
# SELECT columns must be in the GROUP clause.
|
460
|
+
coerce_tests! :test_select_avg_with_joins_and_group_by_as_virtual_attribute_with_ar
|
461
|
+
def test_select_avg_with_joins_and_group_by_as_virtual_attribute_with_ar_coerced
|
462
|
+
rails_core = companies(:rails_core)
|
463
|
+
|
464
|
+
firm = DependentFirm
|
465
|
+
.select("companies.*", "AVG(CAST(accounts.credit_limit AS DECIMAL)) AS avg_credit_limit")
|
466
|
+
.where(id: rails_core)
|
467
|
+
.joins(:account)
|
468
|
+
.group(:id, :type, :firm_id, :firm_name, :name, :client_of, :rating, :account_id, :description)
|
469
|
+
.take!
|
470
|
+
|
471
|
+
# all the DependentFirm attributes should be present
|
472
|
+
assert_equal rails_core, firm
|
473
|
+
assert_equal rails_core.name, firm.name
|
474
|
+
|
475
|
+
# avg_credit_limit should be present as a virtual attribute
|
476
|
+
assert_equal(52.5, firm.avg_credit_limit)
|
264
477
|
end
|
265
478
|
|
266
479
|
# Match SQL Server limit implementation
|
@@ -329,14 +542,18 @@ module ActiveRecord
|
|
329
542
|
coerce_tests! :test_quote_ar_object
|
330
543
|
def test_quote_ar_object_coerced
|
331
544
|
value = DatetimePrimaryKey.new(id: @time)
|
332
|
-
|
545
|
+
assert_deprecated do
|
546
|
+
assert_equal "'02-14-2017 12:34:56.79'", @connection.quote(value)
|
547
|
+
end
|
333
548
|
end
|
334
549
|
|
335
550
|
# Use our date format.
|
336
551
|
coerce_tests! :test_type_cast_ar_object
|
337
552
|
def test_type_cast_ar_object_coerced
|
338
553
|
value = DatetimePrimaryKey.new(id: @time)
|
339
|
-
|
554
|
+
assert_deprecated do
|
555
|
+
assert_equal "02-14-2017 12:34:56.79", @connection.type_cast(value)
|
556
|
+
end
|
340
557
|
end
|
341
558
|
end
|
342
559
|
end
|
@@ -396,39 +613,6 @@ module ActiveRecord
|
|
396
613
|
end
|
397
614
|
|
398
615
|
class MigrationTest < ActiveRecord::TestCase
|
399
|
-
# We do not have do the DecimalWithoutScale type.
|
400
|
-
coerce_tests! :test_add_table_with_decimals
|
401
|
-
def test_add_table_with_decimals_coerced
|
402
|
-
Person.connection.drop_table :big_numbers rescue nil
|
403
|
-
assert !BigNumber.table_exists?
|
404
|
-
GiveMeBigNumbers.up
|
405
|
-
BigNumber.reset_column_information
|
406
|
-
assert BigNumber.create(
|
407
|
-
:bank_balance => 1586.43,
|
408
|
-
:big_bank_balance => BigDecimal("1000234000567.95"),
|
409
|
-
:world_population => 6000000000,
|
410
|
-
:my_house_population => 3,
|
411
|
-
:value_of_e => BigDecimal("2.7182818284590452353602875")
|
412
|
-
)
|
413
|
-
b = BigNumber.first
|
414
|
-
assert_not_nil b
|
415
|
-
assert_not_nil b.bank_balance
|
416
|
-
assert_not_nil b.big_bank_balance
|
417
|
-
assert_not_nil b.world_population
|
418
|
-
assert_not_nil b.my_house_population
|
419
|
-
assert_not_nil b.value_of_e
|
420
|
-
assert_kind_of BigDecimal, b.world_population
|
421
|
-
assert_equal "6000000000.0", b.world_population.to_s
|
422
|
-
assert_kind_of Integer, b.my_house_population
|
423
|
-
assert_equal 3, b.my_house_population
|
424
|
-
assert_kind_of BigDecimal, b.bank_balance
|
425
|
-
assert_equal BigDecimal("1586.43"), b.bank_balance
|
426
|
-
assert_kind_of BigDecimal, b.big_bank_balance
|
427
|
-
assert_equal BigDecimal("1000234000567.95"), b.big_bank_balance
|
428
|
-
GiveMeBigNumbers.down
|
429
|
-
assert_raise(ActiveRecord::StatementInvalid) { BigNumber.first }
|
430
|
-
end
|
431
|
-
|
432
616
|
# For some reason our tests set Rails.@_env which breaks test env switching.
|
433
617
|
coerce_tests! :test_internal_metadata_stores_environment_when_other_data_exists
|
434
618
|
coerce_tests! :test_internal_metadata_stores_environment
|
@@ -584,11 +768,6 @@ module ActiveRecord
|
|
584
768
|
end
|
585
769
|
end
|
586
770
|
|
587
|
-
class DatabaseTasksDumpSchemaCacheTest < ActiveRecord::TestCase
|
588
|
-
# Skip this test with /tmp/my_schema_cache.yml path on Windows.
|
589
|
-
coerce_tests! :test_dump_schema_cache if RbConfig::CONFIG["host_os"] =~ /mswin|mingw/
|
590
|
-
end
|
591
|
-
|
592
771
|
class DatabaseTasksCreateAllTest < ActiveRecord::TestCase
|
593
772
|
# We extend `local_database?` so that common VM IPs can be used.
|
594
773
|
coerce_tests! :test_ignores_remote_databases, :test_warning_for_remote_databases
|
@@ -653,7 +832,11 @@ class EagerAssociationTest < ActiveRecord::TestCase
|
|
653
832
|
end
|
654
833
|
|
655
834
|
require "models/topic"
|
835
|
+
require "models/customer"
|
836
|
+
require "models/non_primary_key"
|
656
837
|
class FinderTest < ActiveRecord::TestCase
|
838
|
+
fixtures :customers, :topics, :authors
|
839
|
+
|
657
840
|
# We have implicit ordering, via FETCH.
|
658
841
|
coerce_tests! %r{doesn't have implicit ordering},
|
659
842
|
:test_find_doesnt_have_implicit_ordering
|
@@ -698,6 +881,87 @@ class FinderTest < ActiveRecord::TestCase
|
|
698
881
|
end
|
699
882
|
end
|
700
883
|
end
|
884
|
+
|
885
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
886
|
+
coerce_tests! :test_include_on_unloaded_relation_with_match
|
887
|
+
def test_include_on_unloaded_relation_with_match_coerced
|
888
|
+
assert_sql(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do
|
889
|
+
assert_equal true, Customer.where(name: "David").include?(customers(:david))
|
890
|
+
end
|
891
|
+
end
|
892
|
+
|
893
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
894
|
+
coerce_tests! :test_include_on_unloaded_relation_without_match
|
895
|
+
def test_include_on_unloaded_relation_without_match_coerced
|
896
|
+
assert_sql(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do
|
897
|
+
assert_equal false, Customer.where(name: "David").include?(customers(:mary))
|
898
|
+
end
|
899
|
+
end
|
900
|
+
|
901
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
902
|
+
coerce_tests! :test_member_on_unloaded_relation_with_match
|
903
|
+
def test_member_on_unloaded_relation_with_match_coerced
|
904
|
+
assert_sql(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do
|
905
|
+
assert_equal true, Customer.where(name: "David").member?(customers(:david))
|
906
|
+
end
|
907
|
+
end
|
908
|
+
|
909
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
910
|
+
coerce_tests! :test_member_on_unloaded_relation_without_match
|
911
|
+
def test_member_on_unloaded_relation_without_match_coerced
|
912
|
+
assert_sql(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do
|
913
|
+
assert_equal false, Customer.where(name: "David").member?(customers(:mary))
|
914
|
+
end
|
915
|
+
end
|
916
|
+
|
917
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
918
|
+
coerce_tests! :test_implicit_order_column_is_configurable
|
919
|
+
def test_implicit_order_column_is_configurable_coerced
|
920
|
+
old_implicit_order_column = Topic.implicit_order_column
|
921
|
+
Topic.implicit_order_column = "title"
|
922
|
+
|
923
|
+
assert_equal topics(:fifth), Topic.first
|
924
|
+
assert_equal topics(:third), Topic.last
|
925
|
+
|
926
|
+
c = Topic.connection
|
927
|
+
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) {
|
928
|
+
Topic.last
|
929
|
+
}
|
930
|
+
ensure
|
931
|
+
Topic.implicit_order_column = old_implicit_order_column
|
932
|
+
end
|
933
|
+
|
934
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
935
|
+
coerce_tests! :test_implicit_order_set_to_primary_key
|
936
|
+
def test_implicit_order_set_to_primary_key_coerced
|
937
|
+
old_implicit_order_column = Topic.implicit_order_column
|
938
|
+
Topic.implicit_order_column = "id"
|
939
|
+
|
940
|
+
c = Topic.connection
|
941
|
+
assert_sql(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) {
|
942
|
+
Topic.last
|
943
|
+
}
|
944
|
+
ensure
|
945
|
+
Topic.implicit_order_column = old_implicit_order_column
|
946
|
+
end
|
947
|
+
|
948
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
949
|
+
coerce_tests! :test_implicit_order_for_model_without_primary_key
|
950
|
+
def test_implicit_order_for_model_without_primary_key_coerced
|
951
|
+
old_implicit_order_column = NonPrimaryKey.implicit_order_column
|
952
|
+
NonPrimaryKey.implicit_order_column = "created_at"
|
953
|
+
|
954
|
+
c = NonPrimaryKey.connection
|
955
|
+
|
956
|
+
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) {
|
957
|
+
NonPrimaryKey.last
|
958
|
+
}
|
959
|
+
ensure
|
960
|
+
NonPrimaryKey.implicit_order_column = old_implicit_order_column
|
961
|
+
end
|
962
|
+
|
963
|
+
# SQL Server is unable to use aliased SELECT in the HAVING clause.
|
964
|
+
coerce_tests! :test_include_on_unloaded_relation_with_having_referencing_aliased_select
|
701
965
|
end
|
702
966
|
|
703
967
|
module ActiveRecord
|
@@ -929,7 +1193,14 @@ class RelationTest < ActiveRecord::TestCase
|
|
929
1193
|
coerce_tests! :test_reorder_with_first
|
930
1194
|
def test_reorder_with_first_coerced
|
931
1195
|
sql_log = capture_sql do
|
932
|
-
|
1196
|
+
message = <<~MSG.squish
|
1197
|
+
`.reorder(nil)` with `.first` / `.first!` no longer
|
1198
|
+
takes non-deterministic result in Rails 6.2.
|
1199
|
+
To continue taking non-deterministic result, use `.take` / `.take!` instead.
|
1200
|
+
MSG
|
1201
|
+
assert_deprecated(message) do
|
1202
|
+
assert Post.order(:title).reorder(nil).first
|
1203
|
+
end
|
933
1204
|
end
|
934
1205
|
assert sql_log.none? { |sql| /order by [posts].[title]/i.match?(sql) }, "ORDER BY title was used in the query: #{sql_log}"
|
935
1206
|
assert sql_log.all? { |sql| /order by \[posts\]\.\[id\]/i.match?(sql) }, "default ORDER BY ID was not used in the query: #{sql_log}"
|
@@ -982,6 +1253,31 @@ class RelationTest < ActiveRecord::TestCase
|
|
982
1253
|
end
|
983
1254
|
end
|
984
1255
|
|
1256
|
+
module ActiveRecord
|
1257
|
+
class RelationTest < ActiveRecord::TestCase
|
1258
|
+
# Skipping this test. SQL Server doesn't support optimizer hint as comments
|
1259
|
+
coerce_tests! :test_relation_with_optimizer_hints_filters_sql_comment_delimiters
|
1260
|
+
|
1261
|
+
coerce_tests! :test_does_not_duplicate_optimizer_hints_on_merge
|
1262
|
+
def test_does_not_duplicate_optimizer_hints_on_merge_coerced
|
1263
|
+
escaped_table = Post.connection.quote_table_name("posts")
|
1264
|
+
expected = "SELECT #{escaped_table}.* FROM #{escaped_table} OPTION (OMGHINT)"
|
1265
|
+
query = Post.optimizer_hints("OMGHINT").merge(Post.optimizer_hints("OMGHINT")).to_sql
|
1266
|
+
assert_equal expected, query
|
1267
|
+
end
|
1268
|
+
|
1269
|
+
# Original Rails test fails on Windows CI because the dump file was not being binary read.
|
1270
|
+
coerce_tests! :test_marshal_load_legacy_relation
|
1271
|
+
def test_marshal_load_legacy_relation_coerced
|
1272
|
+
path = File.expand_path(
|
1273
|
+
"support/marshal_compatibility_fixtures/legacy_relation.dump",
|
1274
|
+
ARTest::SQLServer.root_activerecord_test
|
1275
|
+
)
|
1276
|
+
assert_equal 11, Marshal.load(File.binread(path)).size
|
1277
|
+
end
|
1278
|
+
end
|
1279
|
+
end
|
1280
|
+
|
985
1281
|
require "models/post"
|
986
1282
|
class SanitizeTest < ActiveRecord::TestCase
|
987
1283
|
# Use nvarchar string (N'') in assert
|
@@ -1233,8 +1529,43 @@ end
|
|
1233
1529
|
module ActiveRecord
|
1234
1530
|
module ConnectionAdapters
|
1235
1531
|
class SchemaCacheTest < ActiveRecord::TestCase
|
1532
|
+
# Tests fail on Windows AppVeyor CI with 'Permission denied' error when renaming file during `File.atomic_write` call.
|
1533
|
+
coerce_tests! :test_yaml_dump_and_load, :test_yaml_dump_and_load_with_gzip if RbConfig::CONFIG["host_os"] =~ /mswin|mingw/
|
1534
|
+
|
1535
|
+
# Ruby 2.5 and 2.6 have issues to marshal Time before 1900. 2012.sql has one column with default value 1753
|
1536
|
+
coerce_tests! :test_marshal_dump_and_load_with_gzip, :test_marshal_dump_and_load_via_disk
|
1537
|
+
|
1538
|
+
# Tests fail on Windows AppVeyor CI with 'Permission denied' error when renaming file during `File.atomic_write` call.
|
1539
|
+
unless RbConfig::CONFIG["host_os"] =~ /mswin|mingw/
|
1540
|
+
def test_marshal_dump_and_load_with_gzip_coerced
|
1541
|
+
with_marshable_time_defaults { original_test_marshal_dump_and_load_with_gzip }
|
1542
|
+
end
|
1543
|
+
def test_marshal_dump_and_load_via_disk_coerced
|
1544
|
+
with_marshable_time_defaults { original_test_marshal_dump_and_load_via_disk }
|
1545
|
+
end
|
1546
|
+
end
|
1547
|
+
|
1236
1548
|
private
|
1237
1549
|
|
1550
|
+
def with_marshable_time_defaults
|
1551
|
+
# Detect problems
|
1552
|
+
if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.7")
|
1553
|
+
column = @connection.columns(:sst_datatypes).find { |c| c.name == "datetime" }
|
1554
|
+
current_default = column.default if column.default.is_a?(Time) && column.default.year < 1900
|
1555
|
+
end
|
1556
|
+
|
1557
|
+
# Correct problems
|
1558
|
+
if current_default.present?
|
1559
|
+
@connection.change_column_default(:sst_datatypes, :datetime, current_default.dup.change(year: 1900))
|
1560
|
+
end
|
1561
|
+
|
1562
|
+
# Run original test
|
1563
|
+
yield
|
1564
|
+
ensure
|
1565
|
+
# Revert changes
|
1566
|
+
@connection.change_column_default(:sst_datatypes, :datetime, current_default) if current_default.present?
|
1567
|
+
end
|
1568
|
+
|
1238
1569
|
# We need to give the full path for this to work.
|
1239
1570
|
def schema_dump_path
|
1240
1571
|
File.join ARTest::SQLServer.root_activerecord, "test/assets/schema_dump_5_1.yml"
|
@@ -1243,23 +1574,26 @@ module ActiveRecord
|
|
1243
1574
|
end
|
1244
1575
|
end
|
1245
1576
|
|
1577
|
+
require "models/post"
|
1578
|
+
require "models/comment"
|
1246
1579
|
class UnsafeRawSqlTest < ActiveRecord::TestCase
|
1580
|
+
fixtures :posts
|
1581
|
+
|
1247
1582
|
# Use LEN() vs length() function.
|
1248
1583
|
coerce_tests! %r{order: always allows Arel}
|
1249
1584
|
test "order: always allows Arel" do
|
1250
|
-
|
1251
|
-
ids_disabled = with_unsafe_raw_sql_disabled { Post.order(Arel.sql("len(title)")).pluck(:title) }
|
1585
|
+
titles = Post.order(Arel.sql("len(title)")).pluck(:title)
|
1252
1586
|
|
1253
|
-
|
1587
|
+
assert_not_empty titles
|
1254
1588
|
end
|
1255
1589
|
|
1256
1590
|
# Use LEN() vs length() function.
|
1257
1591
|
coerce_tests! %r{pluck: always allows Arel}
|
1258
1592
|
test "pluck: always allows Arel" do
|
1259
|
-
|
1260
|
-
|
1593
|
+
excepted_values = Post.includes(:comments).pluck(:title).map { |title| [title, title.size] }
|
1594
|
+
values = Post.includes(:comments).pluck(:title, Arel.sql("len(title)"))
|
1261
1595
|
|
1262
|
-
assert_equal
|
1596
|
+
assert_equal excepted_values, values
|
1263
1597
|
end
|
1264
1598
|
|
1265
1599
|
# Use LEN() vs length() function.
|
@@ -1267,11 +1601,73 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
|
|
1267
1601
|
test "order: allows valid Array arguments" do
|
1268
1602
|
ids_expected = Post.order(Arel.sql("author_id, len(title)")).pluck(:id)
|
1269
1603
|
|
1270
|
-
|
1271
|
-
|
1604
|
+
ids = Post.order(["author_id", "len(title)"]).pluck(:id)
|
1605
|
+
|
1606
|
+
assert_equal ids_expected, ids
|
1607
|
+
end
|
1608
|
+
|
1609
|
+
test "order: allows string column names that are quoted" do
|
1610
|
+
ids_expected = Post.order(Arel.sql("id")).pluck(:id)
|
1611
|
+
|
1612
|
+
ids = Post.order("[id]").pluck(:id)
|
1272
1613
|
|
1273
|
-
assert_equal ids_expected,
|
1274
|
-
|
1614
|
+
assert_equal ids_expected, ids
|
1615
|
+
end
|
1616
|
+
|
1617
|
+
test "order: allows string column names that are quoted with table" do
|
1618
|
+
ids_expected = Post.order(Arel.sql("id")).pluck(:id)
|
1619
|
+
|
1620
|
+
ids = Post.order("[posts].[id]").pluck(:id)
|
1621
|
+
|
1622
|
+
assert_equal ids_expected, ids
|
1623
|
+
end
|
1624
|
+
|
1625
|
+
test "order: allows string column names that are quoted with table and user" do
|
1626
|
+
ids_expected = Post.order(Arel.sql("id")).pluck(:id)
|
1627
|
+
|
1628
|
+
ids = Post.order("[dbo].[posts].[id]").pluck(:id)
|
1629
|
+
|
1630
|
+
assert_equal ids_expected, ids
|
1631
|
+
end
|
1632
|
+
|
1633
|
+
test "order: allows string column names that are quoted with table, user and database" do
|
1634
|
+
ids_expected = Post.order(Arel.sql("id")).pluck(:id)
|
1635
|
+
|
1636
|
+
ids = Post.order("[activerecord_unittest].[dbo].[posts].[id]").pluck(:id)
|
1637
|
+
|
1638
|
+
assert_equal ids_expected, ids
|
1639
|
+
end
|
1640
|
+
|
1641
|
+
test "pluck: allows string column name that are quoted" do
|
1642
|
+
titles_expected = Post.pluck(Arel.sql("title"))
|
1643
|
+
|
1644
|
+
titles = Post.pluck("[title]")
|
1645
|
+
|
1646
|
+
assert_equal titles_expected, titles
|
1647
|
+
end
|
1648
|
+
|
1649
|
+
test "pluck: allows string column name that are quoted with table" do
|
1650
|
+
titles_expected = Post.pluck(Arel.sql("title"))
|
1651
|
+
|
1652
|
+
titles = Post.pluck("[posts].[title]")
|
1653
|
+
|
1654
|
+
assert_equal titles_expected, titles
|
1655
|
+
end
|
1656
|
+
|
1657
|
+
test "pluck: allows string column name that are quoted with table and user" do
|
1658
|
+
titles_expected = Post.pluck(Arel.sql("title"))
|
1659
|
+
|
1660
|
+
titles = Post.pluck("[dbo].[posts].[title]")
|
1661
|
+
|
1662
|
+
assert_equal titles_expected, titles
|
1663
|
+
end
|
1664
|
+
|
1665
|
+
test "pluck: allows string column name that are quoted with table, user and database" do
|
1666
|
+
titles_expected = Post.pluck(Arel.sql("title"))
|
1667
|
+
|
1668
|
+
titles = Post.pluck("[activerecord_unittest].[dbo].[posts].[title]")
|
1669
|
+
|
1670
|
+
assert_equal titles_expected, titles
|
1275
1671
|
end
|
1276
1672
|
end
|
1277
1673
|
|
@@ -1313,6 +1709,25 @@ class RelationMergingTest < ActiveRecord::TestCase
|
|
1313
1709
|
relation = Post.all.merge(Post.order([Arel.sql("title LIKE ?"), "%suffix"]))
|
1314
1710
|
assert_equal ["title LIKE N'%suffix'"], relation.order_values
|
1315
1711
|
end
|
1712
|
+
|
1713
|
+
# Same as original but change first regexp to match sp_executesql binding syntax
|
1714
|
+
coerce_tests! :test_merge_doesnt_duplicate_same_clauses
|
1715
|
+
def test_merge_doesnt_duplicate_same_clauses_coerced
|
1716
|
+
david, mary, bob = authors(:david, :mary, :bob)
|
1717
|
+
|
1718
|
+
non_mary_and_bob = Author.where.not(id: [mary, bob])
|
1719
|
+
|
1720
|
+
author_id = Author.connection.quote_table_name("authors.id")
|
1721
|
+
assert_sql(/WHERE #{Regexp.escape(author_id)} NOT IN \((@\d), \g<1>\)'/) do
|
1722
|
+
assert_equal [david], non_mary_and_bob.merge(non_mary_and_bob)
|
1723
|
+
end
|
1724
|
+
|
1725
|
+
only_david = Author.where("#{author_id} IN (?)", david)
|
1726
|
+
|
1727
|
+
assert_sql(/WHERE \(#{Regexp.escape(author_id)} IN \(1\)\)\z/) do
|
1728
|
+
assert_equal [david], only_david.merge(only_david)
|
1729
|
+
end
|
1730
|
+
end
|
1316
1731
|
end
|
1317
1732
|
|
1318
1733
|
module ActiveRecord
|
@@ -1400,6 +1815,8 @@ end
|
|
1400
1815
|
|
1401
1816
|
require "models/citation"
|
1402
1817
|
class EagerLoadingTooManyIdsTest < ActiveRecord::TestCase
|
1818
|
+
fixtures :citations
|
1819
|
+
|
1403
1820
|
# Original Rails test fails with SQL Server error message "The query processor ran out of internal resources and
|
1404
1821
|
# could not produce a query plan". This error goes away if you change database compatibility level to 110 (SQL 2012)
|
1405
1822
|
# (see https://www.mssqltips.com/sqlservertip/5279/sql-server-error-query-processor-ran-out-of-internal-resources-and-could-not-produce-a-query-plan/).
|
@@ -1407,14 +1824,14 @@ class EagerLoadingTooManyIdsTest < ActiveRecord::TestCase
|
|
1407
1824
|
# unprepared statement is used if the number of values exceeds the adapter's `bind_params_length`. The coerced test
|
1408
1825
|
# still does this as there will be 32,768 remaining citation records in the database and the `bind_params_length` of
|
1409
1826
|
# adapter is 2,098.
|
1410
|
-
coerce_tests! :
|
1411
|
-
def
|
1827
|
+
coerce_tests! :test_eager_loading_too_many_ids
|
1828
|
+
def test_eager_loading_too_many_ids_coerced
|
1412
1829
|
# Remove excess records.
|
1413
1830
|
Citation.limit(32768).order(id: :desc).delete_all
|
1414
1831
|
|
1415
1832
|
# Perform test
|
1416
1833
|
citation_count = Citation.count
|
1417
|
-
assert_sql(/WHERE \
|
1834
|
+
assert_sql(/WHERE \[citations\]\.\[id\] IN \(0, 1/) do
|
1418
1835
|
assert_equal citation_count, Citation.eager_load(:citations).offset(0).size
|
1419
1836
|
end
|
1420
1837
|
end
|
@@ -1437,22 +1854,125 @@ class ActiveRecordSchemaTest < ActiveRecord::TestCase
|
|
1437
1854
|
end
|
1438
1855
|
end
|
1439
1856
|
|
1440
|
-
|
1441
|
-
|
1442
|
-
|
1443
|
-
|
1444
|
-
|
1857
|
+
class ReloadModelsTest < ActiveRecord::TestCase
|
1858
|
+
# Skip test on Windows. The number of arguments passed to `IO.popen` in
|
1859
|
+
# `activesupport/lib/active_support/testing/isolation.rb` exceeds what Windows can handle.
|
1860
|
+
coerce_tests! :test_has_one_with_reload if RbConfig::CONFIG["host_os"] =~ /mswin|mingw/
|
1861
|
+
end
|
1862
|
+
|
1863
|
+
require "models/post"
|
1864
|
+
class AnnotateTest < ActiveRecord::TestCase
|
1865
|
+
# Same as original coerced test except our SQL starts with `EXEC sp_executesql`.
|
1866
|
+
# TODO: Remove coerce after Rails 7 (see https://github.com/rails/rails/pull/42027)
|
1867
|
+
coerce_tests! :test_annotate_wraps_content_in_an_inline_comment
|
1868
|
+
def test_annotate_wraps_content_in_an_inline_comment_coerced
|
1869
|
+
quoted_posts_id, quoted_posts = regexp_escape_table_name("posts.id"), regexp_escape_table_name("posts")
|
1870
|
+
|
1871
|
+
assert_sql(%r{SELECT #{quoted_posts_id} FROM #{quoted_posts} /\* foo \*/}i) do
|
1872
|
+
posts = Post.select(:id).annotate("foo")
|
1873
|
+
assert posts.first
|
1874
|
+
end
|
1875
|
+
end
|
1876
|
+
|
1877
|
+
# Same as original coerced test except our SQL starts with `EXEC sp_executesql`.
|
1878
|
+
# TODO: Remove coerce after Rails 7 (see https://github.com/rails/rails/pull/42027)
|
1879
|
+
coerce_tests! :test_annotate_is_sanitized
|
1880
|
+
def test_annotate_is_sanitized_coerced
|
1881
|
+
quoted_posts_id, quoted_posts = regexp_escape_table_name("posts.id"), regexp_escape_table_name("posts")
|
1882
|
+
|
1883
|
+
assert_sql(%r{SELECT #{quoted_posts_id} FROM #{quoted_posts} /\* foo \*/}i) do
|
1884
|
+
posts = Post.select(:id).annotate("*/foo/*")
|
1885
|
+
assert posts.first
|
1886
|
+
end
|
1887
|
+
|
1888
|
+
assert_sql(%r{SELECT #{quoted_posts_id} FROM #{quoted_posts} /\* foo \*/}i) do
|
1889
|
+
posts = Post.select(:id).annotate("**//foo//**")
|
1890
|
+
assert posts.first
|
1891
|
+
end
|
1892
|
+
|
1893
|
+
assert_sql(%r{SELECT #{quoted_posts_id} FROM #{quoted_posts} /\* foo \*/ /\* bar \*/}i) do
|
1894
|
+
posts = Post.select(:id).annotate("*/foo/*").annotate("*/bar")
|
1895
|
+
assert posts.first
|
1896
|
+
end
|
1897
|
+
|
1898
|
+
assert_sql(%r{SELECT #{quoted_posts_id} FROM #{quoted_posts} /\* \+ MAX_EXECUTION_TIME\(1\) \*/}i) do
|
1899
|
+
posts = Post.select(:id).annotate("+ MAX_EXECUTION_TIME(1)")
|
1900
|
+
assert posts.first
|
1445
1901
|
end
|
1446
1902
|
end
|
1447
1903
|
end
|
1448
1904
|
|
1449
|
-
class
|
1450
|
-
|
1451
|
-
|
1905
|
+
class MarshalSerializationTest < ActiveRecord::TestCase
|
1906
|
+
private
|
1907
|
+
|
1908
|
+
def marshal_fixture_path(file_name)
|
1909
|
+
File.expand_path(
|
1910
|
+
"support/marshal_compatibility_fixtures/#{ActiveRecord::Base.connection.adapter_name}/#{file_name}.dump",
|
1911
|
+
ARTest::SQLServer.test_root_sqlserver
|
1912
|
+
)
|
1913
|
+
end
|
1452
1914
|
end
|
1453
1915
|
|
1454
|
-
class
|
1455
|
-
#
|
1456
|
-
#
|
1457
|
-
|
1916
|
+
class NestedThroughAssociationsTest < ActiveRecord::TestCase
|
1917
|
+
# Same as original but replace order with "order(:id)" to ensure that assert_includes_and_joins_equal doesn't raise
|
1918
|
+
# "A column has been specified more than once in the order by list"
|
1919
|
+
# Example: original test generate queries like "ORDER BY authors.id, [authors].[id]". We don't support duplicate columns in the order list
|
1920
|
+
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
|
1921
|
+
def test_has_many_through_has_many_with_has_many_through_habtm_source_reflection_preload_via_joins_coerced
|
1922
|
+
# preload table schemas
|
1923
|
+
Author.joins(:category_post_comments).first
|
1924
|
+
|
1925
|
+
assert_includes_and_joins_equal(
|
1926
|
+
Author.where("comments.id" => comments(:does_it_hurt).id).order(:id),
|
1927
|
+
[authors(:david), authors(:mary)], :category_post_comments
|
1928
|
+
)
|
1929
|
+
end
|
1930
|
+
|
1931
|
+
def test_has_many_through_has_and_belongs_to_many_with_has_many_source_reflection_preload_via_joins_coerced
|
1932
|
+
# preload table schemas
|
1933
|
+
Category.joins(:post_comments).first
|
1934
|
+
|
1935
|
+
assert_includes_and_joins_equal(
|
1936
|
+
Category.where("comments.id" => comments(:more_greetings).id).order(:id),
|
1937
|
+
[categories(:general), categories(:technology)], :post_comments
|
1938
|
+
)
|
1939
|
+
end
|
1940
|
+
end
|
1941
|
+
|
1942
|
+
class BasePreventWritesTest < ActiveRecord::TestCase
|
1943
|
+
# SQL Server does not have query for release_savepoint
|
1944
|
+
coerce_tests! %r{an empty transaction does not raise if preventing writes}
|
1945
|
+
test "an empty transaction does not raise if preventing writes coerced" do
|
1946
|
+
ActiveRecord::Base.while_preventing_writes do
|
1947
|
+
assert_queries(1, ignore_none: true) do
|
1948
|
+
Bird.transaction do
|
1949
|
+
ActiveRecord::Base.connection.materialize_transactions
|
1950
|
+
end
|
1951
|
+
end
|
1952
|
+
end
|
1953
|
+
end
|
1954
|
+
|
1955
|
+
class BasePreventWritesLegacyTest < ActiveRecord::TestCase
|
1956
|
+
# SQL Server does not have query for release_savepoint
|
1957
|
+
coerce_tests! %r{an empty transaction does not raise if preventing writes}
|
1958
|
+
test "an empty transaction does not raise if preventing writes coerced" do
|
1959
|
+
ActiveRecord::Base.connection_handler.while_preventing_writes do
|
1960
|
+
assert_queries(1, ignore_none: true) do
|
1961
|
+
Bird.transaction do
|
1962
|
+
ActiveRecord::Base.connection.materialize_transactions
|
1963
|
+
end
|
1964
|
+
end
|
1965
|
+
end
|
1966
|
+
end
|
1967
|
+
end
|
1968
|
+
end
|
1969
|
+
|
1970
|
+
class MigratorTest < ActiveRecord::TestCase
|
1971
|
+
# Test fails on Windows AppVeyor CI for unknown reason.
|
1972
|
+
coerce_tests! :test_migrator_db_has_no_schema_migrations_table if RbConfig::CONFIG["host_os"] =~ /mswin|mingw/
|
1973
|
+
end
|
1974
|
+
|
1975
|
+
class MultiDbMigratorTest < ActiveRecord::TestCase
|
1976
|
+
# Test fails on Windows AppVeyor CI for unknown reason.
|
1977
|
+
coerce_tests! :test_migrator_db_has_no_schema_migrations_table if RbConfig::CONFIG["host_os"] =~ /mswin|mingw/
|
1458
1978
|
end
|