activerecord 7.2.1 → 8.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +188 -786
  3. data/README.rdoc +1 -1
  4. data/lib/active_record/associations/association.rb +25 -5
  5. data/lib/active_record/associations/builder/association.rb +7 -6
  6. data/lib/active_record/associations/collection_association.rb +10 -8
  7. data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
  8. data/lib/active_record/associations/has_many_through_association.rb +3 -2
  9. data/lib/active_record/associations/join_dependency/join_association.rb +3 -2
  10. data/lib/active_record/associations/join_dependency.rb +4 -4
  11. data/lib/active_record/associations/preloader/association.rb +2 -2
  12. data/lib/active_record/associations/singular_association.rb +8 -3
  13. data/lib/active_record/associations.rb +34 -4
  14. data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
  15. data/lib/active_record/attribute_assignment.rb +9 -1
  16. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -0
  17. data/lib/active_record/attributes.rb +1 -2
  18. data/lib/active_record/autosave_association.rb +69 -27
  19. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +16 -10
  20. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
  21. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +0 -1
  22. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +0 -1
  23. data/lib/active_record/connection_adapters/abstract/database_statements.rb +90 -43
  24. data/lib/active_record/connection_adapters/abstract/query_cache.rb +12 -4
  25. data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
  26. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
  27. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +26 -5
  28. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -5
  29. data/lib/active_record/connection_adapters/abstract_adapter.rb +24 -25
  30. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +20 -38
  31. data/lib/active_record/connection_adapters/mysql/quoting.rb +0 -8
  32. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
  33. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +43 -45
  34. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +42 -98
  35. data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -8
  36. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +64 -42
  37. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
  38. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
  39. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +0 -1
  40. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +50 -6
  41. data/lib/active_record/connection_adapters/postgresql_adapter.rb +38 -90
  42. data/lib/active_record/connection_adapters/schema_cache.rb +1 -3
  43. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +76 -100
  44. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
  45. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +13 -0
  46. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +8 -1
  47. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +55 -12
  48. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +37 -67
  49. data/lib/active_record/connection_adapters/trilogy_adapter.rb +0 -17
  50. data/lib/active_record/connection_handling.rb +22 -0
  51. data/lib/active_record/core.rb +14 -7
  52. data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -1
  53. data/lib/active_record/encryption/config.rb +3 -1
  54. data/lib/active_record/encryption/encryptable_record.rb +4 -4
  55. data/lib/active_record/encryption/encrypted_attribute_type.rb +10 -1
  56. data/lib/active_record/encryption/encryptor.rb +15 -8
  57. data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
  58. data/lib/active_record/encryption/key_provider.rb +1 -1
  59. data/lib/active_record/encryption/scheme.rb +8 -1
  60. data/lib/active_record/encryption.rb +2 -0
  61. data/lib/active_record/enum.rb +7 -10
  62. data/lib/active_record/errors.rb +13 -5
  63. data/lib/active_record/fixtures.rb +0 -1
  64. data/lib/active_record/future_result.rb +14 -10
  65. data/lib/active_record/gem_version.rb +4 -4
  66. data/lib/active_record/insert_all.rb +1 -1
  67. data/lib/active_record/migration/command_recorder.rb +22 -5
  68. data/lib/active_record/migration/compatibility.rb +5 -2
  69. data/lib/active_record/migration.rb +35 -33
  70. data/lib/active_record/model_schema.rb +2 -3
  71. data/lib/active_record/nested_attributes.rb +11 -2
  72. data/lib/active_record/persistence.rb +128 -130
  73. data/lib/active_record/query_logs.rb +97 -39
  74. data/lib/active_record/query_logs_formatter.rb +17 -28
  75. data/lib/active_record/querying.rb +6 -6
  76. data/lib/active_record/railtie.rb +8 -14
  77. data/lib/active_record/reflection.rb +9 -4
  78. data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
  79. data/lib/active_record/relation/batches.rb +132 -72
  80. data/lib/active_record/relation/calculations.rb +24 -19
  81. data/lib/active_record/relation/delegation.rb +25 -14
  82. data/lib/active_record/relation/finder_methods.rb +18 -18
  83. data/lib/active_record/relation/merger.rb +8 -8
  84. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
  85. data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
  86. data/lib/active_record/relation/predicate_builder.rb +6 -1
  87. data/lib/active_record/relation/query_methods.rb +58 -37
  88. data/lib/active_record/relation/record_fetch_warning.rb +2 -2
  89. data/lib/active_record/relation/spawn_methods.rb +1 -1
  90. data/lib/active_record/relation.rb +72 -61
  91. data/lib/active_record/result.rb +68 -7
  92. data/lib/active_record/sanitization.rb +7 -6
  93. data/lib/active_record/schema_dumper.rb +5 -0
  94. data/lib/active_record/schema_migration.rb +2 -1
  95. data/lib/active_record/scoping/named.rb +5 -2
  96. data/lib/active_record/statement_cache.rb +12 -12
  97. data/lib/active_record/store.rb +7 -3
  98. data/lib/active_record/tasks/database_tasks.rb +36 -16
  99. data/lib/active_record/tasks/mysql_database_tasks.rb +0 -2
  100. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -2
  101. data/lib/active_record/test_fixtures.rb +12 -0
  102. data/lib/active_record/token_for.rb +1 -1
  103. data/lib/active_record/validations/uniqueness.rb +9 -8
  104. data/lib/active_record.rb +15 -0
  105. data/lib/arel/collectors/bind.rb +1 -1
  106. metadata +14 -14
@@ -84,6 +84,19 @@ module ActiveRecord
84
84
  class ConnectionTimeoutError < ConnectionNotEstablished
85
85
  end
86
86
 
87
+ # Raised when a database connection pool is requested but
88
+ # has not been defined.
89
+ class ConnectionNotDefined < ConnectionNotEstablished
90
+ def initialize(message = nil, connection_name: nil, role: nil, shard: nil)
91
+ super(message)
92
+ @connection_name = connection_name
93
+ @role = role
94
+ @shard = shard
95
+ end
96
+
97
+ attr_reader :connection_name, :role, :shard
98
+ end
99
+
87
100
  # Raised when connection to the database could not been established because it was not
88
101
  # able to connect to the host or when the authorization failed.
89
102
  class DatabaseConnectionError < ConnectionNotEstablished
@@ -484,11 +497,6 @@ module ActiveRecord
484
497
  # relation.limit!(5) # => ActiveRecord::UnmodifiableRelation
485
498
  class UnmodifiableRelation < ActiveRecordError
486
499
  end
487
- deprecate_constant(
488
- :ImmutableRelation,
489
- "ActiveRecord::UnmodifiableRelation",
490
- deprecator: ActiveRecord.deprecator
491
- )
492
500
 
493
501
  # TransactionIsolationError will be raised under the following conditions:
494
502
  #
@@ -2,7 +2,6 @@
2
2
 
3
3
  require "erb"
4
4
  require "yaml"
5
- require "zlib"
6
5
  require "set"
7
6
  require "active_support/dependencies"
8
7
  require "active_support/core_ext/digest/uuid"
@@ -100,17 +100,21 @@ module ActiveRecord
100
100
  def execute_or_skip
101
101
  return unless pending?
102
102
 
103
- @pool.with_connection do |connection|
104
- return unless @mutex.try_lock
105
- begin
106
- if pending?
107
- @event_buffer = EventBuffer.new(self, @instrumenter)
108
- connection.with_instrumenter(@event_buffer) do
109
- execute_query(connection, async: true)
103
+ @session.synchronize do
104
+ return unless pending?
105
+
106
+ @pool.with_connection do |connection|
107
+ return unless @mutex.try_lock
108
+ begin
109
+ if pending?
110
+ @event_buffer = EventBuffer.new(self, @instrumenter)
111
+ connection.with_instrumenter(@event_buffer) do
112
+ execute_query(connection, async: true)
113
+ end
110
114
  end
115
+ ensure
116
+ @mutex.unlock
111
117
  end
112
- ensure
113
- @mutex.unlock
114
118
  end
115
119
  end
116
120
  end
@@ -163,7 +167,7 @@ module ActiveRecord
163
167
  end
164
168
 
165
169
  def exec_query(connection, *args, **kwargs)
166
- connection.internal_exec_query(*args, **kwargs)
170
+ connection.raw_exec_query(*args, **kwargs)
167
171
  end
168
172
 
169
173
  class SelectAll < FutureResult # :nodoc:
@@ -7,10 +7,10 @@ module ActiveRecord
7
7
  end
8
8
 
9
9
  module VERSION
10
- MAJOR = 7
11
- MINOR = 2
12
- TINY = 1
13
- PRE = nil
10
+ MAJOR = 8
11
+ MINOR = 0
12
+ TINY = 0
13
+ PRE = "beta1"
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -240,7 +240,7 @@ module ActiveRecord
240
240
 
241
241
  values_list = insert_all.map_key_with_value do |key, value|
242
242
  next value if Arel::Nodes::SqlLiteral === value
243
- connection.with_yaml_fallback(types[key].serialize(value))
243
+ types[key].serialize(types[key].cast(value))
244
244
  end
245
245
 
246
246
  connection.visitor.compile(Arel::Nodes::ValuesList.new(values_list))
@@ -22,10 +22,12 @@ module ActiveRecord
22
22
  # * change_table_comment (must supply a +:from+ and +:to+ option)
23
23
  # * create_enum
24
24
  # * create_join_table
25
+ # * create_virtual_table
25
26
  # * create_table
26
27
  # * disable_extension
27
28
  # * drop_enum (must supply a list of values)
28
29
  # * drop_join_table
30
+ # * drop_virtual_table (must supply options)
29
31
  # * drop_table (must supply a block)
30
32
  # * enable_extension
31
33
  # * remove_column (must supply a type)
@@ -55,6 +57,8 @@ module ActiveRecord
55
57
  :add_exclusion_constraint, :remove_exclusion_constraint,
56
58
  :add_unique_constraint, :remove_unique_constraint,
57
59
  :create_enum, :drop_enum, :rename_enum, :add_enum_value, :rename_enum_value,
60
+ :create_schema, :drop_schema,
61
+ :create_virtual_table, :drop_virtual_table
58
62
  ]
59
63
  include JoinTable
60
64
 
@@ -163,7 +167,9 @@ module ActiveRecord
163
167
  add_exclusion_constraint: :remove_exclusion_constraint,
164
168
  add_unique_constraint: :remove_unique_constraint,
165
169
  enable_extension: :disable_extension,
166
- create_enum: :drop_enum
170
+ create_enum: :drop_enum,
171
+ create_schema: :drop_schema,
172
+ create_virtual_table: :drop_virtual_table
167
173
  }.each do |cmd, inv|
168
174
  [[inv, cmd], [cmd, inv]].uniq.each do |method, inverse|
169
175
  class_eval <<-EOV, __FILE__, __LINE__ + 1
@@ -196,13 +202,18 @@ module ActiveRecord
196
202
  end
197
203
 
198
204
  def invert_drop_table(args, &block)
199
- if args.last.is_a?(Hash)
200
- args.last.delete(:if_exists)
205
+ options = args.extract_options!
206
+ options.delete(:if_exists)
207
+
208
+ if args.size > 1
209
+ raise ActiveRecord::IrreversibleMigration, "To avoid mistakes, drop_table is only reversible if given a single table name."
201
210
  end
202
- if args.size == 1 && block == nil
211
+
212
+ if args.size == 1 && options == {} && block == nil
203
213
  raise ActiveRecord::IrreversibleMigration, "To avoid mistakes, drop_table is only reversible if given options or a block (can be empty)."
204
214
  end
205
- super
215
+
216
+ super(args.push(options), &block)
206
217
  end
207
218
 
208
219
  def invert_rename_table(args)
@@ -372,6 +383,12 @@ module ActiveRecord
372
383
  [:rename_enum_value, [type_name, from: options[:to], to: options[:from]]]
373
384
  end
374
385
 
386
+ def invert_drop_virtual_table(args)
387
+ _enum, values = args.dup.tap(&:extract_options!)
388
+ raise ActiveRecord::IrreversibleMigration, "drop_virtual_table is only reversible if given options." unless values
389
+ super
390
+ end
391
+
375
392
  def respond_to_missing?(method, _)
376
393
  super || delegate.respond_to?(method)
377
394
  end
@@ -21,7 +21,7 @@ module ActiveRecord
21
21
  # New migration functionality that will never be backward compatible should be added directly to `ActiveRecord::Migration`.
22
22
  #
23
23
  # There are classes for each prior Rails version. Each class descends from the *next* Rails version, so:
24
- # 5.2 < 6.0 < 6.1 < 7.0 < 7.1 < 7.2
24
+ # 5.2 < 6.0 < 6.1 < 7.0 < 7.1 < 7.2 < 8.0
25
25
  #
26
26
  # If you are introducing new migration functionality that should only apply from Rails 7 onward, then you should
27
27
  # find the class that immediately precedes it (6.1), and override the relevant migration methods to undo your changes.
@@ -29,7 +29,10 @@ module ActiveRecord
29
29
  # For example, Rails 6 added a default value for the `precision` option on datetime columns. So in this file, the `V5_2`
30
30
  # class sets the value of `precision` to `nil` if it's not explicitly provided. This way, the default value will not apply
31
31
  # for migrations written for 5.2, but will for migrations written for 6.0.
32
- V7_2 = Current
32
+ V8_0 = Current
33
+
34
+ class V7_2 < V8_0
35
+ end
33
36
 
34
37
  class V7_1 < V7_2
35
38
  end
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "benchmark"
4
3
  require "set"
5
- require "zlib"
6
4
  require "active_support/core_ext/array/access"
7
5
  require "active_support/core_ext/enumerable"
8
6
  require "active_support/core_ext/module/attribute_accessors"
@@ -21,7 +19,7 @@ module ActiveRecord
21
19
  # For example the following migration is not reversible.
22
20
  # Rolling back this migration will raise an ActiveRecord::IrreversibleMigration error.
23
21
  #
24
- # class IrreversibleMigrationExample < ActiveRecord::Migration[7.2]
22
+ # class IrreversibleMigrationExample < ActiveRecord::Migration[8.0]
25
23
  # def change
26
24
  # create_table :distributors do |t|
27
25
  # t.string :zipcode
@@ -39,7 +37,7 @@ module ActiveRecord
39
37
  #
40
38
  # 1. Define <tt>#up</tt> and <tt>#down</tt> methods instead of <tt>#change</tt>:
41
39
  #
42
- # class ReversibleMigrationExample < ActiveRecord::Migration[7.2]
40
+ # class ReversibleMigrationExample < ActiveRecord::Migration[8.0]
43
41
  # def up
44
42
  # create_table :distributors do |t|
45
43
  # t.string :zipcode
@@ -64,7 +62,7 @@ module ActiveRecord
64
62
  #
65
63
  # 2. Use the #reversible method in <tt>#change</tt> method:
66
64
  #
67
- # class ReversibleMigrationExample < ActiveRecord::Migration[7.2]
65
+ # class ReversibleMigrationExample < ActiveRecord::Migration[8.0]
68
66
  # def change
69
67
  # create_table :distributors do |t|
70
68
  # t.string :zipcode
@@ -250,7 +248,7 @@ module ActiveRecord
250
248
  #
251
249
  # Example of a simple migration:
252
250
  #
253
- # class AddSsl < ActiveRecord::Migration[7.2]
251
+ # class AddSsl < ActiveRecord::Migration[8.0]
254
252
  # def up
255
253
  # add_column :accounts, :ssl_enabled, :boolean, default: true
256
254
  # end
@@ -270,7 +268,7 @@ module ActiveRecord
270
268
  #
271
269
  # Example of a more complex migration that also needs to initialize data:
272
270
  #
273
- # class AddSystemSettings < ActiveRecord::Migration[7.2]
271
+ # class AddSystemSettings < ActiveRecord::Migration[8.0]
274
272
  # def up
275
273
  # create_table :system_settings do |t|
276
274
  # t.string :name
@@ -357,7 +355,7 @@ module ActiveRecord
357
355
  #
358
356
  # === Deletion
359
357
  #
360
- # * <tt>drop_table(name)</tt>: Drops the table called +name+.
358
+ # * <tt>drop_table(*names)</tt>: Drops the given tables.
361
359
  # * <tt>drop_join_table(table_1, table_2, options)</tt>: Drops the join table
362
360
  # specified by the given arguments.
363
361
  # * <tt>remove_column(table_name, column_name, type, options)</tt>: Removes the column
@@ -399,7 +397,7 @@ module ActiveRecord
399
397
  # $ bin/rails generate migration add_fieldname_to_tablename fieldname:string
400
398
  #
401
399
  # This will generate the file <tt>timestamp_add_fieldname_to_tablename.rb</tt>, which will look like this:
402
- # class AddFieldnameToTablename < ActiveRecord::Migration[7.2]
400
+ # class AddFieldnameToTablename < ActiveRecord::Migration[8.0]
403
401
  # def change
404
402
  # add_column :tablenames, :fieldname, :string
405
403
  # end
@@ -425,7 +423,7 @@ module ActiveRecord
425
423
  #
426
424
  # Not all migrations change the schema. Some just fix the data:
427
425
  #
428
- # class RemoveEmptyTags < ActiveRecord::Migration[7.2]
426
+ # class RemoveEmptyTags < ActiveRecord::Migration[8.0]
429
427
  # def up
430
428
  # Tag.all.each { |tag| tag.destroy if tag.pages.empty? }
431
429
  # end
@@ -438,7 +436,7 @@ module ActiveRecord
438
436
  #
439
437
  # Others remove columns when they migrate up instead of down:
440
438
  #
441
- # class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration[7.2]
439
+ # class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration[8.0]
442
440
  # def up
443
441
  # remove_column :items, :incomplete_items_count
444
442
  # remove_column :items, :completed_items_count
@@ -452,7 +450,7 @@ module ActiveRecord
452
450
  #
453
451
  # And sometimes you need to do something in SQL not abstracted directly by migrations:
454
452
  #
455
- # class MakeJoinUnique < ActiveRecord::Migration[7.2]
453
+ # class MakeJoinUnique < ActiveRecord::Migration[8.0]
456
454
  # def up
457
455
  # execute "ALTER TABLE `pages_linked_pages` ADD UNIQUE `page_id_linked_page_id` (`page_id`,`linked_page_id`)"
458
456
  # end
@@ -469,7 +467,7 @@ module ActiveRecord
469
467
  # <tt>Base#reset_column_information</tt> in order to ensure that the model has the
470
468
  # latest column data from after the new column was added. Example:
471
469
  #
472
- # class AddPeopleSalary < ActiveRecord::Migration[7.2]
470
+ # class AddPeopleSalary < ActiveRecord::Migration[8.0]
473
471
  # def up
474
472
  # add_column :people, :salary, :integer
475
473
  # Person.reset_column_information
@@ -531,7 +529,7 @@ module ActiveRecord
531
529
  # To define a reversible migration, define the +change+ method in your
532
530
  # migration like this:
533
531
  #
534
- # class TenderloveMigration < ActiveRecord::Migration[7.2]
532
+ # class TenderloveMigration < ActiveRecord::Migration[8.0]
535
533
  # def change
536
534
  # create_table(:horses) do |t|
537
535
  # t.column :content, :text
@@ -561,7 +559,7 @@ module ActiveRecord
561
559
  # can't execute inside a transaction though, and for these situations
562
560
  # you can turn the automatic transactions off.
563
561
  #
564
- # class ChangeEnum < ActiveRecord::Migration[7.2]
562
+ # class ChangeEnum < ActiveRecord::Migration[8.0]
565
563
  # disable_ddl_transaction!
566
564
  #
567
565
  # def up
@@ -604,7 +602,7 @@ module ActiveRecord
604
602
  end
605
603
  end
606
604
 
607
- def drop_table(table_name, **options)
605
+ def drop_table(*table_names, **options)
608
606
  if block_given?
609
607
  super { |t| yield compatible_table_definition(t) }
610
608
  else
@@ -715,13 +713,7 @@ module ActiveRecord
715
713
 
716
714
  def load_schema_if_pending!
717
715
  if any_schema_needs_update?
718
- # Roundtrip to Rake to allow plugins to hook into database initialization.
719
- root = defined?(ENGINE_ROOT) ? ENGINE_ROOT : Rails.root
720
-
721
- FileUtils.cd(root) do
722
- Base.connection_handler.clear_all_connections!(:all)
723
- system("bin/rails db:test:prepare")
724
- end
716
+ load_schema!
725
717
  end
726
718
 
727
719
  check_pending_migrations
@@ -785,6 +777,16 @@ module ActiveRecord
785
777
  def env
786
778
  ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
787
779
  end
780
+
781
+ def load_schema!
782
+ # Roundtrip to Rake to allow plugins to hook into database initialization.
783
+ root = defined?(ENGINE_ROOT) ? ENGINE_ROOT : Rails.root
784
+
785
+ FileUtils.cd(root) do
786
+ Base.connection_handler.clear_all_connections!(:all)
787
+ system("bin/rails db:test:prepare")
788
+ end
789
+ end
788
790
  end
789
791
 
790
792
  def disable_ddl_transaction # :nodoc:
@@ -822,7 +824,7 @@ module ActiveRecord
822
824
  # and create the table 'apples' on the way up, and the reverse
823
825
  # on the way down.
824
826
  #
825
- # class FixTLMigration < ActiveRecord::Migration[7.2]
827
+ # class FixTLMigration < ActiveRecord::Migration[8.0]
826
828
  # def change
827
829
  # revert do
828
830
  # create_table(:horses) do |t|
@@ -841,7 +843,7 @@ module ActiveRecord
841
843
  #
842
844
  # require_relative "20121212123456_tenderlove_migration"
843
845
  #
844
- # class FixupTLMigration < ActiveRecord::Migration[7.2]
846
+ # class FixupTLMigration < ActiveRecord::Migration[8.0]
845
847
  # def change
846
848
  # revert TenderloveMigration
847
849
  #
@@ -892,7 +894,7 @@ module ActiveRecord
892
894
  # when the three columns 'first_name', 'last_name' and 'full_name' exist,
893
895
  # even when migrating down:
894
896
  #
895
- # class SplitNameMigration < ActiveRecord::Migration[7.2]
897
+ # class SplitNameMigration < ActiveRecord::Migration[8.0]
896
898
  # def change
897
899
  # add_column :users, :first_name, :string
898
900
  # add_column :users, :last_name, :string
@@ -920,7 +922,7 @@ module ActiveRecord
920
922
  # In the following example, the new column +published+ will be given
921
923
  # the value +true+ for all existing records.
922
924
  #
923
- # class AddPublishedToPosts < ActiveRecord::Migration[7.2]
925
+ # class AddPublishedToPosts < ActiveRecord::Migration[8.0]
924
926
  # def change
925
927
  # add_column :posts, :published, :boolean, default: false
926
928
  # up_only do
@@ -972,16 +974,16 @@ module ActiveRecord
972
974
  when :down then announce "reverting"
973
975
  end
974
976
 
975
- time = nil
977
+ time_elapsed = nil
976
978
  ActiveRecord::Tasks::DatabaseTasks.migration_connection.pool.with_connection do |conn|
977
- time = Benchmark.measure do
979
+ time_elapsed = ActiveSupport::Benchmark.realtime do
978
980
  exec_migration(conn, direction)
979
981
  end
980
982
  end
981
983
 
982
984
  case direction
983
- when :up then announce "migrated (%.4fs)" % time.real; write
984
- when :down then announce "reverted (%.4fs)" % time.real; write
985
+ when :up then announce "migrated (%.4fs)" % time_elapsed; write
986
+ when :down then announce "reverted (%.4fs)" % time_elapsed; write
985
987
  end
986
988
  end
987
989
 
@@ -1022,8 +1024,8 @@ module ActiveRecord
1022
1024
  def say_with_time(message)
1023
1025
  say(message)
1024
1026
  result = nil
1025
- time = Benchmark.measure { result = yield }
1026
- say "%.4fs" % time.real, :subitem
1027
+ time_elapsed = ActiveSupport::Benchmark.realtime { result = yield }
1028
+ say "%.4fs" % time_elapsed, :subitem
1027
1029
  say("#{result} rows", :subitem) if result.is_a?(Integer)
1028
1030
  result
1029
1031
  end
@@ -431,7 +431,6 @@ module ActiveRecord
431
431
  end
432
432
 
433
433
  def columns
434
- load_schema unless @columns
435
434
  @columns ||= columns_hash.values.freeze
436
435
  end
437
436
 
@@ -503,7 +502,7 @@ module ActiveRecord
503
502
  # when just after creating a table you want to populate it with some default
504
503
  # values, e.g.:
505
504
  #
506
- # class CreateJobLevels < ActiveRecord::Migration[7.2]
505
+ # class CreateJobLevels < ActiveRecord::Migration[8.0]
507
506
  # def up
508
507
  # create_table :job_levels do |t|
509
508
  # t.integer :id
@@ -595,7 +594,7 @@ module ActiveRecord
595
594
  columns_hash = columns_hash.except(*ignored_columns) unless ignored_columns.empty?
596
595
  @columns_hash = columns_hash.freeze
597
596
 
598
- super
597
+ _default_attributes # Precompute to cache DB-dependent attribute types
599
598
  end
600
599
 
601
600
  # Guesses the table name, but does not decorate it with prefix and suffix information.
@@ -524,12 +524,12 @@ module ActiveRecord
524
524
  unless reject_new_record?(association_name, attributes)
525
525
  association.reader.build(attributes.except(*UNASSIGNABLE_KEYS))
526
526
  end
527
- elsif existing_record = existing_records.detect { |record| record.id.to_s == attributes["id"].to_s }
527
+ elsif existing_record = find_record_by_id(association.klass, existing_records, attributes["id"])
528
528
  unless call_reject_if(association_name, attributes)
529
529
  # Make sure we are operating on the actual object which is in the association's
530
530
  # proxy_target array (either by finding it, or adding it if not found)
531
531
  # Take into account that the proxy_target may have changed due to callbacks
532
- target_record = association.target.detect { |record| record.id.to_s == attributes["id"].to_s }
532
+ target_record = find_record_by_id(association.klass, association.target, attributes["id"])
533
533
  if target_record
534
534
  existing_record = target_record
535
535
  else
@@ -620,5 +620,14 @@ module ActiveRecord
620
620
  raise RecordNotFound.new("Couldn't find #{model} with ID=#{record_id} for #{self.class.name} with ID=#{id}",
621
621
  model, "id", record_id)
622
622
  end
623
+
624
+ def find_record_by_id(klass, records, id)
625
+ if klass.composite_primary_key?
626
+ id = Array(id).map(&:to_s)
627
+ records.find { |record| Array(record.id).map(&:to_s) == id }
628
+ else
629
+ records.find { |record| record.id.to_s == id.to_s }
630
+ end
631
+ end
623
632
  end
624
633
  end