activerecord 7.0.0.alpha2 → 7.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +539 -11
  3. data/lib/active_record/associations/association.rb +2 -8
  4. data/lib/active_record/associations/builder/collection_association.rb +9 -2
  5. data/lib/active_record/associations/collection_association.rb +10 -2
  6. data/lib/active_record/associations/join_dependency.rb +6 -2
  7. data/lib/active_record/associations/preloader/association.rb +68 -48
  8. data/lib/active_record/associations/preloader/batch.rb +3 -6
  9. data/lib/active_record/associations/preloader/through_association.rb +19 -9
  10. data/lib/active_record/associations/preloader.rb +14 -24
  11. data/lib/active_record/associations/through_association.rb +2 -2
  12. data/lib/active_record/associations.rb +16 -3
  13. data/lib/active_record/asynchronous_queries_tracker.rb +3 -0
  14. data/lib/active_record/attribute_methods/dirty.rb +9 -1
  15. data/lib/active_record/attribute_methods.rb +7 -5
  16. data/lib/active_record/autosave_association.rb +3 -3
  17. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +6 -26
  18. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -2
  19. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +18 -5
  20. data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -17
  21. data/lib/active_record/connection_adapters/abstract/database_statements.rb +1 -1
  22. data/lib/active_record/connection_adapters/abstract/quoting.rb +33 -70
  23. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +4 -0
  24. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +9 -2
  25. data/lib/active_record/connection_adapters/abstract/transaction.rb +12 -19
  26. data/lib/active_record/connection_adapters/abstract_adapter.rb +37 -8
  27. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +1 -0
  28. data/lib/active_record/connection_adapters/column.rb +4 -0
  29. data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -2
  30. data/lib/active_record/connection_adapters/mysql/quoting.rb +23 -24
  31. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +1 -1
  32. data/lib/active_record/connection_adapters/pool_config.rb +7 -5
  33. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -1
  34. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +2 -0
  35. data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -44
  36. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
  37. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +18 -1
  38. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
  39. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +15 -4
  40. data/lib/active_record/connection_adapters/postgresql_adapter.rb +48 -5
  41. data/lib/active_record/connection_adapters/schema_cache.rb +3 -1
  42. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +4 -2
  43. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +15 -16
  44. data/lib/active_record/connection_handling.rb +31 -19
  45. data/lib/active_record/core.rb +13 -24
  46. data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
  47. data/lib/active_record/database_configurations/database_config.rb +0 -9
  48. data/lib/active_record/database_configurations/hash_config.rb +40 -8
  49. data/lib/active_record/database_configurations.rb +2 -27
  50. data/lib/active_record/delegated_type.rb +19 -0
  51. data/lib/active_record/encryption/encryptable_record.rb +1 -1
  52. data/lib/active_record/encryption/encrypted_attribute_type.rb +1 -1
  53. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +1 -2
  54. data/lib/active_record/encryption/message_serializer.rb +11 -1
  55. data/lib/active_record/encryption/scheme.rb +1 -1
  56. data/lib/active_record/enum.rb +8 -1
  57. data/lib/active_record/errors.rb +1 -1
  58. data/lib/active_record/explain_registry.rb +11 -6
  59. data/lib/active_record/fixture_set/table_row.rb +1 -1
  60. data/lib/active_record/fixtures.rb +1 -9
  61. data/lib/active_record/future_result.rb +2 -2
  62. data/lib/active_record/gem_version.rb +1 -1
  63. data/lib/active_record/insert_all.rb +52 -15
  64. data/lib/active_record/integration.rb +3 -2
  65. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  66. data/lib/active_record/locking/pessimistic.rb +9 -3
  67. data/lib/active_record/log_subscriber.rb +8 -1
  68. data/lib/active_record/middleware/shard_selector.rb +60 -0
  69. data/lib/active_record/migration.rb +2 -2
  70. data/lib/active_record/model_schema.rb +1 -28
  71. data/lib/active_record/nested_attributes.rb +11 -10
  72. data/lib/active_record/no_touching.rb +1 -1
  73. data/lib/active_record/persistence.rb +99 -21
  74. data/lib/active_record/query_logs.rb +18 -83
  75. data/lib/active_record/railtie.rb +11 -1
  76. data/lib/active_record/railties/databases.rake +4 -91
  77. data/lib/active_record/reflection.rb +22 -6
  78. data/lib/active_record/relation/calculations.rb +1 -10
  79. data/lib/active_record/relation/finder_methods.rb +0 -13
  80. data/lib/active_record/relation/query_methods.rb +5 -14
  81. data/lib/active_record/relation/record_fetch_warning.rb +5 -7
  82. data/lib/active_record/relation/where_clause.rb +2 -15
  83. data/lib/active_record/relation.rb +11 -15
  84. data/lib/active_record/result.rb +0 -5
  85. data/lib/active_record/runtime_registry.rb +10 -12
  86. data/lib/active_record/schema_dumper.rb +7 -0
  87. data/lib/active_record/schema_migration.rb +4 -0
  88. data/lib/active_record/scoping.rb +34 -22
  89. data/lib/active_record/suppressor.rb +11 -15
  90. data/lib/active_record/tasks/database_tasks.rb +18 -44
  91. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -1
  92. data/lib/active_record/validations/uniqueness.rb +1 -1
  93. data/lib/active_record.rb +41 -33
  94. data/lib/arel/crud.rb +12 -2
  95. data/lib/arel/delete_manager.rb +16 -0
  96. data/lib/arel/filter_predications.rb +9 -0
  97. data/lib/arel/nodes/delete_statement.rb +5 -1
  98. data/lib/arel/nodes/filter.rb +10 -0
  99. data/lib/arel/nodes/function.rb +1 -0
  100. data/lib/arel/nodes/update_statement.rb +5 -1
  101. data/lib/arel/nodes.rb +1 -0
  102. data/lib/arel/predications.rb +10 -2
  103. data/lib/arel/update_manager.rb +16 -0
  104. data/lib/arel/visitors/mysql.rb +2 -1
  105. data/lib/arel/visitors/to_sql.rb +15 -0
  106. data/lib/arel.rb +1 -0
  107. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  108. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  109. metadata +18 -12
@@ -335,7 +335,7 @@ module ActiveRecord
335
335
  end
336
336
  end
337
337
 
338
- persisted + memory
338
+ persisted + memory.reject(&:persisted?)
339
339
  end
340
340
 
341
341
  def _create_record(attributes, raise = false, &block)
@@ -456,6 +456,10 @@ module ActiveRecord
456
456
 
457
457
  yield(record) if block_given?
458
458
 
459
+ if !index && @replaced_or_added_targets.include?(record)
460
+ index = @target.index(record)
461
+ end
462
+
459
463
  @replaced_or_added_targets << record if inversing || index || record.new_record?
460
464
 
461
465
  if index
@@ -480,7 +484,11 @@ module ActiveRecord
480
484
 
481
485
  def callbacks_for(callback_name)
482
486
  full_callback_name = "#{callback_name}_for_#{reflection.name}"
483
- owner.class.send(full_callback_name)
487
+ if owner.class.respond_to?(full_callback_name)
488
+ owner.class.send(full_callback_name)
489
+ else
490
+ []
491
+ end
484
492
  end
485
493
 
486
494
  def include_in_memory?(record)
@@ -3,8 +3,12 @@
3
3
  module ActiveRecord
4
4
  module Associations
5
5
  class JoinDependency # :nodoc:
6
- autoload :JoinBase, "active_record/associations/join_dependency/join_base"
7
- autoload :JoinAssociation, "active_record/associations/join_dependency/join_association"
6
+ extend ActiveSupport::Autoload
7
+
8
+ eager_autoload do
9
+ autoload :JoinBase
10
+ autoload :JoinAssociation
11
+ end
8
12
 
9
13
  class Aliases # :nodoc:
10
14
  def initialize(tables)
@@ -23,11 +23,7 @@ module ActiveRecord
23
23
  end
24
24
 
25
25
  def records_for(loaders)
26
- ids = loaders.flat_map(&:owner_keys).uniq
27
-
28
- scope.where(association_key_name => ids).load do |record|
29
- loaders.each { |l| l.set_inverse(record) }
30
- end
26
+ LoaderRecords.new(loaders, self).records
31
27
  end
32
28
 
33
29
  def load_records_in_batch(loaders)
@@ -38,6 +34,52 @@ module ActiveRecord
38
34
  loader.run
39
35
  end
40
36
  end
37
+
38
+ def load_records_for_keys(keys, &block)
39
+ scope.where(association_key_name => keys).load(&block)
40
+ end
41
+ end
42
+
43
+ class LoaderRecords
44
+ def initialize(loaders, loader_query)
45
+ @loader_query = loader_query
46
+ @loaders = loaders
47
+ @keys_to_load = Set.new
48
+ @already_loaded_records_by_key = {}
49
+
50
+ populate_keys_to_load_and_already_loaded_records
51
+ end
52
+
53
+ def records
54
+ load_records + already_loaded_records
55
+ end
56
+
57
+ private
58
+ attr_reader :loader_query, :loaders, :keys_to_load, :already_loaded_records_by_key
59
+
60
+ def populate_keys_to_load_and_already_loaded_records
61
+ loaders.each do |loader|
62
+ loader.owners_by_key.each do |key, owners|
63
+ if loaded_owner = owners.find { |owner| loader.loaded?(owner) }
64
+ already_loaded_records_by_key[key] = loader.target_for(loaded_owner)
65
+ else
66
+ keys_to_load << key
67
+ end
68
+ end
69
+ end
70
+
71
+ @keys_to_load.subtract(already_loaded_records_by_key.keys)
72
+ end
73
+
74
+ def load_records
75
+ loader_query.load_records_for_keys(keys_to_load) do |record|
76
+ loaders.each { |l| l.set_inverse(record) }
77
+ end
78
+ end
79
+
80
+ def already_loaded_records
81
+ already_loaded_records_by_key.values.flatten
82
+ end
41
83
  end
42
84
 
43
85
  attr_reader :klass
@@ -57,12 +99,8 @@ module ActiveRecord
57
99
  @klass.table_name
58
100
  end
59
101
 
60
- def data_available?
61
- already_loaded?
62
- end
63
-
64
102
  def future_classes
65
- if run? || already_loaded?
103
+ if run?
66
104
  []
67
105
  else
68
106
  [@klass]
@@ -81,11 +119,6 @@ module ActiveRecord
81
119
  return self if run?
82
120
  @run = true
83
121
 
84
- if already_loaded?
85
- fetch_from_preloaded_records
86
- return self
87
- end
88
-
89
122
  records = records_by_owner
90
123
 
91
124
  owners.each do |owner|
@@ -96,25 +129,17 @@ module ActiveRecord
96
129
  end
97
130
 
98
131
  def records_by_owner
99
- ensure_loaded unless defined?(@records_by_owner)
132
+ load_records unless defined?(@records_by_owner)
100
133
 
101
134
  @records_by_owner
102
135
  end
103
136
 
104
137
  def preloaded_records
105
- ensure_loaded unless defined?(@preloaded_records)
138
+ load_records unless defined?(@preloaded_records)
106
139
 
107
140
  @preloaded_records
108
141
  end
109
142
 
110
- def ensure_loaded
111
- if already_loaded?
112
- fetch_from_preloaded_records
113
- else
114
- load_records
115
- end
116
- end
117
-
118
143
  # The name of the key on the associated records
119
144
  def association_key_name
120
145
  reflection.join_primary_key(klass)
@@ -124,8 +149,19 @@ module ActiveRecord
124
149
  LoaderQuery.new(scope, association_key_name)
125
150
  end
126
151
 
127
- def owner_keys
128
- @owner_keys ||= owners_by_key.keys
152
+ def owners_by_key
153
+ @owners_by_key ||= owners.each_with_object({}) do |owner, result|
154
+ key = convert_key(owner[owner_key_name])
155
+ (result[key] ||= []) << owner if key
156
+ end
157
+ end
158
+
159
+ def loaded?(owner)
160
+ owner.association(reflection.name).loaded?
161
+ end
162
+
163
+ def target_for(owner)
164
+ Array.wrap(owner.association(reflection.name).target)
129
165
  end
130
166
 
131
167
  def scope
@@ -169,7 +205,7 @@ module ActiveRecord
169
205
  return if preload_scope && !preload_scope.empty_scope?
170
206
  return if reflection.collection?
171
207
 
172
- unscoped_records.each do |record|
208
+ unscoped_records.select { |r| r[association_key_name].present? }.each do |record|
173
209
  owners = owners_by_key[convert_key(record[association_key_name])]
174
210
  owners&.each_with_index do |owner, i|
175
211
  association = owner.association(reflection.name)
@@ -185,25 +221,16 @@ module ActiveRecord
185
221
  private
186
222
  attr_reader :owners, :reflection, :preload_scope, :model
187
223
 
188
- def already_loaded?
189
- @already_loaded ||= owners.all? { |o| o.association(reflection.name).loaded? }
190
- end
191
-
192
- def fetch_from_preloaded_records
193
- @records_by_owner = owners.index_with do |owner|
194
- Array(owner.association(reflection.name).target)
195
- end
196
-
197
- @preloaded_records = records_by_owner.flat_map(&:last)
198
- end
199
-
200
224
  # The name of the key on the model which declares the association
201
225
  def owner_key_name
202
226
  reflection.join_foreign_key
203
227
  end
204
228
 
205
229
  def associate_records_to_owner(owner, records)
230
+ return if loaded?(owner)
231
+
206
232
  association = owner.association(reflection.name)
233
+
207
234
  if reflection.collection?
208
235
  association.target = records
209
236
  else
@@ -211,13 +238,6 @@ module ActiveRecord
211
238
  end
212
239
  end
213
240
 
214
- def owners_by_key
215
- @owners_by_key ||= owners.each_with_object({}) do |owner, result|
216
- key = convert_key(owner[owner_key_name])
217
- (result[key] ||= []) << owner if key
218
- end
219
- end
220
-
221
241
  def key_conversion_required?
222
242
  unless defined?(@key_conversion_required)
223
243
  @key_conversion_required = (association_key_type != owner_key_type)
@@ -243,7 +263,7 @@ module ActiveRecord
243
263
  end
244
264
 
245
265
  def reflection_scope
246
- @reflection_scope ||= reflection.join_scopes(klass.arel_table, klass.predicate_builder, klass).inject(&:merge!) || klass.unscoped
266
+ @reflection_scope ||= reflection.join_scopes(klass.arel_table, klass.predicate_builder, klass).inject(klass.unscoped, &:merge!)
247
267
  end
248
268
 
249
269
  def build_scope
@@ -6,7 +6,7 @@ module ActiveRecord
6
6
  class Batch # :nodoc:
7
7
  def initialize(preloaders, available_records:)
8
8
  @preloaders = preloaders.reject(&:empty?)
9
- @available_records = available_records.flatten.group_by(&:class)
9
+ @available_records = available_records.flatten.group_by { |r| r.class.base_class }
10
10
  end
11
11
 
12
12
  def call
@@ -14,12 +14,9 @@ module ActiveRecord
14
14
  until branches.empty?
15
15
  loaders = branches.flat_map(&:runnable_loaders)
16
16
 
17
- loaders.each { |loader| loader.associate_records_from_unscoped(@available_records[loader.klass]) }
17
+ loaders.each { |loader| loader.associate_records_from_unscoped(@available_records[loader.klass.base_class]) }
18
18
 
19
- already_loaded = loaders.select(&:data_available?)
20
- if already_loaded.any?
21
- already_loaded.each(&:run)
22
- elsif loaders.any?
19
+ if loaders.any?
23
20
  future_tables = branches.flat_map do |branch|
24
21
  branch.future_classes - branch.runnable_loaders.map(&:klass)
25
22
  end.map(&:table_name).uniq
@@ -10,10 +10,13 @@ module ActiveRecord
10
10
 
11
11
  def records_by_owner
12
12
  return @records_by_owner if defined?(@records_by_owner)
13
- source_records_by_owner = source_preloaders.map(&:records_by_owner).reduce(:merge)
14
- through_records_by_owner = through_preloaders.map(&:records_by_owner).reduce(:merge)
15
13
 
16
14
  @records_by_owner = owners.each_with_object({}) do |owner, result|
15
+ if loaded?(owner)
16
+ result[owner] = target_for(owner)
17
+ next
18
+ end
19
+
17
20
  through_records = through_records_by_owner[owner] || []
18
21
 
19
22
  if owners.first.association(through_reflection.name).loaded?
@@ -35,12 +38,6 @@ module ActiveRecord
35
38
  end
36
39
  end
37
40
 
38
- def data_available?
39
- return true if super()
40
- through_preloaders.all?(&:run?) &&
41
- source_preloaders.all?(&:run?)
42
- end
43
-
44
41
  def runnable_loaders
45
42
  if data_available?
46
43
  [self]
@@ -52,7 +49,7 @@ module ActiveRecord
52
49
  end
53
50
 
54
51
  def future_classes
55
- if run? || data_available?
52
+ if run?
56
53
  []
57
54
  elsif through_preloaders.all?(&:run?)
58
55
  source_preloaders.flat_map(&:future_classes).uniq
@@ -67,6 +64,11 @@ module ActiveRecord
67
64
  end
68
65
 
69
66
  private
67
+ def data_available?
68
+ owners.all? { |owner| loaded?(owner) } ||
69
+ through_preloaders.all?(&:run?) && source_preloaders.all?(&:run?)
70
+ end
71
+
70
72
  def source_preloaders
71
73
  @source_preloaders ||= ActiveRecord::Associations::Preloader.new(records: middle_records, associations: source_reflection.name, scope: scope, associate_by_default: false).loaders
72
74
  end
@@ -87,6 +89,14 @@ module ActiveRecord
87
89
  reflection.source_reflection
88
90
  end
89
91
 
92
+ def source_records_by_owner
93
+ @source_records_by_owner ||= source_preloaders.map(&:records_by_owner).reduce(:merge)
94
+ end
95
+
96
+ def through_records_by_owner
97
+ @through_records_by_owner ||= through_preloaders.map(&:records_by_owner).reduce(:merge)
98
+ end
99
+
90
100
  def preload_index
91
101
  @preload_index ||= preloaded_records.each_with_object({}).with_index do |(record, result), index|
92
102
  result[record] = index
@@ -93,25 +93,21 @@ module ActiveRecord
93
93
  # associations before querying the database. This can save database
94
94
  # queries by reusing in-memory objects. The optimization is only applied
95
95
  # to single associations (i.e. :belongs_to, :has_one) with no scopes.
96
- def initialize(associate_by_default: true, **kwargs)
97
- if kwargs.empty?
98
- ActiveSupport::Deprecation.warn("Calling `Preloader#initialize` without arguments is deprecated and will be removed in Rails 7.0.")
99
- else
100
- @records = kwargs[:records]
101
- @associations = kwargs[:associations]
102
- @scope = kwargs[:scope]
103
- @available_records = kwargs[:available_records] || []
104
- @associate_by_default = associate_by_default
96
+ def initialize(records:, associations:, scope: nil, available_records: [], associate_by_default: true)
97
+ @records = records
98
+ @associations = associations
99
+ @scope = scope
100
+ @available_records = available_records || []
101
+ @associate_by_default = associate_by_default
105
102
 
106
- @tree = Branch.new(
107
- parent: nil,
108
- association: nil,
109
- children: associations,
110
- associate_by_default: @associate_by_default,
111
- scope: @scope
112
- )
113
- @tree.preloaded_records = records
114
- end
103
+ @tree = Branch.new(
104
+ parent: nil,
105
+ association: nil,
106
+ children: @associations,
107
+ associate_by_default: @associate_by_default,
108
+ scope: @scope
109
+ )
110
+ @tree.preloaded_records = @records
115
111
  end
116
112
 
117
113
  def empty?
@@ -124,12 +120,6 @@ module ActiveRecord
124
120
  loaders
125
121
  end
126
122
 
127
- def preload(records, associations, preload_scope = nil)
128
- ActiveSupport::Deprecation.warn("`preload` is deprecated and will be removed in Rails 7.0. Call `Preloader.new(kwargs).call` instead.")
129
-
130
- Preloader.new(records: records, associations: associations, scope: preload_scope).call
131
- end
132
-
133
123
  def branches
134
124
  @tree.children
135
125
  end
@@ -74,8 +74,8 @@ module ActiveRecord
74
74
  end
75
75
  end
76
76
 
77
- # Note: this does not capture all cases, for example it would be crazy to try to
78
- # properly support stale-checking for nested associations.
77
+ # Note: this does not capture all cases, for example it would be impractical
78
+ # to try to properly support stale-checking for nested associations.
79
79
  def stale_state
80
80
  if through_reflection.belongs_to?
81
81
  owner[through_reflection.foreign_key] && owner[through_reflection.foreign_key].to_s
@@ -59,6 +59,18 @@ module ActiveRecord
59
59
  end
60
60
  end
61
61
 
62
+ class InverseOfAssociationRecursiveError < ActiveRecordError # :nodoc:
63
+ attr_reader :reflection
64
+ def initialize(reflection = nil)
65
+ if reflection
66
+ @reflection = reflection
67
+ super("Inverse association #{reflection.name} (#{reflection.options[:inverse_of].inspect} in #{reflection.class_name}) is recursive.")
68
+ else
69
+ super("Inverse association is recursive.")
70
+ end
71
+ end
72
+ end
73
+
62
74
  class HasManyThroughAssociationNotFoundError < ActiveRecordError # :nodoc:
63
75
  attr_reader :owner_class, :reflection
64
76
 
@@ -748,9 +760,10 @@ module ActiveRecord
748
760
  # inverse detection only works on #has_many, #has_one, and
749
761
  # #belongs_to associations.
750
762
  #
751
- # <tt>:foreign_key</tt> and <tt>:through</tt> options on the associations,
752
- # or a custom scope, will also prevent the association's inverse
753
- # from being found automatically.
763
+ # <tt>:foreign_key</tt> and <tt>:through</tt> options on the associations
764
+ # will also prevent the association's inverse from being found automatically,
765
+ # as will a custom scopes in some cases. See further details in the
766
+ # {Active Record Associations guide}[https://guides.rubyonrails.org/association_basics.html#bi-directional-associations].
754
767
  #
755
768
  # The automatic guessing of the inverse association uses a heuristic based
756
769
  # on the name of the class, so it may not work for all associations,
@@ -7,6 +7,9 @@ module ActiveRecord
7
7
  def active?
8
8
  true
9
9
  end
10
+
11
+ def finalize
12
+ end
10
13
  end
11
14
  end
12
15
 
@@ -229,7 +229,15 @@ module ActiveRecord
229
229
  end
230
230
 
231
231
  def attribute_names_for_partial_inserts
232
- partial_inserts? ? changed_attribute_names_to_save : attribute_names
232
+ if partial_inserts?
233
+ changed_attribute_names_to_save
234
+ else
235
+ attribute_names.reject do |attr_name|
236
+ if column_for_attribute(attr_name).default_function
237
+ !attribute_changed?(attr_name)
238
+ end
239
+ end
240
+ end
233
241
  end
234
242
  end
235
243
  end
@@ -387,20 +387,22 @@ module ActiveRecord
387
387
  attribute_names.index_with { |name| @attributes[name] }
388
388
  end
389
389
 
390
- # Filters the primary keys and readonly attributes from the attribute names.
390
+ # Filters the primary keys, readonly attributes and virtual columns from the attribute names.
391
391
  def attributes_for_update(attribute_names)
392
392
  attribute_names &= self.class.column_names
393
393
  attribute_names.delete_if do |name|
394
- self.class.readonly_attribute?(name)
394
+ self.class.readonly_attribute?(name) ||
395
+ column_for_attribute(name).virtual?
395
396
  end
396
397
  end
397
398
 
398
- # Filters out the primary keys, from the attribute names, when the primary
399
+ # Filters out the virtual columns and also primary keys, from the attribute names, when the primary
399
400
  # key is to be generated (e.g. the id attribute has no value).
400
401
  def attributes_for_create(attribute_names)
401
402
  attribute_names &= self.class.column_names
402
403
  attribute_names.delete_if do |name|
403
- pk_attribute?(name) && id.nil?
404
+ (pk_attribute?(name) && id.nil?) ||
405
+ column_for_attribute(name).virtual?
404
406
  end
405
407
  end
406
408
 
@@ -411,7 +413,7 @@ module ActiveRecord
411
413
  inspected_value = if value.is_a?(String) && value.length > 50
412
414
  "#{value[0, 50]}...".inspect
413
415
  elsif value.is_a?(Date) || value.is_a?(Time)
414
- %("#{value.to_s(:inspect)}")
416
+ %("#{value.to_formatted_s(:inspect)}")
415
417
  else
416
418
  value.inspect
417
419
  end
@@ -404,6 +404,8 @@ module ActiveRecord
404
404
  saved = true
405
405
 
406
406
  if autosave != false && (new_record_before_save || record.new_record?)
407
+ association.set_inverse_instance(record)
408
+
407
409
  if autosave
408
410
  saved = association.insert_record(record, false)
409
411
  elsif !reflection.nested?
@@ -447,9 +449,7 @@ module ActiveRecord
447
449
  if (autosave && record.changed_for_autosave?) || record_changed?(reflection, record, key)
448
450
  unless reflection.through_reflection
449
451
  record[reflection.foreign_key] = key
450
- if inverse_reflection = reflection.inverse_of
451
- record.association(inverse_reflection.name).inversed_from(self)
452
- end
452
+ association.set_inverse_instance(record)
453
453
  end
454
454
 
455
455
  saved = record.save(validate: !autosave)
@@ -81,11 +81,11 @@ module ActiveRecord
81
81
  end
82
82
 
83
83
  def prevent_writes # :nodoc:
84
- Thread.current[:prevent_writes]
84
+ ActiveSupport::IsolatedExecutionState[:active_record_prevent_writes]
85
85
  end
86
86
 
87
87
  def prevent_writes=(prevent_writes) # :nodoc:
88
- Thread.current[:prevent_writes] = prevent_writes
88
+ ActiveSupport::IsolatedExecutionState[:active_record_prevent_writes] = prevent_writes
89
89
  end
90
90
 
91
91
  # Prevent writing to the database regardless of role.
@@ -126,7 +126,7 @@ module ActiveRecord
126
126
  def establish_connection(config, owner_name: Base, role: ActiveRecord::Base.current_role, shard: Base.current_shard)
127
127
  owner_name = StringConnectionOwner.new(config.to_s) if config.is_a?(Symbol)
128
128
 
129
- pool_config = resolve_pool_config(config, owner_name)
129
+ pool_config = resolve_pool_config(config, owner_name, role, shard)
130
130
  db_config = pool_config.db_config
131
131
 
132
132
  # Protects the connection named `ActiveRecord::Base` from being removed
@@ -218,15 +218,6 @@ module ActiveRecord
218
218
  pool && pool.connected?
219
219
  end
220
220
 
221
- # Remove the connection for this class. This will close the active
222
- # connection and the defined connection (if they exist). The result
223
- # can be used as an argument for #establish_connection, for easily
224
- # re-establishing the connection.
225
- def remove_connection(owner, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard)
226
- remove_connection_pool(owner, role: role, shard: shard)&.configuration_hash
227
- end
228
- deprecate remove_connection: "Use #remove_connection_pool, which now returns a DatabaseConfig object instead of a Hash"
229
-
230
221
  def remove_connection_pool(owner, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard)
231
222
  if pool_manager = get_pool_manager(owner)
232
223
  pool_config = pool_manager.remove_pool_config(role, shard)
@@ -250,19 +241,8 @@ module ActiveRecord
250
241
  attr_reader :owner_to_pool_manager
251
242
 
252
243
  # Returns the pool manager for an owner.
253
- #
254
- # Using `"primary"` to look up the pool manager for `ActiveRecord::Base` is
255
- # deprecated in favor of looking it up by `"ActiveRecord::Base"`.
256
- #
257
- # During the deprecation period, if `"primary"` is passed, the pool manager
258
- # for `ActiveRecord::Base` will still be returned.
259
244
  def get_pool_manager(owner)
260
- return owner_to_pool_manager[owner] if owner_to_pool_manager.key?(owner)
261
-
262
- if owner == "primary"
263
- ActiveSupport::Deprecation.warn("Using `\"primary\"` as a `connection_specification_name` is deprecated and will be removed in Rails 7.0.0. Please use `ActiveRecord::Base`.")
264
- owner_to_pool_manager[Base.name]
265
- end
245
+ owner_to_pool_manager[owner]
266
246
  end
267
247
 
268
248
  # Returns an instance of PoolConfig for a given adapter.
@@ -275,7 +255,7 @@ module ActiveRecord
275
255
  # pool_config.db_config.configuration_hash
276
256
  # # => { host: "localhost", database: "foo", adapter: "sqlite3" }
277
257
  #
278
- def resolve_pool_config(config, owner_name)
258
+ def resolve_pool_config(config, owner_name, role, shard)
279
259
  db_config = Base.configurations.resolve(config)
280
260
 
281
261
  raise(AdapterNotSpecified, "database configuration does not specify adapter") unless db_config.adapter
@@ -305,7 +285,7 @@ module ActiveRecord
305
285
  raise AdapterNotFound, "database configuration specifies nonexistent #{db_config.adapter} adapter"
306
286
  end
307
287
 
308
- ConnectionAdapters::PoolConfig.new(owner_name, db_config)
288
+ ConnectionAdapters::PoolConfig.new(owner_name, db_config, role, shard)
309
289
  end
310
290
  end
311
291
  end
@@ -110,7 +110,7 @@ module ActiveRecord
110
110
  def wait_poll(timeout)
111
111
  @num_waiting += 1
112
112
 
113
- t0 = Concurrent.monotonic_time
113
+ t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
114
114
  elapsed = 0
115
115
  loop do
116
116
  ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
@@ -119,7 +119,7 @@ module ActiveRecord
119
119
 
120
120
  return remove if any?
121
121
 
122
- elapsed = Concurrent.monotonic_time - t0
122
+ elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0
123
123
  if elapsed >= timeout
124
124
  msg = "could not obtain a connection from the pool within %0.3f seconds (waited %0.3f seconds); all pooled connections were in use" %
125
125
  [timeout, elapsed]