activerecord-sqlserver-adapter 8.0.10 → 8.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.devcontainer/Dockerfile +1 -1
- data/.github/workflows/ci.yml +34 -3
- data/CHANGELOG.md +14 -68
- data/Dockerfile.ci +1 -1
- data/Gemfile +7 -9
- data/Guardfile +2 -2
- data/README.md +33 -13
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/activerecord-sqlserver-adapter.gemspec +15 -16
- data/compose.ci.yaml +8 -1
- data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +1 -2
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +4 -4
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +118 -83
- data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +3 -4
- data/lib/active_record/connection_adapters/sqlserver/quoting.rb +7 -7
- data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +24 -12
- data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +17 -8
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +162 -156
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver/showplan.rb +5 -5
- data/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +2 -7
- data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +3 -1
- data/lib/active_record/connection_adapters/sqlserver/type/data.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/date.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +3 -4
- data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver/type/time.rb +4 -6
- data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/utils.rb +10 -12
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +118 -66
- data/lib/active_record/connection_adapters/sqlserver_column.rb +17 -9
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +5 -5
- data/lib/arel/visitors/sqlserver.rb +55 -26
- data/test/cases/active_schema_test_sqlserver.rb +45 -23
- data/test/cases/adapter_test_sqlserver.rb +72 -59
- data/test/cases/coerced_tests.rb +365 -161
- data/test/cases/column_test_sqlserver.rb +328 -316
- data/test/cases/connection_test_sqlserver.rb +15 -11
- data/test/cases/enum_test_sqlserver.rb +8 -9
- data/test/cases/execute_procedure_test_sqlserver.rb +1 -1
- data/test/cases/fetch_test_sqlserver.rb +1 -1
- data/test/cases/helper_sqlserver.rb +7 -3
- data/test/cases/index_test_sqlserver.rb +8 -6
- data/test/cases/insert_all_test_sqlserver.rb +3 -28
- data/test/cases/json_test_sqlserver.rb +8 -8
- data/test/cases/lateral_test_sqlserver.rb +2 -2
- data/test/cases/migration_test_sqlserver.rb +12 -12
- data/test/cases/pessimistic_locking_test_sqlserver.rb +6 -6
- data/test/cases/primary_keys_test_sqlserver.rb +4 -4
- data/test/cases/rake_test_sqlserver.rb +15 -7
- data/test/cases/schema_dumper_test_sqlserver.rb +109 -113
- data/test/cases/schema_test_sqlserver.rb +7 -7
- data/test/cases/transaction_test_sqlserver.rb +6 -8
- data/test/cases/trigger_test_sqlserver.rb +1 -1
- data/test/cases/utils_test_sqlserver.rb +3 -3
- data/test/cases/view_test_sqlserver.rb +12 -8
- data/test/cases/virtual_column_test_sqlserver.rb +113 -0
- data/test/migrations/create_clients_and_change_column_collation.rb +2 -2
- data/test/models/sqlserver/edge_schema.rb +2 -2
- data/test/schema/sqlserver_specific_schema.rb +49 -37
- data/test/support/coerceable_test_sqlserver.rb +10 -10
- data/test/support/connection_reflection.rb +0 -5
- data/test/support/core_ext/backtrace_cleaner.rb +36 -0
- data/test/support/query_assertions.rb +6 -6
- data/test/support/rake_helpers.rb +6 -10
- metadata +12 -107
data/test/cases/coerced_tests.rb
CHANGED
|
@@ -2,7 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
require "cases/helper_sqlserver"
|
|
4
4
|
|
|
5
|
+
require "models/author"
|
|
6
|
+
require "models/book"
|
|
7
|
+
require "models/car"
|
|
8
|
+
require "models/citation"
|
|
9
|
+
require "models/comment"
|
|
10
|
+
require "models/computer"
|
|
11
|
+
require "models/customer"
|
|
12
|
+
require "models/dashboard"
|
|
13
|
+
require "models/developer"
|
|
5
14
|
require "models/event"
|
|
15
|
+
require "models/non_primary_key"
|
|
16
|
+
require "models/post"
|
|
17
|
+
require "models/tag"
|
|
18
|
+
require "models/task"
|
|
19
|
+
require "models/topic"
|
|
20
|
+
|
|
6
21
|
class UniquenessValidationTest < ActiveRecord::TestCase
|
|
7
22
|
# So sp_executesql swallows this exception. Run without prepared to see it.
|
|
8
23
|
coerce_tests! :test_validate_uniqueness_with_limit
|
|
@@ -62,7 +77,6 @@ class UniquenessValidationWithIndexTest < ActiveRecord::TestCase
|
|
|
62
77
|
end
|
|
63
78
|
end
|
|
64
79
|
|
|
65
|
-
require "models/event"
|
|
66
80
|
module ActiveRecord
|
|
67
81
|
class AdapterTest < ActiveRecord::TestCase
|
|
68
82
|
# Legacy binds are not supported.
|
|
@@ -174,21 +188,20 @@ module ActiveRecord
|
|
|
174
188
|
end
|
|
175
189
|
end
|
|
176
190
|
|
|
177
|
-
require "models/topic"
|
|
178
191
|
class AttributeMethodsTest < ActiveRecord::TestCase
|
|
179
192
|
# Use IFF for boolean statement in SELECT
|
|
180
193
|
coerce_tests! %r{typecast attribute from select to false}
|
|
181
194
|
def test_typecast_attribute_from_select_to_false_coerced
|
|
182
|
-
Topic.create(:
|
|
183
|
-
topic = Topic.all.merge!(:
|
|
195
|
+
Topic.create(title: "Budget")
|
|
196
|
+
topic = Topic.all.merge!(select: "topics.*, IIF (1 = 2, 1, 0) as is_test").first
|
|
184
197
|
assert_not_predicate topic, :is_test?
|
|
185
198
|
end
|
|
186
199
|
|
|
187
200
|
# Use IFF for boolean statement in SELECT
|
|
188
201
|
coerce_tests! %r{typecast attribute from select to true}
|
|
189
202
|
def test_typecast_attribute_from_select_to_true_coerced
|
|
190
|
-
Topic.create(:
|
|
191
|
-
topic = Topic.all.merge!(:
|
|
203
|
+
Topic.create(title: "Budget")
|
|
204
|
+
topic = Topic.all.merge!(select: "topics.*, IIF (1 = 1, 1, 0) as is_test").first
|
|
192
205
|
assert_predicate topic, :is_test?
|
|
193
206
|
end
|
|
194
207
|
end
|
|
@@ -248,7 +261,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
|
|
|
248
261
|
def test_belongs_to_coerced
|
|
249
262
|
client = Client.find(3)
|
|
250
263
|
first_firm = companies(:first_firm)
|
|
251
|
-
assert_queries_and_values_match(/FETCH NEXT @3 ROWS ONLY/, [
|
|
264
|
+
assert_queries_and_values_match(/FETCH NEXT @3 ROWS ONLY/, ["Firm", "Agency", 1, 1]) do
|
|
252
265
|
assert_equal first_firm, client.firm
|
|
253
266
|
assert_equal first_firm.name, client.firm.name
|
|
254
267
|
end
|
|
@@ -283,7 +296,7 @@ module ActiveRecord
|
|
|
283
296
|
|
|
284
297
|
original_test_payload_row_count_on_select_all
|
|
285
298
|
ensure
|
|
286
|
-
Book.where(author_id: nil, name:
|
|
299
|
+
Book.where(author_id: nil, name: "row count book 1").delete_all
|
|
287
300
|
Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
|
|
288
301
|
end
|
|
289
302
|
|
|
@@ -294,7 +307,7 @@ module ActiveRecord
|
|
|
294
307
|
|
|
295
308
|
original_test_payload_row_count_on_pluck
|
|
296
309
|
ensure
|
|
297
|
-
Book.where(author_id: nil, name:
|
|
310
|
+
Book.where(author_id: nil, name: "row count book 2").delete_all
|
|
298
311
|
Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
|
|
299
312
|
end
|
|
300
313
|
|
|
@@ -305,9 +318,16 @@ module ActiveRecord
|
|
|
305
318
|
|
|
306
319
|
original_test_payload_row_count_on_raw_sql
|
|
307
320
|
ensure
|
|
308
|
-
Book.where(author_id: nil, name:
|
|
321
|
+
Book.where(author_id: nil, name: "row count book 3").delete_all
|
|
309
322
|
Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
|
|
310
323
|
end
|
|
324
|
+
|
|
325
|
+
# Fix randomly failing test. The loading of the model's schema was affecting the test.
|
|
326
|
+
coerce_tests! :test_payload_affected_rows
|
|
327
|
+
def test_payload_affected_rows_coerced
|
|
328
|
+
Book.create!(name: "TEMP RECORD TO RUN SCHEMA QUERIES").destroy!
|
|
329
|
+
original_test_payload_affected_rows
|
|
330
|
+
end
|
|
311
331
|
end
|
|
312
332
|
end
|
|
313
333
|
|
|
@@ -377,11 +397,11 @@ class CalculationsTest < ActiveRecord::TestCase
|
|
|
377
397
|
rails_core = companies(:rails_core)
|
|
378
398
|
|
|
379
399
|
account = Account
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
400
|
+
.select(:firm_id, "AVG(CAST(credit_limit AS DECIMAL)) AS avg_credit_limit")
|
|
401
|
+
.where(firm: rails_core)
|
|
402
|
+
.group(:firm_id)
|
|
403
|
+
.order(:firm_id)
|
|
404
|
+
.take!
|
|
385
405
|
|
|
386
406
|
# id was not selected, so it should be nil
|
|
387
407
|
# (cannot select id because it wasn't used in the GROUP BY clause)
|
|
@@ -421,7 +441,6 @@ class CalculationsTest < ActiveRecord::TestCase
|
|
|
421
441
|
assert_equal(52.5, firm.avg_credit_limit)
|
|
422
442
|
end
|
|
423
443
|
|
|
424
|
-
|
|
425
444
|
# In SQL Server the `AVG()` function for a list of integers returns an integer so need to cast values as decimals before averaging.
|
|
426
445
|
# SELECT columns must be in the GROUP clause.
|
|
427
446
|
coerce_tests! :test_select_avg_with_joins_and_group_by_as_virtual_attribute_with_ar
|
|
@@ -429,11 +448,11 @@ class CalculationsTest < ActiveRecord::TestCase
|
|
|
429
448
|
rails_core = companies(:rails_core)
|
|
430
449
|
|
|
431
450
|
firm = DependentFirm
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
451
|
+
.select("companies.*", "AVG(CAST(accounts.credit_limit AS DECIMAL)) AS avg_credit_limit")
|
|
452
|
+
.where(id: rails_core)
|
|
453
|
+
.joins(:account)
|
|
454
|
+
.group(:id, :type, :firm_id, :firm_name, :name, :client_of, :rating, :account_id, :description, :status)
|
|
455
|
+
.take!
|
|
437
456
|
|
|
438
457
|
# all the DependentFirm attributes should be present
|
|
439
458
|
assert_equal rails_core, firm
|
|
@@ -499,8 +518,8 @@ module ActiveRecord
|
|
|
499
518
|
five = columns.detect { |c| c.name == "five" }
|
|
500
519
|
|
|
501
520
|
assert_equal "hello", one.default
|
|
502
|
-
assert_equal true,
|
|
503
|
-
assert_equal false,
|
|
521
|
+
assert_equal true, two.fetch_cast_type(connection).deserialize(two.default)
|
|
522
|
+
assert_equal false, three.fetch_cast_type(connection).deserialize(three.default)
|
|
504
523
|
assert_equal 1, four.default
|
|
505
524
|
assert_equal "hello", five.default
|
|
506
525
|
end
|
|
@@ -569,7 +588,7 @@ module ActiveRecord
|
|
|
569
588
|
# Our defaults are real 70000 integers vs '70000' strings.
|
|
570
589
|
coerce_tests! :test_rename_column_preserves_default_value_not_null
|
|
571
590
|
def test_rename_column_preserves_default_value_not_null_coerced
|
|
572
|
-
add_column "test_models", "salary", :integer, :
|
|
591
|
+
add_column "test_models", "salary", :integer, default: 70000
|
|
573
592
|
default_before = connection.columns("test_models").find { |c| c.name == "salary" }.default
|
|
574
593
|
assert_equal 70000, default_before
|
|
575
594
|
rename_column "test_models", "salary", "annual_salary"
|
|
@@ -583,8 +602,8 @@ module ActiveRecord
|
|
|
583
602
|
coerce_tests! :test_remove_column_with_multi_column_index
|
|
584
603
|
def test_remove_column_with_multi_column_index_coerced
|
|
585
604
|
add_column "test_models", :hat_size, :integer
|
|
586
|
-
add_column "test_models", :hat_style, :string, :
|
|
587
|
-
add_index "test_models", ["hat_style", "hat_size"], :
|
|
605
|
+
add_column "test_models", :hat_style, :string, limit: 100
|
|
606
|
+
add_index "test_models", ["hat_style", "hat_size"], unique: true
|
|
588
607
|
assert_equal 1, connection.indexes("test_models").size
|
|
589
608
|
remove_column("test_models", "hat_size")
|
|
590
609
|
assert_equal [], connection.indexes("test_models").map(&:name)
|
|
@@ -611,14 +630,20 @@ class MigrationTest < ActiveRecord::TestCase
|
|
|
611
630
|
coerce_tests! :test_add_column_with_casted_type_if_not_exists_set_to_true
|
|
612
631
|
def test_add_column_with_casted_type_if_not_exists_set_to_true_coerced
|
|
613
632
|
migration_a = Class.new(ActiveRecord::Migration::Current) {
|
|
614
|
-
def version
|
|
633
|
+
def version
|
|
634
|
+
100
|
|
635
|
+
end
|
|
636
|
+
|
|
615
637
|
def migrate(x)
|
|
616
638
|
add_column "people", "last_name", :binary
|
|
617
639
|
end
|
|
618
640
|
}.new
|
|
619
641
|
|
|
620
642
|
migration_b = Class.new(ActiveRecord::Migration::Current) {
|
|
621
|
-
def version
|
|
643
|
+
def version
|
|
644
|
+
101
|
|
645
|
+
end
|
|
646
|
+
|
|
622
647
|
def migrate(x)
|
|
623
648
|
add_column "people", "last_name", :binary, if_not_exists: true
|
|
624
649
|
end
|
|
@@ -647,7 +672,10 @@ module ActiveRecord
|
|
|
647
672
|
long_table_name = "a" * (connection.table_name_length + 1)
|
|
648
673
|
migration = Class.new(ActiveRecord::Migration[7.0]) {
|
|
649
674
|
@@long_table_name = long_table_name
|
|
650
|
-
def version
|
|
675
|
+
def version
|
|
676
|
+
100
|
|
677
|
+
end
|
|
678
|
+
|
|
651
679
|
def migrate(x)
|
|
652
680
|
create_table @@long_table_name
|
|
653
681
|
end
|
|
@@ -658,7 +686,11 @@ module ActiveRecord
|
|
|
658
686
|
end
|
|
659
687
|
assert_match(/The identifier that starts with '#{long_table_name[0...-1]}' is too long/i, error.message)
|
|
660
688
|
ensure
|
|
661
|
-
|
|
689
|
+
begin
|
|
690
|
+
connection.drop_table(long_table_name)
|
|
691
|
+
rescue
|
|
692
|
+
nil
|
|
693
|
+
end
|
|
662
694
|
end
|
|
663
695
|
|
|
664
696
|
# 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).
|
|
@@ -669,7 +701,10 @@ module ActiveRecord
|
|
|
669
701
|
|
|
670
702
|
migration = Class.new(ActiveRecord::Migration[7.0]) {
|
|
671
703
|
@@long_table_name = long_table_name
|
|
672
|
-
def version
|
|
704
|
+
def version
|
|
705
|
+
100
|
|
706
|
+
end
|
|
707
|
+
|
|
673
708
|
def migrate(x)
|
|
674
709
|
rename_table :more_testings, @@long_table_name
|
|
675
710
|
end
|
|
@@ -680,14 +715,22 @@ module ActiveRecord
|
|
|
680
715
|
assert_not connection.table_exists?(:more_testings)
|
|
681
716
|
assert connection.table_exists?(long_table_name[0...-1])
|
|
682
717
|
ensure
|
|
683
|
-
|
|
684
|
-
|
|
718
|
+
begin
|
|
719
|
+
connection.drop_table(:more_testings)
|
|
720
|
+
rescue
|
|
721
|
+
nil
|
|
722
|
+
end
|
|
723
|
+
begin
|
|
724
|
+
connection.drop_table(long_table_name[0...-1])
|
|
725
|
+
rescue
|
|
726
|
+
nil
|
|
727
|
+
end
|
|
685
728
|
end
|
|
686
729
|
|
|
687
730
|
# SQL Server has a different maximum index name length.
|
|
688
731
|
coerce_tests! :test_add_index_errors_on_too_long_name_7_0
|
|
689
732
|
def test_add_index_errors_on_too_long_name_7_0_coerced
|
|
690
|
-
long_index_name =
|
|
733
|
+
long_index_name = "a" * (connection.index_name_length + 1)
|
|
691
734
|
|
|
692
735
|
migration = Class.new(ActiveRecord::Migration[7.0]) {
|
|
693
736
|
@@long_index_name = long_index_name
|
|
@@ -700,13 +743,13 @@ module ActiveRecord
|
|
|
700
743
|
error = assert_raises(StandardError) do
|
|
701
744
|
ActiveRecord::Migrator.new(:up, [migration], @schema_migration, @internal_metadata).migrate
|
|
702
745
|
end
|
|
703
|
-
assert_match(/Index name
|
|
746
|
+
assert_match(/Index name '#{long_index_name}' on table 'testings' is too long/i, error.message)
|
|
704
747
|
end
|
|
705
748
|
|
|
706
749
|
# SQL Server has a different maximum index name length.
|
|
707
750
|
coerce_tests! :test_create_table_add_index_errors_on_too_long_name_7_0
|
|
708
751
|
def test_create_table_add_index_errors_on_too_long_name_7_0_coerced
|
|
709
|
-
long_index_name =
|
|
752
|
+
long_index_name = "a" * (connection.index_name_length + 1)
|
|
710
753
|
|
|
711
754
|
migration = Class.new(ActiveRecord::Migration[7.0]) {
|
|
712
755
|
@@long_index_name = long_index_name
|
|
@@ -723,9 +766,41 @@ module ActiveRecord
|
|
|
723
766
|
error = assert_raises(StandardError) do
|
|
724
767
|
ActiveRecord::Migrator.new(:up, [migration], @schema_migration, @internal_metadata).migrate
|
|
725
768
|
end
|
|
726
|
-
assert_match(/Index name
|
|
769
|
+
assert_match(/Index name '#{long_index_name}' on table 'more_testings' is too long/i, error.message)
|
|
770
|
+
ensure
|
|
771
|
+
begin
|
|
772
|
+
connection.drop_table :more_testings
|
|
773
|
+
rescue
|
|
774
|
+
nil
|
|
775
|
+
end
|
|
776
|
+
end
|
|
777
|
+
|
|
778
|
+
# Foreign key count is the same as PostgreSQL/SQLite.
|
|
779
|
+
coerce_tests! :test_remove_foreign_key_on_8_0
|
|
780
|
+
def test_remove_foreign_key_on_8_0_coerced
|
|
781
|
+
connection.create_table(:sub_testings) do |t|
|
|
782
|
+
t.references :testing, foreign_key: true, type: :bigint
|
|
783
|
+
t.references :experiment, foreign_key: {to_table: :testings}, type: :bigint
|
|
784
|
+
end
|
|
785
|
+
|
|
786
|
+
migration = Class.new(ActiveRecord::Migration[8.0]) do
|
|
787
|
+
def up
|
|
788
|
+
change_table(:sub_testings) do |t|
|
|
789
|
+
t.remove_foreign_key :testings
|
|
790
|
+
t.remove_foreign_key :testings, column: :experiment_id
|
|
791
|
+
end
|
|
792
|
+
end
|
|
793
|
+
end
|
|
794
|
+
|
|
795
|
+
assert_raise(StandardError, match: /Table 'sub_testings' has no foreign key for testings$/) {
|
|
796
|
+
ActiveRecord::Migrator.new(:up, [migration], @schema_migration, @internal_metadata).migrate
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
foreign_keys = @connection.foreign_keys("sub_testings")
|
|
800
|
+
assert_equal 2, foreign_keys.size
|
|
727
801
|
ensure
|
|
728
|
-
connection.drop_table :
|
|
802
|
+
connection.drop_table(:sub_testings, if_exists: true)
|
|
803
|
+
ActiveRecord::Base.clear_cache!
|
|
729
804
|
end
|
|
730
805
|
end
|
|
731
806
|
end
|
|
@@ -758,19 +833,26 @@ module ActiveRecord
|
|
|
758
833
|
def setup
|
|
759
834
|
@sqlserver_tasks =
|
|
760
835
|
Class.new do
|
|
761
|
-
def create
|
|
836
|
+
def create
|
|
837
|
+
end
|
|
762
838
|
|
|
763
|
-
def drop
|
|
839
|
+
def drop
|
|
840
|
+
end
|
|
764
841
|
|
|
765
|
-
def purge
|
|
842
|
+
def purge
|
|
843
|
+
end
|
|
766
844
|
|
|
767
|
-
def charset
|
|
845
|
+
def charset
|
|
846
|
+
end
|
|
768
847
|
|
|
769
|
-
def collation
|
|
848
|
+
def collation
|
|
849
|
+
end
|
|
770
850
|
|
|
771
|
-
def structure_dump(*)
|
|
851
|
+
def structure_dump(*)
|
|
852
|
+
end
|
|
772
853
|
|
|
773
|
-
def structure_load(*)
|
|
854
|
+
def structure_load(*)
|
|
855
|
+
end
|
|
774
856
|
end.new
|
|
775
857
|
|
|
776
858
|
$stdout, @original_stdout = StringIO.new, $stdout
|
|
@@ -791,7 +873,7 @@ module ActiveRecord
|
|
|
791
873
|
|
|
792
874
|
def test_sqlserver_create
|
|
793
875
|
with_stubbed_new do
|
|
794
|
-
assert_called(eval("@sqlserver_tasks"), :create) do
|
|
876
|
+
assert_called(eval("@sqlserver_tasks", binding, __FILE__, __LINE__), :create) do
|
|
795
877
|
ActiveRecord::Tasks::DatabaseTasks.create "adapter" => :sqlserver
|
|
796
878
|
end
|
|
797
879
|
end
|
|
@@ -804,7 +886,7 @@ module ActiveRecord
|
|
|
804
886
|
|
|
805
887
|
def test_sqlserver_drop
|
|
806
888
|
with_stubbed_new do
|
|
807
|
-
assert_called(eval("@sqlserver_tasks"), :drop) do
|
|
889
|
+
assert_called(eval("@sqlserver_tasks", binding, __FILE__, __LINE__), :drop) do
|
|
808
890
|
ActiveRecord::Tasks::DatabaseTasks.drop "adapter" => :sqlserver
|
|
809
891
|
end
|
|
810
892
|
end
|
|
@@ -817,7 +899,7 @@ module ActiveRecord
|
|
|
817
899
|
|
|
818
900
|
def test_sqlserver_purge
|
|
819
901
|
with_stubbed_new do
|
|
820
|
-
assert_called(eval("@sqlserver_tasks"), :purge) do
|
|
902
|
+
assert_called(eval("@sqlserver_tasks", binding, __FILE__, __LINE__), :purge) do
|
|
821
903
|
ActiveRecord::Tasks::DatabaseTasks.purge "adapter" => :sqlserver
|
|
822
904
|
end
|
|
823
905
|
end
|
|
@@ -830,7 +912,7 @@ module ActiveRecord
|
|
|
830
912
|
|
|
831
913
|
def test_sqlserver_charset
|
|
832
914
|
with_stubbed_new do
|
|
833
|
-
assert_called(eval("@sqlserver_tasks"), :charset) do
|
|
915
|
+
assert_called(eval("@sqlserver_tasks", binding, __FILE__, __LINE__), :charset) do
|
|
834
916
|
ActiveRecord::Tasks::DatabaseTasks.charset "adapter" => :sqlserver
|
|
835
917
|
end
|
|
836
918
|
end
|
|
@@ -843,7 +925,7 @@ module ActiveRecord
|
|
|
843
925
|
|
|
844
926
|
def test_sqlserver_collation
|
|
845
927
|
with_stubbed_new do
|
|
846
|
-
assert_called(eval("@sqlserver_tasks"), :collation) do
|
|
928
|
+
assert_called(eval("@sqlserver_tasks", binding, __FILE__, __LINE__), :collation) do
|
|
847
929
|
ActiveRecord::Tasks::DatabaseTasks.collation "adapter" => :sqlserver
|
|
848
930
|
end
|
|
849
931
|
end
|
|
@@ -857,10 +939,10 @@ module ActiveRecord
|
|
|
857
939
|
def test_sqlserver_structure_dump
|
|
858
940
|
with_stubbed_new do
|
|
859
941
|
assert_called_with(
|
|
860
|
-
eval("@sqlserver_tasks"), :structure_dump,
|
|
942
|
+
eval("@sqlserver_tasks", binding, __FILE__, __LINE__), :structure_dump,
|
|
861
943
|
["awesome-file.sql", nil]
|
|
862
944
|
) do
|
|
863
|
-
ActiveRecord::Tasks::DatabaseTasks.structure_dump({
|
|
945
|
+
ActiveRecord::Tasks::DatabaseTasks.structure_dump({"adapter" => :sqlserver}, "awesome-file.sql")
|
|
864
946
|
end
|
|
865
947
|
end
|
|
866
948
|
end
|
|
@@ -873,11 +955,11 @@ module ActiveRecord
|
|
|
873
955
|
def test_sqlserver_structure_load
|
|
874
956
|
with_stubbed_new do
|
|
875
957
|
assert_called_with(
|
|
876
|
-
eval("@sqlserver_tasks"),
|
|
958
|
+
eval("@sqlserver_tasks", binding, __FILE__, __LINE__),
|
|
877
959
|
:structure_load,
|
|
878
960
|
["awesome-file.sql", nil]
|
|
879
961
|
) do
|
|
880
|
-
ActiveRecord::Tasks::DatabaseTasks.structure_load({
|
|
962
|
+
ActiveRecord::Tasks::DatabaseTasks.structure_load({"adapter" => :sqlserver}, "awesome-file.sql")
|
|
881
963
|
end
|
|
882
964
|
end
|
|
883
965
|
end
|
|
@@ -910,15 +992,12 @@ class EagerAssociationTest < ActiveRecord::TestCase
|
|
|
910
992
|
coerce_tests! %r{including association based on sql condition and no database column}
|
|
911
993
|
end
|
|
912
994
|
|
|
913
|
-
require "models/topic"
|
|
914
|
-
require "models/customer"
|
|
915
|
-
require "models/non_primary_key"
|
|
916
995
|
class FinderTest < ActiveRecord::TestCase
|
|
917
996
|
fixtures :customers, :topics, :authors
|
|
918
997
|
|
|
919
998
|
# We have implicit ordering, via FETCH.
|
|
920
999
|
coerce_tests! %r{doesn't have implicit ordering},
|
|
921
|
-
|
|
1000
|
+
:test_find_doesnt_have_implicit_ordering
|
|
922
1001
|
|
|
923
1002
|
# Assert SQL Server limit implementation
|
|
924
1003
|
coerce_tests! :test_take_and_first_and_last_with_integer_should_use_sql_limit
|
|
@@ -986,8 +1065,8 @@ class FinderTest < ActiveRecord::TestCase
|
|
|
986
1065
|
end
|
|
987
1066
|
|
|
988
1067
|
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
|
989
|
-
coerce_tests! :
|
|
990
|
-
def
|
|
1068
|
+
coerce_tests! :test_implicit_order_column_is_configurable_with_a_single_value
|
|
1069
|
+
def test_implicit_order_column_is_configurable_with_a_single_value_coerced
|
|
991
1070
|
old_implicit_order_column = Topic.implicit_order_column
|
|
992
1071
|
Topic.implicit_order_column = "title"
|
|
993
1072
|
|
|
@@ -1002,6 +1081,32 @@ class FinderTest < ActiveRecord::TestCase
|
|
|
1002
1081
|
Topic.implicit_order_column = old_implicit_order_column
|
|
1003
1082
|
end
|
|
1004
1083
|
|
|
1084
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
|
1085
|
+
coerce_tests! :test_implicit_order_column_is_configurable_with_multiple_values
|
|
1086
|
+
def test_implicit_order_column_is_configurable_with_multiple_values_coerced
|
|
1087
|
+
old_implicit_order_column = Topic.implicit_order_column
|
|
1088
|
+
Topic.implicit_order_column = ["title", "author_name"]
|
|
1089
|
+
|
|
1090
|
+
assert_queries_and_values_match(/ORDER BY #{Regexp.escape(quote_table_name("topics.title"))} DESC, #{Regexp.escape(quote_table_name("topics.author_name"))} DESC, #{Regexp.escape(quote_table_name("topics.id"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/i, [1]) {
|
|
1091
|
+
Topic.last
|
|
1092
|
+
}
|
|
1093
|
+
ensure
|
|
1094
|
+
Topic.implicit_order_column = old_implicit_order_column
|
|
1095
|
+
end
|
|
1096
|
+
|
|
1097
|
+
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
|
1098
|
+
coerce_tests! :test_ordering_does_not_append_primary_keys_or_query_constraints_if_passed_an_implicit_order_column_array_ending_in_nil
|
|
1099
|
+
def test_ordering_does_not_append_primary_keys_or_query_constraints_if_passed_an_implicit_order_column_array_ending_in_nil_coerced
|
|
1100
|
+
old_implicit_order_column = Topic.implicit_order_column
|
|
1101
|
+
Topic.implicit_order_column = ["author_name", nil]
|
|
1102
|
+
|
|
1103
|
+
assert_queries_and_values_match(/ORDER BY #{Regexp.escape(quote_table_name("topics.author_name"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/i, [1]) {
|
|
1104
|
+
Topic.last
|
|
1105
|
+
}
|
|
1106
|
+
ensure
|
|
1107
|
+
Topic.implicit_order_column = old_implicit_order_column
|
|
1108
|
+
end
|
|
1109
|
+
|
|
1005
1110
|
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
|
1006
1111
|
coerce_tests! :test_implicit_order_set_to_primary_key
|
|
1007
1112
|
def test_implicit_order_set_to_primary_key_coerced
|
|
@@ -1186,8 +1291,6 @@ class LeftOuterJoinAssociationTest < ActiveRecord::TestCase
|
|
|
1186
1291
|
coerce_tests! :test_does_not_override_select
|
|
1187
1292
|
end
|
|
1188
1293
|
|
|
1189
|
-
require "models/developer"
|
|
1190
|
-
require "models/computer"
|
|
1191
1294
|
class NestedRelationScopingTest < ActiveRecord::TestCase
|
|
1192
1295
|
# Assert SQL Server limit implementation
|
|
1193
1296
|
coerce_tests! :test_merge_options
|
|
@@ -1203,7 +1306,6 @@ class NestedRelationScopingTest < ActiveRecord::TestCase
|
|
|
1203
1306
|
end
|
|
1204
1307
|
end
|
|
1205
1308
|
|
|
1206
|
-
require "models/topic"
|
|
1207
1309
|
class PersistenceTest < ActiveRecord::TestCase
|
|
1208
1310
|
# Rails test required updating a identity column.
|
|
1209
1311
|
coerce_tests! :test_update_columns_changing_id
|
|
@@ -1230,20 +1332,26 @@ class PersistenceTest < ActiveRecord::TestCase
|
|
|
1230
1332
|
coerce_tests! :test_model_with_no_auto_populated_fields_still_returns_primary_key_after_insert
|
|
1231
1333
|
end
|
|
1232
1334
|
|
|
1233
|
-
require "models/author"
|
|
1234
1335
|
class UpdateAllTest < ActiveRecord::TestCase
|
|
1235
|
-
#
|
|
1336
|
+
# Regular expression slightly different.
|
|
1236
1337
|
coerce_tests! :test_update_all_doesnt_ignore_order
|
|
1237
1338
|
def test_update_all_doesnt_ignore_order_coerced
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1339
|
+
assert_equal authors(:david).id + 1, authors(:mary).id # make sure there is going to be a duplicate PK error
|
|
1340
|
+
test_update_with_order_succeeds = lambda do |order|
|
|
1341
|
+
Author.order(order).update_all("id = id + 1")
|
|
1342
|
+
rescue ActiveRecord::ActiveRecordError
|
|
1343
|
+
false
|
|
1344
|
+
end
|
|
1345
|
+
|
|
1346
|
+
if test_update_with_order_succeeds.call("id DESC")
|
|
1347
|
+
# test that this wasn't a fluke and using an incorrect order results in an exception
|
|
1348
|
+
assert_not test_update_with_order_succeeds.call("id ASC")
|
|
1349
|
+
else
|
|
1350
|
+
# test that we're failing because the current Arel's engine doesn't support UPDATE ORDER BY queries is using subselects instead
|
|
1351
|
+
assert_queries_match(/\AUPDATE .+ \(SELECT .* ORDER BY id DESC.*\)/i) do
|
|
1352
|
+
test_update_with_order_succeeds.call("id DESC")
|
|
1353
|
+
end
|
|
1244
1354
|
end
|
|
1245
|
-
_(david.reload.name).must_equal "David"
|
|
1246
|
-
_(mary.reload.name).must_equal "Test"
|
|
1247
1355
|
end
|
|
1248
1356
|
|
|
1249
1357
|
# SELECT columns must be in the GROUP clause.
|
|
@@ -1256,7 +1364,7 @@ class UpdateAllTest < ActiveRecord::TestCase
|
|
|
1256
1364
|
|
|
1257
1365
|
assert_operator posts.length, :>, 0
|
|
1258
1366
|
assert posts.all? { |post| post.comments.length >= minimum_comments_count }
|
|
1259
|
-
assert posts.all? { |post| "ig"
|
|
1367
|
+
assert posts.all? { |post| post.title == "ig" }
|
|
1260
1368
|
|
|
1261
1369
|
post = Post.select(:id, :title).group(:title).joins(:comments).group("posts.id").having("count(comments.id) < #{minimum_comments_count}").first
|
|
1262
1370
|
assert_not_equal "ig", post.title
|
|
@@ -1281,7 +1389,6 @@ class DeleteAllTest < ActiveRecord::TestCase
|
|
|
1281
1389
|
end
|
|
1282
1390
|
end
|
|
1283
1391
|
|
|
1284
|
-
require "models/topic"
|
|
1285
1392
|
module ActiveRecord
|
|
1286
1393
|
class PredicateBuilderTest < ActiveRecord::TestCase
|
|
1287
1394
|
# Same as original test except string has `N` prefix to indicate unicode string.
|
|
@@ -1293,7 +1400,7 @@ module ActiveRecord
|
|
|
1293
1400
|
# Same as original test except string has `N` prefix to indicate unicode string.
|
|
1294
1401
|
coerce_tests! :test_registering_new_handlers_for_association
|
|
1295
1402
|
def test_registering_new_handlers_for_association_coerced
|
|
1296
|
-
assert_match %r{#{Regexp.escape(topic_title)} ~ N'rails'}i, Reply.joins(:topic).where(topics: {
|
|
1403
|
+
assert_match %r{#{Regexp.escape(topic_title)} ~ N'rails'}i, Reply.joins(:topic).where(topics: {title: /rails/}).to_sql
|
|
1297
1404
|
end
|
|
1298
1405
|
|
|
1299
1406
|
# Same as original test except string has `N` prefix to indicate unicode string.
|
|
@@ -1312,7 +1419,6 @@ module ActiveRecord
|
|
|
1312
1419
|
end
|
|
1313
1420
|
end
|
|
1314
1421
|
|
|
1315
|
-
require "models/task"
|
|
1316
1422
|
class QueryCacheTest < ActiveRecord::TestCase
|
|
1317
1423
|
# SQL Server adapter not in list of supported adapters in original test.
|
|
1318
1424
|
coerce_tests! :test_cache_does_not_wrap_results_in_arrays
|
|
@@ -1323,7 +1429,6 @@ class QueryCacheTest < ActiveRecord::TestCase
|
|
|
1323
1429
|
end
|
|
1324
1430
|
end
|
|
1325
1431
|
|
|
1326
|
-
require "models/post"
|
|
1327
1432
|
class RelationTest < ActiveRecord::TestCase
|
|
1328
1433
|
# Use LEN() instead of LENGTH() function.
|
|
1329
1434
|
coerce_tests! :test_reverse_order_with_function
|
|
@@ -1351,7 +1456,7 @@ class RelationTest < ActiveRecord::TestCase
|
|
|
1351
1456
|
assert Post.order(:title).reorder(nil).take
|
|
1352
1457
|
end
|
|
1353
1458
|
assert sql_log.none? { |sql| /order by \[posts\]\.\[title\]/i.match?(sql) }, "ORDER BY title was used in the query: #{sql_log}"
|
|
1354
|
-
assert sql_log.all?
|
|
1459
|
+
assert sql_log.all? { |sql| /order by \[posts\]\.\[id\]/i.match?(sql) }, "default ORDER BY ID was not used in the query: #{sql_log}"
|
|
1355
1460
|
end
|
|
1356
1461
|
|
|
1357
1462
|
# We have implicit ordering, via FETCH.
|
|
@@ -1363,7 +1468,7 @@ class RelationTest < ActiveRecord::TestCase
|
|
|
1363
1468
|
end
|
|
1364
1469
|
assert_equal posts(:welcome), post
|
|
1365
1470
|
assert sql_log.none? { |sql| /order by \[posts\]\.\[title\]/i.match?(sql) }, "ORDER BY title was used in the query: #{sql_log}"
|
|
1366
|
-
assert sql_log.all?
|
|
1471
|
+
assert sql_log.all? { |sql| /order by \[posts\]\.\[id\]/i.match?(sql) }, "default ORDER BY ID was not used in the query: #{sql_log}"
|
|
1367
1472
|
end
|
|
1368
1473
|
|
|
1369
1474
|
# We are not doing order duplicate removal anymore.
|
|
@@ -1377,7 +1482,7 @@ class RelationTest < ActiveRecord::TestCase
|
|
|
1377
1482
|
def test_multiple_where_and_having_clauses_coerced
|
|
1378
1483
|
post = Post.first
|
|
1379
1484
|
having_then_where = Post.having(id: post.id).where(title: post.title)
|
|
1380
|
-
|
|
1485
|
+
.having(id: post.id).where(title: post.title).group(:id).select(:id)
|
|
1381
1486
|
|
|
1382
1487
|
assert_equal [post], having_then_where
|
|
1383
1488
|
end
|
|
@@ -1458,10 +1563,15 @@ module ActiveRecord
|
|
|
1458
1563
|
query = Post.optimizer_hints("OMGHINT").merge(Post.optimizer_hints("OMGHINT")).to_sql
|
|
1459
1564
|
assert_equal expected, query
|
|
1460
1565
|
end
|
|
1566
|
+
|
|
1567
|
+
# Order column must be in the GROUP clause. However, with implicit ordering we can't test this when selecting non-aggregate expression column.
|
|
1568
|
+
coerce_tests! %r{no queries when using pick with non-aggregate expression and empty IN}
|
|
1569
|
+
|
|
1570
|
+
# Order column must be in the GROUP clause. However, with implicit ordering we can't test this when selecting aggregate expression column.
|
|
1571
|
+
coerce_tests! %r{runs queries when using pick with aggregate expression despite empty IN}
|
|
1461
1572
|
end
|
|
1462
1573
|
end
|
|
1463
1574
|
|
|
1464
|
-
require "models/post"
|
|
1465
1575
|
class SanitizeTest < ActiveRecord::TestCase
|
|
1466
1576
|
# Use nvarchar string (N'') in assert
|
|
1467
1577
|
coerce_tests! :test_sanitize_sql_like_example_use_case
|
|
@@ -1495,14 +1605,14 @@ end
|
|
|
1495
1605
|
|
|
1496
1606
|
class SchemaDumperTest < ActiveRecord::TestCase
|
|
1497
1607
|
# Use nvarchar string (N'') in assert
|
|
1498
|
-
coerce_tests! :
|
|
1499
|
-
def
|
|
1500
|
-
versions = %w
|
|
1608
|
+
coerce_tests! :test_dump_schema_versions_outputs_lexically_reverse_ordered_versions_regardless_of_database_order
|
|
1609
|
+
def test_dump_schema_versions_outputs_lexically_reverse_ordered_versions_regardless_of_database_order_coerced
|
|
1610
|
+
versions = %w[20100101010101 20100201010101 20100301010101]
|
|
1501
1611
|
versions.shuffle.each do |v|
|
|
1502
1612
|
@schema_migration.create_version(v)
|
|
1503
1613
|
end
|
|
1504
1614
|
|
|
1505
|
-
schema_info = ActiveRecord::Base.lease_connection.
|
|
1615
|
+
schema_info = ActiveRecord::Base.lease_connection.dump_schema_versions
|
|
1506
1616
|
expected = <<~STR
|
|
1507
1617
|
INSERT INTO #{ActiveRecord::Base.lease_connection.quote_table_name("schema_migrations")} (version) VALUES
|
|
1508
1618
|
(N'20100301010101'),
|
|
@@ -1527,7 +1637,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
|
|
|
1527
1637
|
# Fall through false positive with no filter.
|
|
1528
1638
|
coerce_tests! :test_schema_dumps_partial_indices
|
|
1529
1639
|
def test_schema_dumps_partial_indices_coerced
|
|
1530
|
-
index_definition = standard_dump.split(
|
|
1640
|
+
index_definition = standard_dump.split("\n").grep(/t.index.*company_partial_index/).first.strip
|
|
1531
1641
|
assert_equal 't.index ["firm_id", "type"], name: "company_partial_index", where: "([rating]>(10))"', index_definition
|
|
1532
1642
|
end
|
|
1533
1643
|
|
|
@@ -1544,7 +1654,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
|
|
|
1544
1654
|
# SQL Server formats the check constraint expression differently.
|
|
1545
1655
|
coerce_tests! :test_schema_dumps_check_constraints
|
|
1546
1656
|
def test_schema_dumps_check_constraints_coerced
|
|
1547
|
-
constraint_definition = dump_table_schema("products").split(
|
|
1657
|
+
constraint_definition = dump_table_schema("products").split("\n").grep(/t.check_constraint.*products_price_check/).first.strip
|
|
1548
1658
|
assert_equal 't.check_constraint "[price]>[discounted_price]", name: "products_price_check"', constraint_definition
|
|
1549
1659
|
end
|
|
1550
1660
|
end
|
|
@@ -1564,14 +1674,14 @@ class SchemaDumperDefaultsCoerceTest < ActiveRecord::TestCase
|
|
|
1564
1674
|
setup do
|
|
1565
1675
|
@connection = ActiveRecord::Base.lease_connection
|
|
1566
1676
|
@connection.create_table :dump_defaults, force: true do |t|
|
|
1567
|
-
t.string
|
|
1568
|
-
t.date
|
|
1677
|
+
t.string :string_with_default, default: "Hello!"
|
|
1678
|
+
t.date :date_with_default, default: "2014-06-05"
|
|
1569
1679
|
t.datetime :datetime_with_default, default: "2014-06-05 07:17:04"
|
|
1570
|
-
t.time
|
|
1571
|
-
t.decimal
|
|
1680
|
+
t.time :time_with_default, default: "07:17:04"
|
|
1681
|
+
t.decimal :decimal_with_default, default: "1234567890.0123456789", precision: 20, scale: 10
|
|
1572
1682
|
|
|
1573
|
-
t.text
|
|
1574
|
-
t.text
|
|
1683
|
+
t.text :text_with_default, default: "John' Doe"
|
|
1684
|
+
t.text :uuid, default: -> { "newid()" }
|
|
1575
1685
|
end
|
|
1576
1686
|
end
|
|
1577
1687
|
|
|
@@ -1588,7 +1698,6 @@ class TestAdapterWithInvalidConnection < ActiveRecord::TestCase
|
|
|
1588
1698
|
coerce_tests! %r{inspect on Model class does not raise}
|
|
1589
1699
|
end
|
|
1590
1700
|
|
|
1591
|
-
require "models/topic"
|
|
1592
1701
|
class TransactionTest < ActiveRecord::TestCase
|
|
1593
1702
|
# SQL Server does not have query for release_savepoint.
|
|
1594
1703
|
coerce_tests! :test_releasing_named_savepoints
|
|
@@ -1634,7 +1743,7 @@ class TransactionTest < ActiveRecord::TestCase
|
|
|
1634
1743
|
/DELETE/i,
|
|
1635
1744
|
/^SAVE TRANSACTION/i,
|
|
1636
1745
|
/DELETE/i,
|
|
1637
|
-
/COMMIT/i
|
|
1746
|
+
/COMMIT/i
|
|
1638
1747
|
]
|
|
1639
1748
|
|
|
1640
1749
|
assert_equal expected_queries.size, actual_queries.size
|
|
@@ -1670,7 +1779,7 @@ class TransactionTest < ActiveRecord::TestCase
|
|
|
1670
1779
|
/^SAVE TRANSACTION/i,
|
|
1671
1780
|
/DELETE/i,
|
|
1672
1781
|
/DELETE/i,
|
|
1673
|
-
/COMMIT/i
|
|
1782
|
+
/COMMIT/i
|
|
1674
1783
|
]
|
|
1675
1784
|
|
|
1676
1785
|
assert_equal expected_queries.size, actual_queries.size
|
|
@@ -1680,7 +1789,6 @@ class TransactionTest < ActiveRecord::TestCase
|
|
|
1680
1789
|
end
|
|
1681
1790
|
end
|
|
1682
1791
|
|
|
1683
|
-
require "models/tag"
|
|
1684
1792
|
class TransactionIsolationTest < ActiveRecord::TestCase
|
|
1685
1793
|
# SQL Server will lock the table for counts even when both
|
|
1686
1794
|
# connections are `READ COMMITTED`. So we bypass with `READPAST`.
|
|
@@ -1698,9 +1806,27 @@ class TransactionIsolationTest < ActiveRecord::TestCase
|
|
|
1698
1806
|
|
|
1699
1807
|
# I really need some help understanding this one.
|
|
1700
1808
|
coerce_tests! %r{repeatable read}
|
|
1809
|
+
|
|
1810
|
+
private
|
|
1811
|
+
|
|
1812
|
+
# Need to handle the resetting of the isolation level in the adapter by `SQLServerRealTransaction#commit` for each
|
|
1813
|
+
# connection pool. After the resetting events have been removed we can assert the number of expected isolation level
|
|
1814
|
+
# events. This workaround assumes that the `count` also matches the number of connection pools used in the test.
|
|
1815
|
+
# Note: MySQL & PostgreSQL do not reset the connection and SQLite does support transaction isolation.
|
|
1816
|
+
undef_method :assert_begin_isolation_level_event
|
|
1817
|
+
def assert_begin_isolation_level_event(events, isolation: "READ COMMITTED", count: 1)
|
|
1818
|
+
isolation_events = events.select { |event| event.match(/SET TRANSACTION ISOLATION LEVEL/) }
|
|
1819
|
+
|
|
1820
|
+
count.times do
|
|
1821
|
+
index_of_reset_starting_isolation_level_event = isolation_events.index("SET TRANSACTION ISOLATION LEVEL READ COMMITTED")
|
|
1822
|
+
assert index_of_reset_starting_isolation_level_event.present?
|
|
1823
|
+
isolation_events.delete_at(index_of_reset_starting_isolation_level_event)
|
|
1824
|
+
end
|
|
1825
|
+
|
|
1826
|
+
assert_equal count, isolation_events.count { |event| event.match(/SET TRANSACTION ISOLATION LEVEL #{isolation}/) }
|
|
1827
|
+
end
|
|
1701
1828
|
end
|
|
1702
1829
|
|
|
1703
|
-
require "models/book"
|
|
1704
1830
|
class ViewWithPrimaryKeyTest < ActiveRecord::TestCase
|
|
1705
1831
|
# We have a few view tables. use includes vs equality.
|
|
1706
1832
|
coerce_tests! :test_views
|
|
@@ -1724,7 +1850,6 @@ class ViewWithoutPrimaryKeyTest < ActiveRecord::TestCase
|
|
|
1724
1850
|
end
|
|
1725
1851
|
end
|
|
1726
1852
|
|
|
1727
|
-
require "models/author"
|
|
1728
1853
|
class YamlSerializationTest < ActiveRecord::TestCase
|
|
1729
1854
|
coerce_tests! :test_types_of_virtual_columns_are_not_changed_on_round_trip
|
|
1730
1855
|
def test_types_of_virtual_columns_are_not_changed_on_round_trip_coerced
|
|
@@ -1773,7 +1898,7 @@ class TimePrecisionTest < ActiveRecord::TestCase
|
|
|
1773
1898
|
coerce_tests! :test_time_precision_is_truncated_on_assignment
|
|
1774
1899
|
def test_time_precision_is_truncated_on_assignment_coerced
|
|
1775
1900
|
@connection.create_table(:foos, force: true)
|
|
1776
|
-
@connection.add_column :foos, :start,
|
|
1901
|
+
@connection.add_column :foos, :start, :time, precision: 0
|
|
1777
1902
|
@connection.add_column :foos, :finish, :time, precision: 6
|
|
1778
1903
|
|
|
1779
1904
|
time = ::Time.now.change(nsec: 123456789)
|
|
@@ -1809,8 +1934,8 @@ class DefaultNumbersTest < ActiveRecord::TestCase
|
|
|
1809
1934
|
coerce_tests! :test_default_negative_integer
|
|
1810
1935
|
def test_default_negative_integer_coerced
|
|
1811
1936
|
record = DefaultNumber.new
|
|
1812
|
-
assert_equal
|
|
1813
|
-
assert_equal
|
|
1937
|
+
assert_equal(-5, record.negative_integer)
|
|
1938
|
+
assert_equal(-5, record.negative_integer_before_type_cast)
|
|
1814
1939
|
end
|
|
1815
1940
|
|
|
1816
1941
|
# We do better with native types and do not return strings for everything.
|
|
@@ -1839,7 +1964,6 @@ module ActiveRecord
|
|
|
1839
1964
|
end
|
|
1840
1965
|
end
|
|
1841
1966
|
|
|
1842
|
-
require "models/book"
|
|
1843
1967
|
module ActiveRecord
|
|
1844
1968
|
class StatementCacheTest < ActiveRecord::TestCase
|
|
1845
1969
|
# Getting random failures.
|
|
@@ -1852,7 +1976,7 @@ module ActiveRecord
|
|
|
1852
1976
|
|
|
1853
1977
|
original_test_statement_cache_values_differ
|
|
1854
1978
|
ensure
|
|
1855
|
-
Book.where(author_id: nil, name:
|
|
1979
|
+
Book.where(author_id: nil, name: "my book").delete_all
|
|
1856
1980
|
Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
|
|
1857
1981
|
end
|
|
1858
1982
|
end
|
|
@@ -1862,53 +1986,38 @@ module ActiveRecord
|
|
|
1862
1986
|
module ConnectionAdapters
|
|
1863
1987
|
class SchemaCacheTest < ActiveRecord::TestCase
|
|
1864
1988
|
# Tests fail on Windows AppVeyor CI with 'Permission denied' error when renaming file during `File.atomic_write` call.
|
|
1865
|
-
coerce_tests! :test_yaml_dump_and_load, :test_yaml_dump_and_load_with_gzip if RbConfig::CONFIG["host_os"]
|
|
1866
|
-
|
|
1867
|
-
#
|
|
1868
|
-
coerce_tests! :
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1989
|
+
coerce_tests! :test_yaml_dump_and_load, :test_yaml_dump_and_load_with_gzip if /mswin|mingw/.match?(RbConfig::CONFIG["host_os"])
|
|
1990
|
+
|
|
1991
|
+
# Cast type in SQL Server is :varchar rather than Unicode :string.
|
|
1992
|
+
coerce_tests! :test_yaml_load_8_0_dump_without_cast_type_still_get_the_right_one
|
|
1993
|
+
def test_yaml_load_8_0_dump_without_cast_type_still_get_the_right_one
|
|
1994
|
+
cache = load_bound_reflection(schema_dump_8_0_path)
|
|
1995
|
+
|
|
1996
|
+
assert_no_queries do
|
|
1997
|
+
columns = cache.columns_hash("courses")
|
|
1998
|
+
assert_equal 3, columns.size
|
|
1999
|
+
cast_type = columns["name"].fetch_cast_type(@connection)
|
|
2000
|
+
assert_not_nil cast_type, "expected cast_type to be present"
|
|
2001
|
+
assert_equal :varchar, cast_type.type
|
|
1877
2002
|
end
|
|
1878
2003
|
end
|
|
1879
2004
|
|
|
1880
2005
|
private
|
|
1881
2006
|
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
current_default = column.default if column.default.is_a?(Time) && column.default.year < 1900
|
|
1887
|
-
end
|
|
1888
|
-
|
|
1889
|
-
# Correct problems
|
|
1890
|
-
if current_default.present?
|
|
1891
|
-
@connection.change_column_default(:sst_datatypes, :datetime, current_default.dup.change(year: 1900))
|
|
1892
|
-
end
|
|
1893
|
-
|
|
1894
|
-
# Run original test
|
|
1895
|
-
yield
|
|
1896
|
-
ensure
|
|
1897
|
-
# Revert changes
|
|
1898
|
-
@connection.change_column_default(:sst_datatypes, :datetime, current_default) if current_default.present?
|
|
2007
|
+
# We need to give the full paths for this to work.
|
|
2008
|
+
undef_method :schema_dump_5_1_path
|
|
2009
|
+
def schema_dump_5_1_path
|
|
2010
|
+
File.join(ARTest::SQLServer.root_activerecord, "test/assets/schema_dump_5_1.yml")
|
|
1899
2011
|
end
|
|
1900
2012
|
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
File.join(ARTest::SQLServer.root_activerecord, "test/assets/schema_dump_5_1.yml")
|
|
2013
|
+
undef_method :schema_dump_8_0_path
|
|
2014
|
+
def schema_dump_8_0_path
|
|
2015
|
+
File.join(ARTest::SQLServer.root_activerecord, "test/assets/schema_dump_8_0.yml")
|
|
1905
2016
|
end
|
|
1906
2017
|
end
|
|
1907
2018
|
end
|
|
1908
2019
|
end
|
|
1909
2020
|
|
|
1910
|
-
require "models/post"
|
|
1911
|
-
require "models/comment"
|
|
1912
2021
|
class UnsafeRawSqlTest < ActiveRecord::TestCase
|
|
1913
2022
|
fixtures :posts
|
|
1914
2023
|
|
|
@@ -2029,9 +2138,9 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
|
|
|
2029
2138
|
test "order: allows valid arguments with COLLATE" do
|
|
2030
2139
|
collation_name = "Latin1_General_CS_AS_WS"
|
|
2031
2140
|
|
|
2032
|
-
ids_expected = Post.order(Arel.sql(%
|
|
2141
|
+
ids_expected = Post.order(Arel.sql(%(author_id, title COLLATE #{collation_name} DESC))).pluck(:id)
|
|
2033
2142
|
|
|
2034
|
-
ids = Post.order(["author_id", %
|
|
2143
|
+
ids = Post.order(["author_id", %(title COLLATE #{collation_name} DESC)]).pluck(:id)
|
|
2035
2144
|
|
|
2036
2145
|
assert_equal ids_expected, ids
|
|
2037
2146
|
end
|
|
@@ -2105,14 +2214,13 @@ module ActiveRecord
|
|
|
2105
2214
|
end
|
|
2106
2215
|
end
|
|
2107
2216
|
|
|
2108
|
-
require "models/book"
|
|
2109
2217
|
class EnumTest < ActiveRecord::TestCase
|
|
2110
2218
|
# Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
|
|
2111
2219
|
coerce_tests! %r{enums are distinct per class}
|
|
2112
2220
|
test "enums are distinct per class coerced" do
|
|
2113
2221
|
Book.lease_connection.remove_index(:books, column: [:author_id, :name])
|
|
2114
2222
|
|
|
2115
|
-
send(:
|
|
2223
|
+
send(:"original_enums are distinct per class")
|
|
2116
2224
|
ensure
|
|
2117
2225
|
Book.where(author_id: nil, name: nil).delete_all
|
|
2118
2226
|
Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
|
|
@@ -2123,7 +2231,7 @@ class EnumTest < ActiveRecord::TestCase
|
|
|
2123
2231
|
test "creating new objects with enum scopes coerced" do
|
|
2124
2232
|
Book.lease_connection.remove_index(:books, column: [:author_id, :name])
|
|
2125
2233
|
|
|
2126
|
-
send(:
|
|
2234
|
+
send(:"original_creating new objects with enum scopes")
|
|
2127
2235
|
ensure
|
|
2128
2236
|
Book.where(author_id: nil, name: nil).delete_all
|
|
2129
2237
|
Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
|
|
@@ -2134,7 +2242,7 @@ class EnumTest < ActiveRecord::TestCase
|
|
|
2134
2242
|
test "enums are inheritable coerced" do
|
|
2135
2243
|
Book.lease_connection.remove_index(:books, column: [:author_id, :name])
|
|
2136
2244
|
|
|
2137
|
-
send(:
|
|
2245
|
+
send(:"original_enums are inheritable")
|
|
2138
2246
|
ensure
|
|
2139
2247
|
Book.where(author_id: nil, name: nil).delete_all
|
|
2140
2248
|
Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
|
|
@@ -2145,14 +2253,13 @@ class EnumTest < ActiveRecord::TestCase
|
|
|
2145
2253
|
test "serializable? with large number label coerced" do
|
|
2146
2254
|
Book.lease_connection.remove_index(:books, column: [:author_id, :name])
|
|
2147
2255
|
|
|
2148
|
-
send(:
|
|
2256
|
+
send(:"original_serializable\\? with large number label")
|
|
2149
2257
|
ensure
|
|
2150
2258
|
Book.where(author_id: nil, name: nil).delete_all
|
|
2151
2259
|
Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
|
|
2152
2260
|
end
|
|
2153
2261
|
end
|
|
2154
2262
|
|
|
2155
|
-
require "models/citation"
|
|
2156
2263
|
class EagerLoadingTooManyIdsTest < ActiveRecord::TestCase
|
|
2157
2264
|
fixtures :citations
|
|
2158
2265
|
|
|
@@ -2187,7 +2294,7 @@ end
|
|
|
2187
2294
|
class ReloadModelsTest < ActiveRecord::TestCase
|
|
2188
2295
|
# Skip test on Windows. The number of arguments passed to `IO.popen` in
|
|
2189
2296
|
# `activesupport/lib/active_support/testing/isolation.rb` exceeds what Windows can handle.
|
|
2190
|
-
coerce_tests! :test_has_one_with_reload if RbConfig::CONFIG["host_os"]
|
|
2297
|
+
coerce_tests! :test_has_one_with_reload if /mswin|mingw/.match?(RbConfig::CONFIG["host_os"])
|
|
2191
2298
|
end
|
|
2192
2299
|
|
|
2193
2300
|
class MarshalSerializationTest < ActiveRecord::TestCase
|
|
@@ -2283,15 +2390,14 @@ end
|
|
|
2283
2390
|
|
|
2284
2391
|
class MigratorTest < ActiveRecord::TestCase
|
|
2285
2392
|
# Test fails on Windows AppVeyor CI for unknown reason.
|
|
2286
|
-
coerce_tests! :test_migrator_db_has_no_schema_migrations_table if RbConfig::CONFIG["host_os"]
|
|
2393
|
+
coerce_tests! :test_migrator_db_has_no_schema_migrations_table if /mswin|mingw/.match?(RbConfig::CONFIG["host_os"])
|
|
2287
2394
|
end
|
|
2288
2395
|
|
|
2289
2396
|
class MultiDbMigratorTest < ActiveRecord::TestCase
|
|
2290
2397
|
# Test fails on Windows AppVeyor CI for unknown reason.
|
|
2291
|
-
coerce_tests! :test_migrator_db_has_no_schema_migrations_table if RbConfig::CONFIG["host_os"]
|
|
2398
|
+
coerce_tests! :test_migrator_db_has_no_schema_migrations_table if /mswin|mingw/.match?(RbConfig::CONFIG["host_os"])
|
|
2292
2399
|
end
|
|
2293
2400
|
|
|
2294
|
-
require "models/book"
|
|
2295
2401
|
class FieldOrderedValuesTest < ActiveRecord::TestCase
|
|
2296
2402
|
# Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
|
|
2297
2403
|
coerce_tests! :test_in_order_of_with_enums_values
|
|
@@ -2338,12 +2444,11 @@ class FieldOrderedValuesTest < ActiveRecord::TestCase
|
|
|
2338
2444
|
end
|
|
2339
2445
|
end
|
|
2340
2446
|
|
|
2341
|
-
require "models/dashboard"
|
|
2342
2447
|
class QueryLogsTest < ActiveRecord::TestCase
|
|
2343
2448
|
# Invalid character encoding causes `ActiveRecord::StatementInvalid` error similar to Postgres.
|
|
2344
2449
|
coerce_tests! :test_invalid_encoding_query
|
|
2345
2450
|
def test_invalid_encoding_query_coerced
|
|
2346
|
-
ActiveRecord::QueryLogs.tags = [
|
|
2451
|
+
ActiveRecord::QueryLogs.tags = [:application]
|
|
2347
2452
|
assert_raises ActiveRecord::StatementInvalid do
|
|
2348
2453
|
ActiveRecord::Base.lease_connection.execute "select 1 as '\xFF'"
|
|
2349
2454
|
end
|
|
@@ -2356,8 +2461,8 @@ class InsertAllTest < ActiveRecord::TestCase
|
|
|
2356
2461
|
def test_insert_all_returns_requested_sql_fields_coerced
|
|
2357
2462
|
skip unless supports_insert_returning?
|
|
2358
2463
|
|
|
2359
|
-
result = Book.insert_all! [{
|
|
2360
|
-
assert_equal %w[
|
|
2464
|
+
result = Book.insert_all! [{name: "Rework", author_id: 1}], returning: Arel.sql("UPPER(INSERTED.name) as name")
|
|
2465
|
+
assert_equal %w[REWORK], result.pluck("name")
|
|
2361
2466
|
end
|
|
2362
2467
|
|
|
2363
2468
|
# Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
|
|
@@ -2397,7 +2502,7 @@ module ActiveRecord
|
|
|
2397
2502
|
undef_method :invalid_add_column_option_exception_message
|
|
2398
2503
|
def invalid_add_column_option_exception_message(key)
|
|
2399
2504
|
default_keys = [":limit", ":precision", ":scale", ":default", ":null", ":collation", ":comment", ":primary_key", ":if_exists", ":if_not_exists"]
|
|
2400
|
-
default_keys.concat([":is_identity"]) # SQL Server additional valid keys
|
|
2505
|
+
default_keys.concat([":is_identity", ":as", ":stored"]) # SQL Server additional valid keys
|
|
2401
2506
|
|
|
2402
2507
|
"Unknown key: :#{key}. Valid keys are: #{default_keys.join(", ")}"
|
|
2403
2508
|
end
|
|
@@ -2405,11 +2510,11 @@ module ActiveRecord
|
|
|
2405
2510
|
end
|
|
2406
2511
|
end
|
|
2407
2512
|
|
|
2408
|
-
|
|
2409
2513
|
# Need to use `install_unregistered_type_fallback` instead of `install_unregistered_type_error` so that message-pack
|
|
2410
2514
|
# can read and write `ActiveRecord::ConnectionAdapters::SQLServer::Type::Data` objects.
|
|
2411
2515
|
class ActiveRecordMessagePackTest < ActiveRecord::TestCase
|
|
2412
2516
|
private
|
|
2517
|
+
|
|
2413
2518
|
undef_method :serializer
|
|
2414
2519
|
def serializer
|
|
2415
2520
|
@serializer ||= ::MessagePack::Factory.new.tap do |factory|
|
|
@@ -2432,7 +2537,7 @@ end
|
|
|
2432
2537
|
|
|
2433
2538
|
module ActiveRecord
|
|
2434
2539
|
module ConnectionAdapters
|
|
2435
|
-
class ConnectionHandlersShardingDbTest
|
|
2540
|
+
class ConnectionHandlersShardingDbTest < ActiveRecord::TestCase
|
|
2436
2541
|
# Tests are not about a specific adapter.
|
|
2437
2542
|
coerce_all_tests!
|
|
2438
2543
|
end
|
|
@@ -2556,7 +2661,6 @@ module ActiveRecord
|
|
|
2556
2661
|
end
|
|
2557
2662
|
end
|
|
2558
2663
|
|
|
2559
|
-
|
|
2560
2664
|
module ActiveRecord
|
|
2561
2665
|
class TableMetadataTest < ActiveSupport::TestCase
|
|
2562
2666
|
# Adapter returns an object that is subclass of what is expected in the original test.
|
|
@@ -2623,7 +2727,7 @@ module ActiveRecord
|
|
|
2623
2727
|
relation = Company.with_recursive(
|
|
2624
2728
|
top_companies_and_children: [
|
|
2625
2729
|
Company.where(firm_id: nil),
|
|
2626
|
-
Company.joins("JOIN top_companies_and_children ON companies.firm_id = top_companies_and_children.id")
|
|
2730
|
+
Company.joins("JOIN top_companies_and_children ON companies.firm_id = top_companies_and_children.id")
|
|
2627
2731
|
]
|
|
2628
2732
|
).from("top_companies_and_children AS companies")
|
|
2629
2733
|
|
|
@@ -2633,3 +2737,103 @@ module ActiveRecord
|
|
|
2633
2737
|
end
|
|
2634
2738
|
end
|
|
2635
2739
|
|
|
2740
|
+
module ActiveRecord
|
|
2741
|
+
class AdapterConnectionTest < ActiveRecord::TestCase
|
|
2742
|
+
# Original method only handled the core adapters.
|
|
2743
|
+
undef_method :raw_transaction_open?
|
|
2744
|
+
def raw_transaction_open?(connection)
|
|
2745
|
+
transaction_count = connection.instance_variable_get(:@raw_connection).execute("SELECT @@TRANCOUNT AS TRANSACTION_COUNT").first["TRANSACTION_COUNT"]
|
|
2746
|
+
transaction_count > 0
|
|
2747
|
+
rescue
|
|
2748
|
+
false
|
|
2749
|
+
end
|
|
2750
|
+
end
|
|
2751
|
+
end
|
|
2752
|
+
|
|
2753
|
+
class EachTest < ActiveRecord::TestCase
|
|
2754
|
+
# Match SQL Server limit implementation.
|
|
2755
|
+
coerce_tests! :test_in_batches_executes_range_queries_when_unconstrained
|
|
2756
|
+
def test_in_batches_executes_range_queries_when_unconstrained_coerced
|
|
2757
|
+
quoted_posts_id = Regexp.escape(quote_table_name("posts.id"))
|
|
2758
|
+
|
|
2759
|
+
relations = assert_queries_match(/ORDER BY #{quoted_posts_id} ASC OFFSET @\S ROWS FETCH NEXT @\S ROWS ONLY/i, count: 6) do
|
|
2760
|
+
assert_queries_match(/ORDER BY #{quoted_posts_id} ASC OFFSET 0 ROWS FETCH NEXT @\S ROWS ONLY/i, count: 1) do
|
|
2761
|
+
Post.in_batches(of: 2).to_a
|
|
2762
|
+
end
|
|
2763
|
+
end
|
|
2764
|
+
|
|
2765
|
+
assert_queries_match(/WHERE #{quoted_posts_id} > .+ AND #{quoted_posts_id} <= .+/i) do
|
|
2766
|
+
relations.each { |relation| assert_kind_of Post, relation.first }
|
|
2767
|
+
end
|
|
2768
|
+
end
|
|
2769
|
+
|
|
2770
|
+
# Match SQL Server limit implementation.
|
|
2771
|
+
coerce_tests! :test_in_batches_executes_in_queries_when_unconstrained_and_opted_out_of_ranges
|
|
2772
|
+
def test_in_batches_executes_in_queries_when_unconstrained_and_opted_out_of_ranges_coerced
|
|
2773
|
+
quoted_posts_id = Regexp.escape(quote_table_name("posts.id"))
|
|
2774
|
+
|
|
2775
|
+
relations = assert_queries_match(/ORDER BY #{quoted_posts_id} ASC OFFSET 0 ROWS FETCH NEXT @\S ROWS ONLY/i, count: 6) do
|
|
2776
|
+
Post.in_batches(of: 2, use_ranges: false).to_a
|
|
2777
|
+
end
|
|
2778
|
+
|
|
2779
|
+
assert_queries_match(/#{quoted_posts_id} IN \(.+\)/i) do
|
|
2780
|
+
relations.each { |relation| assert_kind_of Post, relation.first }
|
|
2781
|
+
end
|
|
2782
|
+
end
|
|
2783
|
+
|
|
2784
|
+
# Match SQL Server limit implementation.
|
|
2785
|
+
coerce_tests! :test_in_batches_executes_in_queries_when_constrained
|
|
2786
|
+
def test_in_batches_executes_in_queries_when_constrained_coerced
|
|
2787
|
+
quoted_posts_id = Regexp.escape(quote_table_name("posts.id"))
|
|
2788
|
+
|
|
2789
|
+
relations = assert_queries_match(/ORDER BY #{quoted_posts_id} ASC OFFSET 0 ROWS FETCH NEXT @\S ROWS ONLY/i, count: 3) do
|
|
2790
|
+
Post.where("id < ?", 5).in_batches(of: 2).to_a
|
|
2791
|
+
end
|
|
2792
|
+
|
|
2793
|
+
assert_queries_match(/#{quoted_posts_id} IN \(.+\)/i) do
|
|
2794
|
+
relations.each { |relation| assert_kind_of Post, relation.first }
|
|
2795
|
+
end
|
|
2796
|
+
end
|
|
2797
|
+
|
|
2798
|
+
# Match SQL Server limit implementation.
|
|
2799
|
+
coerce_tests! :test_in_batches_executes_range_queries_when_constrained_and_opted_in_into_ranges
|
|
2800
|
+
def test_in_batches_executes_range_queries_when_constrained_and_opted_in_into_ranges_coerced
|
|
2801
|
+
quoted_posts_id = Regexp.escape(quote_table_name("posts.id"))
|
|
2802
|
+
|
|
2803
|
+
relations = assert_queries_match(/ORDER BY #{quoted_posts_id} ASC OFFSET @\S ROWS FETCH NEXT @\S ROWS ONLY/i, count: 3) do
|
|
2804
|
+
assert_queries_match(/ORDER BY #{quoted_posts_id} ASC OFFSET 0 ROWS FETCH NEXT @\S ROWS ONLY/i, count: 1) do
|
|
2805
|
+
Post.where("id < ?", 5).in_batches(of: 2, use_ranges: true).to_a
|
|
2806
|
+
end
|
|
2807
|
+
end
|
|
2808
|
+
|
|
2809
|
+
assert_queries_match(/#{quoted_posts_id} > .+ AND #{quoted_posts_id} <= .+/i) do
|
|
2810
|
+
relations.each { |relation| assert_kind_of Post, relation.first }
|
|
2811
|
+
end
|
|
2812
|
+
end
|
|
2813
|
+
|
|
2814
|
+
# Match SQL Server SQL format.
|
|
2815
|
+
coerce_tests! :test_in_batches_should_unscope_cursor_after_pluck
|
|
2816
|
+
def test_in_batches_should_unscope_cursor_after_pluck_coerced
|
|
2817
|
+
all_ids = Post.limit(2).pluck(:id)
|
|
2818
|
+
found_ids = []
|
|
2819
|
+
# only a single clause on id (i.e. not 'id IN (?,?) AND id = ?', but only 'id = ?')
|
|
2820
|
+
assert_queries_match(/WHERE #{Regexp.escape(quote_table_name("posts.id"))} = \S+ ORDER BY/) do
|
|
2821
|
+
Post.where(id: all_ids).in_batches(of: 1) do |relation|
|
|
2822
|
+
found_ids << relation.pick(:id)
|
|
2823
|
+
end
|
|
2824
|
+
end
|
|
2825
|
+
assert_equal all_ids.sort, found_ids
|
|
2826
|
+
end
|
|
2827
|
+
|
|
2828
|
+
# Match SQL Server SQL format.
|
|
2829
|
+
coerce_tests! :test_in_batches_loaded_should_unscope_cursor_after_pluck
|
|
2830
|
+
def test_in_batches_loaded_should_unscope_cursor_after_pluck_coerced
|
|
2831
|
+
all_ids = Post.limit(2).pluck(:id)
|
|
2832
|
+
# only a single clause on id (i.e. not 'id IN (?,?) AND id = ?', but only 'id = ?')
|
|
2833
|
+
assert_queries_match(/WHERE #{Regexp.escape(quote_table_name("posts.id"))} = \S+;/) do
|
|
2834
|
+
Post.where(id: all_ids).in_batches(of: 1, load: true) do |relation|
|
|
2835
|
+
relation.delete_all
|
|
2836
|
+
end
|
|
2837
|
+
end
|
|
2838
|
+
end
|
|
2839
|
+
end
|