activerecord 7.2.0 → 7.2.2.1

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +196 -0
  3. data/lib/active_record/associations/has_many_through_association.rb +7 -1
  4. data/lib/active_record/associations/join_dependency/join_association.rb +3 -2
  5. data/lib/active_record/associations/join_dependency.rb +5 -5
  6. data/lib/active_record/associations.rb +28 -16
  7. data/lib/active_record/attribute_assignment.rb +9 -1
  8. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -0
  9. data/lib/active_record/attributes.rb +6 -5
  10. data/lib/active_record/callbacks.rb +1 -1
  11. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +49 -43
  12. data/lib/active_record/connection_adapters/abstract/query_cache.rb +45 -16
  13. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1 -1
  14. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +7 -3
  15. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +1 -1
  16. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
  17. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +2 -0
  18. data/lib/active_record/connection_adapters/postgresql_adapter.rb +3 -3
  19. data/lib/active_record/core.rb +41 -9
  20. data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -1
  21. data/lib/active_record/encryption/encryptable_record.rb +1 -1
  22. data/lib/active_record/encryption/encrypted_attribute_type.rb +2 -2
  23. data/lib/active_record/encryption/encryptor.rb +1 -1
  24. data/lib/active_record/encryption/key_provider.rb +1 -1
  25. data/lib/active_record/encryption.rb +2 -0
  26. data/lib/active_record/enum.rb +8 -0
  27. data/lib/active_record/gem_version.rb +2 -2
  28. data/lib/active_record/marshalling.rb +4 -1
  29. data/lib/active_record/model_schema.rb +5 -2
  30. data/lib/active_record/nested_attributes.rb +13 -2
  31. data/lib/active_record/query_cache.rb +4 -5
  32. data/lib/active_record/query_logs.rb +1 -1
  33. data/lib/active_record/railtie.rb +7 -12
  34. data/lib/active_record/reflection.rb +15 -8
  35. data/lib/active_record/relation/batches.rb +4 -4
  36. data/lib/active_record/relation/calculations.rb +1 -1
  37. data/lib/active_record/relation/predicate_builder.rb +1 -1
  38. data/lib/active_record/relation/query_methods.rb +39 -24
  39. data/lib/active_record/scoping/named.rb +1 -0
  40. data/lib/active_record/tasks/database_tasks.rb +12 -1
  41. data/lib/active_record/testing/query_assertions.rb +2 -2
  42. data/lib/active_record/validations/uniqueness.rb +1 -0
  43. data/lib/arel/visitors/sqlite.rb +25 -0
  44. metadata +13 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9ec33405aa24cc15f0f8a4092bc3a04acafc8191a4df5b41896ce648a5937687
4
- data.tar.gz: 89efd888d4ed72e2d3ff6e75297c1dc4d0dcb19c15ed32c39fd53a2947a51b0b
3
+ metadata.gz: 0fa184c958d89b0bfbb95921e93d9dee4396b91ea57216f69cdaa0907fca900f
4
+ data.tar.gz: bab6a4f88acae3ad29b12d2d84229ad386acd8bca6e617804285b8d7534ea1ea
5
5
  SHA512:
6
- metadata.gz: 84757d9eab7d9709fcd86d0e39d111f6fabf6dc09f15167c432c1ce097d87524e8839be9843eaf7b0be0acc53f92c92dafdae3e21df3c996a34eb01ca6f5703c
7
- data.tar.gz: 83c46935ce453cec3e536ba27caa6b76fd5cdf59d8ca762e6eba91d2137a616d3a1e01cbfe049c945363130e55998e2afde0a8ff1d975ee2359ad38b560980ed
6
+ metadata.gz: 91cf7ccf875cbd0a5c687e354edd6f4338d9b7edcc900952f9b4fef3843c03488abcfba7c2fd4a001954c9891283c232cb9719ad5f3e9383f23656c04930ff2f
7
+ data.tar.gz: 4577e8f11d54b506f3d0fe841a25a1bbb7df529f22f51f08f0c70ec097098e80adab1d95f24f7048879ce5eb3aff6d330c124ad34c6667ebfdcc1c5823832821
data/CHANGELOG.md CHANGED
@@ -1,3 +1,199 @@
1
+ ## Rails 7.2.2.1 (December 10, 2024) ##
2
+
3
+ * No changes.
4
+
5
+
6
+ ## Rails 7.2.2 (October 30, 2024) ##
7
+
8
+ * Fix support for `query_cache: false` in `database.yml`.
9
+
10
+ `query_cache: false` would no longer entirely disable the Active Record query cache.
11
+
12
+ *zzak*
13
+
14
+ * Set `.attributes_for_inspect` to `:all` by default.
15
+
16
+ For new applications it is set to `[:id]` in config/environment/production.rb.
17
+
18
+ In the console all the attributes are always shown.
19
+
20
+ *Andrew Novoselac*
21
+
22
+ * `PG::UnableToSend: no connection to the server` is now retryable as a connection-related exception
23
+
24
+ *Kazuma Watanabe*
25
+
26
+ * Fix marshalling of unsaved associated records in 7.1 format.
27
+
28
+ The 7.1 format would only marshal associated records if the association was loaded.
29
+ But associations that would only contain unsaved records would be skipped.
30
+
31
+ *Jean Boussier*
32
+
33
+ * Fix incorrect SQL query when passing an empty hash to `ActiveRecord::Base.insert`.
34
+
35
+ *David Stosik*
36
+
37
+ * Allow to save records with polymorphic join tables that have `inverse_of`
38
+ specified.
39
+
40
+ *Markus Doits*
41
+
42
+ * Fix association scopes applying on the incorrect join when using a polymorphic `has_many through:`.
43
+
44
+ *Joshua Young*
45
+
46
+ * Fix `dependent: :destroy` for bi-directional has one through association.
47
+
48
+ Fixes #50948.
49
+
50
+ ```ruby
51
+ class Left < ActiveRecord::Base
52
+ has_one :middle, dependent: :destroy
53
+ has_one :right, through: :middle
54
+ end
55
+
56
+ class Middle < ActiveRecord::Base
57
+ belongs_to :left, dependent: :destroy
58
+ belongs_to :right, dependent: :destroy
59
+ end
60
+
61
+ class Right < ActiveRecord::Base
62
+ has_one :middle, dependent: :destroy
63
+ has_one :left, through: :middle
64
+ end
65
+ ```
66
+ In the above example `left.destroy` wouldn't destroy its associated `Right`
67
+ record.
68
+
69
+ *Andy Stewart*
70
+
71
+ * Properly handle lazily pinned connection pools.
72
+
73
+ Fixes #53147.
74
+
75
+ When using transactional fixtures with system tests to similar tools
76
+ such as capybara, it could happen that a connection end up pinned by the
77
+ server thread rather than the test thread, causing
78
+ `"Cannot expire connection, it is owned by a different thread"` errors.
79
+
80
+ *Jean Boussier*
81
+
82
+ * Fix `ActiveRecord::Base.with` to accept more than two sub queries.
83
+
84
+ Fixes #53110.
85
+
86
+ ```ruby
87
+ User.with(foo: [User.select(:id), User.select(:id), User.select(:id)]).to_sql
88
+ undefined method `union' for an instance of Arel::Nodes::UnionAll (NoMethodError)
89
+ ```
90
+
91
+ The above now works as expected.
92
+
93
+ *fatkodima*
94
+
95
+ * Properly release pinned connections with non joinable connections.
96
+
97
+ Fixes #52973
98
+
99
+ When running system tests with transactional fixtures on, it could happen that
100
+ the connection leased by the Puma thread wouldn't be properly released back to the pool,
101
+ causing "Cannot expire connection, it is owned by a different thread" errors in later tests.
102
+
103
+ *Jean Boussier*
104
+
105
+ * Make Float distinguish between `float4` and `float8` in PostgreSQL.
106
+
107
+ Fixes #52742
108
+
109
+ *Ryota Kitazawa*, *Takayuki Nagatomi*
110
+
111
+ * Fix an issue where `.left_outer_joins` used with multiple associations that have
112
+ the same child association but different parents does not join all parents.
113
+
114
+ Previously, using `.left_outer_joins` with the same child association would only join one of the parents.
115
+
116
+ Now it will correctly join both parents.
117
+
118
+ Fixes #41498.
119
+
120
+ *Garrett Blehm*
121
+
122
+ * Ensure `ActiveRecord::Encryption.config` is always ready before access.
123
+
124
+ Previously, `ActiveRecord::Encryption` configuration was deferred until `ActiveRecord::Base`
125
+ was loaded. Therefore, accessing `ActiveRecord::Encryption.config` properties before
126
+ `ActiveRecord::Base` was loaded would give incorrect results.
127
+
128
+ `ActiveRecord::Encryption` now has its own loading hook so that its configuration is set as
129
+ soon as needed.
130
+
131
+ When `ActiveRecord::Base` is loaded, even lazily, it in turn triggers the loading of
132
+ `ActiveRecord::Encryption`, thus preserving the original behavior of having its config ready
133
+ before any use of `ActiveRecord::Base`.
134
+
135
+ *Maxime Réty*
136
+
137
+ * Add `TimeZoneConverter#==` method, so objects will be properly compared by
138
+ their type, scale, limit & precision.
139
+
140
+ Address #52699.
141
+
142
+ *Ruy Rocha*
143
+
144
+
145
+ ## Rails 7.2.1.2 (October 23, 2024) ##
146
+
147
+ * No changes.
148
+
149
+
150
+ ## Rails 7.2.1.1 (October 15, 2024) ##
151
+
152
+ * No changes.
153
+
154
+
155
+ ## Rails 7.2.1 (August 22, 2024) ##
156
+
157
+ * Fix detection for `enum` columns with parallelized tests and PostgreSQL.
158
+
159
+ *Rafael Mendonça França*
160
+
161
+ * Allow to eager load nested nil associations.
162
+
163
+ *fatkodima*
164
+
165
+ * Fix swallowing ignore order warning when batching using `BatchEnumerator`.
166
+
167
+ *fatkodima*
168
+
169
+ * Fix memory bloat on the connection pool when using the Fiber `IsolatedExecutionState`.
170
+
171
+ *Jean Boussier*
172
+
173
+ * Restore inferred association class with the same modularized name.
174
+
175
+ *Justin Ko*
176
+
177
+ * Fix `ActiveRecord::Base.inspect` to properly explain how to load schema information.
178
+
179
+ *Jean Boussier*
180
+
181
+ * Check invalid `enum` options for the new syntax.
182
+
183
+ The options using `_` prefix in the old syntax are invalid in the new syntax.
184
+
185
+ *Rafael Mendonça França*
186
+
187
+ * Fix `ActiveRecord::Encryption::EncryptedAttributeType#type` to return
188
+ actual cast type.
189
+
190
+ *Vasiliy Ermolovich*
191
+
192
+ * Fix `create_table` with `:auto_increment` option for MySQL adapter.
193
+
194
+ *fatkodima*
195
+
196
+
1
197
  ## Rails 7.2.0 (August 09, 2024) ##
2
198
 
3
199
  * Handle commas in Sqlite3 default function definitions.
@@ -93,7 +93,13 @@ module ActiveRecord
93
93
  @through_scope = scope
94
94
  record = super
95
95
 
96
- inverse = source_reflection.inverse_of
96
+ inverse =
97
+ if source_reflection.polymorphic?
98
+ source_reflection.polymorphic_inverse_of(record.class)
99
+ else
100
+ source_reflection.inverse_of
101
+ end
102
+
97
103
  if inverse
98
104
  if inverse.collection?
99
105
  record.send(inverse.name) << build_through_record(record)
@@ -25,8 +25,9 @@ module ActiveRecord
25
25
  joins = []
26
26
  chain = []
27
27
 
28
- reflection.chain.each do |reflection|
29
- table, terminated = yield reflection
28
+ reflection_chain = reflection.chain
29
+ reflection_chain.each_with_index do |reflection, index|
30
+ table, terminated = yield reflection, reflection_chain[index..]
30
31
  @table ||= table
31
32
 
32
33
  if terminated
@@ -61,7 +61,7 @@ module ActiveRecord
61
61
  when Hash
62
62
  associations.each do |k, v|
63
63
  cache = hash[k] ||= {}
64
- walk_tree v, cache
64
+ walk_tree v, cache if v
65
65
  end
66
66
  else
67
67
  raise ConfigurationError, associations.inspect
@@ -190,12 +190,12 @@ module ActiveRecord
190
190
  def make_constraints(parent, child, join_type)
191
191
  foreign_table = parent.table
192
192
  foreign_klass = parent.base_klass
193
- child.join_constraints(foreign_table, foreign_klass, join_type, alias_tracker) do |reflection|
194
- table, terminated = @joined_tables[reflection]
193
+ child.join_constraints(foreign_table, foreign_klass, join_type, alias_tracker) do |reflection, remaining_reflection_chain|
194
+ table, terminated = @joined_tables[remaining_reflection_chain]
195
195
  root = reflection == child.reflection
196
196
 
197
197
  if table && (!root || !terminated)
198
- @joined_tables[reflection] = [table, root] if root
198
+ @joined_tables[remaining_reflection_chain] = [table, root] if root
199
199
  next table, true
200
200
  end
201
201
 
@@ -206,7 +206,7 @@ module ActiveRecord
206
206
  root ? name : "#{name}_join"
207
207
  end
208
208
 
209
- @joined_tables[reflection] ||= [table, root] if join_type == Arel::Nodes::OuterJoin
209
+ @joined_tables[remaining_reflection_chain] ||= [table, root] if join_type == Arel::Nodes::OuterJoin
210
210
  table
211
211
  end.concat child.children.flat_map { |c| make_constraints(child, c, join_type) }
212
212
  end
@@ -537,7 +537,7 @@ module ActiveRecord
537
537
  # @group.avatars << Avatar.new # this would work if User belonged_to Avatar rather than the other way around
538
538
  # @group.avatars.delete(@group.avatars.last) # so would this
539
539
  #
540
- # == Setting Inverses
540
+ # === Setting Inverses
541
541
  #
542
542
  # If you are using a #belongs_to on the join model, it is a good idea to set the
543
543
  # <tt>:inverse_of</tt> option on the #belongs_to, which will mean that the following example
@@ -1199,8 +1199,11 @@ module ActiveRecord
1199
1199
  # If you are going to modify the association (rather than just read from it), then it is
1200
1200
  # a good idea to set the <tt>:inverse_of</tt> option on the source association on the
1201
1201
  # join model. This allows associated records to be built which will automatically create
1202
- # the appropriate join model records when they are saved. (See the 'Association Join Models'
1203
- # and 'Setting Inverses' sections above.)
1202
+ # the appropriate join model records when they are saved. See
1203
+ # {Association Join Models}[rdoc-ref:Associations::ClassMethods@Association+Join+Models]
1204
+ # and {Setting Inverses}[rdoc-ref:Associations::ClassMethods@Setting+Inverses] for
1205
+ # more detail.
1206
+ #
1204
1207
  # [+:disable_joins+]
1205
1208
  # Specifies whether joins should be skipped for an association. If set to true, two or more queries
1206
1209
  # will be generated. Note that in some cases, if order or limit is applied, it will be done in-memory
@@ -1229,7 +1232,8 @@ module ActiveRecord
1229
1232
  # [+:inverse_of+]
1230
1233
  # Specifies the name of the #belongs_to association on the associated object
1231
1234
  # that is the inverse of this #has_many association.
1232
- # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
1235
+ # See {Bi-directional associations}[rdoc-ref:Associations::ClassMethods@Bi-directional+associations]
1236
+ # for more detail.
1233
1237
  # [+:extend+]
1234
1238
  # Specifies a module or array of modules that will be extended into the association object returned.
1235
1239
  # Useful for defining methods on associations, especially when they should be shared between multiple
@@ -1270,10 +1274,12 @@ module ActiveRecord
1270
1274
  Reflection.add_reflection self, name, reflection
1271
1275
  end
1272
1276
 
1273
- # Specifies a one-to-one association with another class. This method should only be used
1274
- # if the other class contains the foreign key. If the current class contains the foreign key,
1275
- # then you should use #belongs_to instead. See also ActiveRecord::Associations::ClassMethods's overview
1276
- # on when to use #has_one and when to use #belongs_to.
1277
+ # Specifies a one-to-one association with another class. This method
1278
+ # should only be used if the other class contains the foreign key. If
1279
+ # the current class contains the foreign key, then you should use
1280
+ # #belongs_to instead. See {Is it a belongs_to or has_one
1281
+ # association?}[rdoc-ref:Associations::ClassMethods@Is+it+a+-23belongs_to+or+-23has_one+association-3F]
1282
+ # for more detail on when to use #has_one and when to use #belongs_to.
1277
1283
  #
1278
1284
  # The following methods for retrieval and query of a single associated object will be added:
1279
1285
  #
@@ -1388,8 +1394,10 @@ module ActiveRecord
1388
1394
  # If you are going to modify the association (rather than just read from it), then it is
1389
1395
  # a good idea to set the <tt>:inverse_of</tt> option on the source association on the
1390
1396
  # join model. This allows associated records to be built which will automatically create
1391
- # the appropriate join model records when they are saved. (See the 'Association Join Models'
1392
- # and 'Setting Inverses' sections above.)
1397
+ # the appropriate join model records when they are saved. See
1398
+ # {Association Join Models}[rdoc-ref:Associations::ClassMethods@Association+Join+Models]
1399
+ # and {Setting Inverses}[rdoc-ref:Associations::ClassMethods@Setting+Inverses] for
1400
+ # more detail.
1393
1401
  # [+:disable_joins+]
1394
1402
  # Specifies whether joins should be skipped for an association. If set to true, two or more queries
1395
1403
  # will be generated. Note that in some cases, if order or limit is applied, it will be done in-memory
@@ -1427,7 +1435,8 @@ module ActiveRecord
1427
1435
  # [+:inverse_of+]
1428
1436
  # Specifies the name of the #belongs_to association on the associated object
1429
1437
  # that is the inverse of this #has_one association.
1430
- # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
1438
+ # See {Bi-directional associations}[rdoc-ref:Associations::ClassMethods@Bi-directional+associations]
1439
+ # for more detail.
1431
1440
  # [+:required+]
1432
1441
  # When set to +true+, the association will also have its presence validated.
1433
1442
  # This will validate the association itself, not the id. You can use
@@ -1461,10 +1470,12 @@ module ActiveRecord
1461
1470
  Reflection.add_reflection self, name, reflection
1462
1471
  end
1463
1472
 
1464
- # Specifies a one-to-one association with another class. This method should only be used
1465
- # if this class contains the foreign key. If the other class contains the foreign key,
1466
- # then you should use #has_one instead. See also ActiveRecord::Associations::ClassMethods's overview
1467
- # on when to use #has_one and when to use #belongs_to.
1473
+ # Specifies a one-to-one association with another class. This method
1474
+ # should only be used if this class contains the foreign key. If the
1475
+ # other class contains the foreign key, then you should use #has_one
1476
+ # instead. See {Is it a belongs_to or has_one
1477
+ # association?}[rdoc-ref:Associations::ClassMethods@Is+it+a+-23belongs_to+or+-23has_one+association-3F]
1478
+ # for more detail on when to use #has_one and when to use #belongs_to.
1468
1479
  #
1469
1480
  # Methods will be added for retrieval and query for a single associated object, for which
1470
1481
  # this object holds an id:
@@ -1606,7 +1617,8 @@ module ActiveRecord
1606
1617
  # [+:inverse_of+]
1607
1618
  # Specifies the name of the #has_one or #has_many association on the associated
1608
1619
  # object that is the inverse of this #belongs_to association.
1609
- # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
1620
+ # See {Bi-directional associations}[rdoc-ref:Associations::ClassMethods@Bi-directional+associations]
1621
+ # for more detail.
1610
1622
  # [+:optional+]
1611
1623
  # When set to +true+, the association will not have its presence validated.
1612
1624
  # [+:required+]
@@ -4,21 +4,29 @@ module ActiveRecord
4
4
  module AttributeAssignment
5
5
  private
6
6
  def _assign_attributes(attributes)
7
- multi_parameter_attributes = nil
7
+ multi_parameter_attributes = nested_parameter_attributes = nil
8
8
 
9
9
  attributes.each do |k, v|
10
10
  key = k.to_s
11
11
 
12
12
  if key.include?("(")
13
13
  (multi_parameter_attributes ||= {})[key] = v
14
+ elsif v.is_a?(Hash)
15
+ (nested_parameter_attributes ||= {})[key] = v
14
16
  else
15
17
  _assign_attribute(key, v)
16
18
  end
17
19
  end
18
20
 
21
+ assign_nested_parameter_attributes(nested_parameter_attributes) if nested_parameter_attributes
19
22
  assign_multiparameter_attributes(multi_parameter_attributes) if multi_parameter_attributes
20
23
  end
21
24
 
25
+ # Assign any deferred nested attributes after the base attributes have been set.
26
+ def assign_nested_parameter_attributes(pairs)
27
+ pairs.each { |k, v| _assign_attribute(k, v) }
28
+ end
29
+
22
30
  # Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
23
31
  # by calling new on the column type or aggregation type (through composed_of) object with these parameters.
24
32
  # So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
@@ -32,6 +32,10 @@ module ActiveRecord
32
32
  end
33
33
  end
34
34
 
35
+ def ==(other)
36
+ other.is_a?(self.class) && __getobj__ == other.__getobj__
37
+ end
38
+
35
39
  private
36
40
  def convert_time_to_time_zone(value)
37
41
  return if value.nil?
@@ -25,15 +25,17 @@ module ActiveRecord
25
25
  # column which this will persist to.
26
26
  #
27
27
  # +cast_type+ A symbol such as +:string+ or +:integer+, or a type object
28
- # to be used for this attribute. See the examples below for more
29
- # information about providing custom type objects.
28
+ # to be used for this attribute. If this parameter is not passed, the previously
29
+ # defined type (if any) will be used.
30
+ # Otherwise, the type will be ActiveModel::Type::Value.
31
+ # See the examples below for more information about providing custom type objects.
30
32
  #
31
33
  # ==== Options
32
34
  #
33
35
  # The following options are accepted:
34
36
  #
35
37
  # +default+ The default value to use when no value is provided. If this option
36
- # is not passed, the previous default value (if any) will be used.
38
+ # is not passed, the previously defined default value (if any) on the superclass or in the schema will be used.
37
39
  # Otherwise, the default will be +nil+.
38
40
  #
39
41
  # +array+ (PostgreSQL only) specifies that the type should be an array (see the
@@ -210,8 +212,7 @@ module ActiveRecord
210
212
  #--
211
213
  # Implemented by ActiveModel::AttributeRegistration#attribute.
212
214
 
213
- # This is the low level API which sits beneath +attribute+. It only
214
- # accepts type objects, and will do its work immediately instead of
215
+ # This API only accepts type objects, and will do its work immediately instead of
215
216
  # waiting for the schema to load. While this method
216
217
  # is provided so it can be used by plugin authors, application code
217
218
  # should probably use ClassMethods#attribute.
@@ -418,7 +418,7 @@ module ActiveRecord
418
418
 
419
419
  def destroy # :nodoc:
420
420
  @_destroy_callback_already_called ||= false
421
- return if @_destroy_callback_already_called
421
+ return true if @_destroy_callback_already_called
422
422
  @_destroy_callback_already_called = true
423
423
  _run_destroy_callbacks { super }
424
424
  rescue RecordNotDestroyed => e
@@ -118,6 +118,27 @@ module ActiveRecord
118
118
  # * private methods that require being called in a +synchronize+ blocks
119
119
  # are now explicitly documented
120
120
  class ConnectionPool
121
+ class WeakThreadKeyMap # :nodoc:
122
+ # FIXME: On 3.3 we could use ObjectSpace::WeakKeyMap
123
+ # but it currently cause GC crashes: https://github.com/byroot/rails/pull/3
124
+ def initialize
125
+ @map = {}
126
+ end
127
+
128
+ def clear
129
+ @map.clear
130
+ end
131
+
132
+ def [](key)
133
+ @map[key]
134
+ end
135
+
136
+ def []=(key, value)
137
+ @map.select! { |c, _| c.alive? }
138
+ @map[key] = value
139
+ end
140
+ end
141
+
121
142
  class Lease # :nodoc:
122
143
  attr_accessor :connection, :sticky
123
144
 
@@ -145,48 +166,9 @@ module ActiveRecord
145
166
  end
146
167
 
147
168
  class LeaseRegistry # :nodoc:
148
- if ObjectSpace.const_defined?(:WeakKeyMap) # RUBY_VERSION >= 3.3
149
- WeakKeyMap = ::ObjectSpace::WeakKeyMap # :nodoc:
150
- else
151
- class WeakKeyMap # :nodoc:
152
- def initialize
153
- @map = ObjectSpace::WeakMap.new
154
- @values = nil
155
- @size = 0
156
- end
157
-
158
- alias_method :clear, :initialize
159
-
160
- def [](key)
161
- prune if @map.size != @size
162
- @map[key]
163
- end
164
-
165
- def []=(key, value)
166
- @map[key] = value
167
- prune if @map.size != @size
168
- value
169
- end
170
-
171
- def delete(key)
172
- if value = self[key]
173
- self[key] = nil
174
- prune
175
- end
176
- value
177
- end
178
-
179
- private
180
- def prune(force = false)
181
- @values = @map.values
182
- @size = @map.size
183
- end
184
- end
185
- end
186
-
187
169
  def initialize
188
170
  @mutex = Mutex.new
189
- @map = WeakKeyMap.new
171
+ @map = WeakThreadKeyMap.new
190
172
  end
191
173
 
192
174
  def [](context)
@@ -197,11 +179,36 @@ module ActiveRecord
197
179
 
198
180
  def clear
199
181
  @mutex.synchronize do
200
- @map = WeakKeyMap.new
182
+ @map.clear
201
183
  end
202
184
  end
203
185
  end
204
186
 
187
+ module ExecutorHooks # :nodoc:
188
+ class << self
189
+ def run
190
+ # noop
191
+ end
192
+
193
+ def complete(_)
194
+ ActiveRecord::Base.connection_handler.each_connection_pool do |pool|
195
+ if (connection = pool.active_connection?)
196
+ transaction = connection.current_transaction
197
+ if transaction.closed? || !transaction.joinable?
198
+ pool.release_connection
199
+ end
200
+ end
201
+ end
202
+ end
203
+ end
204
+ end
205
+
206
+ class << self
207
+ def install_executor_hooks(executor = ActiveSupport::Executor)
208
+ executor.register_hook(ExecutorHooks)
209
+ end
210
+ end
211
+
205
212
  include MonitorMixin
206
213
  prepend QueryCache::ConnectionPoolConfiguration
207
214
  include ConnectionAdapters::AbstractPool
@@ -351,6 +358,7 @@ module ActiveRecord
351
358
  end
352
359
 
353
360
  if @pinned_connection.nil?
361
+ connection.steal!
354
362
  connection.lock_thread = nil
355
363
  checkin(connection)
356
364
  end
@@ -630,8 +638,6 @@ module ActiveRecord
630
638
  remove conn
631
639
  end
632
640
  end
633
-
634
- prune_thread_cache
635
641
  end
636
642
 
637
643
  # Disconnect all connections that have been idle for at least