activerecord-sqlserver-adapter 6.1.2.1 → 7.2.4
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/.devcontainer/Dockerfile +30 -0
- data/.devcontainer/boot.sh +22 -0
- data/.devcontainer/devcontainer.json +38 -0
- data/.devcontainer/docker-compose.yml +42 -0
- data/.github/workflows/ci.yml +7 -4
- data/.gitignore +3 -1
- data/CHANGELOG.md +19 -42
- data/Dockerfile.ci +3 -3
- data/Gemfile +6 -1
- data/MIT-LICENSE +1 -1
- data/README.md +113 -27
- data/RUNNING_UNIT_TESTS.md +27 -14
- data/Rakefile +2 -6
- data/VERSION +1 -1
- data/activerecord-sqlserver-adapter.gemspec +3 -3
- data/appveyor.yml +4 -6
- data/docker-compose.ci.yml +2 -1
- data/lib/active_record/connection_adapters/sqlserver/core_ext/abstract_adapter.rb +20 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +6 -4
- data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +5 -23
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +10 -7
- 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 +12 -2
- data/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb +24 -16
- data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +0 -31
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +143 -155
- data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +5 -5
- data/lib/active_record/connection_adapters/sqlserver/quoting.rb +57 -56
- data/lib/active_record/connection_adapters/sqlserver/savepoints.rb +26 -0
- data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +14 -12
- data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +11 -0
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +213 -57
- data/lib/active_record/connection_adapters/sqlserver/showplan.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +13 -2
- data/lib/active_record/connection_adapters/sqlserver/transaction.rb +4 -6
- data/lib/active_record/connection_adapters/sqlserver/type/data.rb +19 -1
- data/lib/active_record/connection_adapters/sqlserver/type/date.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver/type/time.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver/utils.rb +21 -10
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +187 -187
- data/lib/active_record/connection_adapters/sqlserver_column.rb +1 -0
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +42 -33
- data/lib/arel/visitors/sqlserver.rb +77 -34
- data/test/cases/active_schema_test_sqlserver.rb +127 -0
- data/test/cases/adapter_test_sqlserver.rb +114 -26
- data/test/cases/coerced_tests.rb +1121 -340
- data/test/cases/column_test_sqlserver.rb +67 -64
- data/test/cases/connection_test_sqlserver.rb +3 -6
- data/test/cases/dbconsole.rb +19 -0
- data/test/cases/disconnected_test_sqlserver.rb +8 -5
- data/test/cases/eager_load_too_many_ids_test_sqlserver.rb +18 -0
- data/test/cases/enum_test_sqlserver.rb +49 -0
- data/test/cases/execute_procedure_test_sqlserver.rb +9 -5
- data/test/cases/fetch_test_sqlserver.rb +19 -0
- data/test/cases/helper_sqlserver.rb +11 -5
- data/test/cases/index_test_sqlserver.rb +8 -6
- data/test/cases/json_test_sqlserver.rb +1 -1
- data/test/cases/lateral_test_sqlserver.rb +2 -2
- data/test/cases/migration_test_sqlserver.rb +19 -1
- data/test/cases/optimizer_hints_test_sqlserver.rb +21 -12
- data/test/cases/pessimistic_locking_test_sqlserver.rb +8 -7
- data/test/cases/primary_keys_test_sqlserver.rb +2 -2
- data/test/cases/rake_test_sqlserver.rb +10 -5
- data/test/cases/schema_dumper_test_sqlserver.rb +155 -109
- data/test/cases/schema_test_sqlserver.rb +64 -1
- data/test/cases/showplan_test_sqlserver.rb +7 -7
- data/test/cases/specific_schema_test_sqlserver.rb +17 -13
- data/test/cases/transaction_test_sqlserver.rb +13 -8
- data/test/cases/trigger_test_sqlserver.rb +20 -0
- data/test/cases/utils_test_sqlserver.rb +2 -2
- data/test/cases/uuid_test_sqlserver.rb +8 -0
- data/test/cases/view_test_sqlserver.rb +58 -0
- data/test/config.yml +1 -2
- data/test/migrations/transaction_table/1_table_will_never_be_created.rb +1 -1
- data/test/models/sqlserver/alien.rb +5 -0
- data/test/models/sqlserver/table_with_spaces.rb +5 -0
- data/test/models/sqlserver/trigger.rb +8 -0
- data/test/schema/sqlserver_specific_schema.rb +54 -6
- data/test/support/coerceable_test_sqlserver.rb +4 -4
- data/test/support/connection_reflection.rb +3 -9
- data/test/support/core_ext/query_cache.rb +7 -1
- 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/marshal_compatibility_fixtures/SQLServer/rails_7_1_topic.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/SQLServer/rails_7_1_topic_associations.dump +0 -0
- data/test/support/query_assertions.rb +49 -0
- data/test/support/rake_helpers.rb +3 -1
- data/test/support/table_definition_sqlserver.rb +24 -0
- data/test/support/test_in_memory_oltp.rb +2 -2
- metadata +41 -17
- data/lib/active_record/sqlserver_base.rb +0 -18
- data/test/cases/scratchpad_test_sqlserver.rb +0 -8
- 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 +0 -29
data/test/cases/coerced_tests.rb
CHANGED
@@ -47,12 +47,26 @@ class UniquenessValidationTest < ActiveRecord::TestCase
|
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
50
|
+
class UniquenessValidationWithIndexTest < ActiveRecord::TestCase
|
51
|
+
# Need to explicitly set the WHERE clause to truthy.
|
52
|
+
coerce_tests! :test_partial_index
|
53
|
+
def test_partial_index_coerced
|
54
|
+
Topic.validates_uniqueness_of(:title)
|
55
|
+
@connection.add_index(:topics, :title, unique: true, where: "approved=1", name: :topics_index)
|
56
|
+
|
57
|
+
t = Topic.create!(title: "abc")
|
58
|
+
t.author_name = "John"
|
59
|
+
assert_queries_count(1) do
|
60
|
+
t.valid?
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
50
65
|
require "models/event"
|
51
66
|
module ActiveRecord
|
52
67
|
class AdapterTest < ActiveRecord::TestCase
|
53
68
|
# Legacy binds are not supported.
|
54
69
|
coerce_tests! :test_select_all_insert_update_delete_with_casted_binds
|
55
|
-
coerce_tests! :test_select_all_insert_update_delete_with_legacy_binds
|
56
70
|
|
57
71
|
# As far as I can tell, SQL Server does not support null bytes in strings.
|
58
72
|
coerce_tests! :test_update_prepared_statement
|
@@ -92,37 +106,17 @@ module ActiveRecord
|
|
92
106
|
Subscriber.send(:load_schema!)
|
93
107
|
original_test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_is_called_while_preventing_writes
|
94
108
|
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
109
|
|
107
|
-
#
|
108
|
-
coerce_tests! :
|
109
|
-
def
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
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
|
110
|
+
# Invalid character encoding causes `ActiveRecord::StatementInvalid` error similar to Postgres.
|
111
|
+
coerce_tests! :test_doesnt_error_when_a_select_query_has_encoding_errors
|
112
|
+
def test_doesnt_error_when_a_select_query_has_encoding_errors_coerced
|
113
|
+
ActiveRecord::Base.while_preventing_writes do
|
114
|
+
# TinyTDS fail on encoding errors.
|
115
|
+
# But at least we can assert it fails in the client and not before when trying to match the query.
|
116
|
+
assert_raises ActiveRecord::StatementInvalid do
|
117
|
+
@connection.select_all("SELECT '\xC8'")
|
118
|
+
end
|
119
|
+
end
|
126
120
|
end
|
127
121
|
end
|
128
122
|
end
|
@@ -203,7 +197,7 @@ class BasicsTest < ActiveRecord::TestCase
|
|
203
197
|
# Use square brackets as SQL Server escaped character
|
204
198
|
coerce_tests! :test_column_names_are_escaped
|
205
199
|
def test_column_names_are_escaped_coerced
|
206
|
-
conn = ActiveRecord::Base.
|
200
|
+
conn = ActiveRecord::Base.lease_connection
|
207
201
|
assert_equal "[t]]]", conn.quote_column_name("t]")
|
208
202
|
end
|
209
203
|
|
@@ -235,18 +229,6 @@ class BasicsTest < ActiveRecord::TestCase
|
|
235
229
|
end
|
236
230
|
end
|
237
231
|
end
|
238
|
-
|
239
|
-
# SQL Server does not have query for release_savepoint
|
240
|
-
coerce_tests! %r{an empty transaction does not raise if preventing writes}
|
241
|
-
test "an empty transaction does not raise if preventing writes coerced" do
|
242
|
-
ActiveRecord::Base.while_preventing_writes do
|
243
|
-
assert_queries(1, ignore_none: true) do
|
244
|
-
Bird.transaction do
|
245
|
-
ActiveRecord::Base.connection.materialize_transactions
|
246
|
-
end
|
247
|
-
end
|
248
|
-
end
|
249
|
-
end
|
250
232
|
end
|
251
233
|
|
252
234
|
class BelongsToAssociationsTest < ActiveRecord::TestCase
|
@@ -266,7 +248,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
|
|
266
248
|
def test_belongs_to_coerced
|
267
249
|
client = Client.find(3)
|
268
250
|
first_firm = companies(:first_firm)
|
269
|
-
|
251
|
+
assert_queries_match(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do
|
270
252
|
assert_equal first_firm, client.firm
|
271
253
|
assert_equal first_firm.name, client.firm.name
|
272
254
|
end
|
@@ -329,7 +311,7 @@ module ActiveRecord
|
|
329
311
|
|
330
312
|
authors = Author.where(id: [1, 2, 3, nil])
|
331
313
|
assert_equal sql_unprepared, @connection.to_sql(authors.arel)
|
332
|
-
|
314
|
+
assert_queries_match(prepared ? sql_prepared : sql_unprepared) { assert_equal 3, authors.length }
|
333
315
|
|
334
316
|
# prepared_statements: true
|
335
317
|
#
|
@@ -344,7 +326,7 @@ module ActiveRecord
|
|
344
326
|
|
345
327
|
authors = Author.where(id: [1, 2, 3, 9223372036854775808])
|
346
328
|
assert_equal sql_unprepared, @connection.to_sql(authors.arel)
|
347
|
-
|
329
|
+
assert_queries_match(prepared ? sql_prepared : sql_unprepared) { assert_equal 3, authors.length }
|
348
330
|
end
|
349
331
|
end
|
350
332
|
end
|
@@ -357,10 +339,55 @@ module ActiveRecord
|
|
357
339
|
Book.send(:load_schema!)
|
358
340
|
original_test_payload_name_on_load
|
359
341
|
end
|
342
|
+
|
343
|
+
# Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
|
344
|
+
coerce_tests! :test_payload_row_count_on_select_all
|
345
|
+
def test_payload_row_count_on_select_all_coerced
|
346
|
+
connection.remove_index(:books, column: [:author_id, :name])
|
347
|
+
|
348
|
+
original_test_payload_row_count_on_select_all
|
349
|
+
ensure
|
350
|
+
Book.where(author_id: nil, name: 'row count book 1').delete_all
|
351
|
+
Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
|
352
|
+
end
|
353
|
+
|
354
|
+
# Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
|
355
|
+
coerce_tests! :test_payload_row_count_on_pluck
|
356
|
+
def test_payload_row_count_on_pluck_coerced
|
357
|
+
connection.remove_index(:books, column: [:author_id, :name])
|
358
|
+
|
359
|
+
original_test_payload_row_count_on_pluck
|
360
|
+
ensure
|
361
|
+
Book.where(author_id: nil, name: 'row count book 2').delete_all
|
362
|
+
Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
|
363
|
+
end
|
364
|
+
|
365
|
+
# Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
|
366
|
+
coerce_tests! :test_payload_row_count_on_raw_sql
|
367
|
+
def test_payload_row_count_on_raw_sql_coerced
|
368
|
+
connection.remove_index(:books, column: [:author_id, :name])
|
369
|
+
|
370
|
+
original_test_payload_row_count_on_raw_sql
|
371
|
+
ensure
|
372
|
+
Book.where(author_id: nil, name: 'row count book 3').delete_all
|
373
|
+
Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
|
374
|
+
end
|
360
375
|
end
|
361
376
|
end
|
362
377
|
|
363
378
|
class CalculationsTest < ActiveRecord::TestCase
|
379
|
+
# SELECT columns must be in the GROUP clause.
|
380
|
+
coerce_tests! :test_should_count_with_group_by_qualified_name_on_loaded
|
381
|
+
def test_should_count_with_group_by_qualified_name_on_loaded_coerced
|
382
|
+
accounts = Account.group("accounts.id").select("accounts.id")
|
383
|
+
expected = { 1 => 1, 2 => 1, 3 => 1, 4 => 1, 5 => 1, 6 => 1 }
|
384
|
+
assert_not_predicate accounts, :loaded?
|
385
|
+
assert_equal expected, accounts.count
|
386
|
+
accounts.load
|
387
|
+
assert_predicate accounts, :loaded?
|
388
|
+
assert_equal expected, accounts.count(:id)
|
389
|
+
end
|
390
|
+
|
364
391
|
# Fix randomly failing test. The loading of the model's schema was affecting the test.
|
365
392
|
coerce_tests! :test_offset_is_kept
|
366
393
|
def test_offset_is_kept_coerced
|
@@ -439,7 +466,7 @@ class CalculationsTest < ActiveRecord::TestCase
|
|
439
466
|
FROM companies
|
440
467
|
INNER JOIN accounts ON companies.id = accounts.firm_id
|
441
468
|
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
|
469
|
+
GROUP BY companies.id, companies.type, companies.firm_id, companies.firm_name, companies.name, companies.client_of, companies.rating, companies.account_id, companies.description, companies.status
|
443
470
|
ORDER BY companies.id
|
444
471
|
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY
|
445
472
|
SQL
|
@@ -465,7 +492,7 @@ class CalculationsTest < ActiveRecord::TestCase
|
|
465
492
|
.select("companies.*", "AVG(CAST(accounts.credit_limit AS DECIMAL)) AS avg_credit_limit")
|
466
493
|
.where(id: rails_core)
|
467
494
|
.joins(:account)
|
468
|
-
.group(:id, :type, :firm_id, :firm_name, :name, :client_of, :rating, :account_id, :description)
|
495
|
+
.group(:id, :type, :firm_id, :firm_name, :name, :client_of, :rating, :account_id, :description, :status)
|
469
496
|
.take!
|
470
497
|
|
471
498
|
# all the DependentFirm attributes should be present
|
@@ -479,7 +506,7 @@ class CalculationsTest < ActiveRecord::TestCase
|
|
479
506
|
# Match SQL Server limit implementation
|
480
507
|
coerce_tests! :test_limit_is_kept
|
481
508
|
def test_limit_is_kept_coerced
|
482
|
-
queries =
|
509
|
+
queries = capture_sql { Account.limit(1).count }
|
483
510
|
assert_equal 1, queries.length
|
484
511
|
assert_match(/ORDER BY \[accounts\]\.\[id\] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/, queries.first)
|
485
512
|
end
|
@@ -487,7 +514,7 @@ class CalculationsTest < ActiveRecord::TestCase
|
|
487
514
|
# Match SQL Server limit implementation
|
488
515
|
coerce_tests! :test_limit_with_offset_is_kept
|
489
516
|
def test_limit_with_offset_is_kept_coerced
|
490
|
-
queries =
|
517
|
+
queries = capture_sql { Account.limit(1).offset(1).count }
|
491
518
|
assert_equal 1, queries.length
|
492
519
|
assert_match(/ORDER BY \[accounts\]\.\[id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY.*@0 = 1, @1 = 1/, queries.first)
|
493
520
|
end
|
@@ -496,12 +523,15 @@ class CalculationsTest < ActiveRecord::TestCase
|
|
496
523
|
coerce_tests! :test_distinct_count_all_with_custom_select_and_order
|
497
524
|
def test_distinct_count_all_with_custom_select_and_order_coerced
|
498
525
|
accounts = Account.distinct.select("credit_limit % 10 AS the_limit").order(Arel.sql("credit_limit % 10"))
|
499
|
-
|
500
|
-
|
526
|
+
assert_queries_count(1) { assert_equal 3, accounts.count(:all) }
|
527
|
+
assert_queries_count(1) { assert_equal 3, accounts.load.size }
|
501
528
|
end
|
502
529
|
|
503
530
|
# Leave it up to users to format selects/functions so HAVING works correctly.
|
504
531
|
coerce_tests! :test_having_with_strong_parameters
|
532
|
+
|
533
|
+
# SELECT columns must be in the GROUP clause. Since since `ids` only selects the primary key you cannot perform this query in SQL Server.
|
534
|
+
coerce_tests! :test_ids_with_includes_and_non_primary_key_order
|
505
535
|
end
|
506
536
|
|
507
537
|
module ActiveRecord
|
@@ -531,29 +561,46 @@ module ActiveRecord
|
|
531
561
|
assert_equal 1, four.default
|
532
562
|
assert_equal "hello", five.default
|
533
563
|
end
|
534
|
-
end
|
535
|
-
end
|
536
|
-
end
|
537
564
|
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
def test_quote_ar_object_coerced
|
544
|
-
value = DatetimePrimaryKey.new(id: @time)
|
545
|
-
assert_deprecated do
|
546
|
-
assert_equal "'02-14-2017 12:34:56.79'", @connection.quote(value)
|
565
|
+
# Use precision 6 by default for datetime/timestamp columns. SQL Server uses `datetime2` for date-times with precision.
|
566
|
+
coerce_tests! :test_add_column_with_postgresql_datetime_type
|
567
|
+
def test_add_column_with_postgresql_datetime_type_coerced
|
568
|
+
connection.create_table :testings do |t|
|
569
|
+
t.column :foo, :datetime
|
547
570
|
end
|
571
|
+
|
572
|
+
column = connection.columns(:testings).find { |c| c.name == "foo" }
|
573
|
+
|
574
|
+
assert_equal :datetime, column.type
|
575
|
+
assert_equal "datetime2(6)", column.sql_type
|
576
|
+
end
|
577
|
+
|
578
|
+
# Use precision 6 by default for datetime/timestamp columns. SQL Server uses `datetime2` for date-times with precision.
|
579
|
+
coerce_tests! :test_change_column_with_timestamp_type
|
580
|
+
def test_change_column_with_timestamp_type_coerced
|
581
|
+
connection.create_table :testings do |t|
|
582
|
+
t.column :foo, :datetime, null: false
|
583
|
+
end
|
584
|
+
|
585
|
+
connection.change_column :testings, :foo, :timestamp
|
586
|
+
|
587
|
+
column = connection.columns(:testings).find { |c| c.name == "foo" }
|
588
|
+
|
589
|
+
assert_equal :datetime, column.type
|
590
|
+
assert_equal "datetime2(6)", column.sql_type
|
548
591
|
end
|
549
592
|
|
550
|
-
# Use
|
551
|
-
coerce_tests! :
|
552
|
-
def
|
553
|
-
|
554
|
-
|
555
|
-
assert_equal "02-14-2017 12:34:56.79", @connection.type_cast(value)
|
593
|
+
# Use precision 6 by default for datetime/timestamp columns. SQL Server uses `datetime2` for date-times with precision.
|
594
|
+
coerce_tests! :test_add_column_with_timestamp_type
|
595
|
+
def test_add_column_with_timestamp_type_coerced
|
596
|
+
connection.create_table :testings do |t|
|
597
|
+
t.column :foo, :timestamp
|
556
598
|
end
|
599
|
+
|
600
|
+
column = connection.columns(:testings).find { |c| c.name == "foo" }
|
601
|
+
|
602
|
+
assert_equal :datetime, column.type
|
603
|
+
assert_equal "datetime2(6)", column.sql_type
|
557
604
|
end
|
558
605
|
end
|
559
606
|
end
|
@@ -616,6 +663,129 @@ class MigrationTest < ActiveRecord::TestCase
|
|
616
663
|
# For some reason our tests set Rails.@_env which breaks test env switching.
|
617
664
|
coerce_tests! :test_internal_metadata_stores_environment_when_other_data_exists
|
618
665
|
coerce_tests! :test_internal_metadata_stores_environment
|
666
|
+
|
667
|
+
# Same as original but using binary type instead of blob
|
668
|
+
coerce_tests! :test_add_column_with_casted_type_if_not_exists_set_to_true
|
669
|
+
def test_add_column_with_casted_type_if_not_exists_set_to_true_coerced
|
670
|
+
migration_a = Class.new(ActiveRecord::Migration::Current) {
|
671
|
+
def version; 100 end
|
672
|
+
def migrate(x)
|
673
|
+
add_column "people", "last_name", :binary
|
674
|
+
end
|
675
|
+
}.new
|
676
|
+
|
677
|
+
migration_b = Class.new(ActiveRecord::Migration::Current) {
|
678
|
+
def version; 101 end
|
679
|
+
def migrate(x)
|
680
|
+
add_column "people", "last_name", :binary, if_not_exists: true
|
681
|
+
end
|
682
|
+
}.new
|
683
|
+
|
684
|
+
ActiveRecord::Migrator.new(:up, [migration_a], @schema_migration, @internal_metadata, 100).migrate
|
685
|
+
assert_column Person, :last_name, "migration_a should have created the last_name column on people"
|
686
|
+
|
687
|
+
assert_nothing_raised do
|
688
|
+
ActiveRecord::Migrator.new(:up, [migration_b], @schema_migration, @internal_metadata, 101).migrate
|
689
|
+
end
|
690
|
+
ensure
|
691
|
+
Person.reset_column_information
|
692
|
+
if Person.column_names.include?("last_name")
|
693
|
+
Person.lease_connection.remove_column("people", "last_name")
|
694
|
+
end
|
695
|
+
end
|
696
|
+
end
|
697
|
+
|
698
|
+
module ActiveRecord
|
699
|
+
class Migration
|
700
|
+
class CompatibilityTest < ActiveRecord::TestCase
|
701
|
+
# Error message depends on the database adapter.
|
702
|
+
coerce_tests! :test_create_table_on_7_0
|
703
|
+
def test_create_table_on_7_0_coerced
|
704
|
+
long_table_name = "a" * (connection.table_name_length + 1)
|
705
|
+
migration = Class.new(ActiveRecord::Migration[7.0]) {
|
706
|
+
@@long_table_name = long_table_name
|
707
|
+
def version; 100 end
|
708
|
+
def migrate(x)
|
709
|
+
create_table @@long_table_name
|
710
|
+
end
|
711
|
+
}.new
|
712
|
+
|
713
|
+
error = assert_raises(StandardError) do
|
714
|
+
ActiveRecord::Migrator.new(:up, [migration], @schema_migration, @internal_metadata).migrate
|
715
|
+
end
|
716
|
+
assert_match(/The identifier that starts with '#{long_table_name[0...-1]}' is too long/i, error.message)
|
717
|
+
ensure
|
718
|
+
connection.drop_table(long_table_name) rescue nil
|
719
|
+
end
|
720
|
+
|
721
|
+
# SQL Server truncates long table names when renaming (https://learn.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-rename-transact-sql?view=sql-server-ver16).
|
722
|
+
coerce_tests! :test_rename_table_on_7_0
|
723
|
+
def test_rename_table_on_7_0_coerced
|
724
|
+
long_table_name = "a" * (connection.table_name_length + 1)
|
725
|
+
connection.create_table(:more_testings)
|
726
|
+
|
727
|
+
migration = Class.new(ActiveRecord::Migration[7.0]) {
|
728
|
+
@@long_table_name = long_table_name
|
729
|
+
def version; 100 end
|
730
|
+
def migrate(x)
|
731
|
+
rename_table :more_testings, @@long_table_name
|
732
|
+
end
|
733
|
+
}.new
|
734
|
+
|
735
|
+
ActiveRecord::Migrator.new(:up, [migration], @schema_migration, @internal_metadata).migrate
|
736
|
+
assert connection.table_exists?(long_table_name[0...-1])
|
737
|
+
assert_not connection.table_exists?(:more_testings)
|
738
|
+
assert connection.table_exists?(long_table_name[0...-1])
|
739
|
+
ensure
|
740
|
+
connection.drop_table(:more_testings) rescue nil
|
741
|
+
connection.drop_table(long_table_name[0...-1]) rescue nil
|
742
|
+
end
|
743
|
+
|
744
|
+
# SQL Server has a different maximum index name length.
|
745
|
+
coerce_tests! :test_add_index_errors_on_too_long_name_7_0
|
746
|
+
def test_add_index_errors_on_too_long_name_7_0_coerced
|
747
|
+
long_index_name = 'a' * (connection.index_name_length + 1)
|
748
|
+
|
749
|
+
migration = Class.new(ActiveRecord::Migration[7.0]) {
|
750
|
+
@@long_index_name = long_index_name
|
751
|
+
def migrate(x)
|
752
|
+
add_column :testings, :very_long_column_name_to_test_with, :string
|
753
|
+
add_index :testings, [:foo, :bar, :very_long_column_name_to_test_with], name: @@long_index_name
|
754
|
+
end
|
755
|
+
}.new
|
756
|
+
|
757
|
+
error = assert_raises(StandardError) do
|
758
|
+
ActiveRecord::Migrator.new(:up, [migration], @schema_migration, @internal_metadata).migrate
|
759
|
+
end
|
760
|
+
assert_match(/Index name \'#{long_index_name}\' on table \'testings\' is too long/i, error.message)
|
761
|
+
end
|
762
|
+
|
763
|
+
# SQL Server has a different maximum index name length.
|
764
|
+
coerce_tests! :test_create_table_add_index_errors_on_too_long_name_7_0
|
765
|
+
def test_create_table_add_index_errors_on_too_long_name_7_0_coerced
|
766
|
+
long_index_name = 'a' * (connection.index_name_length + 1)
|
767
|
+
|
768
|
+
migration = Class.new(ActiveRecord::Migration[7.0]) {
|
769
|
+
@@long_index_name = long_index_name
|
770
|
+
def migrate(x)
|
771
|
+
create_table :more_testings do |t|
|
772
|
+
t.integer :foo
|
773
|
+
t.integer :bar
|
774
|
+
t.integer :very_long_column_name_to_test_with
|
775
|
+
t.index [:foo, :bar, :very_long_column_name_to_test_with], name: @@long_index_name
|
776
|
+
end
|
777
|
+
end
|
778
|
+
}.new
|
779
|
+
|
780
|
+
error = assert_raises(StandardError) do
|
781
|
+
ActiveRecord::Migrator.new(:up, [migration], @schema_migration, @internal_metadata).migrate
|
782
|
+
end
|
783
|
+
assert_match(/Index name \'#{long_index_name}\' on table \'more_testings\' is too long/i, error.message)
|
784
|
+
ensure
|
785
|
+
connection.drop_table :more_testings rescue nil
|
786
|
+
end
|
787
|
+
end
|
788
|
+
end
|
619
789
|
end
|
620
790
|
|
621
791
|
class CoreTest < ActiveRecord::TestCase
|
@@ -641,6 +811,7 @@ end
|
|
641
811
|
module ActiveRecord
|
642
812
|
# The original module is hardcoded for PostgreSQL/SQLite/MySQL tests.
|
643
813
|
module DatabaseTasksSetupper
|
814
|
+
undef_method :setup
|
644
815
|
def setup
|
645
816
|
@sqlserver_tasks =
|
646
817
|
Class.new do
|
@@ -663,6 +834,7 @@ module ActiveRecord
|
|
663
834
|
$stderr, @original_stderr = StringIO.new, $stderr
|
664
835
|
end
|
665
836
|
|
837
|
+
undef_method :with_stubbed_new
|
666
838
|
def with_stubbed_new
|
667
839
|
ActiveRecord::Tasks::SQLServerDatabaseTasks.stub(:new, @sqlserver_tasks) do
|
668
840
|
yield
|
@@ -782,52 +954,16 @@ end
|
|
782
954
|
class DefaultScopingTest < ActiveRecord::TestCase
|
783
955
|
# We are not doing order duplicate removal anymore.
|
784
956
|
coerce_tests! :test_order_in_default_scope_should_not_prevail
|
785
|
-
|
786
|
-
# Use our escaped format in assertion.
|
787
|
-
coerce_tests! :test_with_abstract_class_scope_should_be_executed_in_correct_context
|
788
|
-
def test_with_abstract_class_scope_should_be_executed_in_correct_context_coerced
|
789
|
-
vegetarian_pattern, gender_pattern = [/[lions].[is_vegetarian]/, /[lions].[gender]/]
|
790
|
-
assert_match vegetarian_pattern, Lion.all.to_sql
|
791
|
-
assert_match gender_pattern, Lion.female.to_sql
|
792
|
-
end
|
793
|
-
end
|
794
|
-
|
795
|
-
require "models/post"
|
796
|
-
require "models/subscriber"
|
797
|
-
class EachTest < ActiveRecord::TestCase
|
798
|
-
# Quoting in tests does not cope with bracket quoting.
|
799
|
-
coerce_tests! :test_find_in_batches_should_quote_batch_order
|
800
|
-
def test_find_in_batches_should_quote_batch_order_coerced
|
801
|
-
Post.connection
|
802
|
-
assert_sql(/ORDER BY \[posts\]\.\[id\]/) do
|
803
|
-
Post.find_in_batches(:batch_size => 1) do |batch|
|
804
|
-
assert_kind_of Array, batch
|
805
|
-
assert_kind_of Post, batch.first
|
806
|
-
end
|
807
|
-
end
|
808
|
-
end
|
809
|
-
|
810
|
-
# Quoting in tests does not cope with bracket quoting.
|
811
|
-
coerce_tests! :test_in_batches_should_quote_batch_order
|
812
|
-
def test_in_batches_should_quote_batch_order_coerced
|
813
|
-
Post.connection
|
814
|
-
assert_sql(/ORDER BY \[posts\]\.\[id\]/) do
|
815
|
-
Post.in_batches(of: 1) do |relation|
|
816
|
-
assert_kind_of ActiveRecord::Relation, relation
|
817
|
-
assert_kind_of Post, relation.first
|
818
|
-
end
|
819
|
-
end
|
820
|
-
end
|
821
957
|
end
|
822
958
|
|
823
959
|
class EagerAssociationTest < ActiveRecord::TestCase
|
824
|
-
# Use LEN()
|
960
|
+
# Use LEN() instead of LENGTH() function.
|
825
961
|
coerce_tests! :test_count_with_include
|
826
962
|
def test_count_with_include_coerced
|
827
963
|
assert_equal 3, authors(:david).posts_with_comments.where("LEN(comments.body) > 15").references(:comments).count
|
828
964
|
end
|
829
965
|
|
830
|
-
#
|
966
|
+
# The raw SQL in the scope uses `limit 1`.
|
831
967
|
coerce_tests! %r{including association based on sql condition and no database column}
|
832
968
|
end
|
833
969
|
|
@@ -841,20 +977,12 @@ class FinderTest < ActiveRecord::TestCase
|
|
841
977
|
coerce_tests! %r{doesn't have implicit ordering},
|
842
978
|
:test_find_doesnt_have_implicit_ordering
|
843
979
|
|
844
|
-
# Square brackets around column name
|
845
|
-
coerce_tests! :test_exists_does_not_select_columns_without_alias
|
846
|
-
def test_exists_does_not_select_columns_without_alias_coerced
|
847
|
-
assert_sql(/SELECT\s+1 AS one FROM \[topics\].*OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) do
|
848
|
-
Topic.exists?
|
849
|
-
end
|
850
|
-
end
|
851
|
-
|
852
980
|
# Assert SQL Server limit implementation
|
853
981
|
coerce_tests! :test_take_and_first_and_last_with_integer_should_use_sql_limit
|
854
982
|
def test_take_and_first_and_last_with_integer_should_use_sql_limit_coerced
|
855
|
-
|
856
|
-
|
857
|
-
|
983
|
+
assert_queries_match(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.* @0 = 3/) { Topic.take(3).entries }
|
984
|
+
assert_queries_match(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.* @0 = 2/) { Topic.first(2).entries }
|
985
|
+
assert_queries_match(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.* @0 = 5/) { Topic.last(5).entries }
|
858
986
|
end
|
859
987
|
|
860
988
|
# This fails only when run in the full test suite task. Just taking it out of the mix.
|
@@ -885,7 +1013,7 @@ class FinderTest < ActiveRecord::TestCase
|
|
885
1013
|
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
886
1014
|
coerce_tests! :test_include_on_unloaded_relation_with_match
|
887
1015
|
def test_include_on_unloaded_relation_with_match_coerced
|
888
|
-
|
1016
|
+
assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do
|
889
1017
|
assert_equal true, Customer.where(name: "David").include?(customers(:david))
|
890
1018
|
end
|
891
1019
|
end
|
@@ -893,7 +1021,7 @@ class FinderTest < ActiveRecord::TestCase
|
|
893
1021
|
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
894
1022
|
coerce_tests! :test_include_on_unloaded_relation_without_match
|
895
1023
|
def test_include_on_unloaded_relation_without_match_coerced
|
896
|
-
|
1024
|
+
assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do
|
897
1025
|
assert_equal false, Customer.where(name: "David").include?(customers(:mary))
|
898
1026
|
end
|
899
1027
|
end
|
@@ -901,7 +1029,7 @@ class FinderTest < ActiveRecord::TestCase
|
|
901
1029
|
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
902
1030
|
coerce_tests! :test_member_on_unloaded_relation_with_match
|
903
1031
|
def test_member_on_unloaded_relation_with_match_coerced
|
904
|
-
|
1032
|
+
assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do
|
905
1033
|
assert_equal true, Customer.where(name: "David").member?(customers(:david))
|
906
1034
|
end
|
907
1035
|
end
|
@@ -909,7 +1037,7 @@ class FinderTest < ActiveRecord::TestCase
|
|
909
1037
|
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
910
1038
|
coerce_tests! :test_member_on_unloaded_relation_without_match
|
911
1039
|
def test_member_on_unloaded_relation_without_match_coerced
|
912
|
-
|
1040
|
+
assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do
|
913
1041
|
assert_equal false, Customer.where(name: "David").member?(customers(:mary))
|
914
1042
|
end
|
915
1043
|
end
|
@@ -923,8 +1051,8 @@ class FinderTest < ActiveRecord::TestCase
|
|
923
1051
|
assert_equal topics(:fifth), Topic.first
|
924
1052
|
assert_equal topics(:third), Topic.last
|
925
1053
|
|
926
|
-
c = Topic.
|
927
|
-
|
1054
|
+
c = Topic.lease_connection
|
1055
|
+
assert_queries_match(/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
1056
|
Topic.last
|
929
1057
|
}
|
930
1058
|
ensure
|
@@ -937,8 +1065,8 @@ class FinderTest < ActiveRecord::TestCase
|
|
937
1065
|
old_implicit_order_column = Topic.implicit_order_column
|
938
1066
|
Topic.implicit_order_column = "id"
|
939
1067
|
|
940
|
-
c = Topic.
|
941
|
-
|
1068
|
+
c = Topic.lease_connection
|
1069
|
+
assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) {
|
942
1070
|
Topic.last
|
943
1071
|
}
|
944
1072
|
ensure
|
@@ -951,15 +1079,101 @@ class FinderTest < ActiveRecord::TestCase
|
|
951
1079
|
old_implicit_order_column = NonPrimaryKey.implicit_order_column
|
952
1080
|
NonPrimaryKey.implicit_order_column = "created_at"
|
953
1081
|
|
954
|
-
c = NonPrimaryKey.
|
1082
|
+
c = NonPrimaryKey.lease_connection
|
955
1083
|
|
956
|
-
|
1084
|
+
assert_queries_match(/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
1085
|
NonPrimaryKey.last
|
958
1086
|
}
|
959
1087
|
ensure
|
960
1088
|
NonPrimaryKey.implicit_order_column = old_implicit_order_column
|
961
1089
|
end
|
962
1090
|
|
1091
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
1092
|
+
coerce_tests! :test_member_on_unloaded_relation_with_composite_primary_key
|
1093
|
+
def test_member_on_unloaded_relation_with_composite_primary_key_coerced
|
1094
|
+
assert_queries_match(/1 AS one.* FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/) do
|
1095
|
+
book = cpk_books(:cpk_great_author_first_book)
|
1096
|
+
assert Cpk::Book.where(title: "The first book").member?(book)
|
1097
|
+
end
|
1098
|
+
end
|
1099
|
+
|
1100
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
1101
|
+
coerce_tests! :test_implicit_order_column_prepends_query_constraints
|
1102
|
+
def test_implicit_order_column_prepends_query_constraints_coerced
|
1103
|
+
c = ClothingItem.lease_connection
|
1104
|
+
ClothingItem.implicit_order_column = "description"
|
1105
|
+
quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type"))
|
1106
|
+
quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color"))
|
1107
|
+
quoted_descrption = Regexp.escape(c.quote_table_name("clothing_items.description"))
|
1108
|
+
|
1109
|
+
assert_queries_match(/ORDER BY #{quoted_descrption} ASC, #{quoted_type} ASC, #{quoted_color} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do
|
1110
|
+
assert_kind_of ClothingItem, ClothingItem.first
|
1111
|
+
end
|
1112
|
+
ensure
|
1113
|
+
ClothingItem.implicit_order_column = nil
|
1114
|
+
end
|
1115
|
+
|
1116
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
1117
|
+
coerce_tests! %r{#last for a model with composite query constraints}
|
1118
|
+
test "#last for a model with composite query constraints coerced" do
|
1119
|
+
c = ClothingItem.lease_connection
|
1120
|
+
quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type"))
|
1121
|
+
quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color"))
|
1122
|
+
|
1123
|
+
assert_queries_match(/ORDER BY #{quoted_type} DESC, #{quoted_color} DESC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do
|
1124
|
+
assert_kind_of ClothingItem, ClothingItem.last
|
1125
|
+
end
|
1126
|
+
end
|
1127
|
+
|
1128
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
1129
|
+
coerce_tests! %r{#first for a model with composite query constraints}
|
1130
|
+
test "#first for a model with composite query constraints coerced" do
|
1131
|
+
c = ClothingItem.lease_connection
|
1132
|
+
quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type"))
|
1133
|
+
quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color"))
|
1134
|
+
|
1135
|
+
assert_queries_match(/ORDER BY #{quoted_type} ASC, #{quoted_color} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do
|
1136
|
+
assert_kind_of ClothingItem, ClothingItem.first
|
1137
|
+
end
|
1138
|
+
end
|
1139
|
+
|
1140
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
1141
|
+
coerce_tests! :test_implicit_order_column_reorders_query_constraints
|
1142
|
+
def test_implicit_order_column_reorders_query_constraints_coerced
|
1143
|
+
c = ClothingItem.lease_connection
|
1144
|
+
ClothingItem.implicit_order_column = "color"
|
1145
|
+
quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type"))
|
1146
|
+
quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color"))
|
1147
|
+
|
1148
|
+
assert_queries_match(/ORDER BY #{quoted_color} ASC, #{quoted_type} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do
|
1149
|
+
assert_kind_of ClothingItem, ClothingItem.first
|
1150
|
+
end
|
1151
|
+
ensure
|
1152
|
+
ClothingItem.implicit_order_column = nil
|
1153
|
+
end
|
1154
|
+
|
1155
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
1156
|
+
coerce_tests! :test_include_on_unloaded_relation_with_composite_primary_key
|
1157
|
+
def test_include_on_unloaded_relation_with_composite_primary_key_coerced
|
1158
|
+
assert_queries_match(/1 AS one.*OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/) do
|
1159
|
+
book = cpk_books(:cpk_great_author_first_book)
|
1160
|
+
assert Cpk::Book.where(title: "The first book").include?(book)
|
1161
|
+
end
|
1162
|
+
end
|
1163
|
+
|
1164
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
1165
|
+
coerce_tests! :test_nth_to_last_with_order_uses_limit
|
1166
|
+
def test_nth_to_last_with_order_uses_limit_coerced
|
1167
|
+
c = Topic.lease_connection
|
1168
|
+
assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET @(\d) ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1.*@\2 = 1/i) do
|
1169
|
+
Topic.second_to_last
|
1170
|
+
end
|
1171
|
+
|
1172
|
+
assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.updated_at"))} DESC OFFSET @(\d) ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1.*@\2 = 1/i) do
|
1173
|
+
Topic.order(:updated_at).second_to_last
|
1174
|
+
end
|
1175
|
+
end
|
1176
|
+
|
963
1177
|
# SQL Server is unable to use aliased SELECT in the HAVING clause.
|
964
1178
|
coerce_tests! :test_include_on_unloaded_relation_with_having_referencing_aliased_select
|
965
1179
|
end
|
@@ -967,7 +1181,7 @@ end
|
|
967
1181
|
module ActiveRecord
|
968
1182
|
class Migration
|
969
1183
|
class ForeignKeyTest < ActiveRecord::TestCase
|
970
|
-
#
|
1184
|
+
# SQL Server does not support 'restrict' for 'on_update' or 'on_delete'.
|
971
1185
|
coerce_tests! :test_add_on_delete_restrict_foreign_key
|
972
1186
|
def test_add_on_delete_restrict_foreign_key_coerced
|
973
1187
|
assert_raises ArgumentError do
|
@@ -977,6 +1191,19 @@ module ActiveRecord
|
|
977
1191
|
@connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", on_update: :restrict
|
978
1192
|
end
|
979
1193
|
end
|
1194
|
+
|
1195
|
+
# Error message depends on the database adapter.
|
1196
|
+
coerce_tests! :test_add_foreign_key_with_if_not_exists_not_set
|
1197
|
+
def test_add_foreign_key_with_if_not_exists_not_set_coerced
|
1198
|
+
@connection.add_foreign_key :astronauts, :rockets
|
1199
|
+
assert_equal 1, @connection.foreign_keys("astronauts").size
|
1200
|
+
|
1201
|
+
error = assert_raises do
|
1202
|
+
@connection.add_foreign_key :astronauts, :rockets
|
1203
|
+
end
|
1204
|
+
|
1205
|
+
assert_match(/TinyTds::Error: There is already an object named '.*' in the database/, error.message)
|
1206
|
+
end
|
980
1207
|
end
|
981
1208
|
end
|
982
1209
|
end
|
@@ -990,7 +1217,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
|
|
990
1217
|
def test_has_one_coerced
|
991
1218
|
firm = companies(:first_firm)
|
992
1219
|
first_account = Account.find(1)
|
993
|
-
|
1220
|
+
assert_queries_match(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do
|
994
1221
|
assert_equal first_account, firm.account
|
995
1222
|
assert_equal first_account.credit_limit, firm.account.credit_limit
|
996
1223
|
end
|
@@ -1002,33 +1229,12 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
|
|
1002
1229
|
coerce_tests! :test_has_one_through_executes_limited_query
|
1003
1230
|
def test_has_one_through_executes_limited_query_coerced
|
1004
1231
|
boring_club = clubs(:boring_club)
|
1005
|
-
|
1232
|
+
assert_queries_match(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do
|
1006
1233
|
assert_equal boring_club, @member.general_club
|
1007
1234
|
end
|
1008
1235
|
end
|
1009
1236
|
end
|
1010
1237
|
|
1011
|
-
require "models/company"
|
1012
|
-
class InheritanceTest < ActiveRecord::TestCase
|
1013
|
-
# Rails test required inserting to a identity column.
|
1014
|
-
coerce_tests! :test_a_bad_type_column
|
1015
|
-
def test_a_bad_type_column_coerced
|
1016
|
-
Company.connection.with_identity_insert_enabled("companies") do
|
1017
|
-
Company.connection.insert "INSERT INTO companies (id, #{QUOTED_TYPE}, name) VALUES(100, 'bad_class!', 'Not happening')"
|
1018
|
-
end
|
1019
|
-
assert_raise(ActiveRecord::SubclassNotFound) { Company.find(100) }
|
1020
|
-
end
|
1021
|
-
|
1022
|
-
# Use Square brackets around column name
|
1023
|
-
coerce_tests! :test_eager_load_belongs_to_primary_key_quoting
|
1024
|
-
def test_eager_load_belongs_to_primary_key_quoting_coerced
|
1025
|
-
Account.connection
|
1026
|
-
assert_sql(/\[companies\]\.\[id\] = @0.* @0 = 1/) do
|
1027
|
-
Account.all.merge!(:includes => :firm).find(1)
|
1028
|
-
end
|
1029
|
-
end
|
1030
|
-
end
|
1031
|
-
|
1032
1238
|
class LeftOuterJoinAssociationTest < ActiveRecord::TestCase
|
1033
1239
|
# Uses || operator in SQL. Just trust core gets value out of this test.
|
1034
1240
|
coerce_tests! :test_does_not_override_select
|
@@ -1073,6 +1279,9 @@ class PersistenceTest < ActiveRecord::TestCase
|
|
1073
1279
|
assert_not_predicate topic, :approved?
|
1074
1280
|
assert_equal "The First Topic", topic.title
|
1075
1281
|
end
|
1282
|
+
|
1283
|
+
# In SQL Server it's not possible to set the primary key column using a trigger and to get it then to return.
|
1284
|
+
coerce_tests! :test_model_with_no_auto_populated_fields_still_returns_primary_key_after_insert
|
1076
1285
|
end
|
1077
1286
|
|
1078
1287
|
require "models/author"
|
@@ -1084,12 +1293,46 @@ class UpdateAllTest < ActiveRecord::TestCase
|
|
1084
1293
|
_(david.id).must_equal 1
|
1085
1294
|
_(mary.id).must_equal 2
|
1086
1295
|
_(david.name).wont_equal mary.name
|
1087
|
-
|
1296
|
+
assert_queries_match(/UPDATE.*\(SELECT \[authors\].\[id\] FROM \[authors\].*ORDER BY \[authors\].\[id\]/i) do
|
1088
1297
|
Author.where("[id] > 1").order(:id).update_all(name: "Test")
|
1089
1298
|
end
|
1090
1299
|
_(david.reload.name).must_equal "David"
|
1091
1300
|
_(mary.reload.name).must_equal "Test"
|
1092
1301
|
end
|
1302
|
+
|
1303
|
+
# SELECT columns must be in the GROUP clause.
|
1304
|
+
coerce_tests! :test_update_all_with_group_by
|
1305
|
+
def test_update_all_with_group_by_coerced
|
1306
|
+
minimum_comments_count = 2
|
1307
|
+
|
1308
|
+
Post.most_commented(minimum_comments_count).update_all(title: "ig")
|
1309
|
+
posts = Post.select(:id, :title).group(:title).most_commented(minimum_comments_count).all.to_a
|
1310
|
+
|
1311
|
+
assert_operator posts.length, :>, 0
|
1312
|
+
assert posts.all? { |post| post.comments.length >= minimum_comments_count }
|
1313
|
+
assert posts.all? { |post| "ig" == post.title }
|
1314
|
+
|
1315
|
+
post = Post.select(:id, :title).group(:title).joins(:comments).group("posts.id").having("count(comments.id) < #{minimum_comments_count}").first
|
1316
|
+
assert_not_equal "ig", post.title
|
1317
|
+
end
|
1318
|
+
end
|
1319
|
+
|
1320
|
+
class DeleteAllTest < ActiveRecord::TestCase
|
1321
|
+
# SELECT columns must be in the GROUP clause.
|
1322
|
+
coerce_tests! :test_delete_all_with_group_by_and_having
|
1323
|
+
def test_delete_all_with_group_by_and_having_coerced
|
1324
|
+
minimum_comments_count = 2
|
1325
|
+
posts_to_be_deleted = Post.select(:id).most_commented(minimum_comments_count).all.to_a
|
1326
|
+
assert_operator posts_to_be_deleted.length, :>, 0
|
1327
|
+
|
1328
|
+
assert_difference("Post.count", -posts_to_be_deleted.length) do
|
1329
|
+
Post.most_commented(minimum_comments_count).delete_all
|
1330
|
+
end
|
1331
|
+
|
1332
|
+
posts_to_be_deleted.each do |deleted_post|
|
1333
|
+
assert_raise(ActiveRecord::RecordNotFound) { deleted_post.reload }
|
1334
|
+
end
|
1335
|
+
end
|
1093
1336
|
end
|
1094
1337
|
|
1095
1338
|
require "models/topic"
|
@@ -1109,65 +1352,27 @@ module ActiveRecord
|
|
1109
1352
|
end
|
1110
1353
|
end
|
1111
1354
|
|
1112
|
-
class PrimaryKeysTest < ActiveRecord::TestCase
|
1113
|
-
# SQL Server does not have query for release_savepoint
|
1114
|
-
coerce_tests! :test_create_without_primary_key_no_extra_query
|
1115
|
-
def test_create_without_primary_key_no_extra_query_coerced
|
1116
|
-
klass = Class.new(ActiveRecord::Base) do
|
1117
|
-
self.table_name = "dashboards"
|
1118
|
-
end
|
1119
|
-
klass.create! # warmup schema cache
|
1120
|
-
assert_queries(2, ignore_none: true) { klass.create! }
|
1121
|
-
end
|
1122
|
-
end
|
1123
|
-
|
1124
1355
|
require "models/task"
|
1125
1356
|
class QueryCacheTest < ActiveRecord::TestCase
|
1126
1357
|
# SQL Server adapter not in list of supported adapters in original test.
|
1127
1358
|
coerce_tests! :test_cache_does_not_wrap_results_in_arrays
|
1128
1359
|
def test_cache_does_not_wrap_results_in_arrays_coerced
|
1129
1360
|
Task.cache do
|
1130
|
-
assert_equal 2, Task.
|
1131
|
-
end
|
1132
|
-
end
|
1133
|
-
|
1134
|
-
# Same as original test except that we expect one query to be performed to retrieve the table's primary key.
|
1135
|
-
# When we generate the SQL for the `find` it includes ordering on the primary key. If we reset the column
|
1136
|
-
# information then the primary key needs to be retrieved from the database again to generate the SQL causing the
|
1137
|
-
# original test's `assert_no_queries` assertion to fail. Assert that the query was to get the primary key.
|
1138
|
-
coerce_tests! :test_query_cached_even_when_types_are_reset
|
1139
|
-
def test_query_cached_even_when_types_are_reset_coerced
|
1140
|
-
Task.cache do
|
1141
|
-
# Warm the cache
|
1142
|
-
Task.find(1)
|
1143
|
-
|
1144
|
-
# Preload the type cache again (so we don't have those queries issued during our assertions)
|
1145
|
-
Task.connection.send(:reload_type_map)
|
1146
|
-
|
1147
|
-
# Clear places where type information is cached
|
1148
|
-
Task.reset_column_information
|
1149
|
-
Task.initialize_find_by_cache
|
1150
|
-
Task.define_attribute_methods
|
1151
|
-
|
1152
|
-
assert_queries(1, ignore_none: true) do
|
1153
|
-
Task.find(1)
|
1154
|
-
end
|
1155
|
-
|
1156
|
-
assert_includes ActiveRecord::SQLCounter.log_all.first, "TC.CONSTRAINT_TYPE = N''PRIMARY KEY''"
|
1361
|
+
assert_equal 2, Task.lease_connection.select_value("SELECT count(*) AS count_all FROM tasks")
|
1157
1362
|
end
|
1158
1363
|
end
|
1159
1364
|
end
|
1160
1365
|
|
1161
1366
|
require "models/post"
|
1162
1367
|
class RelationTest < ActiveRecord::TestCase
|
1163
|
-
# Use LEN
|
1368
|
+
# Use LEN() instead of LENGTH() function.
|
1164
1369
|
coerce_tests! :test_reverse_order_with_function
|
1165
1370
|
def test_reverse_order_with_function_coerced
|
1166
1371
|
topics = Topic.order(Arel.sql("LEN(title)")).reverse_order
|
1167
1372
|
assert_equal topics(:second).title, topics.first.title
|
1168
1373
|
end
|
1169
1374
|
|
1170
|
-
# Use LEN
|
1375
|
+
# Use LEN() instead of LENGTH() function.
|
1171
1376
|
coerce_tests! :test_reverse_order_with_function_other_predicates
|
1172
1377
|
def test_reverse_order_with_function_other_predicates_coerced
|
1173
1378
|
topics = Topic.order(Arel.sql("author_name, LEN(title), id")).reverse_order
|
@@ -1185,24 +1390,19 @@ class RelationTest < ActiveRecord::TestCase
|
|
1185
1390
|
sql_log = capture_sql do
|
1186
1391
|
assert Post.order(:title).reorder(nil).take
|
1187
1392
|
end
|
1188
|
-
assert sql_log.none? { |sql| /order by [posts]
|
1393
|
+
assert sql_log.none? { |sql| /order by \[posts\]\.\[title\]/i.match?(sql) }, "ORDER BY title was used in the query: #{sql_log}"
|
1189
1394
|
assert sql_log.all? { |sql| /order by \[posts\]\.\[id\]/i.match?(sql) }, "default ORDER BY ID was not used in the query: #{sql_log}"
|
1190
1395
|
end
|
1191
1396
|
|
1192
1397
|
# We have implicit ordering, via FETCH.
|
1193
1398
|
coerce_tests! :test_reorder_with_first
|
1194
1399
|
def test_reorder_with_first_coerced
|
1400
|
+
post = nil
|
1195
1401
|
sql_log = capture_sql do
|
1196
|
-
|
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
|
1402
|
+
post = Post.order(:title).reorder(nil).first
|
1204
1403
|
end
|
1205
|
-
|
1404
|
+
assert_equal posts(:welcome), post
|
1405
|
+
assert sql_log.none? { |sql| /order by \[posts\]\.\[title\]/i.match?(sql) }, "ORDER BY title was used in the query: #{sql_log}"
|
1206
1406
|
assert sql_log.all? { |sql| /order by \[posts\]\.\[id\]/i.match?(sql) }, "default ORDER BY ID was not used in the query: #{sql_log}"
|
1207
1407
|
end
|
1208
1408
|
|
@@ -1212,22 +1412,55 @@ class RelationTest < ActiveRecord::TestCase
|
|
1212
1412
|
# We are not doing order duplicate removal anymore.
|
1213
1413
|
coerce_tests! :test_default_scope_order_with_scope_order
|
1214
1414
|
|
1215
|
-
#
|
1415
|
+
# Order column must be in the GROUP clause.
|
1216
1416
|
coerce_tests! :test_multiple_where_and_having_clauses
|
1417
|
+
def test_multiple_where_and_having_clauses_coerced
|
1418
|
+
post = Post.first
|
1419
|
+
having_then_where = Post.having(id: post.id).where(title: post.title)
|
1420
|
+
.having(id: post.id).where(title: post.title).group(:id).select(:id)
|
1421
|
+
|
1422
|
+
assert_equal [post], having_then_where
|
1423
|
+
end
|
1424
|
+
|
1425
|
+
# Order column must be in the GROUP clause.
|
1217
1426
|
coerce_tests! :test_having_with_binds_for_both_where_and_having
|
1427
|
+
def test_having_with_binds_for_both_where_and_having
|
1428
|
+
post = Post.first
|
1429
|
+
having_then_where = Post.having(id: post.id).where(title: post.title).group(:id).select(:id)
|
1430
|
+
where_then_having = Post.where(title: post.title).having(id: post.id).group(:id).select(:id)
|
1431
|
+
|
1432
|
+
assert_equal [post], having_then_where
|
1433
|
+
assert_equal [post], where_then_having
|
1434
|
+
end
|
1218
1435
|
|
1219
1436
|
# Find any limit via our expression.
|
1220
1437
|
coerce_tests! %r{relations don't load all records in #inspect}
|
1221
1438
|
def test_relations_dont_load_all_records_in_inspect_coerced
|
1222
|
-
|
1439
|
+
assert_queries_match(/NEXT @0 ROWS.*@0 = \d+/) do
|
1223
1440
|
Post.all.inspect
|
1224
1441
|
end
|
1225
1442
|
end
|
1226
1443
|
|
1227
|
-
#
|
1228
|
-
|
1229
|
-
|
1444
|
+
# Find any limit via our expression.
|
1445
|
+
coerce_tests! %r{relations don't load all records in #pretty_print}
|
1446
|
+
def test_relations_dont_load_all_records_in_pretty_print_coerced
|
1447
|
+
assert_queries_match(/FETCH NEXT @(\d) ROWS ONLY/) do
|
1448
|
+
PP.pp Post.all, StringIO.new # avoid outputting.
|
1449
|
+
end
|
1450
|
+
end
|
1451
|
+
|
1452
|
+
# Order column must be in the GROUP clause.
|
1230
1453
|
coerce_tests! :test_empty_complex_chained_relations
|
1454
|
+
def test_empty_complex_chained_relations_coerced
|
1455
|
+
posts = Post.select("comments_count").where("id is not null").group("author_id", "id").where("legacy_comments_count > 0")
|
1456
|
+
|
1457
|
+
assert_queries_count(1) { assert_equal false, posts.empty? }
|
1458
|
+
assert_not_predicate posts, :loaded?
|
1459
|
+
|
1460
|
+
no_posts = posts.where(title: "")
|
1461
|
+
assert_queries_count(1) { assert_equal true, no_posts.empty? }
|
1462
|
+
assert_not_predicate no_posts, :loaded?
|
1463
|
+
end
|
1231
1464
|
|
1232
1465
|
# Can't apply offset without ORDER
|
1233
1466
|
coerce_tests! %r{using a custom table affects the wheres}
|
@@ -1245,7 +1478,7 @@ class RelationTest < ActiveRecord::TestCase
|
|
1245
1478
|
assert_equal post, custom_post_relation.joins(:author).where!(title: post.title).order(:id).take
|
1246
1479
|
end
|
1247
1480
|
|
1248
|
-
# Use LEN()
|
1481
|
+
# Use LEN() instead of LENGTH() function.
|
1249
1482
|
coerce_tests! :test_reverse_arel_assoc_order_with_function
|
1250
1483
|
def test_reverse_arel_assoc_order_with_function_coerced
|
1251
1484
|
topics = Topic.order(Arel.sql("LEN(title)") => :asc).reverse_order
|
@@ -1260,21 +1493,11 @@ module ActiveRecord
|
|
1260
1493
|
|
1261
1494
|
coerce_tests! :test_does_not_duplicate_optimizer_hints_on_merge
|
1262
1495
|
def test_does_not_duplicate_optimizer_hints_on_merge_coerced
|
1263
|
-
escaped_table = Post.
|
1496
|
+
escaped_table = Post.lease_connection.quote_table_name("posts")
|
1264
1497
|
expected = "SELECT #{escaped_table}.* FROM #{escaped_table} OPTION (OMGHINT)"
|
1265
1498
|
query = Post.optimizer_hints("OMGHINT").merge(Post.optimizer_hints("OMGHINT")).to_sql
|
1266
1499
|
assert_equal expected, query
|
1267
1500
|
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
1501
|
end
|
1279
1502
|
end
|
1280
1503
|
|
@@ -1293,17 +1516,44 @@ class SanitizeTest < ActiveRecord::TestCase
|
|
1293
1516
|
}
|
1294
1517
|
end
|
1295
1518
|
|
1296
|
-
|
1519
|
+
assert_queries_match(/LIKE @0/) do
|
1297
1520
|
searchable_post.search_as_method("20% _reduction_!").to_a
|
1298
1521
|
end
|
1299
1522
|
|
1300
|
-
|
1523
|
+
assert_queries_match(/LIKE @0/) do
|
1301
1524
|
searchable_post.search_as_scope("20% _reduction_!").to_a
|
1302
1525
|
end
|
1303
1526
|
end
|
1527
|
+
|
1528
|
+
# Use nvarchar string (N'') in assert
|
1529
|
+
coerce_tests! :test_named_bind_with_literal_colons
|
1530
|
+
def test_named_bind_with_literal_colons_coerced
|
1531
|
+
assert_equal "TO_TIMESTAMP(N'2017/08/02 10:59:00', 'YYYY/MM/DD HH12:MI:SS')", bind("TO_TIMESTAMP(:date, 'YYYY/MM/DD HH12\\:MI\\:SS')", date: "2017/08/02 10:59:00")
|
1532
|
+
assert_raise(ActiveRecord::PreparedStatementInvalid) { bind "TO_TIMESTAMP(:date, 'YYYY/MM/DD HH12:MI:SS')", date: "2017/08/02 10:59:00" }
|
1533
|
+
end
|
1304
1534
|
end
|
1305
1535
|
|
1306
1536
|
class SchemaDumperTest < ActiveRecord::TestCase
|
1537
|
+
# Use nvarchar string (N'') in assert
|
1538
|
+
coerce_tests! :test_dump_schema_information_outputs_lexically_reverse_ordered_versions_regardless_of_database_order
|
1539
|
+
def test_dump_schema_information_outputs_lexically_reverse_ordered_versions_regardless_of_database_order_coerced
|
1540
|
+
versions = %w{ 20100101010101 20100201010101 20100301010101 }
|
1541
|
+
versions.shuffle.each do |v|
|
1542
|
+
@schema_migration.create_version(v)
|
1543
|
+
end
|
1544
|
+
|
1545
|
+
schema_info = ActiveRecord::Base.lease_connection.dump_schema_information
|
1546
|
+
expected = <<~STR
|
1547
|
+
INSERT INTO #{ActiveRecord::Base.lease_connection.quote_table_name("schema_migrations")} (version) VALUES
|
1548
|
+
(N'20100301010101'),
|
1549
|
+
(N'20100201010101'),
|
1550
|
+
(N'20100101010101');
|
1551
|
+
STR
|
1552
|
+
assert_equal expected.strip, schema_info
|
1553
|
+
ensure
|
1554
|
+
@schema_migration.delete_all_versions
|
1555
|
+
end
|
1556
|
+
|
1307
1557
|
# We have precision to 38.
|
1308
1558
|
coerce_tests! :test_schema_dump_keeps_large_precision_integer_columns_as_decimal
|
1309
1559
|
def test_schema_dump_keeps_large_precision_integer_columns_as_decimal_coerced
|
@@ -1311,7 +1561,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
|
|
1311
1561
|
assert_match %r{t.decimal\s+"atoms_in_universe",\s+precision: 38}, output
|
1312
1562
|
end
|
1313
1563
|
|
1314
|
-
# This is a poorly written test and really does not catch the bottom'ness it is meant
|
1564
|
+
# This is a poorly written test and really does not catch the bottom'ness it is meant to. Ours throw it off.
|
1315
1565
|
coerce_tests! :test_foreign_keys_are_dumped_at_the_bottom_to_circumvent_dependency_issues
|
1316
1566
|
|
1317
1567
|
# Fall through false positive with no filter.
|
@@ -1327,11 +1577,50 @@ class SchemaDumperTest < ActiveRecord::TestCase
|
|
1327
1577
|
output = dump_all_table_schema([/^[^n]/])
|
1328
1578
|
assert_match %r{precision: 3,[[:space:]]+scale: 2,[[:space:]]+default: 2\.78}, output
|
1329
1579
|
end
|
1580
|
+
|
1581
|
+
# Tests are not about a specific adapter.
|
1582
|
+
coerce_tests! :test_do_not_dump_foreign_keys_when_bypassed_by_config
|
1583
|
+
|
1584
|
+
# SQL Server formats the check constraint expression differently.
|
1585
|
+
coerce_tests! :test_schema_dumps_check_constraints
|
1586
|
+
def test_schema_dumps_check_constraints_coerced
|
1587
|
+
constraint_definition = dump_table_schema("products").split(/\n/).grep(/t.check_constraint.*products_price_check/).first.strip
|
1588
|
+
assert_equal 't.check_constraint "[price]>[discounted_price]", name: "products_price_check"', constraint_definition
|
1589
|
+
end
|
1330
1590
|
end
|
1331
1591
|
|
1332
1592
|
class SchemaDumperDefaultsTest < ActiveRecord::TestCase
|
1333
1593
|
# These date formats do not match ours. We got these covered in our dumper tests.
|
1334
1594
|
coerce_tests! :test_schema_dump_defaults_with_universally_supported_types
|
1595
|
+
|
1596
|
+
# SQL Server uses different method to generate a UUID than Rails test uses. Reimplemented the
|
1597
|
+
# test in 'SchemaDumperDefaultsCoerceTest'.
|
1598
|
+
coerce_tests! :test_schema_dump_with_text_column
|
1599
|
+
end
|
1600
|
+
|
1601
|
+
class SchemaDumperDefaultsCoerceTest < ActiveRecord::TestCase
|
1602
|
+
include SchemaDumpingHelper
|
1603
|
+
|
1604
|
+
setup do
|
1605
|
+
@connection = ActiveRecord::Base.lease_connection
|
1606
|
+
@connection.create_table :dump_defaults, force: true do |t|
|
1607
|
+
t.string :string_with_default, default: "Hello!"
|
1608
|
+
t.date :date_with_default, default: "2014-06-05"
|
1609
|
+
t.datetime :datetime_with_default, default: "2014-06-05 07:17:04"
|
1610
|
+
t.time :time_with_default, default: "07:17:04"
|
1611
|
+
t.decimal :decimal_with_default, default: "1234567890.0123456789", precision: 20, scale: 10
|
1612
|
+
|
1613
|
+
t.text :text_with_default, default: "John' Doe"
|
1614
|
+
t.text :uuid, default: -> { "newid()" }
|
1615
|
+
end
|
1616
|
+
end
|
1617
|
+
|
1618
|
+
def test_schema_dump_with_text_column_coerced
|
1619
|
+
output = dump_table_schema("dump_defaults")
|
1620
|
+
|
1621
|
+
assert_match %r{t\.text\s+"text_with_default",.*?default: "John' Doe"}, output
|
1622
|
+
assert_match %r{t\.text\s+"uuid",.*?default: -> \{ "newid\(\)" \}}, output
|
1623
|
+
end
|
1335
1624
|
end
|
1336
1625
|
|
1337
1626
|
class TestAdapterWithInvalidConnection < ActiveRecord::TestCase
|
@@ -1341,14 +1630,92 @@ end
|
|
1341
1630
|
|
1342
1631
|
require "models/topic"
|
1343
1632
|
class TransactionTest < ActiveRecord::TestCase
|
1344
|
-
# SQL Server does not have query for release_savepoint
|
1633
|
+
# SQL Server does not have query for release_savepoint.
|
1345
1634
|
coerce_tests! :test_releasing_named_savepoints
|
1346
1635
|
def test_releasing_named_savepoints_coerced
|
1347
1636
|
Topic.transaction do
|
1348
|
-
Topic.
|
1349
|
-
|
1350
|
-
|
1351
|
-
Topic.
|
1637
|
+
Topic.lease_connection.materialize_transactions
|
1638
|
+
|
1639
|
+
Topic.lease_connection.create_savepoint("another")
|
1640
|
+
Topic.lease_connection.release_savepoint("another")
|
1641
|
+
|
1642
|
+
# We do not have a notion of releasing, so this does nothing and doesn't raise an error.
|
1643
|
+
assert_nothing_raised do
|
1644
|
+
Topic.lease_connection.release_savepoint("another")
|
1645
|
+
end
|
1646
|
+
end
|
1647
|
+
end
|
1648
|
+
|
1649
|
+
# SQL Server does not have query for release_savepoint.
|
1650
|
+
coerce_tests! :test_nested_transactions_after_disable_lazy_transactions
|
1651
|
+
def test_nested_transactions_after_disable_lazy_transactions_coerced
|
1652
|
+
Topic.lease_connection.disable_lazy_transactions!
|
1653
|
+
|
1654
|
+
actual_queries = capture_sql(include_schema: true) do
|
1655
|
+
# RealTransaction (begin..commit)
|
1656
|
+
Topic.transaction(requires_new: true) do
|
1657
|
+
# ResetParentTransaction (no queries)
|
1658
|
+
Topic.transaction(requires_new: true) do
|
1659
|
+
Topic.delete_all
|
1660
|
+
# SavepointTransaction (savepoint..release)
|
1661
|
+
Topic.transaction(requires_new: true) do
|
1662
|
+
# ResetParentTransaction (no queries)
|
1663
|
+
Topic.transaction(requires_new: true) do
|
1664
|
+
# no-op
|
1665
|
+
end
|
1666
|
+
end
|
1667
|
+
end
|
1668
|
+
Topic.delete_all
|
1669
|
+
end
|
1670
|
+
end
|
1671
|
+
|
1672
|
+
expected_queries = [
|
1673
|
+
/BEGIN/i,
|
1674
|
+
/DELETE/i,
|
1675
|
+
/^SAVE TRANSACTION/i,
|
1676
|
+
/DELETE/i,
|
1677
|
+
/COMMIT/i,
|
1678
|
+
]
|
1679
|
+
|
1680
|
+
assert_equal expected_queries.size, actual_queries.size
|
1681
|
+
expected_queries.zip(actual_queries) do |expected, actual|
|
1682
|
+
assert_match expected, actual
|
1683
|
+
end
|
1684
|
+
end
|
1685
|
+
|
1686
|
+
# SQL Server does not have query for release_savepoint.
|
1687
|
+
coerce_tests! :test_nested_transactions_skip_excess_savepoints
|
1688
|
+
def test_nested_transactions_skip_excess_savepoints_coerced
|
1689
|
+
actual_queries = capture_sql(include_schema: true) do
|
1690
|
+
# RealTransaction (begin..commit)
|
1691
|
+
Topic.transaction(requires_new: true) do
|
1692
|
+
# ResetParentTransaction (no queries)
|
1693
|
+
Topic.transaction(requires_new: true) do
|
1694
|
+
Topic.delete_all
|
1695
|
+
# SavepointTransaction (savepoint..release)
|
1696
|
+
Topic.transaction(requires_new: true) do
|
1697
|
+
# ResetParentTransaction (no queries)
|
1698
|
+
Topic.transaction(requires_new: true) do
|
1699
|
+
Topic.delete_all
|
1700
|
+
end
|
1701
|
+
end
|
1702
|
+
end
|
1703
|
+
Topic.delete_all
|
1704
|
+
end
|
1705
|
+
end
|
1706
|
+
|
1707
|
+
expected_queries = [
|
1708
|
+
/BEGIN/i,
|
1709
|
+
/DELETE/i,
|
1710
|
+
/^SAVE TRANSACTION/i,
|
1711
|
+
/DELETE/i,
|
1712
|
+
/DELETE/i,
|
1713
|
+
/COMMIT/i,
|
1714
|
+
]
|
1715
|
+
|
1716
|
+
assert_equal expected_queries.size, actual_queries.size
|
1717
|
+
expected_queries.zip(actual_queries) do |expected, actual|
|
1718
|
+
assert_match expected, actual
|
1352
1719
|
end
|
1353
1720
|
end
|
1354
1721
|
end
|
@@ -1402,7 +1769,8 @@ class YamlSerializationTest < ActiveRecord::TestCase
|
|
1402
1769
|
coerce_tests! :test_types_of_virtual_columns_are_not_changed_on_round_trip
|
1403
1770
|
def test_types_of_virtual_columns_are_not_changed_on_round_trip_coerced
|
1404
1771
|
author = Author.select("authors.*, 5 as posts_count").first
|
1405
|
-
|
1772
|
+
dumped_author = YAML.dump(author)
|
1773
|
+
dumped = YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(dumped_author) : YAML.load(dumped_author)
|
1406
1774
|
assert_equal 5, author.posts_count
|
1407
1775
|
assert_equal 5, dumped.posts_count
|
1408
1776
|
end
|
@@ -1463,6 +1831,9 @@ class TimePrecisionTest < ActiveRecord::TestCase
|
|
1463
1831
|
|
1464
1832
|
# SQL Server uses default precision for time.
|
1465
1833
|
coerce_tests! :test_no_time_precision_isnt_truncated_on_assignment
|
1834
|
+
|
1835
|
+
# SQL Server accepts precision of 7 for time.
|
1836
|
+
coerce_tests! :test_invalid_time_precision_raises_error
|
1466
1837
|
end
|
1467
1838
|
|
1468
1839
|
class DefaultNumbersTest < ActiveRecord::TestCase
|
@@ -1478,8 +1849,8 @@ class DefaultNumbersTest < ActiveRecord::TestCase
|
|
1478
1849
|
coerce_tests! :test_default_negative_integer
|
1479
1850
|
def test_default_negative_integer_coerced
|
1480
1851
|
record = DefaultNumber.new
|
1481
|
-
assert_equal -5, record.negative_integer
|
1482
|
-
assert_equal -5, record.negative_integer_before_type_cast
|
1852
|
+
assert_equal (-5), record.negative_integer
|
1853
|
+
assert_equal (-5), record.negative_integer_before_type_cast
|
1483
1854
|
end
|
1484
1855
|
|
1485
1856
|
# We do better with native types and do not return strings for everything.
|
@@ -1517,12 +1888,12 @@ module ActiveRecord
|
|
1517
1888
|
# Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
|
1518
1889
|
coerce_tests! :test_statement_cache_values_differ
|
1519
1890
|
def test_statement_cache_values_differ_coerced
|
1520
|
-
Book.
|
1891
|
+
Book.lease_connection.remove_index(:books, column: [:author_id, :name])
|
1521
1892
|
|
1522
1893
|
original_test_statement_cache_values_differ
|
1523
1894
|
ensure
|
1524
1895
|
Book.where(author_id: nil, name: 'my book').delete_all
|
1525
|
-
Book.
|
1896
|
+
Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
|
1526
1897
|
end
|
1527
1898
|
end
|
1528
1899
|
end
|
@@ -1568,8 +1939,9 @@ module ActiveRecord
|
|
1568
1939
|
end
|
1569
1940
|
|
1570
1941
|
# We need to give the full path for this to work.
|
1942
|
+
undef_method :schema_dump_path
|
1571
1943
|
def schema_dump_path
|
1572
|
-
File.join
|
1944
|
+
File.join(ARTest::SQLServer.root_activerecord, "test/assets/schema_dump_5_1.yml")
|
1573
1945
|
end
|
1574
1946
|
end
|
1575
1947
|
end
|
@@ -1580,7 +1952,7 @@ require "models/comment"
|
|
1580
1952
|
class UnsafeRawSqlTest < ActiveRecord::TestCase
|
1581
1953
|
fixtures :posts
|
1582
1954
|
|
1583
|
-
# Use LEN()
|
1955
|
+
# Use LEN() instead of LENGTH() function.
|
1584
1956
|
coerce_tests! %r{order: always allows Arel}
|
1585
1957
|
test "order: always allows Arel" do
|
1586
1958
|
titles = Post.order(Arel.sql("len(title)")).pluck(:title)
|
@@ -1588,7 +1960,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
|
|
1588
1960
|
assert_not_empty titles
|
1589
1961
|
end
|
1590
1962
|
|
1591
|
-
# Use LEN()
|
1963
|
+
# Use LEN() instead of LENGTH() function.
|
1592
1964
|
coerce_tests! %r{pluck: always allows Arel}
|
1593
1965
|
test "pluck: always allows Arel" do
|
1594
1966
|
excepted_values = Post.includes(:comments).pluck(:title).map { |title| [title, title.size] }
|
@@ -1597,7 +1969,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
|
|
1597
1969
|
assert_equal excepted_values, values
|
1598
1970
|
end
|
1599
1971
|
|
1600
|
-
# Use LEN()
|
1972
|
+
# Use LEN() instead of LENGTH() function.
|
1601
1973
|
coerce_tests! %r{order: allows valid Array arguments}
|
1602
1974
|
test "order: allows valid Array arguments" do
|
1603
1975
|
ids_expected = Post.order(Arel.sql("author_id, len(title)")).pluck(:id)
|
@@ -1607,6 +1979,27 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
|
|
1607
1979
|
assert_equal ids_expected, ids
|
1608
1980
|
end
|
1609
1981
|
|
1982
|
+
# Use LEN() instead of LENGTH() function.
|
1983
|
+
coerce_tests! %r{order: allows nested functions}
|
1984
|
+
test "order: allows nested functions" do
|
1985
|
+
ids_expected = Post.order(Arel.sql("author_id, len(trim(title))")).pluck(:id)
|
1986
|
+
|
1987
|
+
# $DEBUG = true
|
1988
|
+
ids = Post.order("author_id, len(trim(title))").pluck(:id)
|
1989
|
+
|
1990
|
+
assert_equal ids_expected, ids
|
1991
|
+
end
|
1992
|
+
|
1993
|
+
# Use LEN() instead of LENGTH() function.
|
1994
|
+
coerce_tests! %r{pluck: allows nested functions}
|
1995
|
+
test "pluck: allows nested functions" do
|
1996
|
+
title_lengths_expected = Post.pluck(Arel.sql("len(trim(title))"))
|
1997
|
+
|
1998
|
+
title_lengths = Post.pluck("len(trim(title))")
|
1999
|
+
|
2000
|
+
assert_equal title_lengths_expected, title_lengths
|
2001
|
+
end
|
2002
|
+
|
1610
2003
|
test "order: allows string column names that are quoted" do
|
1611
2004
|
ids_expected = Post.order(Arel.sql("id")).pluck(:id)
|
1612
2005
|
|
@@ -1670,6 +2063,18 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
|
|
1670
2063
|
|
1671
2064
|
assert_equal titles_expected, titles
|
1672
2065
|
end
|
2066
|
+
|
2067
|
+
# Collation name should not be quoted. Hardcoded values for different adapters.
|
2068
|
+
coerce_tests! %r{order: allows valid arguments with COLLATE}
|
2069
|
+
test "order: allows valid arguments with COLLATE" do
|
2070
|
+
collation_name = "Latin1_General_CS_AS_WS"
|
2071
|
+
|
2072
|
+
ids_expected = Post.order(Arel.sql(%Q'author_id, title COLLATE #{collation_name} DESC')).pluck(:id)
|
2073
|
+
|
2074
|
+
ids = Post.order(["author_id", %Q'title COLLATE #{collation_name} DESC']).pluck(:id)
|
2075
|
+
|
2076
|
+
assert_equal ids_expected, ids
|
2077
|
+
end
|
1673
2078
|
end
|
1674
2079
|
|
1675
2080
|
class ReservedWordTest < ActiveRecord::TestCase
|
@@ -1718,14 +2123,14 @@ class RelationMergingTest < ActiveRecord::TestCase
|
|
1718
2123
|
|
1719
2124
|
non_mary_and_bob = Author.where.not(id: [mary, bob])
|
1720
2125
|
|
1721
|
-
author_id = Author.
|
1722
|
-
|
2126
|
+
author_id = Author.lease_connection.quote_table_name("authors.id")
|
2127
|
+
assert_queries_match(/WHERE #{Regexp.escape(author_id)} NOT IN \((@\d), \g<1>\)'/) do
|
1723
2128
|
assert_equal [david], non_mary_and_bob.merge(non_mary_and_bob)
|
1724
2129
|
end
|
1725
2130
|
|
1726
2131
|
only_david = Author.where("#{author_id} IN (?)", david)
|
1727
2132
|
|
1728
|
-
|
2133
|
+
assert_queries_match(/WHERE \(#{Regexp.escape(author_id)} IN \(@\d\)\)/) do
|
1729
2134
|
assert_equal [david], only_david.merge(only_david)
|
1730
2135
|
end
|
1731
2136
|
end
|
@@ -1745,45 +2150,56 @@ class EnumTest < ActiveRecord::TestCase
|
|
1745
2150
|
# Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
|
1746
2151
|
coerce_tests! %r{enums are distinct per class}
|
1747
2152
|
test "enums are distinct per class coerced" do
|
1748
|
-
Book.
|
2153
|
+
Book.lease_connection.remove_index(:books, column: [:author_id, :name])
|
1749
2154
|
|
1750
2155
|
send(:'original_enums are distinct per class')
|
1751
2156
|
ensure
|
1752
2157
|
Book.where(author_id: nil, name: nil).delete_all
|
1753
|
-
Book.
|
2158
|
+
Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
|
1754
2159
|
end
|
1755
2160
|
|
1756
2161
|
# Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
|
1757
2162
|
coerce_tests! %r{creating new objects with enum scopes}
|
1758
2163
|
test "creating new objects with enum scopes coerced" do
|
1759
|
-
Book.
|
2164
|
+
Book.lease_connection.remove_index(:books, column: [:author_id, :name])
|
1760
2165
|
|
1761
2166
|
send(:'original_creating new objects with enum scopes')
|
1762
2167
|
ensure
|
1763
2168
|
Book.where(author_id: nil, name: nil).delete_all
|
1764
|
-
Book.
|
2169
|
+
Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
|
1765
2170
|
end
|
1766
2171
|
|
1767
2172
|
# Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
|
1768
2173
|
coerce_tests! %r{enums are inheritable}
|
1769
2174
|
test "enums are inheritable coerced" do
|
1770
|
-
Book.
|
2175
|
+
Book.lease_connection.remove_index(:books, column: [:author_id, :name])
|
1771
2176
|
|
1772
2177
|
send(:'original_enums are inheritable')
|
1773
2178
|
ensure
|
1774
2179
|
Book.where(author_id: nil, name: nil).delete_all
|
1775
|
-
Book.
|
2180
|
+
Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
|
1776
2181
|
end
|
1777
2182
|
|
1778
2183
|
# Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
|
1779
2184
|
coerce_tests! %r{declare multiple enums at a time}
|
1780
2185
|
test "declare multiple enums at a time coerced" do
|
1781
|
-
Book.
|
2186
|
+
Book.lease_connection.remove_index(:books, column: [:author_id, :name])
|
1782
2187
|
|
1783
2188
|
send(:'original_declare multiple enums at a time')
|
1784
2189
|
ensure
|
1785
2190
|
Book.where(author_id: nil, name: nil).delete_all
|
1786
|
-
Book.
|
2191
|
+
Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
|
2192
|
+
end
|
2193
|
+
|
2194
|
+
# Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
|
2195
|
+
coerce_tests! %r{serializable\? with large number label}
|
2196
|
+
test "serializable? with large number label coerced" do
|
2197
|
+
Book.lease_connection.remove_index(:books, column: [:author_id, :name])
|
2198
|
+
|
2199
|
+
send(:'original_serializable\? with large number label')
|
2200
|
+
ensure
|
2201
|
+
Book.where(author_id: nil, name: nil).delete_all
|
2202
|
+
Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
|
1787
2203
|
end
|
1788
2204
|
end
|
1789
2205
|
|
@@ -1796,18 +2212,6 @@ class QueryCacheExpiryTest < ActiveRecord::TestCase
|
|
1796
2212
|
Task.cache { Task.insert({ starting: Time.now }) }
|
1797
2213
|
end
|
1798
2214
|
|
1799
|
-
assert_called(ActiveRecord::Base.connection, :clear_query_cache, times: 2) do
|
1800
|
-
Task.cache { Task.insert_all!([{ starting: Time.now }]) }
|
1801
|
-
end
|
1802
|
-
|
1803
|
-
assert_called(ActiveRecord::Base.connection, :clear_query_cache, times: 2) do
|
1804
|
-
Task.cache { Task.insert!({ starting: Time.now }) }
|
1805
|
-
end
|
1806
|
-
|
1807
|
-
assert_called(ActiveRecord::Base.connection, :clear_query_cache, times: 2) do
|
1808
|
-
Task.cache { Task.insert_all!([{ starting: Time.now }]) }
|
1809
|
-
end
|
1810
|
-
|
1811
2215
|
assert_raises(ArgumentError, /does not support upsert/) do
|
1812
2216
|
Task.cache { Task.upsert({ starting: Time.now }) }
|
1813
2217
|
end
|
@@ -1815,6 +2219,16 @@ class QueryCacheExpiryTest < ActiveRecord::TestCase
|
|
1815
2219
|
assert_raises(ArgumentError, /does not support upsert/) do
|
1816
2220
|
Task.cache { Task.upsert_all([{ starting: Time.now }]) }
|
1817
2221
|
end
|
2222
|
+
|
2223
|
+
Task.cache do
|
2224
|
+
assert_called(ActiveRecord::Base.connection_pool.query_cache, :clear, times: 1) do
|
2225
|
+
Task.insert_all!([ starting: Time.now ])
|
2226
|
+
end
|
2227
|
+
|
2228
|
+
assert_called(ActiveRecord::Base.connection_pool.query_cache, :clear, times: 1) do
|
2229
|
+
Task.insert!({ starting: Time.now })
|
2230
|
+
end
|
2231
|
+
end
|
1818
2232
|
end
|
1819
2233
|
end
|
1820
2234
|
|
@@ -1836,7 +2250,7 @@ class EagerLoadingTooManyIdsTest < ActiveRecord::TestCase
|
|
1836
2250
|
|
1837
2251
|
# Perform test
|
1838
2252
|
citation_count = Citation.count
|
1839
|
-
|
2253
|
+
assert_queries_match(/WHERE \[citations\]\.\[id\] IN \(0, 1/) do
|
1840
2254
|
assert_equal citation_count, Citation.eager_load(:citations).offset(0).size
|
1841
2255
|
end
|
1842
2256
|
end
|
@@ -1844,9 +2258,9 @@ end
|
|
1844
2258
|
|
1845
2259
|
class LogSubscriberTest < ActiveRecord::TestCase
|
1846
2260
|
# Call original test from coerced test. Fixes issue on CI with Rails installed as a gem.
|
1847
|
-
coerce_tests! :
|
1848
|
-
def
|
1849
|
-
|
2261
|
+
coerce_tests! :test_verbose_query_logs
|
2262
|
+
def test_verbose_query_logs_coerced
|
2263
|
+
original_test_verbose_query_logs
|
1850
2264
|
end
|
1851
2265
|
|
1852
2266
|
# Bindings logged slightly differently.
|
@@ -1858,69 +2272,19 @@ class LogSubscriberTest < ActiveRecord::TestCase
|
|
1858
2272
|
end
|
1859
2273
|
end
|
1860
2274
|
|
1861
|
-
class ActiveRecordSchemaTest < ActiveRecord::TestCase
|
1862
|
-
# Workaround for randomly failing test.
|
1863
|
-
coerce_tests! :test_has_primary_key
|
1864
|
-
def test_has_primary_key_coerced
|
1865
|
-
@schema_migration.reset_column_information
|
1866
|
-
original_test_has_primary_key
|
1867
|
-
end
|
1868
|
-
end
|
1869
|
-
|
1870
2275
|
class ReloadModelsTest < ActiveRecord::TestCase
|
1871
2276
|
# Skip test on Windows. The number of arguments passed to `IO.popen` in
|
1872
2277
|
# `activesupport/lib/active_support/testing/isolation.rb` exceeds what Windows can handle.
|
1873
2278
|
coerce_tests! :test_has_one_with_reload if RbConfig::CONFIG["host_os"] =~ /mswin|mingw/
|
1874
2279
|
end
|
1875
2280
|
|
1876
|
-
require "models/post"
|
1877
|
-
class AnnotateTest < ActiveRecord::TestCase
|
1878
|
-
# Same as original coerced test except our SQL starts with `EXEC sp_executesql`.
|
1879
|
-
# TODO: Remove coerce after Rails 7 (see https://github.com/rails/rails/pull/42027)
|
1880
|
-
coerce_tests! :test_annotate_wraps_content_in_an_inline_comment
|
1881
|
-
def test_annotate_wraps_content_in_an_inline_comment_coerced
|
1882
|
-
quoted_posts_id, quoted_posts = regexp_escape_table_name("posts.id"), regexp_escape_table_name("posts")
|
1883
|
-
|
1884
|
-
assert_sql(%r{SELECT #{quoted_posts_id} FROM #{quoted_posts} /\* foo \*/}i) do
|
1885
|
-
posts = Post.select(:id).annotate("foo")
|
1886
|
-
assert posts.first
|
1887
|
-
end
|
1888
|
-
end
|
1889
|
-
|
1890
|
-
# Same as original coerced test except our SQL starts with `EXEC sp_executesql`.
|
1891
|
-
# TODO: Remove coerce after Rails 7 (see https://github.com/rails/rails/pull/42027)
|
1892
|
-
coerce_tests! :test_annotate_is_sanitized
|
1893
|
-
def test_annotate_is_sanitized_coerced
|
1894
|
-
quoted_posts_id, quoted_posts = regexp_escape_table_name("posts.id"), regexp_escape_table_name("posts")
|
1895
|
-
|
1896
|
-
assert_sql(%r{SELECT #{quoted_posts_id} FROM #{quoted_posts} /\* foo \*/}i) do
|
1897
|
-
posts = Post.select(:id).annotate("*/foo/*")
|
1898
|
-
assert posts.first
|
1899
|
-
end
|
1900
|
-
|
1901
|
-
assert_sql(%r{SELECT #{quoted_posts_id} FROM #{quoted_posts} /\* foo \*/}i) do
|
1902
|
-
posts = Post.select(:id).annotate("**//foo//**")
|
1903
|
-
assert posts.first
|
1904
|
-
end
|
1905
|
-
|
1906
|
-
assert_sql(%r{SELECT #{quoted_posts_id} FROM #{quoted_posts} /\* foo \*/ /\* bar \*/}i) do
|
1907
|
-
posts = Post.select(:id).annotate("*/foo/*").annotate("*/bar")
|
1908
|
-
assert posts.first
|
1909
|
-
end
|
1910
|
-
|
1911
|
-
assert_sql(%r{SELECT #{quoted_posts_id} FROM #{quoted_posts} /\* \+ MAX_EXECUTION_TIME\(1\) \*/}i) do
|
1912
|
-
posts = Post.select(:id).annotate("+ MAX_EXECUTION_TIME(1)")
|
1913
|
-
assert posts.first
|
1914
|
-
end
|
1915
|
-
end
|
1916
|
-
end
|
1917
|
-
|
1918
2281
|
class MarshalSerializationTest < ActiveRecord::TestCase
|
1919
2282
|
private
|
1920
2283
|
|
2284
|
+
undef_method :marshal_fixture_path
|
1921
2285
|
def marshal_fixture_path(file_name)
|
1922
2286
|
File.expand_path(
|
1923
|
-
"support/marshal_compatibility_fixtures/#{ActiveRecord::Base.
|
2287
|
+
"support/marshal_compatibility_fixtures/#{ActiveRecord::Base.lease_connection.adapter_name}/#{file_name}.dump",
|
1924
2288
|
ARTest::SQLServer.test_root_sqlserver
|
1925
2289
|
)
|
1926
2290
|
end
|
@@ -1952,31 +2316,56 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
|
|
1952
2316
|
end
|
1953
2317
|
end
|
1954
2318
|
|
1955
|
-
class
|
1956
|
-
#
|
1957
|
-
coerce_tests!
|
1958
|
-
|
1959
|
-
|
1960
|
-
|
1961
|
-
|
1962
|
-
|
1963
|
-
|
1964
|
-
|
2319
|
+
class PreloaderTest < ActiveRecord::TestCase
|
2320
|
+
# Need to handle query parameters in SQL regex.
|
2321
|
+
coerce_tests! :test_preloads_has_many_on_model_with_a_composite_primary_key_through_id_attribute
|
2322
|
+
def test_preloads_has_many_on_model_with_a_composite_primary_key_through_id_attribute_coerced
|
2323
|
+
order = cpk_orders(:cpk_groceries_order_2)
|
2324
|
+
_shop_id, order_id = order.id
|
2325
|
+
order_agreements = Cpk::OrderAgreement.where(order_id: order_id).to_a
|
2326
|
+
|
2327
|
+
assert_not_empty order_agreements
|
2328
|
+
assert_equal order_agreements.sort, order.order_agreements.sort
|
2329
|
+
|
2330
|
+
loaded_order = nil
|
2331
|
+
sql = capture_sql do
|
2332
|
+
loaded_order = Cpk::Order.where(id: order_id).includes(:order_agreements).to_a.first
|
1965
2333
|
end
|
2334
|
+
|
2335
|
+
assert_equal 2, sql.size
|
2336
|
+
preload_sql = sql.last
|
2337
|
+
|
2338
|
+
c = Cpk::OrderAgreement.lease_connection
|
2339
|
+
order_id_column = Regexp.escape(c.quote_table_name("cpk_order_agreements.order_id"))
|
2340
|
+
order_id_constraint = /#{order_id_column} = @0.*@0 = \d+$/
|
2341
|
+
expectation = /SELECT.*WHERE.* #{order_id_constraint}/
|
2342
|
+
|
2343
|
+
assert_match(expectation, preload_sql)
|
2344
|
+
assert_equal order_agreements.sort, loaded_order.order_agreements.sort
|
1966
2345
|
end
|
1967
2346
|
|
1968
|
-
|
1969
|
-
|
1970
|
-
|
1971
|
-
|
1972
|
-
|
1973
|
-
|
1974
|
-
|
1975
|
-
|
1976
|
-
|
1977
|
-
|
1978
|
-
end
|
2347
|
+
# Need to handle query parameters in SQL regex.
|
2348
|
+
coerce_tests! :test_preloads_belongs_to_a_composite_primary_key_model_through_id_attribute
|
2349
|
+
def test_preloads_belongs_to_a_composite_primary_key_model_through_id_attribute_coerced
|
2350
|
+
order_agreement = cpk_order_agreements(:order_agreement_three)
|
2351
|
+
order = cpk_orders(:cpk_groceries_order_2)
|
2352
|
+
assert_equal order, order_agreement.order
|
2353
|
+
|
2354
|
+
loaded_order_agreement = nil
|
2355
|
+
sql = capture_sql do
|
2356
|
+
loaded_order_agreement = Cpk::OrderAgreement.where(id: order_agreement.id).includes(:order).to_a.first
|
1979
2357
|
end
|
2358
|
+
|
2359
|
+
assert_equal 2, sql.size
|
2360
|
+
preload_sql = sql.last
|
2361
|
+
|
2362
|
+
c = Cpk::Order.lease_connection
|
2363
|
+
order_id = Regexp.escape(c.quote_table_name("cpk_orders.id"))
|
2364
|
+
order_constraint = /#{order_id} = @0.*@0 = \d+$/
|
2365
|
+
expectation = /SELECT.*WHERE.* #{order_constraint}/
|
2366
|
+
|
2367
|
+
assert_match(expectation, preload_sql)
|
2368
|
+
assert_equal order, loaded_order_agreement.order
|
1980
2369
|
end
|
1981
2370
|
end
|
1982
2371
|
|
@@ -1989,3 +2378,395 @@ class MultiDbMigratorTest < ActiveRecord::TestCase
|
|
1989
2378
|
# Test fails on Windows AppVeyor CI for unknown reason.
|
1990
2379
|
coerce_tests! :test_migrator_db_has_no_schema_migrations_table if RbConfig::CONFIG["host_os"] =~ /mswin|mingw/
|
1991
2380
|
end
|
2381
|
+
|
2382
|
+
require "models/book"
|
2383
|
+
class FieldOrderedValuesTest < ActiveRecord::TestCase
|
2384
|
+
# Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
|
2385
|
+
coerce_tests! :test_in_order_of_with_enums_values
|
2386
|
+
def test_in_order_of_with_enums_values_coerced
|
2387
|
+
Book.lease_connection.remove_index(:books, column: [:author_id, :name])
|
2388
|
+
|
2389
|
+
original_test_in_order_of_with_enums_values
|
2390
|
+
ensure
|
2391
|
+
Book.where(author_id: nil, name: nil).delete_all
|
2392
|
+
Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
|
2393
|
+
end
|
2394
|
+
|
2395
|
+
# Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
|
2396
|
+
coerce_tests! :test_in_order_of_with_string_column
|
2397
|
+
def test_in_order_of_with_string_column_coerced
|
2398
|
+
Book.lease_connection.remove_index(:books, column: [:author_id, :name])
|
2399
|
+
|
2400
|
+
original_test_in_order_of_with_string_column
|
2401
|
+
ensure
|
2402
|
+
Book.where(author_id: nil, name: nil).delete_all
|
2403
|
+
Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
|
2404
|
+
end
|
2405
|
+
|
2406
|
+
# Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
|
2407
|
+
coerce_tests! :test_in_order_of_with_enums_keys
|
2408
|
+
def test_in_order_of_with_enums_keys_coerced
|
2409
|
+
Book.lease_connection.remove_index(:books, column: [:author_id, :name])
|
2410
|
+
|
2411
|
+
original_test_in_order_of_with_enums_keys
|
2412
|
+
ensure
|
2413
|
+
Book.where(author_id: nil, name: nil).delete_all
|
2414
|
+
Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
|
2415
|
+
end
|
2416
|
+
|
2417
|
+
# Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
|
2418
|
+
coerce_tests! :test_in_order_of_with_nil
|
2419
|
+
def test_in_order_of_with_nil_coerced
|
2420
|
+
Book.lease_connection.remove_index(:books, column: [:author_id, :name])
|
2421
|
+
|
2422
|
+
original_test_in_order_of_with_nil
|
2423
|
+
ensure
|
2424
|
+
Book.where(author_id: nil, name: nil).delete_all
|
2425
|
+
Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
|
2426
|
+
end
|
2427
|
+
end
|
2428
|
+
|
2429
|
+
require "models/dashboard"
|
2430
|
+
class QueryLogsTest < ActiveRecord::TestCase
|
2431
|
+
# SQL requires double single-quotes.
|
2432
|
+
coerce_tests! :test_sql_commenter_format
|
2433
|
+
def test_sql_commenter_format_coerced
|
2434
|
+
ActiveRecord::QueryLogs.update_formatter(:sqlcommenter)
|
2435
|
+
assert_queries_match(%r{/\*application=''active_record''\*/}) do
|
2436
|
+
Dashboard.first
|
2437
|
+
end
|
2438
|
+
end
|
2439
|
+
|
2440
|
+
# SQL requires double single-quotes.
|
2441
|
+
coerce_tests! :test_sqlcommenter_format_value
|
2442
|
+
def test_sqlcommenter_format_value_coerced
|
2443
|
+
ActiveRecord::QueryLogs.update_formatter(:sqlcommenter)
|
2444
|
+
|
2445
|
+
ActiveRecord::QueryLogs.tags = [
|
2446
|
+
:application,
|
2447
|
+
{ tracestate: "congo=t61rcWkgMzE,rojo=00f067aa0ba902b7", custom_proc: -> { "Joe's Shack" } },
|
2448
|
+
]
|
2449
|
+
|
2450
|
+
assert_queries_match(%r{custom_proc=''Joe%27s%20Shack'',tracestate=''congo%3Dt61rcWkgMzE%2Crojo%3D00f067aa0ba902b7''\*/}) do
|
2451
|
+
Dashboard.first
|
2452
|
+
end
|
2453
|
+
end
|
2454
|
+
|
2455
|
+
# SQL requires double single-quotes.
|
2456
|
+
coerce_tests! :test_sqlcommenter_format_value_string_coercible
|
2457
|
+
def test_sqlcommenter_format_value_string_coercible_coerced
|
2458
|
+
ActiveRecord::QueryLogs.update_formatter(:sqlcommenter)
|
2459
|
+
|
2460
|
+
ActiveRecord::QueryLogs.tags = [
|
2461
|
+
:application,
|
2462
|
+
{ custom_proc: -> { 1234 } },
|
2463
|
+
]
|
2464
|
+
|
2465
|
+
assert_queries_match(%r{custom_proc=''1234''\*/}) do
|
2466
|
+
Dashboard.first
|
2467
|
+
end
|
2468
|
+
end
|
2469
|
+
|
2470
|
+
# SQL requires double single-quotes.
|
2471
|
+
coerce_tests! :test_sqlcommenter_format_allows_string_keys
|
2472
|
+
def test_sqlcommenter_format_allows_string_keys_coerced
|
2473
|
+
ActiveRecord::QueryLogs.update_formatter(:sqlcommenter)
|
2474
|
+
|
2475
|
+
ActiveRecord::QueryLogs.tags = [
|
2476
|
+
:application,
|
2477
|
+
{
|
2478
|
+
"string" => "value",
|
2479
|
+
tracestate: "congo=t61rcWkgMzE,rojo=00f067aa0ba902b7",
|
2480
|
+
custom_proc: -> { "Joe's Shack" }
|
2481
|
+
},
|
2482
|
+
]
|
2483
|
+
|
2484
|
+
assert_queries_match(%r{custom_proc=''Joe%27s%20Shack'',string=''value'',tracestate=''congo%3Dt61rcWkgMzE%2Crojo%3D00f067aa0ba902b7''\*/}) do
|
2485
|
+
Dashboard.first
|
2486
|
+
end
|
2487
|
+
end
|
2488
|
+
|
2489
|
+
# Invalid character encoding causes `ActiveRecord::StatementInvalid` error similar to Postgres.
|
2490
|
+
coerce_tests! :test_invalid_encoding_query
|
2491
|
+
def test_invalid_encoding_query_coerced
|
2492
|
+
ActiveRecord::QueryLogs.tags = [ :application ]
|
2493
|
+
assert_raises ActiveRecord::StatementInvalid do
|
2494
|
+
ActiveRecord::Base.lease_connection.execute "select 1 as '\xFF'"
|
2495
|
+
end
|
2496
|
+
end
|
2497
|
+
end
|
2498
|
+
|
2499
|
+
class InsertAllTest < ActiveRecord::TestCase
|
2500
|
+
# Same as original but using INSERTED.name as UPPER argument
|
2501
|
+
coerce_tests! :test_insert_all_returns_requested_sql_fields
|
2502
|
+
def test_insert_all_returns_requested_sql_fields_coerced
|
2503
|
+
skip unless supports_insert_returning?
|
2504
|
+
|
2505
|
+
result = Book.insert_all! [{ name: "Rework", author_id: 1 }], returning: Arel.sql("UPPER(INSERTED.name) as name")
|
2506
|
+
assert_equal %w[ REWORK ], result.pluck("name")
|
2507
|
+
end
|
2508
|
+
end
|
2509
|
+
|
2510
|
+
module ActiveRecord
|
2511
|
+
class Migration
|
2512
|
+
class InvalidOptionsTest < ActiveRecord::TestCase
|
2513
|
+
# Include the additional SQL Server migration options.
|
2514
|
+
undef_method :invalid_add_column_option_exception_message
|
2515
|
+
def invalid_add_column_option_exception_message(key)
|
2516
|
+
default_keys = [":limit", ":precision", ":scale", ":default", ":null", ":collation", ":comment", ":primary_key", ":if_exists", ":if_not_exists"]
|
2517
|
+
default_keys.concat([":is_identity"]) # SQL Server additional valid keys
|
2518
|
+
|
2519
|
+
"Unknown key: :#{key}. Valid keys are: #{default_keys.join(", ")}"
|
2520
|
+
end
|
2521
|
+
end
|
2522
|
+
end
|
2523
|
+
end
|
2524
|
+
|
2525
|
+
# SQL Server does not support upsert. Removed dependency on `insert_all` that uses upsert.
|
2526
|
+
class ActiveRecord::Encryption::ConcurrencyTest < ActiveRecord::EncryptionTestCase
|
2527
|
+
undef_method :thread_encrypting_and_decrypting
|
2528
|
+
def thread_encrypting_and_decrypting(thread_label)
|
2529
|
+
posts = 100.times.collect { |index| EncryptedPost.create! title: "Article #{index} (#{thread_label})", body: "Body #{index} (#{thread_label})" }
|
2530
|
+
|
2531
|
+
Thread.new do
|
2532
|
+
posts.each.with_index do |article, index|
|
2533
|
+
assert_encrypted_attribute article, :title, "Article #{index} (#{thread_label})"
|
2534
|
+
article.decrypt
|
2535
|
+
assert_not_encrypted_attribute article, :title, "Article #{index} (#{thread_label})"
|
2536
|
+
end
|
2537
|
+
end
|
2538
|
+
end
|
2539
|
+
end
|
2540
|
+
|
2541
|
+
# Need to use `install_unregistered_type_fallback` instead of `install_unregistered_type_error` so that message-pack
|
2542
|
+
# can read and write `ActiveRecord::ConnectionAdapters::SQLServer::Type::Data` objects.
|
2543
|
+
class ActiveRecordMessagePackTest < ActiveRecord::TestCase
|
2544
|
+
private
|
2545
|
+
undef_method :serializer
|
2546
|
+
def serializer
|
2547
|
+
@serializer ||= ::MessagePack::Factory.new.tap do |factory|
|
2548
|
+
ActiveRecord::MessagePack::Extensions.install(factory)
|
2549
|
+
ActiveSupport::MessagePack::Extensions.install(factory)
|
2550
|
+
ActiveSupport::MessagePack::Extensions.install_unregistered_type_fallback(factory)
|
2551
|
+
end
|
2552
|
+
end
|
2553
|
+
end
|
2554
|
+
|
2555
|
+
class StoreTest < ActiveRecord::TestCase
|
2556
|
+
# Set the attribute as JSON type for the `StoreTest#saved changes tracking for accessors with json column` test.
|
2557
|
+
Admin::User.attribute :json_options, ActiveRecord::Type::SQLServer::Json.new
|
2558
|
+
end
|
2559
|
+
|
2560
|
+
class TestDatabasesTest < ActiveRecord::TestCase
|
2561
|
+
# Tests are not about a specific adapter.
|
2562
|
+
coerce_all_tests!
|
2563
|
+
end
|
2564
|
+
|
2565
|
+
module ActiveRecord
|
2566
|
+
module ConnectionAdapters
|
2567
|
+
class ConnectionHandlersShardingDbTest < ActiveRecord::TestCase
|
2568
|
+
# Tests are not about a specific adapter.
|
2569
|
+
coerce_all_tests!
|
2570
|
+
end
|
2571
|
+
end
|
2572
|
+
end
|
2573
|
+
|
2574
|
+
module ActiveRecord
|
2575
|
+
module ConnectionAdapters
|
2576
|
+
class ConnectionSwappingNestedTest < ActiveRecord::TestCase
|
2577
|
+
# Tests are not about a specific adapter.
|
2578
|
+
coerce_all_tests!
|
2579
|
+
end
|
2580
|
+
end
|
2581
|
+
end
|
2582
|
+
|
2583
|
+
module ActiveRecord
|
2584
|
+
module ConnectionAdapters
|
2585
|
+
class ConnectionHandlersMultiDbTest < ActiveRecord::TestCase
|
2586
|
+
# Tests are not about a specific adapter.
|
2587
|
+
coerce_tests! :test_switching_connections_via_handler
|
2588
|
+
end
|
2589
|
+
end
|
2590
|
+
end
|
2591
|
+
|
2592
|
+
module ActiveRecord
|
2593
|
+
module ConnectionAdapters
|
2594
|
+
class ConnectionHandlersMultiPoolConfigTest < ActiveRecord::TestCase
|
2595
|
+
# Tests are not about a specific adapter.
|
2596
|
+
coerce_all_tests!
|
2597
|
+
end
|
2598
|
+
end
|
2599
|
+
end
|
2600
|
+
|
2601
|
+
module ActiveRecord
|
2602
|
+
class Migration
|
2603
|
+
class CheckConstraintTest < ActiveRecord::TestCase
|
2604
|
+
# SQL Server formats the check constraint expression differently.
|
2605
|
+
coerce_tests! :test_check_constraints
|
2606
|
+
def test_check_constraints_coerced
|
2607
|
+
check_constraints = @connection.check_constraints("products")
|
2608
|
+
assert_equal 1, check_constraints.size
|
2609
|
+
|
2610
|
+
constraint = check_constraints.first
|
2611
|
+
assert_equal "products", constraint.table_name
|
2612
|
+
assert_equal "products_price_check", constraint.name
|
2613
|
+
assert_equal "[price]>[discounted_price]", constraint.expression
|
2614
|
+
end
|
2615
|
+
|
2616
|
+
# SQL Server formats the check constraint expression differently.
|
2617
|
+
coerce_tests! :test_add_check_constraint
|
2618
|
+
def test_add_check_constraint_coerced
|
2619
|
+
@connection.add_check_constraint :trades, "quantity > 0"
|
2620
|
+
|
2621
|
+
check_constraints = @connection.check_constraints("trades")
|
2622
|
+
assert_equal 1, check_constraints.size
|
2623
|
+
|
2624
|
+
constraint = check_constraints.first
|
2625
|
+
assert_equal "trades", constraint.table_name
|
2626
|
+
assert_equal "chk_rails_2189e9f96c", constraint.name
|
2627
|
+
assert_equal "[quantity]>(0)", constraint.expression
|
2628
|
+
end
|
2629
|
+
|
2630
|
+
# SQL Server formats the check constraint expression differently.
|
2631
|
+
coerce_tests! :test_remove_check_constraint
|
2632
|
+
def test_remove_check_constraint_coerced
|
2633
|
+
@connection.add_check_constraint :trades, "price > 0", name: "price_check"
|
2634
|
+
@connection.add_check_constraint :trades, "quantity > 0", name: "quantity_check"
|
2635
|
+
|
2636
|
+
assert_equal 2, @connection.check_constraints("trades").size
|
2637
|
+
@connection.remove_check_constraint :trades, name: "quantity_check"
|
2638
|
+
assert_equal 1, @connection.check_constraints("trades").size
|
2639
|
+
|
2640
|
+
constraint = @connection.check_constraints("trades").first
|
2641
|
+
assert_equal "trades", constraint.table_name
|
2642
|
+
assert_equal "price_check", constraint.name
|
2643
|
+
assert_equal "[price]>(0)", constraint.expression
|
2644
|
+
|
2645
|
+
@connection.remove_check_constraint :trades, name: :price_check # name as a symbol
|
2646
|
+
assert_empty @connection.check_constraints("trades")
|
2647
|
+
end
|
2648
|
+
end
|
2649
|
+
end
|
2650
|
+
end
|
2651
|
+
|
2652
|
+
module ActiveRecord
|
2653
|
+
module ConnectionAdapters
|
2654
|
+
class PoolConfig
|
2655
|
+
class ResolverTest < ActiveRecord::TestCase
|
2656
|
+
# SQL Server was not included in the list of available adapters in the error message.
|
2657
|
+
coerce_tests! :test_url_invalid_adapter
|
2658
|
+
def test_url_invalid_adapter_coerced
|
2659
|
+
error = assert_raises(AdapterNotFound) do
|
2660
|
+
Base.connection_handler.establish_connection "ridiculous://foo?encoding=utf8"
|
2661
|
+
end
|
2662
|
+
|
2663
|
+
assert_match "Database configuration specifies nonexistent 'ridiculous' adapter. Available adapters are: abstract, fake, mysql2, postgresql, sqlite3, sqlserver, trilogy. Ensure that the adapter is spelled correctly in config/database.yml and that you've added the necessary adapter gem to your Gemfile if it's not in the list of available adapters.", error.message
|
2664
|
+
end
|
2665
|
+
end
|
2666
|
+
end
|
2667
|
+
end
|
2668
|
+
end
|
2669
|
+
|
2670
|
+
module ActiveRecord
|
2671
|
+
class TableMetadataTest < ActiveSupport::TestCase
|
2672
|
+
# Adapter returns an object that is subclass of what is expected in the original test.
|
2673
|
+
coerce_tests! %r{#associated_table creates the right type caster for joined table with different association name}
|
2674
|
+
def associated_table_creates_the_right_type_caster_for_joined_table_with_different_association_name_coerced
|
2675
|
+
base_table_metadata = TableMetadata.new(AuditRequiredDeveloper, Arel::Table.new("developers"))
|
2676
|
+
|
2677
|
+
associated_table_metadata = base_table_metadata.associated_table("audit_logs")
|
2678
|
+
|
2679
|
+
assert associated_table_metadata.arel_table.type_for_attribute(:message).is_a?(ActiveRecord::Type::String)
|
2680
|
+
end
|
2681
|
+
end
|
2682
|
+
end
|
2683
|
+
|
2684
|
+
module ActiveRecord
|
2685
|
+
module TypeCaster
|
2686
|
+
class ConnectionTest < ActiveSupport::TestCase
|
2687
|
+
# Adapter returns an object that is subclass of what is expected in the original test.
|
2688
|
+
coerce_tests! %r{#type_for_attribute is not aware of custom types}
|
2689
|
+
def type_for_attribute_is_not_aware_of_custom_types_coerced
|
2690
|
+
type_caster = Connection.new(AttributedDeveloper, "developers")
|
2691
|
+
|
2692
|
+
type = type_caster.type_for_attribute(:name)
|
2693
|
+
|
2694
|
+
assert_not_equal DeveloperName, type.class
|
2695
|
+
assert type.is_a?(ActiveRecord::Type::String)
|
2696
|
+
end
|
2697
|
+
end
|
2698
|
+
end
|
2699
|
+
end
|
2700
|
+
|
2701
|
+
require "models/car"
|
2702
|
+
class ExplainTest < ActiveRecord::TestCase
|
2703
|
+
# Expected query slightly different from because of 'sp_executesql' and query parameters.
|
2704
|
+
coerce_tests! :test_relation_explain_with_first
|
2705
|
+
def test_relation_explain_with_first_coerced
|
2706
|
+
expected_query = capture_sql {
|
2707
|
+
Car.all.first
|
2708
|
+
}.first[/EXEC sp_executesql N'(.*?) NEXT/, 1]
|
2709
|
+
message = Car.all.explain.first
|
2710
|
+
assert_match(/^EXPLAIN/, message)
|
2711
|
+
assert_match(expected_query, message)
|
2712
|
+
end
|
2713
|
+
|
2714
|
+
# Expected query slightly different from because of 'sp_executesql' and query parameters.
|
2715
|
+
coerce_tests! :test_relation_explain_with_last
|
2716
|
+
def test_relation_explain_with_last_coerced
|
2717
|
+
expected_query = capture_sql {
|
2718
|
+
Car.all.last
|
2719
|
+
}.first[/EXEC sp_executesql N'(.*?) NEXT/, 1]
|
2720
|
+
expected_query = expected_query
|
2721
|
+
message = Car.all.explain.last
|
2722
|
+
|
2723
|
+
assert_match(/^EXPLAIN/, message)
|
2724
|
+
assert_match(expected_query, message)
|
2725
|
+
end
|
2726
|
+
end
|
2727
|
+
|
2728
|
+
module ActiveRecord
|
2729
|
+
module Assertions
|
2730
|
+
class QueryAssertionsTest < ActiveSupport::TestCase
|
2731
|
+
# Query slightly different in original test.
|
2732
|
+
coerce_tests! :test_assert_queries_match
|
2733
|
+
def test_assert_queries_match_coerced
|
2734
|
+
assert_queries_match(/ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/i, count: 1) { Post.first }
|
2735
|
+
assert_queries_match(/ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/i) { Post.first }
|
2736
|
+
|
2737
|
+
error = assert_raises(Minitest::Assertion) {
|
2738
|
+
assert_queries_match(/ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/i, count: 2) { Post.first }
|
2739
|
+
}
|
2740
|
+
assert_match(/1 instead of 2 queries/, error.message)
|
2741
|
+
|
2742
|
+
error = assert_raises(Minitest::Assertion) {
|
2743
|
+
assert_queries_match(/ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/i, count: 0) { Post.first }
|
2744
|
+
}
|
2745
|
+
assert_match(/1 instead of 0 queries/, error.message)
|
2746
|
+
end
|
2747
|
+
end
|
2748
|
+
end
|
2749
|
+
end
|
2750
|
+
|
2751
|
+
module ActiveRecord
|
2752
|
+
class WithTest < ActiveRecord::TestCase
|
2753
|
+
# SQL contains just 'WITH' instead of 'WITH RECURSIVE' as expected by the original test.
|
2754
|
+
coerce_tests! :test_with_recursive
|
2755
|
+
def test_with_recursive_coerced
|
2756
|
+
top_companies = Company.where(firm_id: nil).to_a
|
2757
|
+
child_companies = Company.where(firm_id: top_companies).to_a
|
2758
|
+
top_companies_and_children = (top_companies.map(&:id) + child_companies.map(&:id)).sort
|
2759
|
+
|
2760
|
+
relation = Company.with_recursive(
|
2761
|
+
top_companies_and_children: [
|
2762
|
+
Company.where(firm_id: nil),
|
2763
|
+
Company.joins("JOIN top_companies_and_children ON companies.firm_id = top_companies_and_children.id"),
|
2764
|
+
]
|
2765
|
+
).from("top_companies_and_children AS companies")
|
2766
|
+
|
2767
|
+
assert_equal top_companies_and_children, relation.order(:id).pluck(:id)
|
2768
|
+
assert_match "WITH ", relation.to_sql
|
2769
|
+
end
|
2770
|
+
end
|
2771
|
+
end
|
2772
|
+
|