activerecord 7.2.0 → 7.2.2.1

Sign up to get free protection for your applications and to get access to all the features.
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