activerecord-sqlserver-adapter 8.0.9 → 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 -62
- 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 +121 -90
- 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 +396 -343
- 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/optimizer_hints_test_sqlserver.rb +1 -1
- 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/showplan_test_sqlserver.rb +2 -2
- data/test/cases/specific_schema_test_sqlserver.rb +6 -6
- 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 +25 -3
- 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
|
-
|
|
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
|
|
@@ -257,21 +270,6 @@ end
|
|
|
257
270
|
|
|
258
271
|
module ActiveRecord
|
|
259
272
|
class BindParameterTest < ActiveRecord::TestCase
|
|
260
|
-
# Same as original coerced test except log is found using `EXEC sp_executesql` wrapper.
|
|
261
|
-
coerce_tests! :test_binds_are_logged
|
|
262
|
-
def test_binds_are_logged_coerced
|
|
263
|
-
sub = Arel::Nodes::BindParam.new(1)
|
|
264
|
-
binds = [Relation::QueryAttribute.new("id", 1, Type::Value.new)]
|
|
265
|
-
sql = "select * from topics where id = #{sub.to_sql}"
|
|
266
|
-
|
|
267
|
-
@connection.exec_query(sql, "SQL", binds)
|
|
268
|
-
|
|
269
|
-
logged_sql = "EXEC sp_executesql N'#{sql}', N'#{sub.to_sql} int', #{sub.to_sql} = 1"
|
|
270
|
-
message = @subscriber.calls.find { |args| args[4][:sql] == logged_sql }
|
|
271
|
-
|
|
272
|
-
assert_equal binds, message[4][:binds]
|
|
273
|
-
end
|
|
274
|
-
|
|
275
273
|
# SQL Server adapter does not use a statement cache as query plans are already reused using `EXEC sp_executesql`.
|
|
276
274
|
coerce_tests! :test_statement_cache
|
|
277
275
|
coerce_tests! :test_statement_cache_with_query_cache
|
|
@@ -279,55 +277,6 @@ module ActiveRecord
|
|
|
279
277
|
coerce_tests! :test_statement_cache_with_find_by
|
|
280
278
|
coerce_tests! :test_statement_cache_with_in_clause
|
|
281
279
|
coerce_tests! :test_statement_cache_with_sql_string_literal
|
|
282
|
-
|
|
283
|
-
# Same as original coerced test except prepared statements include `EXEC sp_executesql` wrapper.
|
|
284
|
-
coerce_tests! :test_bind_params_to_sql_with_prepared_statements, :test_bind_params_to_sql_with_unprepared_statements
|
|
285
|
-
def test_bind_params_to_sql_with_prepared_statements_coerced
|
|
286
|
-
assert_bind_params_to_sql_coerced(prepared: true)
|
|
287
|
-
end
|
|
288
|
-
|
|
289
|
-
def test_bind_params_to_sql_with_unprepared_statements_coerced
|
|
290
|
-
@connection.unprepared_statement do
|
|
291
|
-
assert_bind_params_to_sql_coerced(prepared: false)
|
|
292
|
-
end
|
|
293
|
-
end
|
|
294
|
-
|
|
295
|
-
private
|
|
296
|
-
|
|
297
|
-
def assert_bind_params_to_sql_coerced(prepared:)
|
|
298
|
-
table = Author.quoted_table_name
|
|
299
|
-
pk = "#{table}.#{Author.quoted_primary_key}"
|
|
300
|
-
|
|
301
|
-
# prepared_statements: true
|
|
302
|
-
#
|
|
303
|
-
# EXEC sp_executesql N'SELECT [authors].* FROM [authors] WHERE [authors].[id] IN (@0, @1, @2) OR [authors].[id] IS NULL)', N'@0 bigint, @1 bigint, @2 bigint', @0 = 1, @1 = 2, @2 = 3
|
|
304
|
-
#
|
|
305
|
-
# prepared_statements: false
|
|
306
|
-
#
|
|
307
|
-
# SELECT [authors].* FROM [authors] WHERE ([authors].[id] IN (1, 2, 3) OR [authors].[id] IS NULL)
|
|
308
|
-
#
|
|
309
|
-
sql_unprepared = "SELECT #{table}.* FROM #{table} WHERE (#{pk} IN (#{bind_params(1..3)}) OR #{pk} IS NULL)"
|
|
310
|
-
sql_prepared = "EXEC sp_executesql N'SELECT #{table}.* FROM #{table} WHERE (#{pk} IN (#{bind_params(1..3)}) OR #{pk} IS NULL)', N'@0 bigint, @1 bigint, @2 bigint', @0 = 1, @1 = 2, @2 = 3"
|
|
311
|
-
|
|
312
|
-
authors = Author.where(id: [1, 2, 3, nil])
|
|
313
|
-
assert_equal sql_unprepared, @connection.to_sql(authors.arel)
|
|
314
|
-
assert_queries_match(prepared ? sql_prepared : sql_unprepared) { assert_equal 3, authors.length }
|
|
315
|
-
|
|
316
|
-
# prepared_statements: true
|
|
317
|
-
#
|
|
318
|
-
# EXEC sp_executesql N'SELECT [authors].* FROM [authors] WHERE [authors].[id] IN (@0, @1, @2)', N'@0 bigint, @1 bigint, @2 bigint', @0 = 1, @1 = 2, @2 = 3
|
|
319
|
-
#
|
|
320
|
-
# prepared_statements: false
|
|
321
|
-
#
|
|
322
|
-
# SELECT [authors].* FROM [authors] WHERE [authors].[id] IN (1, 2, 3)
|
|
323
|
-
#
|
|
324
|
-
sql_unprepared = "SELECT #{table}.* FROM #{table} WHERE #{pk} IN (#{bind_params(1..3)})"
|
|
325
|
-
sql_prepared = "EXEC sp_executesql N'SELECT #{table}.* FROM #{table} WHERE #{pk} IN (#{bind_params(1..3)})', N'@0 bigint, @1 bigint, @2 bigint', @0 = 1, @1 = 2, @2 = 3"
|
|
326
|
-
|
|
327
|
-
authors = Author.where(id: [1, 2, 3, 9223372036854775808])
|
|
328
|
-
assert_equal sql_unprepared, @connection.to_sql(authors.arel)
|
|
329
|
-
assert_queries_match(prepared ? sql_prepared : sql_unprepared) { assert_equal 3, authors.length }
|
|
330
|
-
end
|
|
331
280
|
end
|
|
332
281
|
end
|
|
333
282
|
|
|
@@ -347,7 +296,7 @@ module ActiveRecord
|
|
|
347
296
|
|
|
348
297
|
original_test_payload_row_count_on_select_all
|
|
349
298
|
ensure
|
|
350
|
-
Book.where(author_id: nil, name:
|
|
299
|
+
Book.where(author_id: nil, name: "row count book 1").delete_all
|
|
351
300
|
Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
|
|
352
301
|
end
|
|
353
302
|
|
|
@@ -358,7 +307,7 @@ module ActiveRecord
|
|
|
358
307
|
|
|
359
308
|
original_test_payload_row_count_on_pluck
|
|
360
309
|
ensure
|
|
361
|
-
Book.where(author_id: nil, name:
|
|
310
|
+
Book.where(author_id: nil, name: "row count book 2").delete_all
|
|
362
311
|
Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
|
|
363
312
|
end
|
|
364
313
|
|
|
@@ -369,9 +318,16 @@ module ActiveRecord
|
|
|
369
318
|
|
|
370
319
|
original_test_payload_row_count_on_raw_sql
|
|
371
320
|
ensure
|
|
372
|
-
Book.where(author_id: nil, name:
|
|
321
|
+
Book.where(author_id: nil, name: "row count book 3").delete_all
|
|
373
322
|
Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
|
|
374
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
|
|
375
331
|
end
|
|
376
332
|
end
|
|
377
333
|
|
|
@@ -391,7 +347,7 @@ class CalculationsTest < ActiveRecord::TestCase
|
|
|
391
347
|
assert_predicate accounts, :loaded?
|
|
392
348
|
assert_equal expected, accounts.count(:id)
|
|
393
349
|
end
|
|
394
|
-
|
|
350
|
+
|
|
395
351
|
# Fix randomly failing test. The loading of the model's schema was affecting the test.
|
|
396
352
|
coerce_tests! :test_offset_is_kept
|
|
397
353
|
def test_offset_is_kept_coerced
|
|
@@ -441,11 +397,11 @@ class CalculationsTest < ActiveRecord::TestCase
|
|
|
441
397
|
rails_core = companies(:rails_core)
|
|
442
398
|
|
|
443
399
|
account = Account
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
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!
|
|
449
405
|
|
|
450
406
|
# id was not selected, so it should be nil
|
|
451
407
|
# (cannot select id because it wasn't used in the GROUP BY clause)
|
|
@@ -485,7 +441,6 @@ class CalculationsTest < ActiveRecord::TestCase
|
|
|
485
441
|
assert_equal(52.5, firm.avg_credit_limit)
|
|
486
442
|
end
|
|
487
443
|
|
|
488
|
-
|
|
489
444
|
# In SQL Server the `AVG()` function for a list of integers returns an integer so need to cast values as decimals before averaging.
|
|
490
445
|
# SELECT columns must be in the GROUP clause.
|
|
491
446
|
coerce_tests! :test_select_avg_with_joins_and_group_by_as_virtual_attribute_with_ar
|
|
@@ -493,11 +448,11 @@ class CalculationsTest < ActiveRecord::TestCase
|
|
|
493
448
|
rails_core = companies(:rails_core)
|
|
494
449
|
|
|
495
450
|
firm = DependentFirm
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
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!
|
|
501
456
|
|
|
502
457
|
# all the DependentFirm attributes should be present
|
|
503
458
|
assert_equal rails_core, firm
|
|
@@ -512,7 +467,7 @@ class CalculationsTest < ActiveRecord::TestCase
|
|
|
512
467
|
def test_limit_is_kept_coerced
|
|
513
468
|
queries = capture_sql { Account.limit(1).count }
|
|
514
469
|
assert_equal 1, queries.length
|
|
515
|
-
assert_match(/ORDER BY \[accounts\]\.\[id\] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY
|
|
470
|
+
assert_match(/ORDER BY \[accounts\]\.\[id\] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/, queries.first)
|
|
516
471
|
end
|
|
517
472
|
|
|
518
473
|
# Match SQL Server limit implementation
|
|
@@ -520,7 +475,7 @@ class CalculationsTest < ActiveRecord::TestCase
|
|
|
520
475
|
def test_limit_with_offset_is_kept_coerced
|
|
521
476
|
queries = capture_sql { Account.limit(1).offset(1).count }
|
|
522
477
|
assert_equal 1, queries.length
|
|
523
|
-
assert_match(/ORDER BY \[accounts\]\.\[id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY
|
|
478
|
+
assert_match(/ORDER BY \[accounts\]\.\[id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY/, queries.first)
|
|
524
479
|
end
|
|
525
480
|
|
|
526
481
|
# SQL Server needs an alias for the calculated column
|
|
@@ -563,8 +518,8 @@ module ActiveRecord
|
|
|
563
518
|
five = columns.detect { |c| c.name == "five" }
|
|
564
519
|
|
|
565
520
|
assert_equal "hello", one.default
|
|
566
|
-
assert_equal true,
|
|
567
|
-
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)
|
|
568
523
|
assert_equal 1, four.default
|
|
569
524
|
assert_equal "hello", five.default
|
|
570
525
|
end
|
|
@@ -633,7 +588,7 @@ module ActiveRecord
|
|
|
633
588
|
# Our defaults are real 70000 integers vs '70000' strings.
|
|
634
589
|
coerce_tests! :test_rename_column_preserves_default_value_not_null
|
|
635
590
|
def test_rename_column_preserves_default_value_not_null_coerced
|
|
636
|
-
add_column "test_models", "salary", :integer, :
|
|
591
|
+
add_column "test_models", "salary", :integer, default: 70000
|
|
637
592
|
default_before = connection.columns("test_models").find { |c| c.name == "salary" }.default
|
|
638
593
|
assert_equal 70000, default_before
|
|
639
594
|
rename_column "test_models", "salary", "annual_salary"
|
|
@@ -647,8 +602,8 @@ module ActiveRecord
|
|
|
647
602
|
coerce_tests! :test_remove_column_with_multi_column_index
|
|
648
603
|
def test_remove_column_with_multi_column_index_coerced
|
|
649
604
|
add_column "test_models", :hat_size, :integer
|
|
650
|
-
add_column "test_models", :hat_style, :string, :
|
|
651
|
-
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
|
|
652
607
|
assert_equal 1, connection.indexes("test_models").size
|
|
653
608
|
remove_column("test_models", "hat_size")
|
|
654
609
|
assert_equal [], connection.indexes("test_models").map(&:name)
|
|
@@ -675,14 +630,20 @@ class MigrationTest < ActiveRecord::TestCase
|
|
|
675
630
|
coerce_tests! :test_add_column_with_casted_type_if_not_exists_set_to_true
|
|
676
631
|
def test_add_column_with_casted_type_if_not_exists_set_to_true_coerced
|
|
677
632
|
migration_a = Class.new(ActiveRecord::Migration::Current) {
|
|
678
|
-
def version
|
|
633
|
+
def version
|
|
634
|
+
100
|
|
635
|
+
end
|
|
636
|
+
|
|
679
637
|
def migrate(x)
|
|
680
638
|
add_column "people", "last_name", :binary
|
|
681
639
|
end
|
|
682
640
|
}.new
|
|
683
641
|
|
|
684
642
|
migration_b = Class.new(ActiveRecord::Migration::Current) {
|
|
685
|
-
def version
|
|
643
|
+
def version
|
|
644
|
+
101
|
|
645
|
+
end
|
|
646
|
+
|
|
686
647
|
def migrate(x)
|
|
687
648
|
add_column "people", "last_name", :binary, if_not_exists: true
|
|
688
649
|
end
|
|
@@ -711,7 +672,10 @@ module ActiveRecord
|
|
|
711
672
|
long_table_name = "a" * (connection.table_name_length + 1)
|
|
712
673
|
migration = Class.new(ActiveRecord::Migration[7.0]) {
|
|
713
674
|
@@long_table_name = long_table_name
|
|
714
|
-
def version
|
|
675
|
+
def version
|
|
676
|
+
100
|
|
677
|
+
end
|
|
678
|
+
|
|
715
679
|
def migrate(x)
|
|
716
680
|
create_table @@long_table_name
|
|
717
681
|
end
|
|
@@ -722,7 +686,11 @@ module ActiveRecord
|
|
|
722
686
|
end
|
|
723
687
|
assert_match(/The identifier that starts with '#{long_table_name[0...-1]}' is too long/i, error.message)
|
|
724
688
|
ensure
|
|
725
|
-
|
|
689
|
+
begin
|
|
690
|
+
connection.drop_table(long_table_name)
|
|
691
|
+
rescue
|
|
692
|
+
nil
|
|
693
|
+
end
|
|
726
694
|
end
|
|
727
695
|
|
|
728
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).
|
|
@@ -733,7 +701,10 @@ module ActiveRecord
|
|
|
733
701
|
|
|
734
702
|
migration = Class.new(ActiveRecord::Migration[7.0]) {
|
|
735
703
|
@@long_table_name = long_table_name
|
|
736
|
-
def version
|
|
704
|
+
def version
|
|
705
|
+
100
|
|
706
|
+
end
|
|
707
|
+
|
|
737
708
|
def migrate(x)
|
|
738
709
|
rename_table :more_testings, @@long_table_name
|
|
739
710
|
end
|
|
@@ -744,14 +715,22 @@ module ActiveRecord
|
|
|
744
715
|
assert_not connection.table_exists?(:more_testings)
|
|
745
716
|
assert connection.table_exists?(long_table_name[0...-1])
|
|
746
717
|
ensure
|
|
747
|
-
|
|
748
|
-
|
|
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
|
|
749
728
|
end
|
|
750
729
|
|
|
751
730
|
# SQL Server has a different maximum index name length.
|
|
752
731
|
coerce_tests! :test_add_index_errors_on_too_long_name_7_0
|
|
753
732
|
def test_add_index_errors_on_too_long_name_7_0_coerced
|
|
754
|
-
long_index_name =
|
|
733
|
+
long_index_name = "a" * (connection.index_name_length + 1)
|
|
755
734
|
|
|
756
735
|
migration = Class.new(ActiveRecord::Migration[7.0]) {
|
|
757
736
|
@@long_index_name = long_index_name
|
|
@@ -764,13 +743,13 @@ module ActiveRecord
|
|
|
764
743
|
error = assert_raises(StandardError) do
|
|
765
744
|
ActiveRecord::Migrator.new(:up, [migration], @schema_migration, @internal_metadata).migrate
|
|
766
745
|
end
|
|
767
|
-
assert_match(/Index name
|
|
746
|
+
assert_match(/Index name '#{long_index_name}' on table 'testings' is too long/i, error.message)
|
|
768
747
|
end
|
|
769
748
|
|
|
770
749
|
# SQL Server has a different maximum index name length.
|
|
771
750
|
coerce_tests! :test_create_table_add_index_errors_on_too_long_name_7_0
|
|
772
751
|
def test_create_table_add_index_errors_on_too_long_name_7_0_coerced
|
|
773
|
-
long_index_name =
|
|
752
|
+
long_index_name = "a" * (connection.index_name_length + 1)
|
|
774
753
|
|
|
775
754
|
migration = Class.new(ActiveRecord::Migration[7.0]) {
|
|
776
755
|
@@long_index_name = long_index_name
|
|
@@ -787,9 +766,41 @@ module ActiveRecord
|
|
|
787
766
|
error = assert_raises(StandardError) do
|
|
788
767
|
ActiveRecord::Migrator.new(:up, [migration], @schema_migration, @internal_metadata).migrate
|
|
789
768
|
end
|
|
790
|
-
assert_match(/Index name
|
|
769
|
+
assert_match(/Index name '#{long_index_name}' on table 'more_testings' is too long/i, error.message)
|
|
791
770
|
ensure
|
|
792
|
-
|
|
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
|
|
801
|
+
ensure
|
|
802
|
+
connection.drop_table(:sub_testings, if_exists: true)
|
|
803
|
+
ActiveRecord::Base.clear_cache!
|
|
793
804
|
end
|
|
794
805
|
end
|
|
795
806
|
end
|
|
@@ -822,19 +833,26 @@ module ActiveRecord
|
|
|
822
833
|
def setup
|
|
823
834
|
@sqlserver_tasks =
|
|
824
835
|
Class.new do
|
|
825
|
-
def create
|
|
836
|
+
def create
|
|
837
|
+
end
|
|
826
838
|
|
|
827
|
-
def drop
|
|
839
|
+
def drop
|
|
840
|
+
end
|
|
828
841
|
|
|
829
|
-
def purge
|
|
842
|
+
def purge
|
|
843
|
+
end
|
|
830
844
|
|
|
831
|
-
def charset
|
|
845
|
+
def charset
|
|
846
|
+
end
|
|
832
847
|
|
|
833
|
-
def collation
|
|
848
|
+
def collation
|
|
849
|
+
end
|
|
834
850
|
|
|
835
|
-
def structure_dump(*)
|
|
851
|
+
def structure_dump(*)
|
|
852
|
+
end
|
|
836
853
|
|
|
837
|
-
def structure_load(*)
|
|
854
|
+
def structure_load(*)
|
|
855
|
+
end
|
|
838
856
|
end.new
|
|
839
857
|
|
|
840
858
|
$stdout, @original_stdout = StringIO.new, $stdout
|
|
@@ -855,7 +873,7 @@ module ActiveRecord
|
|
|
855
873
|
|
|
856
874
|
def test_sqlserver_create
|
|
857
875
|
with_stubbed_new do
|
|
858
|
-
assert_called(eval("@sqlserver_tasks"), :create) do
|
|
876
|
+
assert_called(eval("@sqlserver_tasks", binding, __FILE__, __LINE__), :create) do
|
|
859
877
|
ActiveRecord::Tasks::DatabaseTasks.create "adapter" => :sqlserver
|
|
860
878
|
end
|
|
861
879
|
end
|
|
@@ -868,7 +886,7 @@ module ActiveRecord
|
|
|
868
886
|
|
|
869
887
|
def test_sqlserver_drop
|
|
870
888
|
with_stubbed_new do
|
|
871
|
-
assert_called(eval("@sqlserver_tasks"), :drop) do
|
|
889
|
+
assert_called(eval("@sqlserver_tasks", binding, __FILE__, __LINE__), :drop) do
|
|
872
890
|
ActiveRecord::Tasks::DatabaseTasks.drop "adapter" => :sqlserver
|
|
873
891
|
end
|
|
874
892
|
end
|
|
@@ -881,7 +899,7 @@ module ActiveRecord
|
|
|
881
899
|
|
|
882
900
|
def test_sqlserver_purge
|
|
883
901
|
with_stubbed_new do
|
|
884
|
-
assert_called(eval("@sqlserver_tasks"), :purge) do
|
|
902
|
+
assert_called(eval("@sqlserver_tasks", binding, __FILE__, __LINE__), :purge) do
|
|
885
903
|
ActiveRecord::Tasks::DatabaseTasks.purge "adapter" => :sqlserver
|
|
886
904
|
end
|
|
887
905
|
end
|
|
@@ -894,7 +912,7 @@ module ActiveRecord
|
|
|
894
912
|
|
|
895
913
|
def test_sqlserver_charset
|
|
896
914
|
with_stubbed_new do
|
|
897
|
-
assert_called(eval("@sqlserver_tasks"), :charset) do
|
|
915
|
+
assert_called(eval("@sqlserver_tasks", binding, __FILE__, __LINE__), :charset) do
|
|
898
916
|
ActiveRecord::Tasks::DatabaseTasks.charset "adapter" => :sqlserver
|
|
899
917
|
end
|
|
900
918
|
end
|
|
@@ -907,7 +925,7 @@ module ActiveRecord
|
|
|
907
925
|
|
|
908
926
|
def test_sqlserver_collation
|
|
909
927
|
with_stubbed_new do
|
|
910
|
-
assert_called(eval("@sqlserver_tasks"), :collation) do
|
|
928
|
+
assert_called(eval("@sqlserver_tasks", binding, __FILE__, __LINE__), :collation) do
|
|
911
929
|
ActiveRecord::Tasks::DatabaseTasks.collation "adapter" => :sqlserver
|
|
912
930
|
end
|
|
913
931
|
end
|
|
@@ -921,10 +939,10 @@ module ActiveRecord
|
|
|
921
939
|
def test_sqlserver_structure_dump
|
|
922
940
|
with_stubbed_new do
|
|
923
941
|
assert_called_with(
|
|
924
|
-
eval("@sqlserver_tasks"), :structure_dump,
|
|
942
|
+
eval("@sqlserver_tasks", binding, __FILE__, __LINE__), :structure_dump,
|
|
925
943
|
["awesome-file.sql", nil]
|
|
926
944
|
) do
|
|
927
|
-
ActiveRecord::Tasks::DatabaseTasks.structure_dump({
|
|
945
|
+
ActiveRecord::Tasks::DatabaseTasks.structure_dump({"adapter" => :sqlserver}, "awesome-file.sql")
|
|
928
946
|
end
|
|
929
947
|
end
|
|
930
948
|
end
|
|
@@ -937,11 +955,11 @@ module ActiveRecord
|
|
|
937
955
|
def test_sqlserver_structure_load
|
|
938
956
|
with_stubbed_new do
|
|
939
957
|
assert_called_with(
|
|
940
|
-
eval("@sqlserver_tasks"),
|
|
958
|
+
eval("@sqlserver_tasks", binding, __FILE__, __LINE__),
|
|
941
959
|
:structure_load,
|
|
942
960
|
["awesome-file.sql", nil]
|
|
943
961
|
) do
|
|
944
|
-
ActiveRecord::Tasks::DatabaseTasks.structure_load({
|
|
962
|
+
ActiveRecord::Tasks::DatabaseTasks.structure_load({"adapter" => :sqlserver}, "awesome-file.sql")
|
|
945
963
|
end
|
|
946
964
|
end
|
|
947
965
|
end
|
|
@@ -974,22 +992,19 @@ class EagerAssociationTest < ActiveRecord::TestCase
|
|
|
974
992
|
coerce_tests! %r{including association based on sql condition and no database column}
|
|
975
993
|
end
|
|
976
994
|
|
|
977
|
-
require "models/topic"
|
|
978
|
-
require "models/customer"
|
|
979
|
-
require "models/non_primary_key"
|
|
980
995
|
class FinderTest < ActiveRecord::TestCase
|
|
981
996
|
fixtures :customers, :topics, :authors
|
|
982
997
|
|
|
983
998
|
# We have implicit ordering, via FETCH.
|
|
984
999
|
coerce_tests! %r{doesn't have implicit ordering},
|
|
985
|
-
|
|
1000
|
+
:test_find_doesnt_have_implicit_ordering
|
|
986
1001
|
|
|
987
1002
|
# Assert SQL Server limit implementation
|
|
988
1003
|
coerce_tests! :test_take_and_first_and_last_with_integer_should_use_sql_limit
|
|
989
1004
|
def test_take_and_first_and_last_with_integer_should_use_sql_limit_coerced
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
1005
|
+
assert_queries_and_values_match(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/, [3]) { Topic.take(3).entries }
|
|
1006
|
+
assert_queries_and_values_match(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/, [2]) { Topic.first(2).entries }
|
|
1007
|
+
assert_queries_and_values_match(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/, [5]) { Topic.last(5).entries }
|
|
993
1008
|
end
|
|
994
1009
|
|
|
995
1010
|
# This fails only when run in the full test suite task. Just taking it out of the mix.
|
|
@@ -1020,7 +1035,7 @@ class FinderTest < ActiveRecord::TestCase
|
|
|
1020
1035
|
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
|
1021
1036
|
coerce_tests! :test_include_on_unloaded_relation_with_match
|
|
1022
1037
|
def test_include_on_unloaded_relation_with_match_coerced
|
|
1023
|
-
assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY
|
|
1038
|
+
assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY/) do
|
|
1024
1039
|
assert_equal true, Customer.where(name: "David").include?(customers(:david))
|
|
1025
1040
|
end
|
|
1026
1041
|
end
|
|
@@ -1028,7 +1043,7 @@ class FinderTest < ActiveRecord::TestCase
|
|
|
1028
1043
|
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
|
1029
1044
|
coerce_tests! :test_include_on_unloaded_relation_without_match
|
|
1030
1045
|
def test_include_on_unloaded_relation_without_match_coerced
|
|
1031
|
-
assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY
|
|
1046
|
+
assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY/) do
|
|
1032
1047
|
assert_equal false, Customer.where(name: "David").include?(customers(:mary))
|
|
1033
1048
|
end
|
|
1034
1049
|
end
|
|
@@ -1036,7 +1051,7 @@ class FinderTest < ActiveRecord::TestCase
|
|
|
1036
1051
|
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
|
1037
1052
|
coerce_tests! :test_member_on_unloaded_relation_with_match
|
|
1038
1053
|
def test_member_on_unloaded_relation_with_match_coerced
|
|
1039
|
-
assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY
|
|
1054
|
+
assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY/) do
|
|
1040
1055
|
assert_equal true, Customer.where(name: "David").member?(customers(:david))
|
|
1041
1056
|
end
|
|
1042
1057
|
end
|
|
@@ -1044,14 +1059,14 @@ class FinderTest < ActiveRecord::TestCase
|
|
|
1044
1059
|
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
|
1045
1060
|
coerce_tests! :test_member_on_unloaded_relation_without_match
|
|
1046
1061
|
def test_member_on_unloaded_relation_without_match_coerced
|
|
1047
|
-
assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY
|
|
1062
|
+
assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY/) do
|
|
1048
1063
|
assert_equal false, Customer.where(name: "David").member?(customers(:mary))
|
|
1049
1064
|
end
|
|
1050
1065
|
end
|
|
1051
1066
|
|
|
1052
1067
|
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
|
1053
|
-
coerce_tests! :
|
|
1054
|
-
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
|
|
1055
1070
|
old_implicit_order_column = Topic.implicit_order_column
|
|
1056
1071
|
Topic.implicit_order_column = "title"
|
|
1057
1072
|
|
|
@@ -1059,7 +1074,33 @@ class FinderTest < ActiveRecord::TestCase
|
|
|
1059
1074
|
assert_equal topics(:third), Topic.last
|
|
1060
1075
|
|
|
1061
1076
|
c = Topic.lease_connection
|
|
1062
|
-
|
|
1077
|
+
assert_queries_and_values_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/i, [1]) {
|
|
1078
|
+
Topic.last
|
|
1079
|
+
}
|
|
1080
|
+
ensure
|
|
1081
|
+
Topic.implicit_order_column = old_implicit_order_column
|
|
1082
|
+
end
|
|
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]) {
|
|
1063
1104
|
Topic.last
|
|
1064
1105
|
}
|
|
1065
1106
|
ensure
|
|
@@ -1073,7 +1114,7 @@ class FinderTest < ActiveRecord::TestCase
|
|
|
1073
1114
|
Topic.implicit_order_column = "id"
|
|
1074
1115
|
|
|
1075
1116
|
c = Topic.lease_connection
|
|
1076
|
-
|
|
1117
|
+
assert_queries_and_values_match(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/i, [1]) {
|
|
1077
1118
|
Topic.last
|
|
1078
1119
|
}
|
|
1079
1120
|
ensure
|
|
@@ -1088,7 +1129,7 @@ class FinderTest < ActiveRecord::TestCase
|
|
|
1088
1129
|
|
|
1089
1130
|
c = NonPrimaryKey.lease_connection
|
|
1090
1131
|
|
|
1091
|
-
|
|
1132
|
+
assert_queries_and_values_match(/ORDER BY #{Regexp.escape(c.quote_table_name("non_primary_keys.created_at"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/i, [1]) {
|
|
1092
1133
|
NonPrimaryKey.last
|
|
1093
1134
|
}
|
|
1094
1135
|
ensure
|
|
@@ -1098,7 +1139,7 @@ class FinderTest < ActiveRecord::TestCase
|
|
|
1098
1139
|
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
|
1099
1140
|
coerce_tests! :test_member_on_unloaded_relation_with_composite_primary_key
|
|
1100
1141
|
def test_member_on_unloaded_relation_with_composite_primary_key_coerced
|
|
1101
|
-
assert_queries_match(/1 AS one.* FETCH NEXT @
|
|
1142
|
+
assert_queries_match(/1 AS one.* FETCH NEXT @3 ROWS ONLY/) do
|
|
1102
1143
|
book = cpk_books(:cpk_great_author_first_book)
|
|
1103
1144
|
assert Cpk::Book.where(title: "The first book").member?(book)
|
|
1104
1145
|
end
|
|
@@ -1113,7 +1154,7 @@ class FinderTest < ActiveRecord::TestCase
|
|
|
1113
1154
|
quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color"))
|
|
1114
1155
|
quoted_descrption = Regexp.escape(c.quote_table_name("clothing_items.description"))
|
|
1115
1156
|
|
|
1116
|
-
assert_queries_match(/ORDER BY #{quoted_descrption} ASC, #{quoted_type} ASC, #{quoted_color} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY
|
|
1157
|
+
assert_queries_match(/ORDER BY #{quoted_descrption} ASC, #{quoted_type} ASC, #{quoted_color} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY/i) do
|
|
1117
1158
|
assert_kind_of ClothingItem, ClothingItem.first
|
|
1118
1159
|
end
|
|
1119
1160
|
ensure
|
|
@@ -1127,7 +1168,7 @@ class FinderTest < ActiveRecord::TestCase
|
|
|
1127
1168
|
quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type"))
|
|
1128
1169
|
quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color"))
|
|
1129
1170
|
|
|
1130
|
-
assert_queries_match(/ORDER BY #{quoted_type} DESC, #{quoted_color} DESC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY
|
|
1171
|
+
assert_queries_match(/ORDER BY #{quoted_type} DESC, #{quoted_color} DESC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY/i) do
|
|
1131
1172
|
assert_kind_of ClothingItem, ClothingItem.last
|
|
1132
1173
|
end
|
|
1133
1174
|
end
|
|
@@ -1139,7 +1180,7 @@ class FinderTest < ActiveRecord::TestCase
|
|
|
1139
1180
|
quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type"))
|
|
1140
1181
|
quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color"))
|
|
1141
1182
|
|
|
1142
|
-
assert_queries_match(/ORDER BY #{quoted_type} ASC, #{quoted_color} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY
|
|
1183
|
+
assert_queries_match(/ORDER BY #{quoted_type} ASC, #{quoted_color} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY/i) do
|
|
1143
1184
|
assert_kind_of ClothingItem, ClothingItem.first
|
|
1144
1185
|
end
|
|
1145
1186
|
end
|
|
@@ -1152,7 +1193,7 @@ class FinderTest < ActiveRecord::TestCase
|
|
|
1152
1193
|
quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type"))
|
|
1153
1194
|
quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color"))
|
|
1154
1195
|
|
|
1155
|
-
assert_queries_match(/ORDER BY #{quoted_color} ASC, #{quoted_type} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY
|
|
1196
|
+
assert_queries_match(/ORDER BY #{quoted_color} ASC, #{quoted_type} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY/i) do
|
|
1156
1197
|
assert_kind_of ClothingItem, ClothingItem.first
|
|
1157
1198
|
end
|
|
1158
1199
|
ensure
|
|
@@ -1162,7 +1203,7 @@ class FinderTest < ActiveRecord::TestCase
|
|
|
1162
1203
|
# Check for `FETCH NEXT x ROWS` rather then `LIMIT`.
|
|
1163
1204
|
coerce_tests! :test_include_on_unloaded_relation_with_composite_primary_key
|
|
1164
1205
|
def test_include_on_unloaded_relation_with_composite_primary_key_coerced
|
|
1165
|
-
assert_queries_match(/1 AS one.*OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY
|
|
1206
|
+
assert_queries_match(/1 AS one.*OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY/) do
|
|
1166
1207
|
book = cpk_books(:cpk_great_author_first_book)
|
|
1167
1208
|
assert Cpk::Book.where(title: "The first book").include?(book)
|
|
1168
1209
|
end
|
|
@@ -1172,11 +1213,11 @@ class FinderTest < ActiveRecord::TestCase
|
|
|
1172
1213
|
coerce_tests! :test_nth_to_last_with_order_uses_limit
|
|
1173
1214
|
def test_nth_to_last_with_order_uses_limit_coerced
|
|
1174
1215
|
c = Topic.lease_connection
|
|
1175
|
-
assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET @(\d) ROWS FETCH NEXT @(\d) ROWS ONLY
|
|
1216
|
+
assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET @(\d) ROWS FETCH NEXT @(\d) ROWS ONLY/i) do
|
|
1176
1217
|
Topic.second_to_last
|
|
1177
1218
|
end
|
|
1178
1219
|
|
|
1179
|
-
assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.updated_at"))} DESC OFFSET @(\d) ROWS FETCH NEXT @(\d) ROWS ONLY
|
|
1220
|
+
assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.updated_at"))} DESC OFFSET @(\d) ROWS FETCH NEXT @(\d) ROWS ONLY/i) do
|
|
1180
1221
|
Topic.order(:updated_at).second_to_last
|
|
1181
1222
|
end
|
|
1182
1223
|
end
|
|
@@ -1227,7 +1268,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
|
|
|
1227
1268
|
def test_has_one_coerced
|
|
1228
1269
|
firm = companies(:first_firm)
|
|
1229
1270
|
first_account = Account.find(1)
|
|
1230
|
-
assert_queries_match(/FETCH NEXT @(\d) ROWS ONLY
|
|
1271
|
+
assert_queries_match(/FETCH NEXT @(\d) ROWS ONLY/) do
|
|
1231
1272
|
assert_equal first_account, firm.account
|
|
1232
1273
|
assert_equal first_account.credit_limit, firm.account.credit_limit
|
|
1233
1274
|
end
|
|
@@ -1239,7 +1280,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
|
|
|
1239
1280
|
coerce_tests! :test_has_one_through_executes_limited_query
|
|
1240
1281
|
def test_has_one_through_executes_limited_query_coerced
|
|
1241
1282
|
boring_club = clubs(:boring_club)
|
|
1242
|
-
assert_queries_match(/FETCH NEXT @(\d) ROWS ONLY
|
|
1283
|
+
assert_queries_match(/FETCH NEXT @(\d) ROWS ONLY/) do
|
|
1243
1284
|
assert_equal boring_club, @member.general_club
|
|
1244
1285
|
end
|
|
1245
1286
|
end
|
|
@@ -1250,8 +1291,6 @@ class LeftOuterJoinAssociationTest < ActiveRecord::TestCase
|
|
|
1250
1291
|
coerce_tests! :test_does_not_override_select
|
|
1251
1292
|
end
|
|
1252
1293
|
|
|
1253
|
-
require "models/developer"
|
|
1254
|
-
require "models/computer"
|
|
1255
1294
|
class NestedRelationScopingTest < ActiveRecord::TestCase
|
|
1256
1295
|
# Assert SQL Server limit implementation
|
|
1257
1296
|
coerce_tests! :test_merge_options
|
|
@@ -1267,7 +1306,6 @@ class NestedRelationScopingTest < ActiveRecord::TestCase
|
|
|
1267
1306
|
end
|
|
1268
1307
|
end
|
|
1269
1308
|
|
|
1270
|
-
require "models/topic"
|
|
1271
1309
|
class PersistenceTest < ActiveRecord::TestCase
|
|
1272
1310
|
# Rails test required updating a identity column.
|
|
1273
1311
|
coerce_tests! :test_update_columns_changing_id
|
|
@@ -1294,20 +1332,26 @@ class PersistenceTest < ActiveRecord::TestCase
|
|
|
1294
1332
|
coerce_tests! :test_model_with_no_auto_populated_fields_still_returns_primary_key_after_insert
|
|
1295
1333
|
end
|
|
1296
1334
|
|
|
1297
|
-
require "models/author"
|
|
1298
1335
|
class UpdateAllTest < ActiveRecord::TestCase
|
|
1299
|
-
#
|
|
1336
|
+
# Regular expression slightly different.
|
|
1300
1337
|
coerce_tests! :test_update_all_doesnt_ignore_order
|
|
1301
1338
|
def test_update_all_doesnt_ignore_order_coerced
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
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
|
|
1308
1354
|
end
|
|
1309
|
-
_(david.reload.name).must_equal "David"
|
|
1310
|
-
_(mary.reload.name).must_equal "Test"
|
|
1311
1355
|
end
|
|
1312
1356
|
|
|
1313
1357
|
# SELECT columns must be in the GROUP clause.
|
|
@@ -1320,7 +1364,7 @@ class UpdateAllTest < ActiveRecord::TestCase
|
|
|
1320
1364
|
|
|
1321
1365
|
assert_operator posts.length, :>, 0
|
|
1322
1366
|
assert posts.all? { |post| post.comments.length >= minimum_comments_count }
|
|
1323
|
-
assert posts.all? { |post| "ig"
|
|
1367
|
+
assert posts.all? { |post| post.title == "ig" }
|
|
1324
1368
|
|
|
1325
1369
|
post = Post.select(:id, :title).group(:title).joins(:comments).group("posts.id").having("count(comments.id) < #{minimum_comments_count}").first
|
|
1326
1370
|
assert_not_equal "ig", post.title
|
|
@@ -1345,7 +1389,6 @@ class DeleteAllTest < ActiveRecord::TestCase
|
|
|
1345
1389
|
end
|
|
1346
1390
|
end
|
|
1347
1391
|
|
|
1348
|
-
require "models/topic"
|
|
1349
1392
|
module ActiveRecord
|
|
1350
1393
|
class PredicateBuilderTest < ActiveRecord::TestCase
|
|
1351
1394
|
# Same as original test except string has `N` prefix to indicate unicode string.
|
|
@@ -1357,7 +1400,7 @@ module ActiveRecord
|
|
|
1357
1400
|
# Same as original test except string has `N` prefix to indicate unicode string.
|
|
1358
1401
|
coerce_tests! :test_registering_new_handlers_for_association
|
|
1359
1402
|
def test_registering_new_handlers_for_association_coerced
|
|
1360
|
-
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
|
|
1361
1404
|
end
|
|
1362
1405
|
|
|
1363
1406
|
# Same as original test except string has `N` prefix to indicate unicode string.
|
|
@@ -1376,7 +1419,6 @@ module ActiveRecord
|
|
|
1376
1419
|
end
|
|
1377
1420
|
end
|
|
1378
1421
|
|
|
1379
|
-
require "models/task"
|
|
1380
1422
|
class QueryCacheTest < ActiveRecord::TestCase
|
|
1381
1423
|
# SQL Server adapter not in list of supported adapters in original test.
|
|
1382
1424
|
coerce_tests! :test_cache_does_not_wrap_results_in_arrays
|
|
@@ -1387,7 +1429,6 @@ class QueryCacheTest < ActiveRecord::TestCase
|
|
|
1387
1429
|
end
|
|
1388
1430
|
end
|
|
1389
1431
|
|
|
1390
|
-
require "models/post"
|
|
1391
1432
|
class RelationTest < ActiveRecord::TestCase
|
|
1392
1433
|
# Use LEN() instead of LENGTH() function.
|
|
1393
1434
|
coerce_tests! :test_reverse_order_with_function
|
|
@@ -1415,7 +1456,7 @@ class RelationTest < ActiveRecord::TestCase
|
|
|
1415
1456
|
assert Post.order(:title).reorder(nil).take
|
|
1416
1457
|
end
|
|
1417
1458
|
assert sql_log.none? { |sql| /order by \[posts\]\.\[title\]/i.match?(sql) }, "ORDER BY title was used in the query: #{sql_log}"
|
|
1418
|
-
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}"
|
|
1419
1460
|
end
|
|
1420
1461
|
|
|
1421
1462
|
# We have implicit ordering, via FETCH.
|
|
@@ -1427,7 +1468,7 @@ class RelationTest < ActiveRecord::TestCase
|
|
|
1427
1468
|
end
|
|
1428
1469
|
assert_equal posts(:welcome), post
|
|
1429
1470
|
assert sql_log.none? { |sql| /order by \[posts\]\.\[title\]/i.match?(sql) }, "ORDER BY title was used in the query: #{sql_log}"
|
|
1430
|
-
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}"
|
|
1431
1472
|
end
|
|
1432
1473
|
|
|
1433
1474
|
# We are not doing order duplicate removal anymore.
|
|
@@ -1441,7 +1482,7 @@ class RelationTest < ActiveRecord::TestCase
|
|
|
1441
1482
|
def test_multiple_where_and_having_clauses_coerced
|
|
1442
1483
|
post = Post.first
|
|
1443
1484
|
having_then_where = Post.having(id: post.id).where(title: post.title)
|
|
1444
|
-
|
|
1485
|
+
.having(id: post.id).where(title: post.title).group(:id).select(:id)
|
|
1445
1486
|
|
|
1446
1487
|
assert_equal [post], having_then_where
|
|
1447
1488
|
end
|
|
@@ -1460,7 +1501,7 @@ class RelationTest < ActiveRecord::TestCase
|
|
|
1460
1501
|
# Find any limit via our expression.
|
|
1461
1502
|
coerce_tests! %r{relations don't load all records in #inspect}
|
|
1462
1503
|
def test_relations_dont_load_all_records_in_inspect_coerced
|
|
1463
|
-
assert_queries_match(/NEXT @0 ROWS
|
|
1504
|
+
assert_queries_match(/NEXT @0 ROWS/) do
|
|
1464
1505
|
Post.all.inspect
|
|
1465
1506
|
end
|
|
1466
1507
|
end
|
|
@@ -1522,10 +1563,15 @@ module ActiveRecord
|
|
|
1522
1563
|
query = Post.optimizer_hints("OMGHINT").merge(Post.optimizer_hints("OMGHINT")).to_sql
|
|
1523
1564
|
assert_equal expected, query
|
|
1524
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}
|
|
1525
1572
|
end
|
|
1526
1573
|
end
|
|
1527
1574
|
|
|
1528
|
-
require "models/post"
|
|
1529
1575
|
class SanitizeTest < ActiveRecord::TestCase
|
|
1530
1576
|
# Use nvarchar string (N'') in assert
|
|
1531
1577
|
coerce_tests! :test_sanitize_sql_like_example_use_case
|
|
@@ -1559,19 +1605,19 @@ end
|
|
|
1559
1605
|
|
|
1560
1606
|
class SchemaDumperTest < ActiveRecord::TestCase
|
|
1561
1607
|
# Use nvarchar string (N'') in assert
|
|
1562
|
-
coerce_tests! :
|
|
1563
|
-
def
|
|
1564
|
-
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]
|
|
1565
1611
|
versions.shuffle.each do |v|
|
|
1566
1612
|
@schema_migration.create_version(v)
|
|
1567
1613
|
end
|
|
1568
1614
|
|
|
1569
|
-
schema_info = ActiveRecord::Base.lease_connection.
|
|
1615
|
+
schema_info = ActiveRecord::Base.lease_connection.dump_schema_versions
|
|
1570
1616
|
expected = <<~STR
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1617
|
+
INSERT INTO #{ActiveRecord::Base.lease_connection.quote_table_name("schema_migrations")} (version) VALUES
|
|
1618
|
+
(N'20100301010101'),
|
|
1619
|
+
(N'20100201010101'),
|
|
1620
|
+
(N'20100101010101');
|
|
1575
1621
|
STR
|
|
1576
1622
|
assert_equal expected.strip, schema_info
|
|
1577
1623
|
ensure
|
|
@@ -1591,7 +1637,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
|
|
|
1591
1637
|
# Fall through false positive with no filter.
|
|
1592
1638
|
coerce_tests! :test_schema_dumps_partial_indices
|
|
1593
1639
|
def test_schema_dumps_partial_indices_coerced
|
|
1594
|
-
index_definition = standard_dump.split(
|
|
1640
|
+
index_definition = standard_dump.split("\n").grep(/t.index.*company_partial_index/).first.strip
|
|
1595
1641
|
assert_equal 't.index ["firm_id", "type"], name: "company_partial_index", where: "([rating]>(10))"', index_definition
|
|
1596
1642
|
end
|
|
1597
1643
|
|
|
@@ -1608,7 +1654,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
|
|
|
1608
1654
|
# SQL Server formats the check constraint expression differently.
|
|
1609
1655
|
coerce_tests! :test_schema_dumps_check_constraints
|
|
1610
1656
|
def test_schema_dumps_check_constraints_coerced
|
|
1611
|
-
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
|
|
1612
1658
|
assert_equal 't.check_constraint "[price]>[discounted_price]", name: "products_price_check"', constraint_definition
|
|
1613
1659
|
end
|
|
1614
1660
|
end
|
|
@@ -1628,14 +1674,14 @@ class SchemaDumperDefaultsCoerceTest < ActiveRecord::TestCase
|
|
|
1628
1674
|
setup do
|
|
1629
1675
|
@connection = ActiveRecord::Base.lease_connection
|
|
1630
1676
|
@connection.create_table :dump_defaults, force: true do |t|
|
|
1631
|
-
t.string
|
|
1632
|
-
t.date
|
|
1677
|
+
t.string :string_with_default, default: "Hello!"
|
|
1678
|
+
t.date :date_with_default, default: "2014-06-05"
|
|
1633
1679
|
t.datetime :datetime_with_default, default: "2014-06-05 07:17:04"
|
|
1634
|
-
t.time
|
|
1635
|
-
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
|
|
1636
1682
|
|
|
1637
|
-
t.text
|
|
1638
|
-
t.text
|
|
1683
|
+
t.text :text_with_default, default: "John' Doe"
|
|
1684
|
+
t.text :uuid, default: -> { "newid()" }
|
|
1639
1685
|
end
|
|
1640
1686
|
end
|
|
1641
1687
|
|
|
@@ -1652,7 +1698,6 @@ class TestAdapterWithInvalidConnection < ActiveRecord::TestCase
|
|
|
1652
1698
|
coerce_tests! %r{inspect on Model class does not raise}
|
|
1653
1699
|
end
|
|
1654
1700
|
|
|
1655
|
-
require "models/topic"
|
|
1656
1701
|
class TransactionTest < ActiveRecord::TestCase
|
|
1657
1702
|
# SQL Server does not have query for release_savepoint.
|
|
1658
1703
|
coerce_tests! :test_releasing_named_savepoints
|
|
@@ -1698,7 +1743,7 @@ class TransactionTest < ActiveRecord::TestCase
|
|
|
1698
1743
|
/DELETE/i,
|
|
1699
1744
|
/^SAVE TRANSACTION/i,
|
|
1700
1745
|
/DELETE/i,
|
|
1701
|
-
/COMMIT/i
|
|
1746
|
+
/COMMIT/i
|
|
1702
1747
|
]
|
|
1703
1748
|
|
|
1704
1749
|
assert_equal expected_queries.size, actual_queries.size
|
|
@@ -1734,7 +1779,7 @@ class TransactionTest < ActiveRecord::TestCase
|
|
|
1734
1779
|
/^SAVE TRANSACTION/i,
|
|
1735
1780
|
/DELETE/i,
|
|
1736
1781
|
/DELETE/i,
|
|
1737
|
-
/COMMIT/i
|
|
1782
|
+
/COMMIT/i
|
|
1738
1783
|
]
|
|
1739
1784
|
|
|
1740
1785
|
assert_equal expected_queries.size, actual_queries.size
|
|
@@ -1744,7 +1789,6 @@ class TransactionTest < ActiveRecord::TestCase
|
|
|
1744
1789
|
end
|
|
1745
1790
|
end
|
|
1746
1791
|
|
|
1747
|
-
require "models/tag"
|
|
1748
1792
|
class TransactionIsolationTest < ActiveRecord::TestCase
|
|
1749
1793
|
# SQL Server will lock the table for counts even when both
|
|
1750
1794
|
# connections are `READ COMMITTED`. So we bypass with `READPAST`.
|
|
@@ -1762,9 +1806,27 @@ class TransactionIsolationTest < ActiveRecord::TestCase
|
|
|
1762
1806
|
|
|
1763
1807
|
# I really need some help understanding this one.
|
|
1764
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
|
|
1765
1828
|
end
|
|
1766
1829
|
|
|
1767
|
-
require "models/book"
|
|
1768
1830
|
class ViewWithPrimaryKeyTest < ActiveRecord::TestCase
|
|
1769
1831
|
# We have a few view tables. use includes vs equality.
|
|
1770
1832
|
coerce_tests! :test_views
|
|
@@ -1788,7 +1850,6 @@ class ViewWithoutPrimaryKeyTest < ActiveRecord::TestCase
|
|
|
1788
1850
|
end
|
|
1789
1851
|
end
|
|
1790
1852
|
|
|
1791
|
-
require "models/author"
|
|
1792
1853
|
class YamlSerializationTest < ActiveRecord::TestCase
|
|
1793
1854
|
coerce_tests! :test_types_of_virtual_columns_are_not_changed_on_round_trip
|
|
1794
1855
|
def test_types_of_virtual_columns_are_not_changed_on_round_trip_coerced
|
|
@@ -1837,7 +1898,7 @@ class TimePrecisionTest < ActiveRecord::TestCase
|
|
|
1837
1898
|
coerce_tests! :test_time_precision_is_truncated_on_assignment
|
|
1838
1899
|
def test_time_precision_is_truncated_on_assignment_coerced
|
|
1839
1900
|
@connection.create_table(:foos, force: true)
|
|
1840
|
-
@connection.add_column :foos, :start,
|
|
1901
|
+
@connection.add_column :foos, :start, :time, precision: 0
|
|
1841
1902
|
@connection.add_column :foos, :finish, :time, precision: 6
|
|
1842
1903
|
|
|
1843
1904
|
time = ::Time.now.change(nsec: 123456789)
|
|
@@ -1873,8 +1934,8 @@ class DefaultNumbersTest < ActiveRecord::TestCase
|
|
|
1873
1934
|
coerce_tests! :test_default_negative_integer
|
|
1874
1935
|
def test_default_negative_integer_coerced
|
|
1875
1936
|
record = DefaultNumber.new
|
|
1876
|
-
assert_equal
|
|
1877
|
-
assert_equal
|
|
1937
|
+
assert_equal(-5, record.negative_integer)
|
|
1938
|
+
assert_equal(-5, record.negative_integer_before_type_cast)
|
|
1878
1939
|
end
|
|
1879
1940
|
|
|
1880
1941
|
# We do better with native types and do not return strings for everything.
|
|
@@ -1903,7 +1964,6 @@ module ActiveRecord
|
|
|
1903
1964
|
end
|
|
1904
1965
|
end
|
|
1905
1966
|
|
|
1906
|
-
require "models/book"
|
|
1907
1967
|
module ActiveRecord
|
|
1908
1968
|
class StatementCacheTest < ActiveRecord::TestCase
|
|
1909
1969
|
# Getting random failures.
|
|
@@ -1916,7 +1976,7 @@ module ActiveRecord
|
|
|
1916
1976
|
|
|
1917
1977
|
original_test_statement_cache_values_differ
|
|
1918
1978
|
ensure
|
|
1919
|
-
Book.where(author_id: nil, name:
|
|
1979
|
+
Book.where(author_id: nil, name: "my book").delete_all
|
|
1920
1980
|
Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
|
|
1921
1981
|
end
|
|
1922
1982
|
end
|
|
@@ -1926,53 +1986,38 @@ module ActiveRecord
|
|
|
1926
1986
|
module ConnectionAdapters
|
|
1927
1987
|
class SchemaCacheTest < ActiveRecord::TestCase
|
|
1928
1988
|
# Tests fail on Windows AppVeyor CI with 'Permission denied' error when renaming file during `File.atomic_write` call.
|
|
1929
|
-
coerce_tests! :test_yaml_dump_and_load, :test_yaml_dump_and_load_with_gzip if RbConfig::CONFIG["host_os"]
|
|
1930
|
-
|
|
1931
|
-
#
|
|
1932
|
-
coerce_tests! :
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
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
|
|
1941
2002
|
end
|
|
1942
2003
|
end
|
|
1943
2004
|
|
|
1944
2005
|
private
|
|
1945
2006
|
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
current_default = column.default if column.default.is_a?(Time) && column.default.year < 1900
|
|
1951
|
-
end
|
|
1952
|
-
|
|
1953
|
-
# Correct problems
|
|
1954
|
-
if current_default.present?
|
|
1955
|
-
@connection.change_column_default(:sst_datatypes, :datetime, current_default.dup.change(year: 1900))
|
|
1956
|
-
end
|
|
1957
|
-
|
|
1958
|
-
# Run original test
|
|
1959
|
-
yield
|
|
1960
|
-
ensure
|
|
1961
|
-
# Revert changes
|
|
1962
|
-
@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")
|
|
1963
2011
|
end
|
|
1964
2012
|
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
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")
|
|
1969
2016
|
end
|
|
1970
2017
|
end
|
|
1971
2018
|
end
|
|
1972
2019
|
end
|
|
1973
2020
|
|
|
1974
|
-
require "models/post"
|
|
1975
|
-
require "models/comment"
|
|
1976
2021
|
class UnsafeRawSqlTest < ActiveRecord::TestCase
|
|
1977
2022
|
fixtures :posts
|
|
1978
2023
|
|
|
@@ -2093,9 +2138,9 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase
|
|
|
2093
2138
|
test "order: allows valid arguments with COLLATE" do
|
|
2094
2139
|
collation_name = "Latin1_General_CS_AS_WS"
|
|
2095
2140
|
|
|
2096
|
-
ids_expected = Post.order(Arel.sql(%
|
|
2141
|
+
ids_expected = Post.order(Arel.sql(%(author_id, title COLLATE #{collation_name} DESC))).pluck(:id)
|
|
2097
2142
|
|
|
2098
|
-
ids = Post.order(["author_id", %
|
|
2143
|
+
ids = Post.order(["author_id", %(title COLLATE #{collation_name} DESC)]).pluck(:id)
|
|
2099
2144
|
|
|
2100
2145
|
assert_equal ids_expected, ids
|
|
2101
2146
|
end
|
|
@@ -2148,7 +2193,7 @@ class RelationMergingTest < ActiveRecord::TestCase
|
|
|
2148
2193
|
non_mary_and_bob = Author.where.not(id: [mary, bob])
|
|
2149
2194
|
|
|
2150
2195
|
author_id = Author.lease_connection.quote_table_name("authors.id")
|
|
2151
|
-
assert_queries_match(/WHERE #{Regexp.escape(author_id)} NOT IN \((@\d), \g<1>\)
|
|
2196
|
+
assert_queries_match(/WHERE #{Regexp.escape(author_id)} NOT IN \((@\d), \g<1>\)/) do
|
|
2152
2197
|
assert_equal [david], non_mary_and_bob.merge(non_mary_and_bob)
|
|
2153
2198
|
end
|
|
2154
2199
|
|
|
@@ -2169,14 +2214,13 @@ module ActiveRecord
|
|
|
2169
2214
|
end
|
|
2170
2215
|
end
|
|
2171
2216
|
|
|
2172
|
-
require "models/book"
|
|
2173
2217
|
class EnumTest < ActiveRecord::TestCase
|
|
2174
2218
|
# Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
|
|
2175
2219
|
coerce_tests! %r{enums are distinct per class}
|
|
2176
2220
|
test "enums are distinct per class coerced" do
|
|
2177
2221
|
Book.lease_connection.remove_index(:books, column: [:author_id, :name])
|
|
2178
2222
|
|
|
2179
|
-
send(:
|
|
2223
|
+
send(:"original_enums are distinct per class")
|
|
2180
2224
|
ensure
|
|
2181
2225
|
Book.where(author_id: nil, name: nil).delete_all
|
|
2182
2226
|
Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
|
|
@@ -2187,7 +2231,7 @@ class EnumTest < ActiveRecord::TestCase
|
|
|
2187
2231
|
test "creating new objects with enum scopes coerced" do
|
|
2188
2232
|
Book.lease_connection.remove_index(:books, column: [:author_id, :name])
|
|
2189
2233
|
|
|
2190
|
-
send(:
|
|
2234
|
+
send(:"original_creating new objects with enum scopes")
|
|
2191
2235
|
ensure
|
|
2192
2236
|
Book.where(author_id: nil, name: nil).delete_all
|
|
2193
2237
|
Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
|
|
@@ -2198,7 +2242,7 @@ class EnumTest < ActiveRecord::TestCase
|
|
|
2198
2242
|
test "enums are inheritable coerced" do
|
|
2199
2243
|
Book.lease_connection.remove_index(:books, column: [:author_id, :name])
|
|
2200
2244
|
|
|
2201
|
-
send(:
|
|
2245
|
+
send(:"original_enums are inheritable")
|
|
2202
2246
|
ensure
|
|
2203
2247
|
Book.where(author_id: nil, name: nil).delete_all
|
|
2204
2248
|
Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
|
|
@@ -2209,14 +2253,13 @@ class EnumTest < ActiveRecord::TestCase
|
|
|
2209
2253
|
test "serializable? with large number label coerced" do
|
|
2210
2254
|
Book.lease_connection.remove_index(:books, column: [:author_id, :name])
|
|
2211
2255
|
|
|
2212
|
-
send(:
|
|
2256
|
+
send(:"original_serializable\\? with large number label")
|
|
2213
2257
|
ensure
|
|
2214
2258
|
Book.where(author_id: nil, name: nil).delete_all
|
|
2215
2259
|
Book.lease_connection.add_index(:books, [:author_id, :name], unique: true)
|
|
2216
2260
|
end
|
|
2217
2261
|
end
|
|
2218
2262
|
|
|
2219
|
-
require "models/citation"
|
|
2220
2263
|
class EagerLoadingTooManyIdsTest < ActiveRecord::TestCase
|
|
2221
2264
|
fixtures :citations
|
|
2222
2265
|
|
|
@@ -2251,7 +2294,7 @@ end
|
|
|
2251
2294
|
class ReloadModelsTest < ActiveRecord::TestCase
|
|
2252
2295
|
# Skip test on Windows. The number of arguments passed to `IO.popen` in
|
|
2253
2296
|
# `activesupport/lib/active_support/testing/isolation.rb` exceeds what Windows can handle.
|
|
2254
|
-
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"])
|
|
2255
2298
|
end
|
|
2256
2299
|
|
|
2257
2300
|
class MarshalSerializationTest < ActiveRecord::TestCase
|
|
@@ -2313,7 +2356,7 @@ class PreloaderTest < ActiveRecord::TestCase
|
|
|
2313
2356
|
|
|
2314
2357
|
c = Cpk::OrderAgreement.lease_connection
|
|
2315
2358
|
order_id_column = Regexp.escape(c.quote_table_name("cpk_order_agreements.order_id"))
|
|
2316
|
-
order_id_constraint = /#{order_id_column} = @0
|
|
2359
|
+
order_id_constraint = /#{order_id_column} = @0$/
|
|
2317
2360
|
expectation = /SELECT.*WHERE.* #{order_id_constraint}/
|
|
2318
2361
|
|
|
2319
2362
|
assert_match(expectation, preload_sql)
|
|
@@ -2337,7 +2380,7 @@ class PreloaderTest < ActiveRecord::TestCase
|
|
|
2337
2380
|
|
|
2338
2381
|
c = Cpk::Order.lease_connection
|
|
2339
2382
|
order_id = Regexp.escape(c.quote_table_name("cpk_orders.id"))
|
|
2340
|
-
order_constraint = /#{order_id} = @0
|
|
2383
|
+
order_constraint = /#{order_id} = @0$/
|
|
2341
2384
|
expectation = /SELECT.*WHERE.* #{order_constraint}/
|
|
2342
2385
|
|
|
2343
2386
|
assert_match(expectation, preload_sql)
|
|
@@ -2347,15 +2390,14 @@ end
|
|
|
2347
2390
|
|
|
2348
2391
|
class MigratorTest < ActiveRecord::TestCase
|
|
2349
2392
|
# Test fails on Windows AppVeyor CI for unknown reason.
|
|
2350
|
-
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"])
|
|
2351
2394
|
end
|
|
2352
2395
|
|
|
2353
2396
|
class MultiDbMigratorTest < ActiveRecord::TestCase
|
|
2354
2397
|
# Test fails on Windows AppVeyor CI for unknown reason.
|
|
2355
|
-
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"])
|
|
2356
2399
|
end
|
|
2357
2400
|
|
|
2358
|
-
require "models/book"
|
|
2359
2401
|
class FieldOrderedValuesTest < ActiveRecord::TestCase
|
|
2360
2402
|
# Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
|
|
2361
2403
|
coerce_tests! :test_in_order_of_with_enums_values
|
|
@@ -2402,72 +2444,11 @@ class FieldOrderedValuesTest < ActiveRecord::TestCase
|
|
|
2402
2444
|
end
|
|
2403
2445
|
end
|
|
2404
2446
|
|
|
2405
|
-
require "models/dashboard"
|
|
2406
2447
|
class QueryLogsTest < ActiveRecord::TestCase
|
|
2407
|
-
# SQL requires double single-quotes.
|
|
2408
|
-
coerce_tests! :test_sql_commenter_format
|
|
2409
|
-
def test_sql_commenter_format_coerced
|
|
2410
|
-
ActiveRecord::QueryLogs.tags_formatter = :sqlcommenter
|
|
2411
|
-
ActiveRecord::QueryLogs.tags = [:application]
|
|
2412
|
-
|
|
2413
|
-
assert_queries_match(%r{/\*application=''active_record''\*/}) do
|
|
2414
|
-
Dashboard.first
|
|
2415
|
-
end
|
|
2416
|
-
end
|
|
2417
|
-
|
|
2418
|
-
# SQL requires double single-quotes.
|
|
2419
|
-
coerce_tests! :test_sqlcommenter_format_value
|
|
2420
|
-
def test_sqlcommenter_format_value_coerced
|
|
2421
|
-
ActiveRecord::QueryLogs.tags_formatter = :sqlcommenter
|
|
2422
|
-
|
|
2423
|
-
ActiveRecord::QueryLogs.tags = [
|
|
2424
|
-
:application,
|
|
2425
|
-
{ tracestate: "congo=t61rcWkgMzE,rojo=00f067aa0ba902b7", custom_proc: -> { "Joe's Shack" } },
|
|
2426
|
-
]
|
|
2427
|
-
|
|
2428
|
-
assert_queries_match(%r{custom_proc=''Joe%27s%20Shack'',tracestate=''congo%3Dt61rcWkgMzE%2Crojo%3D00f067aa0ba902b7''\*/}) do
|
|
2429
|
-
Dashboard.first
|
|
2430
|
-
end
|
|
2431
|
-
end
|
|
2432
|
-
|
|
2433
|
-
# SQL requires double single-quotes.
|
|
2434
|
-
coerce_tests! :test_sqlcommenter_format_value_string_coercible
|
|
2435
|
-
def test_sqlcommenter_format_value_string_coercible_coerced
|
|
2436
|
-
ActiveRecord::QueryLogs.tags_formatter = :sqlcommenter
|
|
2437
|
-
|
|
2438
|
-
ActiveRecord::QueryLogs.tags = [
|
|
2439
|
-
:application,
|
|
2440
|
-
{ custom_proc: -> { 1234 } },
|
|
2441
|
-
]
|
|
2442
|
-
|
|
2443
|
-
assert_queries_match(%r{custom_proc=''1234''\*/}) do
|
|
2444
|
-
Dashboard.first
|
|
2445
|
-
end
|
|
2446
|
-
end
|
|
2447
|
-
|
|
2448
|
-
# SQL requires double single-quotes.
|
|
2449
|
-
coerce_tests! :test_sqlcommenter_format_allows_string_keys
|
|
2450
|
-
def test_sqlcommenter_format_allows_string_keys_coerced
|
|
2451
|
-
ActiveRecord::QueryLogs.tags_formatter = :sqlcommenter
|
|
2452
|
-
|
|
2453
|
-
ActiveRecord::QueryLogs.tags = [
|
|
2454
|
-
:application,
|
|
2455
|
-
{
|
|
2456
|
-
"string" => "value",
|
|
2457
|
-
tracestate: "congo=t61rcWkgMzE,rojo=00f067aa0ba902b7",
|
|
2458
|
-
custom_proc: -> { "Joe's Shack" }
|
|
2459
|
-
},
|
|
2460
|
-
]
|
|
2461
|
-
|
|
2462
|
-
assert_queries_match(%r{custom_proc=''Joe%27s%20Shack'',string=''value'',tracestate=''congo%3Dt61rcWkgMzE%2Crojo%3D00f067aa0ba902b7''\*/}) do
|
|
2463
|
-
Dashboard.first
|
|
2464
|
-
end
|
|
2465
|
-
end
|
|
2466
|
-
|
|
2467
2448
|
# Invalid character encoding causes `ActiveRecord::StatementInvalid` error similar to Postgres.
|
|
2468
2449
|
coerce_tests! :test_invalid_encoding_query
|
|
2469
2450
|
def test_invalid_encoding_query_coerced
|
|
2470
|
-
ActiveRecord::QueryLogs.tags = [
|
|
2451
|
+
ActiveRecord::QueryLogs.tags = [:application]
|
|
2471
2452
|
assert_raises ActiveRecord::StatementInvalid do
|
|
2472
2453
|
ActiveRecord::Base.lease_connection.execute "select 1 as '\xFF'"
|
|
2473
2454
|
end
|
|
@@ -2480,8 +2461,8 @@ class InsertAllTest < ActiveRecord::TestCase
|
|
|
2480
2461
|
def test_insert_all_returns_requested_sql_fields_coerced
|
|
2481
2462
|
skip unless supports_insert_returning?
|
|
2482
2463
|
|
|
2483
|
-
result = Book.insert_all! [{
|
|
2484
|
-
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")
|
|
2485
2466
|
end
|
|
2486
2467
|
|
|
2487
2468
|
# Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite.
|
|
@@ -2521,7 +2502,7 @@ module ActiveRecord
|
|
|
2521
2502
|
undef_method :invalid_add_column_option_exception_message
|
|
2522
2503
|
def invalid_add_column_option_exception_message(key)
|
|
2523
2504
|
default_keys = [":limit", ":precision", ":scale", ":default", ":null", ":collation", ":comment", ":primary_key", ":if_exists", ":if_not_exists"]
|
|
2524
|
-
default_keys.concat([":is_identity"]) # SQL Server additional valid keys
|
|
2505
|
+
default_keys.concat([":is_identity", ":as", ":stored"]) # SQL Server additional valid keys
|
|
2525
2506
|
|
|
2526
2507
|
"Unknown key: :#{key}. Valid keys are: #{default_keys.join(", ")}"
|
|
2527
2508
|
end
|
|
@@ -2529,11 +2510,11 @@ module ActiveRecord
|
|
|
2529
2510
|
end
|
|
2530
2511
|
end
|
|
2531
2512
|
|
|
2532
|
-
|
|
2533
2513
|
# Need to use `install_unregistered_type_fallback` instead of `install_unregistered_type_error` so that message-pack
|
|
2534
2514
|
# can read and write `ActiveRecord::ConnectionAdapters::SQLServer::Type::Data` objects.
|
|
2535
2515
|
class ActiveRecordMessagePackTest < ActiveRecord::TestCase
|
|
2536
2516
|
private
|
|
2517
|
+
|
|
2537
2518
|
undef_method :serializer
|
|
2538
2519
|
def serializer
|
|
2539
2520
|
@serializer ||= ::MessagePack::Factory.new.tap do |factory|
|
|
@@ -2556,7 +2537,7 @@ end
|
|
|
2556
2537
|
|
|
2557
2538
|
module ActiveRecord
|
|
2558
2539
|
module ConnectionAdapters
|
|
2559
|
-
class ConnectionHandlersShardingDbTest
|
|
2540
|
+
class ConnectionHandlersShardingDbTest < ActiveRecord::TestCase
|
|
2560
2541
|
# Tests are not about a specific adapter.
|
|
2561
2542
|
coerce_all_tests!
|
|
2562
2543
|
end
|
|
@@ -2680,7 +2661,6 @@ module ActiveRecord
|
|
|
2680
2661
|
end
|
|
2681
2662
|
end
|
|
2682
2663
|
|
|
2683
|
-
|
|
2684
2664
|
module ActiveRecord
|
|
2685
2665
|
class TableMetadataTest < ActiveSupport::TestCase
|
|
2686
2666
|
# Adapter returns an object that is subclass of what is expected in the original test.
|
|
@@ -2712,33 +2692,6 @@ module ActiveRecord
|
|
|
2712
2692
|
end
|
|
2713
2693
|
end
|
|
2714
2694
|
|
|
2715
|
-
require "models/car"
|
|
2716
|
-
class ExplainTest < ActiveRecord::TestCase
|
|
2717
|
-
# Expected query slightly different from because of 'sp_executesql' and query parameters.
|
|
2718
|
-
coerce_tests! :test_relation_explain_with_first
|
|
2719
|
-
def test_relation_explain_with_first_coerced
|
|
2720
|
-
expected_query = capture_sql {
|
|
2721
|
-
Car.all.first
|
|
2722
|
-
}.first[/EXEC sp_executesql N'(.*?) NEXT/, 1]
|
|
2723
|
-
message = Car.all.explain.first
|
|
2724
|
-
assert_match(/^EXPLAIN/, message)
|
|
2725
|
-
assert_match(expected_query, message)
|
|
2726
|
-
end
|
|
2727
|
-
|
|
2728
|
-
# Expected query slightly different from because of 'sp_executesql' and query parameters.
|
|
2729
|
-
coerce_tests! :test_relation_explain_with_last
|
|
2730
|
-
def test_relation_explain_with_last_coerced
|
|
2731
|
-
expected_query = capture_sql {
|
|
2732
|
-
Car.all.last
|
|
2733
|
-
}.first[/EXEC sp_executesql N'(.*?) NEXT/, 1]
|
|
2734
|
-
expected_query = expected_query
|
|
2735
|
-
message = Car.all.explain.last
|
|
2736
|
-
|
|
2737
|
-
assert_match(/^EXPLAIN/, message)
|
|
2738
|
-
assert_match(expected_query, message)
|
|
2739
|
-
end
|
|
2740
|
-
end
|
|
2741
|
-
|
|
2742
2695
|
module ActiveRecord
|
|
2743
2696
|
module Assertions
|
|
2744
2697
|
class QueryAssertionsTest < ActiveSupport::TestCase
|
|
@@ -2774,7 +2727,7 @@ module ActiveRecord
|
|
|
2774
2727
|
relation = Company.with_recursive(
|
|
2775
2728
|
top_companies_and_children: [
|
|
2776
2729
|
Company.where(firm_id: nil),
|
|
2777
|
-
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")
|
|
2778
2731
|
]
|
|
2779
2732
|
).from("top_companies_and_children AS companies")
|
|
2780
2733
|
|
|
@@ -2784,3 +2737,103 @@ module ActiveRecord
|
|
|
2784
2737
|
end
|
|
2785
2738
|
end
|
|
2786
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
|