activerecord 7.2.3 → 8.1.3

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.
Files changed (198) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +612 -1055
  3. data/README.rdoc +1 -1
  4. data/lib/active_record/association_relation.rb +2 -1
  5. data/lib/active_record/associations/association.rb +35 -11
  6. data/lib/active_record/associations/builder/association.rb +23 -11
  7. data/lib/active_record/associations/builder/belongs_to.rb +17 -4
  8. data/lib/active_record/associations/builder/collection_association.rb +7 -3
  9. data/lib/active_record/associations/builder/has_one.rb +1 -1
  10. data/lib/active_record/associations/builder/singular_association.rb +33 -5
  11. data/lib/active_record/associations/collection_association.rb +1 -1
  12. data/lib/active_record/associations/collection_proxy.rb +22 -4
  13. data/lib/active_record/associations/deprecation.rb +88 -0
  14. data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
  15. data/lib/active_record/associations/errors.rb +3 -0
  16. data/lib/active_record/associations/has_many_through_association.rb +3 -2
  17. data/lib/active_record/associations/join_dependency.rb +4 -2
  18. data/lib/active_record/associations/preloader/association.rb +2 -2
  19. data/lib/active_record/associations/preloader/batch.rb +7 -1
  20. data/lib/active_record/associations/preloader/branch.rb +1 -0
  21. data/lib/active_record/associations/singular_association.rb +8 -3
  22. data/lib/active_record/associations.rb +192 -24
  23. data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
  24. data/lib/active_record/attribute_methods/primary_key.rb +4 -8
  25. data/lib/active_record/attribute_methods/query.rb +34 -0
  26. data/lib/active_record/attribute_methods/serialization.rb +16 -3
  27. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -14
  28. data/lib/active_record/attributes.rb +3 -0
  29. data/lib/active_record/autosave_association.rb +69 -27
  30. data/lib/active_record/base.rb +1 -2
  31. data/lib/active_record/coders/json.rb +14 -5
  32. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +35 -28
  33. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -4
  34. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +51 -13
  35. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +412 -88
  36. data/lib/active_record/connection_adapters/abstract/database_statements.rb +137 -75
  37. data/lib/active_record/connection_adapters/abstract/query_cache.rb +27 -5
  38. data/lib/active_record/connection_adapters/abstract/quoting.rb +16 -25
  39. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +11 -7
  40. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +32 -35
  41. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
  42. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +122 -32
  43. data/lib/active_record/connection_adapters/abstract/transaction.rb +40 -8
  44. data/lib/active_record/connection_adapters/abstract_adapter.rb +150 -91
  45. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +63 -52
  46. data/lib/active_record/connection_adapters/column.rb +17 -4
  47. data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
  48. data/lib/active_record/connection_adapters/mysql/quoting.rb +0 -8
  49. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
  50. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +41 -10
  51. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +73 -46
  52. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +89 -94
  53. data/lib/active_record/connection_adapters/mysql2_adapter.rb +2 -10
  54. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  55. data/lib/active_record/connection_adapters/postgresql/column.rb +8 -2
  56. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +76 -45
  57. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -3
  58. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
  59. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
  60. data/lib/active_record/connection_adapters/postgresql/quoting.rb +21 -10
  61. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
  62. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +9 -17
  63. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +14 -33
  64. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +71 -32
  65. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +139 -63
  66. data/lib/active_record/connection_adapters/postgresql_adapter.rb +78 -105
  67. data/lib/active_record/connection_adapters/schema_cache.rb +3 -5
  68. data/lib/active_record/connection_adapters/sqlite3/column.rb +8 -2
  69. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +90 -98
  70. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +0 -8
  71. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
  72. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +27 -2
  73. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +13 -14
  74. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +102 -37
  75. data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
  76. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +38 -67
  77. data/lib/active_record/connection_adapters/trilogy_adapter.rb +1 -18
  78. data/lib/active_record/connection_adapters.rb +1 -56
  79. data/lib/active_record/connection_handling.rb +25 -2
  80. data/lib/active_record/core.rb +33 -17
  81. data/lib/active_record/counter_cache.rb +33 -8
  82. data/lib/active_record/database_configurations/database_config.rb +9 -1
  83. data/lib/active_record/database_configurations/hash_config.rb +67 -9
  84. data/lib/active_record/database_configurations/url_config.rb +13 -3
  85. data/lib/active_record/database_configurations.rb +7 -3
  86. data/lib/active_record/delegated_type.rb +1 -1
  87. data/lib/active_record/dynamic_matchers.rb +54 -69
  88. data/lib/active_record/encryption/config.rb +3 -1
  89. data/lib/active_record/encryption/encryptable_record.rb +8 -8
  90. data/lib/active_record/encryption/encrypted_attribute_type.rb +11 -2
  91. data/lib/active_record/encryption/encryptor.rb +28 -8
  92. data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
  93. data/lib/active_record/encryption/scheme.rb +9 -2
  94. data/lib/active_record/enum.rb +33 -30
  95. data/lib/active_record/errors.rb +33 -9
  96. data/lib/active_record/explain.rb +1 -1
  97. data/lib/active_record/explain_registry.rb +51 -2
  98. data/lib/active_record/filter_attribute_handler.rb +73 -0
  99. data/lib/active_record/fixtures.rb +2 -4
  100. data/lib/active_record/future_result.rb +15 -9
  101. data/lib/active_record/gem_version.rb +2 -2
  102. data/lib/active_record/inheritance.rb +1 -1
  103. data/lib/active_record/insert_all.rb +14 -9
  104. data/lib/active_record/locking/optimistic.rb +8 -1
  105. data/lib/active_record/locking/pessimistic.rb +5 -0
  106. data/lib/active_record/log_subscriber.rb +3 -13
  107. data/lib/active_record/middleware/shard_selector.rb +34 -17
  108. data/lib/active_record/migration/command_recorder.rb +45 -12
  109. data/lib/active_record/migration/compatibility.rb +37 -24
  110. data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
  111. data/lib/active_record/migration.rb +48 -42
  112. data/lib/active_record/model_schema.rb +38 -13
  113. data/lib/active_record/nested_attributes.rb +6 -6
  114. data/lib/active_record/persistence.rb +162 -133
  115. data/lib/active_record/query_cache.rb +22 -15
  116. data/lib/active_record/query_logs.rb +100 -52
  117. data/lib/active_record/query_logs_formatter.rb +17 -28
  118. data/lib/active_record/querying.rb +8 -8
  119. data/lib/active_record/railtie.rb +35 -30
  120. data/lib/active_record/railties/controller_runtime.rb +11 -6
  121. data/lib/active_record/railties/databases.rake +26 -38
  122. data/lib/active_record/railties/job_checkpoints.rb +15 -0
  123. data/lib/active_record/railties/job_runtime.rb +10 -11
  124. data/lib/active_record/reflection.rb +53 -21
  125. data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
  126. data/lib/active_record/relation/batches.rb +147 -73
  127. data/lib/active_record/relation/calculations.rb +52 -40
  128. data/lib/active_record/relation/delegation.rb +25 -15
  129. data/lib/active_record/relation/finder_methods.rb +40 -24
  130. data/lib/active_record/relation/merger.rb +8 -8
  131. data/lib/active_record/relation/predicate_builder/array_handler.rb +3 -1
  132. data/lib/active_record/relation/predicate_builder/association_query_value.rb +9 -9
  133. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +8 -8
  134. data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
  135. data/lib/active_record/relation/predicate_builder.rb +22 -7
  136. data/lib/active_record/relation/query_attribute.rb +3 -1
  137. data/lib/active_record/relation/query_methods.rb +140 -86
  138. data/lib/active_record/relation/spawn_methods.rb +7 -7
  139. data/lib/active_record/relation/where_clause.rb +2 -9
  140. data/lib/active_record/relation.rb +107 -75
  141. data/lib/active_record/result.rb +109 -24
  142. data/lib/active_record/runtime_registry.rb +42 -58
  143. data/lib/active_record/sanitization.rb +9 -6
  144. data/lib/active_record/schema_dumper.rb +18 -11
  145. data/lib/active_record/schema_migration.rb +2 -1
  146. data/lib/active_record/scoping/named.rb +5 -2
  147. data/lib/active_record/scoping.rb +0 -1
  148. data/lib/active_record/signed_id.rb +43 -15
  149. data/lib/active_record/statement_cache.rb +24 -20
  150. data/lib/active_record/store.rb +51 -22
  151. data/lib/active_record/structured_event_subscriber.rb +85 -0
  152. data/lib/active_record/table_metadata.rb +6 -23
  153. data/lib/active_record/tasks/abstract_tasks.rb +76 -0
  154. data/lib/active_record/tasks/database_tasks.rb +85 -85
  155. data/lib/active_record/tasks/mysql_database_tasks.rb +3 -42
  156. data/lib/active_record/tasks/postgresql_database_tasks.rb +7 -40
  157. data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -28
  158. data/lib/active_record/test_databases.rb +14 -4
  159. data/lib/active_record/test_fixtures.rb +39 -2
  160. data/lib/active_record/testing/query_assertions.rb +8 -2
  161. data/lib/active_record/timestamp.rb +4 -2
  162. data/lib/active_record/token_for.rb +1 -1
  163. data/lib/active_record/transaction.rb +2 -5
  164. data/lib/active_record/transactions.rb +37 -16
  165. data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
  166. data/lib/active_record/type/internal/timezone.rb +7 -0
  167. data/lib/active_record/type/json.rb +13 -2
  168. data/lib/active_record/type/serialized.rb +16 -4
  169. data/lib/active_record/type/type_map.rb +1 -1
  170. data/lib/active_record/type_caster/connection.rb +2 -1
  171. data/lib/active_record/validations/associated.rb +1 -1
  172. data/lib/active_record/validations/uniqueness.rb +8 -8
  173. data/lib/active_record.rb +84 -49
  174. data/lib/arel/alias_predication.rb +2 -0
  175. data/lib/arel/collectors/bind.rb +2 -2
  176. data/lib/arel/collectors/sql_string.rb +1 -1
  177. data/lib/arel/collectors/substitute_binds.rb +2 -2
  178. data/lib/arel/crud.rb +6 -11
  179. data/lib/arel/nodes/binary.rb +1 -1
  180. data/lib/arel/nodes/count.rb +2 -2
  181. data/lib/arel/nodes/function.rb +4 -10
  182. data/lib/arel/nodes/named_function.rb +2 -2
  183. data/lib/arel/nodes/node.rb +2 -2
  184. data/lib/arel/nodes/sql_literal.rb +1 -1
  185. data/lib/arel/nodes.rb +0 -2
  186. data/lib/arel/predications.rb +1 -3
  187. data/lib/arel/select_manager.rb +7 -2
  188. data/lib/arel/table.rb +3 -7
  189. data/lib/arel/visitors/dot.rb +0 -3
  190. data/lib/arel/visitors/postgresql.rb +55 -0
  191. data/lib/arel/visitors/sqlite.rb +55 -8
  192. data/lib/arel/visitors/to_sql.rb +3 -21
  193. data/lib/arel.rb +3 -1
  194. data/lib/rails/generators/active_record/application_record/USAGE +1 -1
  195. metadata +16 -13
  196. data/lib/active_record/explain_subscriber.rb +0 -34
  197. data/lib/active_record/normalization.rb +0 -163
  198. data/lib/active_record/relation/record_fetch_warning.rb +0 -52
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "benchmark"
4
- require "set"
5
- require "zlib"
6
3
  require "active_support/core_ext/array/access"
7
4
  require "active_support/core_ext/enumerable"
8
5
  require "active_support/core_ext/module/attribute_accessors"
@@ -21,7 +18,7 @@ module ActiveRecord
21
18
  # For example the following migration is not reversible.
22
19
  # Rolling back this migration will raise an ActiveRecord::IrreversibleMigration error.
23
20
  #
24
- # class IrreversibleMigrationExample < ActiveRecord::Migration[7.2]
21
+ # class IrreversibleMigrationExample < ActiveRecord::Migration[8.1]
25
22
  # def change
26
23
  # create_table :distributors do |t|
27
24
  # t.string :zipcode
@@ -39,7 +36,7 @@ module ActiveRecord
39
36
  #
40
37
  # 1. Define <tt>#up</tt> and <tt>#down</tt> methods instead of <tt>#change</tt>:
41
38
  #
42
- # class ReversibleMigrationExample < ActiveRecord::Migration[7.2]
39
+ # class ReversibleMigrationExample < ActiveRecord::Migration[8.1]
43
40
  # def up
44
41
  # create_table :distributors do |t|
45
42
  # t.string :zipcode
@@ -64,7 +61,7 @@ module ActiveRecord
64
61
  #
65
62
  # 2. Use the #reversible method in <tt>#change</tt> method:
66
63
  #
67
- # class ReversibleMigrationExample < ActiveRecord::Migration[7.2]
64
+ # class ReversibleMigrationExample < ActiveRecord::Migration[8.1]
68
65
  # def change
69
66
  # create_table :distributors do |t|
70
67
  # t.string :zipcode
@@ -151,11 +148,10 @@ module ActiveRecord
151
148
  include ActiveSupport::ActionableError
152
149
 
153
150
  action "Run pending migrations" do
154
- ActiveRecord::Tasks::DatabaseTasks.migrate
151
+ ActiveRecord::Tasks::DatabaseTasks.migrate_all
155
152
 
156
153
  if ActiveRecord.dump_schema_after_migration
157
- connection = ActiveRecord::Tasks::DatabaseTasks.migration_connection
158
- ActiveRecord::Tasks::DatabaseTasks.dump_schema(connection.pool.db_config)
154
+ ActiveRecord::Tasks::DatabaseTasks.dump_all
159
155
  end
160
156
  end
161
157
 
@@ -250,7 +246,7 @@ module ActiveRecord
250
246
  #
251
247
  # Example of a simple migration:
252
248
  #
253
- # class AddSsl < ActiveRecord::Migration[7.2]
249
+ # class AddSsl < ActiveRecord::Migration[8.1]
254
250
  # def up
255
251
  # add_column :accounts, :ssl_enabled, :boolean, default: true
256
252
  # end
@@ -270,7 +266,7 @@ module ActiveRecord
270
266
  #
271
267
  # Example of a more complex migration that also needs to initialize data:
272
268
  #
273
- # class AddSystemSettings < ActiveRecord::Migration[7.2]
269
+ # class AddSystemSettings < ActiveRecord::Migration[8.1]
274
270
  # def up
275
271
  # create_table :system_settings do |t|
276
272
  # t.string :name
@@ -357,7 +353,7 @@ module ActiveRecord
357
353
  #
358
354
  # === Deletion
359
355
  #
360
- # * <tt>drop_table(name)</tt>: Drops the table called +name+.
356
+ # * <tt>drop_table(*names)</tt>: Drops the given tables.
361
357
  # * <tt>drop_join_table(table_1, table_2, options)</tt>: Drops the join table
362
358
  # specified by the given arguments.
363
359
  # * <tt>remove_column(table_name, column_name, type, options)</tt>: Removes the column
@@ -399,7 +395,7 @@ module ActiveRecord
399
395
  # $ bin/rails generate migration add_fieldname_to_tablename fieldname:string
400
396
  #
401
397
  # 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]
398
+ # class AddFieldnameToTablename < ActiveRecord::Migration[8.1]
403
399
  # def change
404
400
  # add_column :tablenames, :fieldname, :string
405
401
  # end
@@ -425,7 +421,7 @@ module ActiveRecord
425
421
  #
426
422
  # Not all migrations change the schema. Some just fix the data:
427
423
  #
428
- # class RemoveEmptyTags < ActiveRecord::Migration[7.2]
424
+ # class RemoveEmptyTags < ActiveRecord::Migration[8.1]
429
425
  # def up
430
426
  # Tag.all.each { |tag| tag.destroy if tag.pages.empty? }
431
427
  # end
@@ -438,7 +434,7 @@ module ActiveRecord
438
434
  #
439
435
  # Others remove columns when they migrate up instead of down:
440
436
  #
441
- # class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration[7.2]
437
+ # class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration[8.1]
442
438
  # def up
443
439
  # remove_column :items, :incomplete_items_count
444
440
  # remove_column :items, :completed_items_count
@@ -452,7 +448,7 @@ module ActiveRecord
452
448
  #
453
449
  # And sometimes you need to do something in SQL not abstracted directly by migrations:
454
450
  #
455
- # class MakeJoinUnique < ActiveRecord::Migration[7.2]
451
+ # class MakeJoinUnique < ActiveRecord::Migration[8.1]
456
452
  # def up
457
453
  # execute "ALTER TABLE `pages_linked_pages` ADD UNIQUE `page_id_linked_page_id` (`page_id`,`linked_page_id`)"
458
454
  # end
@@ -469,7 +465,7 @@ module ActiveRecord
469
465
  # <tt>Base#reset_column_information</tt> in order to ensure that the model has the
470
466
  # latest column data from after the new column was added. Example:
471
467
  #
472
- # class AddPeopleSalary < ActiveRecord::Migration[7.2]
468
+ # class AddPeopleSalary < ActiveRecord::Migration[8.1]
473
469
  # def up
474
470
  # add_column :people, :salary, :integer
475
471
  # Person.reset_column_information
@@ -531,7 +527,7 @@ module ActiveRecord
531
527
  # To define a reversible migration, define the +change+ method in your
532
528
  # migration like this:
533
529
  #
534
- # class TenderloveMigration < ActiveRecord::Migration[7.2]
530
+ # class TenderloveMigration < ActiveRecord::Migration[8.1]
535
531
  # def change
536
532
  # create_table(:horses) do |t|
537
533
  # t.column :content, :text
@@ -561,7 +557,7 @@ module ActiveRecord
561
557
  # can't execute inside a transaction though, and for these situations
562
558
  # you can turn the automatic transactions off.
563
559
  #
564
- # class ChangeEnum < ActiveRecord::Migration[7.2]
560
+ # class ChangeEnum < ActiveRecord::Migration[8.1]
565
561
  # disable_ddl_transaction!
566
562
  #
567
563
  # def up
@@ -574,6 +570,7 @@ module ActiveRecord
574
570
  class Migration
575
571
  autoload :CommandRecorder, "active_record/migration/command_recorder"
576
572
  autoload :Compatibility, "active_record/migration/compatibility"
573
+ autoload :DefaultSchemaVersionsFormatter, "active_record/migration/default_schema_versions_formatter"
577
574
  autoload :JoinTable, "active_record/migration/join_table"
578
575
  autoload :ExecutionStrategy, "active_record/migration/execution_strategy"
579
576
  autoload :DefaultStrategy, "active_record/migration/default_strategy"
@@ -604,7 +601,7 @@ module ActiveRecord
604
601
  end
605
602
  end
606
603
 
607
- def drop_table(table_name, **options)
604
+ def drop_table(*table_names, **options)
608
605
  if block_given?
609
606
  super { |t| yield compatible_table_definition(t) }
610
607
  else
@@ -681,10 +678,6 @@ module ActiveRecord
681
678
  paths = all_configs.flat_map { |config| config.migrations_paths || Migrator.migrations_paths }.uniq
682
679
  @file_watcher.new([], paths.index_with(["rb"]), &block)
683
680
  end
684
-
685
- def connection
686
- ActiveRecord::Tasks::DatabaseTasks.migration_connection
687
- end
688
681
  end
689
682
 
690
683
  class << self
@@ -715,13 +708,7 @@ module ActiveRecord
715
708
 
716
709
  def load_schema_if_pending!
717
710
  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
711
+ load_schema!
725
712
  end
726
713
 
727
714
  check_pending_migrations
@@ -760,7 +747,7 @@ module ActiveRecord
760
747
  private
761
748
  def any_schema_needs_update?
762
749
  !db_configs_in_current_env.all? do |db_config|
763
- Tasks::DatabaseTasks.schema_up_to_date?(db_config, ActiveRecord.schema_format)
750
+ Tasks::DatabaseTasks.schema_up_to_date?(db_config)
764
751
  end
765
752
  end
766
753
 
@@ -785,6 +772,21 @@ module ActiveRecord
785
772
  def env
786
773
  ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
787
774
  end
775
+
776
+ def load_schema!
777
+ # Roundtrip to Rake to allow plugins to hook into database initialization.
778
+ root = defined?(ENGINE_ROOT) ? ENGINE_ROOT : Rails.root
779
+
780
+ FileUtils.cd(root) do
781
+ Base.connection_handler.clear_all_connections!(:all)
782
+ system("bin/rails db:test:prepare")
783
+ end
784
+ end
785
+
786
+ def respond_to_missing?(method, include_private = false)
787
+ return false if nearest_delegate == delegate
788
+ nearest_delegate.respond_to?(method, include_private)
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.1]
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.1]
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.1]
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.1]
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
@@ -1173,6 +1175,10 @@ module ActiveRecord
1173
1175
  def command_recorder
1174
1176
  CommandRecorder.new(connection)
1175
1177
  end
1178
+
1179
+ def respond_to_missing?(method, include_private = false)
1180
+ execution_strategy.respond_to?(method, include_private) || super
1181
+ end
1176
1182
  end
1177
1183
 
1178
1184
  # MigrationProxy is used to defer loading of the actual migration classes
@@ -48,7 +48,7 @@ module ActiveRecord
48
48
  # way of creating a namespace for tables in a shared database. By default, the prefix is the
49
49
  # empty string.
50
50
  #
51
- # If you are organising your models within modules you can add a prefix to the models within
51
+ # If you are organizing your models within modules you can add a prefix to the models within
52
52
  # a namespace by defining a singleton method in the parent module called table_name_prefix which
53
53
  # returns your chosen prefix.
54
54
 
@@ -65,7 +65,7 @@ module ActiveRecord
65
65
  # Works like +table_name_prefix=+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp",
66
66
  # "people_basecamp"). By default, the suffix is the empty string.
67
67
  #
68
- # If you are organising your models within modules, you can add a suffix to the models within
68
+ # If you are organizing your models within modules, you can add a suffix to the models within
69
69
  # a namespace by defining a singleton method in the parent module called table_name_suffix which
70
70
  # returns your chosen suffix.
71
71
 
@@ -113,17 +113,19 @@ module ActiveRecord
113
113
  # :singleton-method: implicit_order_column
114
114
  # :call-seq: implicit_order_column
115
115
  #
116
- # The name of the column records are ordered by if no explicit order clause
116
+ # The name of the column(s) records are ordered by if no explicit order clause
117
117
  # is used during an ordered finder call. If not set the primary key is used.
118
118
 
119
119
  ##
120
120
  # :singleton-method: implicit_order_column=
121
121
  # :call-seq: implicit_order_column=(column_name)
122
122
  #
123
- # Sets the column to sort records by when no explicit order clause is used
124
- # during an ordered finder call. Useful when the primary key is not an
125
- # auto-incrementing integer, for example when it's a UUID. Records are subsorted
126
- # by the primary key if it exists to ensure deterministic results.
123
+ # Sets the column(s) to sort records by when no explicit order clause is used
124
+ # during an ordered finder call. Useful for models where the primary key isn't an
125
+ # auto-incrementing integer (such as UUID).
126
+ #
127
+ # By default, records are subsorted by primary key to ensure deterministic results.
128
+ # To disable this subsort behavior, set `implicit_order_column` to `["column_name", nil]`.
127
129
 
128
130
  ##
129
131
  # :singleton-method: immutable_strings_by_default=
@@ -179,6 +181,7 @@ module ActiveRecord
179
181
  self.protected_environments = ["production"]
180
182
 
181
183
  self.ignored_columns = [].freeze
184
+ self.only_columns = [].freeze
182
185
 
183
186
  delegate :type_for_attribute, :column_for_attribute, to: :class
184
187
 
@@ -276,15 +279,14 @@ module ActiveRecord
276
279
  end
277
280
 
278
281
  @table_name = value
279
- @quoted_table_name = nil
280
282
  @arel_table = nil
281
283
  @sequence_name = nil unless @explicit_sequence_name
282
284
  @predicate_builder = nil
283
285
  end
284
286
 
285
- # Returns a quoted version of the table name, used to construct SQL statements.
287
+ # Returns a quoted version of the table name.
286
288
  def quoted_table_name
287
- @quoted_table_name ||= adapter_class.quote_table_name(table_name)
289
+ adapter_class.quote_table_name(table_name)
288
290
  end
289
291
 
290
292
  # Computes the table name, (re)sets it internally, and returns it.
@@ -333,6 +335,12 @@ module ActiveRecord
333
335
  @ignored_columns || superclass.ignored_columns
334
336
  end
335
337
 
338
+ # The list of columns names the model should allow. Only columns are used to define
339
+ # attribute accessors, and are referenced in SQL queries.
340
+ def only_columns
341
+ @only_columns || superclass.only_columns
342
+ end
343
+
336
344
  # Sets the columns names the model should ignore. Ignored columns won't have attribute
337
345
  # accessors defined, and won't be referenced in SQL queries.
338
346
  #
@@ -365,10 +373,17 @@ module ActiveRecord
365
373
  # user = Project.create!(name: "First Project")
366
374
  # user.category # => raises NoMethodError
367
375
  def ignored_columns=(columns)
376
+ check_model_columns(@only_columns.present?)
368
377
  reload_schema_from_cache
369
378
  @ignored_columns = columns.map(&:to_s).freeze
370
379
  end
371
380
 
381
+ def only_columns=(columns)
382
+ check_model_columns(@ignored_columns.present?)
383
+ reload_schema_from_cache
384
+ @only_columns = columns.map(&:to_s).freeze
385
+ end
386
+
372
387
  def sequence_name
373
388
  if base_class?
374
389
  @sequence_name ||= reset_sequence_name
@@ -502,7 +517,7 @@ module ActiveRecord
502
517
  # when just after creating a table you want to populate it with some default
503
518
  # values, e.g.:
504
519
  #
505
- # class CreateJobLevels < ActiveRecord::Migration[7.2]
520
+ # class CreateJobLevels < ActiveRecord::Migration[8.1]
506
521
  # def up
507
522
  # create_table :job_levels do |t|
508
523
  # t.integer :id
@@ -578,6 +593,7 @@ module ActiveRecord
578
593
  child_class.reload_schema_from_cache(false)
579
594
  child_class.class_eval do
580
595
  @ignored_columns = nil
596
+ @only_columns = nil
581
597
  end
582
598
  end
583
599
 
@@ -591,7 +607,11 @@ module ActiveRecord
591
607
  end
592
608
 
593
609
  columns_hash = schema_cache.columns_hash(table_name)
594
- columns_hash = columns_hash.except(*ignored_columns) unless ignored_columns.empty?
610
+ if only_columns.present?
611
+ columns_hash = columns_hash.slice(*only_columns)
612
+ elsif ignored_columns.present?
613
+ columns_hash = columns_hash.except(*ignored_columns)
614
+ end
595
615
  @columns_hash = columns_hash.freeze
596
616
 
597
617
  _default_attributes # Precompute to cache DB-dependent attribute types
@@ -621,7 +641,8 @@ module ActiveRecord
621
641
  end
622
642
 
623
643
  def type_for_column(connection, column)
624
- type = connection.lookup_cast_type_from_column(column)
644
+ # TODO: Remove the need for a connection after we release 8.1.
645
+ type = column.fetch_cast_type(connection)
625
646
 
626
647
  if immutable_strings_by_default && type.respond_to?(:to_immutable_string)
627
648
  type = type.to_immutable_string
@@ -629,6 +650,10 @@ module ActiveRecord
629
650
 
630
651
  type
631
652
  end
653
+
654
+ def check_model_columns(columns_present)
655
+ raise ArgumentError, "You can not use both only_columns and ignored_columns in the same model." if columns_present
656
+ end
632
657
  end
633
658
  end
634
659
  end
@@ -387,6 +387,8 @@ module ActiveRecord
387
387
  generated_association_methods.module_eval <<-eoruby, __FILE__, __LINE__ + 1
388
388
  silence_redefinition_of_method :#{association_name}_attributes=
389
389
  def #{association_name}_attributes=(attributes)
390
+ association = association(:#{association_name})
391
+ deprecated_associations_api_guard(association, __method__)
390
392
  assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes)
391
393
  end
392
394
  eoruby
@@ -524,12 +526,12 @@ module ActiveRecord
524
526
  unless reject_new_record?(association_name, attributes)
525
527
  association.reader.build(attributes.except(*UNASSIGNABLE_KEYS))
526
528
  end
527
- elsif existing_record = find_record_by_id(existing_records, attributes["id"])
529
+ elsif existing_record = find_record_by_id(association.klass, existing_records, attributes["id"])
528
530
  unless call_reject_if(association_name, attributes)
529
531
  # Make sure we are operating on the actual object which is in the association's
530
532
  # proxy_target array (either by finding it, or adding it if not found)
531
533
  # Take into account that the proxy_target may have changed due to callbacks
532
- target_record = find_record_by_id(association.target, attributes["id"])
534
+ target_record = find_record_by_id(association.klass, association.target, attributes["id"])
533
535
  if target_record
534
536
  existing_record = target_record
535
537
  else
@@ -621,10 +623,8 @@ module ActiveRecord
621
623
  model, "id", record_id)
622
624
  end
623
625
 
624
- def find_record_by_id(records, id)
625
- return if records.empty?
626
-
627
- if records.first.class.composite_primary_key?
626
+ def find_record_by_id(klass, records, id)
627
+ if klass.composite_primary_key?
628
628
  id = Array(id).map(&:to_s)
629
629
  records.find { |record| Array(record.id).map(&:to_s) == id }
630
630
  else