activerecord 5.1.7 → 5.2.4.3

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 (261) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +556 -685
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +5 -5
  5. data/examples/performance.rb +2 -0
  6. data/examples/simple.rb +2 -0
  7. data/lib/active_record.rb +11 -4
  8. data/lib/active_record/aggregations.rb +6 -5
  9. data/lib/active_record/association_relation.rb +7 -5
  10. data/lib/active_record/associations.rb +40 -63
  11. data/lib/active_record/associations/alias_tracker.rb +19 -27
  12. data/lib/active_record/associations/association.rb +41 -37
  13. data/lib/active_record/associations/association_scope.rb +38 -50
  14. data/lib/active_record/associations/belongs_to_association.rb +27 -8
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
  16. data/lib/active_record/associations/builder/association.rb +4 -7
  17. data/lib/active_record/associations/builder/belongs_to.rb +12 -4
  18. data/lib/active_record/associations/builder/collection_association.rb +3 -3
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -1
  20. data/lib/active_record/associations/builder/has_many.rb +2 -0
  21. data/lib/active_record/associations/builder/has_one.rb +2 -0
  22. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  23. data/lib/active_record/associations/collection_association.rb +59 -47
  24. data/lib/active_record/associations/collection_proxy.rb +20 -49
  25. data/lib/active_record/associations/foreign_association.rb +2 -0
  26. data/lib/active_record/associations/has_many_association.rb +12 -1
  27. data/lib/active_record/associations/has_many_through_association.rb +36 -30
  28. data/lib/active_record/associations/has_one_association.rb +12 -1
  29. data/lib/active_record/associations/has_one_through_association.rb +13 -8
  30. data/lib/active_record/associations/join_dependency.rb +48 -93
  31. data/lib/active_record/associations/join_dependency/join_association.rb +39 -63
  32. data/lib/active_record/associations/join_dependency/join_base.rb +9 -8
  33. data/lib/active_record/associations/join_dependency/join_part.rb +9 -9
  34. data/lib/active_record/associations/preloader.rb +18 -38
  35. data/lib/active_record/associations/preloader/association.rb +45 -61
  36. data/lib/active_record/associations/preloader/through_association.rb +71 -79
  37. data/lib/active_record/associations/singular_association.rb +14 -16
  38. data/lib/active_record/associations/through_association.rb +26 -11
  39. data/lib/active_record/attribute_assignment.rb +2 -5
  40. data/lib/active_record/attribute_decorators.rb +3 -2
  41. data/lib/active_record/attribute_methods.rb +65 -24
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -0
  43. data/lib/active_record/attribute_methods/dirty.rb +30 -214
  44. data/lib/active_record/attribute_methods/primary_key.rb +7 -6
  45. data/lib/active_record/attribute_methods/query.rb +2 -0
  46. data/lib/active_record/attribute_methods/read.rb +9 -3
  47. data/lib/active_record/attribute_methods/serialization.rb +23 -0
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +6 -8
  49. data/lib/active_record/attribute_methods/write.rb +21 -9
  50. data/lib/active_record/attributes.rb +6 -5
  51. data/lib/active_record/autosave_association.rb +35 -19
  52. data/lib/active_record/base.rb +2 -0
  53. data/lib/active_record/callbacks.rb +8 -6
  54. data/lib/active_record/coders/json.rb +2 -0
  55. data/lib/active_record/coders/yaml_column.rb +2 -0
  56. data/lib/active_record/collection_cache_key.rb +12 -8
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +139 -41
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -0
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +174 -33
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +15 -5
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +13 -31
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +14 -5
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +64 -6
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +152 -81
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +66 -21
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +84 -97
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +92 -165
  70. data/lib/active_record/connection_adapters/column.rb +3 -1
  71. data/lib/active_record/connection_adapters/connection_specification.rb +17 -3
  72. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +13 -2
  73. data/lib/active_record/connection_adapters/mysql/column.rb +2 -0
  74. data/lib/active_record/connection_adapters/mysql/database_statements.rb +47 -2
  75. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +2 -0
  76. data/lib/active_record/connection_adapters/mysql/quoting.rb +9 -10
  77. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +5 -3
  78. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +7 -10
  79. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +30 -30
  80. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +106 -1
  81. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +2 -0
  82. data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -2
  83. data/lib/active_record/connection_adapters/postgresql/column.rb +2 -0
  84. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +6 -0
  85. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +2 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid.rb +3 -1
  87. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +2 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +2 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -1
  99. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -1
  101. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +2 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +4 -2
  104. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +4 -2
  106. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
  109. data/lib/active_record/connection_adapters/postgresql/quoting.rb +18 -0
  110. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
  111. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +50 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +24 -11
  113. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +20 -13
  114. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +233 -111
  115. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -0
  116. data/lib/active_record/connection_adapters/postgresql/utils.rb +3 -1
  117. data/lib/active_record/connection_adapters/postgresql_adapter.rb +57 -73
  118. data/lib/active_record/connection_adapters/schema_cache.rb +4 -2
  119. data/lib/active_record/connection_adapters/sql_type_metadata.rb +2 -0
  120. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +2 -0
  121. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +22 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +2 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +6 -15
  124. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +3 -2
  125. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +75 -1
  126. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +81 -94
  127. data/lib/active_record/connection_adapters/statement_pool.rb +2 -0
  128. data/lib/active_record/connection_handling.rb +4 -2
  129. data/lib/active_record/core.rb +41 -61
  130. data/lib/active_record/counter_cache.rb +10 -3
  131. data/lib/active_record/define_callbacks.rb +5 -3
  132. data/lib/active_record/dynamic_matchers.rb +9 -9
  133. data/lib/active_record/enum.rb +18 -13
  134. data/lib/active_record/errors.rb +42 -3
  135. data/lib/active_record/explain.rb +3 -1
  136. data/lib/active_record/explain_registry.rb +2 -0
  137. data/lib/active_record/explain_subscriber.rb +2 -0
  138. data/lib/active_record/fixture_set/file.rb +2 -0
  139. data/lib/active_record/fixtures.rb +67 -60
  140. data/lib/active_record/gem_version.rb +5 -3
  141. data/lib/active_record/inheritance.rb +49 -19
  142. data/lib/active_record/integration.rb +58 -19
  143. data/lib/active_record/internal_metadata.rb +2 -0
  144. data/lib/active_record/legacy_yaml_adapter.rb +3 -1
  145. data/lib/active_record/locking/optimistic.rb +14 -17
  146. data/lib/active_record/locking/pessimistic.rb +9 -6
  147. data/lib/active_record/log_subscriber.rb +43 -0
  148. data/lib/active_record/migration.rb +189 -139
  149. data/lib/active_record/migration/command_recorder.rb +11 -9
  150. data/lib/active_record/migration/compatibility.rb +47 -9
  151. data/lib/active_record/migration/join_table.rb +2 -0
  152. data/lib/active_record/model_schema.rb +16 -21
  153. data/lib/active_record/nested_attributes.rb +18 -6
  154. data/lib/active_record/no_touching.rb +3 -1
  155. data/lib/active_record/null_relation.rb +2 -0
  156. data/lib/active_record/persistence.rb +167 -16
  157. data/lib/active_record/query_cache.rb +6 -8
  158. data/lib/active_record/querying.rb +4 -2
  159. data/lib/active_record/railtie.rb +62 -6
  160. data/lib/active_record/railties/console_sandbox.rb +2 -0
  161. data/lib/active_record/railties/controller_runtime.rb +2 -0
  162. data/lib/active_record/railties/databases.rake +46 -36
  163. data/lib/active_record/readonly_attributes.rb +3 -2
  164. data/lib/active_record/reflection.rb +108 -194
  165. data/lib/active_record/relation.rb +120 -214
  166. data/lib/active_record/relation/batches.rb +20 -5
  167. data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
  168. data/lib/active_record/relation/calculations.rb +45 -19
  169. data/lib/active_record/relation/delegation.rb +45 -27
  170. data/lib/active_record/relation/finder_methods.rb +75 -76
  171. data/lib/active_record/relation/from_clause.rb +2 -8
  172. data/lib/active_record/relation/merger.rb +53 -23
  173. data/lib/active_record/relation/predicate_builder.rb +60 -79
  174. data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -7
  175. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  176. data/lib/active_record/relation/predicate_builder/base_handler.rb +2 -2
  177. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +12 -1
  178. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  179. data/lib/active_record/relation/predicate_builder/range_handler.rb +26 -9
  180. data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
  181. data/lib/active_record/relation/query_attribute.rb +28 -2
  182. data/lib/active_record/relation/query_methods.rb +128 -99
  183. data/lib/active_record/relation/record_fetch_warning.rb +2 -0
  184. data/lib/active_record/relation/spawn_methods.rb +4 -2
  185. data/lib/active_record/relation/where_clause.rb +65 -68
  186. data/lib/active_record/relation/where_clause_factory.rb +5 -48
  187. data/lib/active_record/result.rb +2 -0
  188. data/lib/active_record/runtime_registry.rb +2 -0
  189. data/lib/active_record/sanitization.rb +129 -121
  190. data/lib/active_record/schema.rb +4 -2
  191. data/lib/active_record/schema_dumper.rb +36 -26
  192. data/lib/active_record/schema_migration.rb +2 -0
  193. data/lib/active_record/scoping.rb +9 -8
  194. data/lib/active_record/scoping/default.rb +8 -9
  195. data/lib/active_record/scoping/named.rb +23 -7
  196. data/lib/active_record/secure_token.rb +2 -0
  197. data/lib/active_record/serialization.rb +2 -0
  198. data/lib/active_record/statement_cache.rb +23 -13
  199. data/lib/active_record/store.rb +3 -1
  200. data/lib/active_record/suppressor.rb +2 -0
  201. data/lib/active_record/table_metadata.rb +12 -3
  202. data/lib/active_record/tasks/database_tasks.rb +25 -14
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +9 -48
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +10 -2
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -3
  206. data/lib/active_record/timestamp.rb +6 -6
  207. data/lib/active_record/touch_later.rb +2 -0
  208. data/lib/active_record/transactions.rb +33 -28
  209. data/lib/active_record/translation.rb +2 -0
  210. data/lib/active_record/type.rb +4 -1
  211. data/lib/active_record/type/adapter_specific_registry.rb +2 -0
  212. data/lib/active_record/type/date.rb +2 -0
  213. data/lib/active_record/type/date_time.rb +2 -0
  214. data/lib/active_record/type/decimal_without_scale.rb +2 -0
  215. data/lib/active_record/type/hash_lookup_type_map.rb +2 -0
  216. data/lib/active_record/type/internal/timezone.rb +2 -0
  217. data/lib/active_record/type/json.rb +30 -0
  218. data/lib/active_record/type/serialized.rb +2 -0
  219. data/lib/active_record/type/text.rb +2 -0
  220. data/lib/active_record/type/time.rb +2 -0
  221. data/lib/active_record/type/type_map.rb +2 -0
  222. data/lib/active_record/type/unsigned_integer.rb +2 -0
  223. data/lib/active_record/type_caster.rb +2 -0
  224. data/lib/active_record/type_caster/connection.rb +2 -0
  225. data/lib/active_record/type_caster/map.rb +3 -1
  226. data/lib/active_record/validations.rb +2 -0
  227. data/lib/active_record/validations/absence.rb +2 -0
  228. data/lib/active_record/validations/associated.rb +2 -0
  229. data/lib/active_record/validations/length.rb +2 -0
  230. data/lib/active_record/validations/presence.rb +2 -0
  231. data/lib/active_record/validations/uniqueness.rb +35 -5
  232. data/lib/active_record/version.rb +2 -0
  233. data/lib/rails/generators/active_record.rb +3 -1
  234. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  235. data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
  236. data/lib/rails/generators/active_record/migration.rb +2 -0
  237. data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -1
  238. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +0 -0
  239. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +0 -0
  240. data/lib/rails/generators/active_record/model/model_generator.rb +2 -23
  241. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +0 -0
  242. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  243. metadata +23 -36
  244. data/lib/active_record/associations/preloader/belongs_to.rb +0 -15
  245. data/lib/active_record/associations/preloader/collection_association.rb +0 -17
  246. data/lib/active_record/associations/preloader/has_many.rb +0 -15
  247. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  248. data/lib/active_record/associations/preloader/has_one.rb +0 -15
  249. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  250. data/lib/active_record/associations/preloader/singular_association.rb +0 -18
  251. data/lib/active_record/attribute.rb +0 -240
  252. data/lib/active_record/attribute/user_provided_default.rb +0 -30
  253. data/lib/active_record/attribute_mutation_tracker.rb +0 -122
  254. data/lib/active_record/attribute_set.rb +0 -113
  255. data/lib/active_record/attribute_set/builder.rb +0 -126
  256. data/lib/active_record/attribute_set/yaml_encoder.rb +0 -41
  257. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -10
  258. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  259. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
  260. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -59
  261. data/lib/active_record/type/internal/abstract_json.rb +0 -37
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module AttributeMethods
3
5
  module TimeZoneConversion
@@ -54,17 +56,13 @@ module ActiveRecord
54
56
  extend ActiveSupport::Concern
55
57
 
56
58
  included do
57
- mattr_accessor :time_zone_aware_attributes, instance_writer: false
58
- self.time_zone_aware_attributes = false
59
-
60
- class_attribute :skip_time_zone_conversion_for_attributes, instance_writer: false
61
- self.skip_time_zone_conversion_for_attributes = []
59
+ mattr_accessor :time_zone_aware_attributes, instance_writer: false, default: false
62
60
 
63
- class_attribute :time_zone_aware_types, instance_writer: false
64
- self.time_zone_aware_types = [:datetime, :time]
61
+ class_attribute :skip_time_zone_conversion_for_attributes, instance_writer: false, default: []
62
+ class_attribute :time_zone_aware_types, instance_writer: false, default: [ :datetime, :time ]
65
63
  end
66
64
 
67
- module ClassMethods
65
+ module ClassMethods # :nodoc:
68
66
  private
69
67
 
70
68
  def inherited(subclass)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module AttributeMethods
3
5
  module Write
@@ -7,17 +9,19 @@ module ActiveRecord
7
9
  attribute_method_suffix "="
8
10
  end
9
11
 
10
- module ClassMethods
12
+ module ClassMethods # :nodoc:
11
13
  private
12
14
 
13
15
  def define_method_attribute=(name)
14
16
  safe_name = name.unpack("h*".freeze).first
15
17
  ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
18
+ sync_with_transaction_state = "sync_with_transaction_state" if name == primary_key
16
19
 
17
20
  generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
18
21
  def __temp__#{safe_name}=(value)
19
22
  name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
20
- write_attribute(name, value)
23
+ #{sync_with_transaction_state}
24
+ _write_attribute(name, value)
21
25
  end
22
26
  alias_method #{(name + '=').inspect}, :__temp__#{safe_name}=
23
27
  undef_method :__temp__#{safe_name}=
@@ -35,21 +39,29 @@ module ActiveRecord
35
39
  attr_name.to_s
36
40
  end
37
41
 
38
- name = self.class.primary_key if name == "id".freeze && self.class.primary_key
39
- @attributes.write_from_user(name, value)
40
- value
42
+ primary_key = self.class.primary_key
43
+ name = primary_key if name == "id".freeze && primary_key
44
+ sync_with_transaction_state if name == primary_key
45
+ _write_attribute(name, value)
41
46
  end
42
47
 
43
- def raw_write_attribute(attr_name, value) # :nodoc:
44
- name = attr_name.to_s
45
- @attributes.write_cast_value(name, value)
48
+ # This method exists to avoid the expensive primary_key check internally, without
49
+ # breaking compatibility with the write_attribute API
50
+ def _write_attribute(attr_name, value) # :nodoc:
51
+ @attributes.write_from_user(attr_name.to_s, value)
46
52
  value
47
53
  end
48
54
 
49
55
  private
56
+ def write_attribute_without_type_cast(attr_name, value)
57
+ name = attr_name.to_s
58
+ @attributes.write_cast_value(name, value)
59
+ value
60
+ end
61
+
50
62
  # Handle *= for method_missing.
51
63
  def attribute=(attribute_name, value)
52
- write_attribute(attribute_name, value)
64
+ _write_attribute(attribute_name, value)
53
65
  end
54
66
  end
55
67
  end
@@ -1,4 +1,6 @@
1
- require "active_record/attribute/user_provided_default"
1
+ # frozen_string_literal: true
2
+
3
+ require "active_model/attribute/user_provided_default"
2
4
 
3
5
  module ActiveRecord
4
6
  # See ActiveRecord::Attributes::ClassMethods for documentation
@@ -6,8 +8,7 @@ module ActiveRecord
6
8
  extend ActiveSupport::Concern
7
9
 
8
10
  included do
9
- class_attribute :attributes_to_define_after_schema_loads, instance_accessor: false # :internal:
10
- self.attributes_to_define_after_schema_loads = {}
11
+ class_attribute :attributes_to_define_after_schema_loads, instance_accessor: false, default: {} # :internal:
11
12
  end
12
13
 
13
14
  module ClassMethods
@@ -249,14 +250,14 @@ module ActiveRecord
249
250
  if value == NO_DEFAULT_PROVIDED
250
251
  default_attribute = _default_attributes[name].with_type(type)
251
252
  elsif from_user
252
- default_attribute = Attribute::UserProvidedDefault.new(
253
+ default_attribute = ActiveModel::Attribute::UserProvidedDefault.new(
253
254
  name,
254
255
  value,
255
256
  type,
256
257
  _default_attributes.fetch(name.to_s) { nil },
257
258
  )
258
259
  else
259
- default_attribute = Attribute.from_database(name, value, type)
260
+ default_attribute = ActiveModel::Attribute.from_database(name, value, type)
260
261
  end
261
262
  _default_attributes[name] = default_attribute
262
263
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  # = Active Record Autosave Association
3
5
  #
@@ -140,8 +142,7 @@ module ActiveRecord
140
142
 
141
143
  included do
142
144
  Associations::Builder::Association.extensions << AssociationBuilderExtension
143
- mattr_accessor :index_nested_attribute_errors, instance_writer: false
144
- self.index_nested_attribute_errors = false
145
+ mattr_accessor :index_nested_attribute_errors, instance_writer: false, default: false
145
146
  end
146
147
 
147
148
  module ClassMethods # :nodoc:
@@ -216,13 +217,7 @@ module ActiveRecord
216
217
  method = :validate_single_association
217
218
  end
218
219
 
219
- define_non_cyclic_method(validation_method) do
220
- send(method, reflection)
221
- # TODO: remove the following line as soon as the return value of
222
- # callbacks is ignored, that is, returning `false` does not
223
- # display a deprecation warning or halts the callback chain.
224
- true
225
- end
220
+ define_non_cyclic_method(validation_method) { send(method, reflection) }
226
221
  validate validation_method
227
222
  after_validation :_ensure_no_duplicate_errors
228
223
  end
@@ -277,7 +272,7 @@ module ActiveRecord
277
272
  # or saved. If +autosave+ is +false+ only new records will be returned,
278
273
  # unless the parent is/was a new record itself.
279
274
  def associated_records_to_validate_or_save(association, new_record, autosave)
280
- if new_record
275
+ if new_record || custom_validation_context?
281
276
  association && association.target
282
277
  elsif autosave
283
278
  association.target.find_all(&:changed_for_autosave?)
@@ -309,7 +304,7 @@ module ActiveRecord
309
304
  def validate_single_association(reflection)
310
305
  association = association_instance_get(reflection.name)
311
306
  record = association && association.reader
312
- association_valid?(reflection, record) if record
307
+ association_valid?(reflection, record) if record && (record.changed_for_autosave? || custom_validation_context?)
313
308
  end
314
309
 
315
310
  # Validate the associated records if <tt>:validate</tt> or
@@ -329,7 +324,7 @@ module ActiveRecord
329
324
  def association_valid?(reflection, record, index = nil)
330
325
  return true if record.destroyed? || (reflection.options[:autosave] && record.marked_for_destruction?)
331
326
 
332
- context = validation_context unless [:create, :update].include?(validation_context)
327
+ context = validation_context if custom_validation_context?
333
328
 
334
329
  unless valid = record.valid?(context)
335
330
  if reflection.options[:autosave]
@@ -369,7 +364,6 @@ module ActiveRecord
369
364
  # association whether or not the parent was a new record before saving.
370
365
  def before_save_collection_association
371
366
  @new_record_before_save = new_record?
372
- true
373
367
  end
374
368
 
375
369
  def after_save_collection_association
@@ -388,10 +382,14 @@ module ActiveRecord
388
382
  if association = association_instance_get(reflection.name)
389
383
  autosave = reflection.options[:autosave]
390
384
 
385
+ # By saving the instance variable in a local variable,
386
+ # we make the whole callback re-entrant.
387
+ new_record_before_save = @new_record_before_save
388
+
391
389
  # reconstruct the scope now that we know the owner's id
392
- association.reset_scope if association.respond_to?(:reset_scope)
390
+ association.reset_scope
393
391
 
394
- if records = associated_records_to_validate_or_save(association, @new_record_before_save, autosave)
392
+ if records = associated_records_to_validate_or_save(association, new_record_before_save, autosave)
395
393
  if autosave
396
394
  records_to_destroy = records.select(&:marked_for_destruction?)
397
395
  records_to_destroy.each { |record| association.destroy(record) }
@@ -403,11 +401,16 @@ module ActiveRecord
403
401
 
404
402
  saved = true
405
403
 
406
- if autosave != false && (@new_record_before_save || record.new_record?)
404
+ if autosave != false && (new_record_before_save || record.new_record?)
407
405
  if autosave
408
406
  saved = association.insert_record(record, false)
409
- else
410
- association.insert_record(record) unless reflection.nested?
407
+ elsif !reflection.nested?
408
+ association_saved = association.insert_record(record)
409
+
410
+ if reflection.validate?
411
+ errors.add(reflection.name) unless association_saved
412
+ saved = association_saved
413
+ end
411
414
  end
412
415
  elsif autosave
413
416
  saved = record.save(validate: false)
@@ -442,6 +445,9 @@ module ActiveRecord
442
445
  if (autosave && record.changed_for_autosave?) || new_record? || record_changed?(reflection, record, key)
443
446
  unless reflection.through_reflection
444
447
  record[reflection.foreign_key] = key
448
+ if inverse_reflection = reflection.inverse_of
449
+ record.association(inverse_reflection.name).loaded!
450
+ end
445
451
  end
446
452
 
447
453
  saved = record.save(validate: !autosave)
@@ -455,10 +461,16 @@ module ActiveRecord
455
461
  # If the record is new or it has changed, returns true.
456
462
  def record_changed?(reflection, record, key)
457
463
  record.new_record? ||
458
- (record.has_attribute?(reflection.foreign_key) && record[reflection.foreign_key] != key) ||
464
+ association_foreign_key_changed?(reflection, record, key) ||
459
465
  record.will_save_change_to_attribute?(reflection.foreign_key)
460
466
  end
461
467
 
468
+ def association_foreign_key_changed?(reflection, record, key)
469
+ return false if reflection.through_reflection?
470
+
471
+ record.has_attribute?(reflection.foreign_key) && record[reflection.foreign_key] != key
472
+ end
473
+
462
474
  # Saves the associated record if it's new or <tt>:autosave</tt> is enabled.
463
475
  #
464
476
  # In addition, it will destroy the association if it was marked for destruction.
@@ -487,6 +499,10 @@ module ActiveRecord
487
499
  end
488
500
  end
489
501
 
502
+ def custom_validation_context?
503
+ validation_context && [:create, :update].exclude?(validation_context)
504
+ end
505
+
490
506
  def _ensure_no_duplicate_errors
491
507
  errors.messages.each_key do |attribute|
492
508
  errors[attribute].uniq!
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "yaml"
2
4
  require "active_support/benchmarkable"
3
5
  require "active_support/dependencies"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  # = Active Record \Callbacks
3
5
  #
@@ -96,9 +98,9 @@ module ActiveRecord
96
98
  # == Types of callbacks
97
99
  #
98
100
  # There are four types of callbacks accepted by the callback macros: Method references (symbol), callback objects,
99
- # inline methods (using a proc), and inline eval methods (using a string). Method references and callback objects
101
+ # inline methods (using a proc). Method references and callback objects
100
102
  # are the recommended approaches, inline methods using a proc are sometimes appropriate (such as for
101
- # creating mix-ins), and inline eval methods are deprecated.
103
+ # creating mix-ins).
102
104
  #
103
105
  # The method reference callbacks work by specifying a protected or private method available in the object, like this:
104
106
  #
@@ -230,7 +232,7 @@ module ActiveRecord
230
232
  #
231
233
  # For example:
232
234
  #
233
- # class Topic
235
+ # class Topic < ActiveRecord::Base
234
236
  # has_many :children
235
237
  #
236
238
  # after_save :log_children
@@ -238,7 +240,7 @@ module ActiveRecord
238
240
  #
239
241
  # private
240
242
  #
241
- # def log_chidren
243
+ # def log_children
242
244
  # # Child processing
243
245
  # end
244
246
  #
@@ -255,7 +257,7 @@ module ActiveRecord
255
257
  #
256
258
  # For example:
257
259
  #
258
- # class Topic
260
+ # class Topic < ActiveRecord::Base
259
261
  # has_many :children
260
262
  #
261
263
  # after_commit :log_children
@@ -263,7 +265,7 @@ module ActiveRecord
263
265
  #
264
266
  # private
265
267
  #
266
- # def log_chidren
268
+ # def log_children
267
269
  # # Child processing
268
270
  # end
269
271
  #
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Coders # :nodoc:
3
5
  class JSON # :nodoc:
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "yaml"
2
4
 
3
5
  module ActiveRecord
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module CollectionCacheKey
3
5
  def collection_cache_key(collection = all, timestamp_column = :updated_at) # :nodoc:
4
- query_signature = Digest::MD5.hexdigest(collection.to_sql)
6
+ query_signature = ActiveSupport::Digest.hexdigest(collection.to_sql)
5
7
  key = "#{collection.model_name.cache_key}/query-#{query_signature}"
6
8
 
7
9
  if collection.loaded? || collection.distinct_value
@@ -10,24 +12,26 @@ module ActiveRecord
10
12
  timestamp = collection.max_by(&timestamp_column)._read_attribute(timestamp_column)
11
13
  end
12
14
  else
13
- column_type = type_for_attribute(timestamp_column.to_s)
14
- column = "#{connection.quote_table_name(collection.table_name)}.#{connection.quote_column_name(timestamp_column)}"
15
+ if collection.eager_loading?
16
+ collection = collection.send(:apply_join_dependency)
17
+ end
18
+ column_type = type_for_attribute(timestamp_column)
19
+ column = connection.column_name_from_arel_node(collection.arel_attribute(timestamp_column))
15
20
  select_values = "COUNT(*) AS #{connection.quote_column_name("size")}, MAX(%s) AS timestamp"
16
21
 
17
22
  if collection.has_limit_or_offset?
18
- query = collection.spawn
19
- query.select_values = [column]
23
+ query = collection.select("#{column} AS collection_cache_key_timestamp")
20
24
  subquery_alias = "subquery_for_cache_key"
21
- subquery_column = "#{subquery_alias}.#{timestamp_column}"
25
+ subquery_column = "#{subquery_alias}.collection_cache_key_timestamp"
22
26
  subquery = query.arel.as(subquery_alias)
23
- arel = Arel::SelectManager.new(query.engine).project(select_values % subquery_column).from(subquery)
27
+ arel = Arel::SelectManager.new(subquery).project(select_values % subquery_column)
24
28
  else
25
29
  query = collection.unscope(:order)
26
30
  query.select_values = [select_values % column]
27
31
  arel = query.arel
28
32
  end
29
33
 
30
- result = connection.select_one(arel, nil, query.bound_attributes)
34
+ result = connection.select_one(arel, nil)
31
35
 
32
36
  if result.blank?
33
37
  size = 0
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "thread"
2
4
  require "concurrent/map"
3
5
  require "monitor"
@@ -61,15 +63,13 @@ module ActiveRecord
61
63
  # There are several connection-pooling-related options that you can add to
62
64
  # your database connection configuration:
63
65
  #
64
- # * +pool+: number indicating size of connection pool (default 5)
65
- # * +checkout_timeout+: number of seconds to block and wait for a connection
66
- # before giving up and raising a timeout error (default 5 seconds).
67
- # * +reaping_frequency+: frequency in seconds to periodically run the
68
- # Reaper, which attempts to find and recover connections from dead
69
- # threads, which can occur if a programmer forgets to close a
70
- # connection at the end of a thread or a thread dies unexpectedly.
71
- # Regardless of this setting, the Reaper will be invoked before every
72
- # blocking wait. (Default +nil+, which means don't schedule the Reaper).
66
+ # * +pool+: maximum number of connections the pool may manage (default 5).
67
+ # * +idle_timeout+: number of seconds that a connection will be kept
68
+ # unused in the pool before it is automatically disconnected (default
69
+ # 300 seconds). Set this to zero to keep connections forever.
70
+ # * +checkout_timeout+: number of seconds to wait for a connection to
71
+ # become available before giving up and raising a timeout error (default
72
+ # 5 seconds).
73
73
  #
74
74
  #--
75
75
  # Synchronization policy:
@@ -80,11 +80,8 @@ module ActiveRecord
80
80
  # * private methods that require being called in a +synchronize+ blocks
81
81
  # are now explicitly documented
82
82
  class ConnectionPool
83
- # Threadsafe, fair, FIFO queue. Meant to be used by ConnectionPool
84
- # with which it shares a Monitor. But could be a generic Queue.
85
- #
86
- # The Queue in stdlib's 'thread' could replace this class except
87
- # stdlib's doesn't support waiting with a timeout.
83
+ # Threadsafe, fair, LIFO queue. Meant to be used by ConnectionPool
84
+ # with which it shares a Monitor.
88
85
  class Queue
89
86
  def initialize(lock = Monitor.new)
90
87
  @lock = lock
@@ -173,7 +170,7 @@ module ActiveRecord
173
170
 
174
171
  # Removes and returns the head of the queue if possible, or +nil+.
175
172
  def remove
176
- @queue.shift
173
+ @queue.pop
177
174
  end
178
175
 
179
176
  # Remove and return the head the queue if the number of
@@ -270,7 +267,7 @@ module ActiveRecord
270
267
  # Connections must be leased while holding the main pool mutex. This is
271
268
  # an internal subclass that also +.leases+ returned connections while
272
269
  # still in queue's critical section (queue synchronizes with the same
273
- # +@lock+ as the main pool) so that a returned connection is already
270
+ # <tt>@lock</tt> as the main pool) so that a returned connection is already
274
271
  # leased and there is no need to re-enter synchronized block.
275
272
  class ConnectionLeasingQueue < Queue # :nodoc:
276
273
  include BiasableQueue
@@ -283,12 +280,12 @@ module ActiveRecord
283
280
  end
284
281
  end
285
282
 
286
- # Every +frequency+ seconds, the reaper will call +reap+ on +pool+.
287
- # A reaper instantiated with a +nil+ frequency will never reap the
288
- # connection pool.
283
+ # Every +frequency+ seconds, the reaper will call +reap+ and +flush+ on
284
+ # +pool+. A reaper instantiated with a zero frequency will never reap
285
+ # the connection pool.
289
286
  #
290
- # Configure the frequency by setting "reaping_frequency" in your
291
- # database yaml file.
287
+ # Configure the frequency by setting +reaping_frequency+ in your database
288
+ # yaml file (default 60 seconds).
292
289
  class Reaper
293
290
  attr_reader :pool, :frequency
294
291
 
@@ -298,11 +295,12 @@ module ActiveRecord
298
295
  end
299
296
 
300
297
  def run
301
- return unless frequency
298
+ return unless frequency && frequency > 0
302
299
  Thread.new(frequency, pool) { |t, p|
303
300
  loop do
304
301
  sleep t
305
302
  p.reap
303
+ p.flush
306
304
  end
307
305
  }
308
306
  end
@@ -312,7 +310,7 @@ module ActiveRecord
312
310
  include QueryCache::ConnectionPoolConfiguration
313
311
 
314
312
  attr_accessor :automatic_reconnect, :checkout_timeout, :schema_cache
315
- attr_reader :spec, :connections, :size, :reaper
313
+ attr_reader :spec, :size, :reaper
316
314
 
317
315
  # Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
318
316
  # object which describes database connection information (e.g. adapter,
@@ -326,8 +324,10 @@ module ActiveRecord
326
324
  @spec = spec
327
325
 
328
326
  @checkout_timeout = (spec.config[:checkout_timeout] && spec.config[:checkout_timeout].to_f) || 5
329
- @reaper = Reaper.new(self, (spec.config[:reaping_frequency] && spec.config[:reaping_frequency].to_f))
330
- @reaper.run
327
+ if @idle_timeout = spec.config.fetch(:idle_timeout, 300)
328
+ @idle_timeout = @idle_timeout.to_f
329
+ @idle_timeout = nil if @idle_timeout <= 0
330
+ end
331
331
 
332
332
  # default max pool size to 5
333
333
  @size = (spec.config[:pool] && spec.config[:pool].to_i) || 5
@@ -340,7 +340,7 @@ module ActiveRecord
340
340
  # then that +thread+ does indeed own that +conn+. However, an absence of a such
341
341
  # mapping does not mean that the +thread+ doesn't own the said connection. In
342
342
  # that case +conn.owner+ attr should be consulted.
343
- # Access and modification of +@thread_cached_conns+ does not require
343
+ # Access and modification of <tt>@thread_cached_conns</tt> does not require
344
344
  # synchronization.
345
345
  @thread_cached_conns = Concurrent::Map.new(initial_capacity: @size)
346
346
 
@@ -357,6 +357,12 @@ module ActiveRecord
357
357
  @available = ConnectionLeasingQueue.new self
358
358
 
359
359
  @lock_thread = false
360
+
361
+ # +reaping_frequency+ is configurable mostly for historical reasons, but it could
362
+ # also be useful if someone wants a very low +idle_timeout+.
363
+ reaping_frequency = spec.config.fetch(:reaping_frequency, 60)
364
+ @reaper = Reaper.new(self, reaping_frequency && reaping_frequency.to_f)
365
+ @reaper.run
360
366
  end
361
367
 
362
368
  def lock_thread=(lock_thread)
@@ -373,7 +379,7 @@ module ActiveRecord
373
379
  # #connection can be called any number of times; the connection is
374
380
  # held in a cache keyed by a thread.
375
381
  def connection
376
- @thread_cached_conns[connection_cache_key(@lock_thread || Thread.current)] ||= checkout
382
+ @thread_cached_conns[connection_cache_key(current_thread)] ||= checkout
377
383
  end
378
384
 
379
385
  # Returns true if there is an open connection being used for the current thread.
@@ -382,7 +388,7 @@ module ActiveRecord
382
388
  # #connection or #with_connection methods. Connections obtained through
383
389
  # #checkout will not be detected by #active_connection?
384
390
  def active_connection?
385
- @thread_cached_conns[connection_cache_key(Thread.current)]
391
+ @thread_cached_conns[connection_cache_key(current_thread)]
386
392
  end
387
393
 
388
394
  # Signal that the thread is finished with the current connection.
@@ -417,6 +423,21 @@ module ActiveRecord
417
423
  synchronize { @connections.any? }
418
424
  end
419
425
 
426
+ # Returns an array containing the connections currently in the pool.
427
+ # Access to the array does not require synchronization on the pool because
428
+ # the array is newly created and not retained by the pool.
429
+ #
430
+ # However; this method bypasses the ConnectionPool's thread-safe connection
431
+ # access pattern. A returned connection may be owned by another thread,
432
+ # unowned, or by happen-stance owned by the calling thread.
433
+ #
434
+ # Calling methods on a connection without ownership is subject to the
435
+ # thread-safety guarantees of the underlying method. Many of the methods
436
+ # on connection adapter classes are inherently multi-thread unsafe.
437
+ def connections
438
+ synchronize { @connections.dup }
439
+ end
440
+
420
441
  # Disconnects all connections in the pool, and clears the pool.
421
442
  #
422
443
  # Raises:
@@ -449,6 +470,21 @@ module ActiveRecord
449
470
  disconnect(false)
450
471
  end
451
472
 
473
+ # Discards all connections in the pool (even if they're currently
474
+ # leased!), along with the pool itself. Any further interaction with the
475
+ # pool (except #spec and #schema_cache) is undefined.
476
+ #
477
+ # See AbstractAdapter#discard!
478
+ def discard! # :nodoc:
479
+ synchronize do
480
+ return if @connections.nil? # already discarded
481
+ @connections.each do |conn|
482
+ conn.discard!
483
+ end
484
+ @connections = @available = @thread_cached_conns = nil
485
+ end
486
+ end
487
+
452
488
  # Clears the cache which maps classes and re-connects connections that
453
489
  # require reloading.
454
490
  #
@@ -574,6 +610,35 @@ module ActiveRecord
574
610
  end
575
611
  end
576
612
 
613
+ # Disconnect all connections that have been idle for at least
614
+ # +minimum_idle+ seconds. Connections currently checked out, or that were
615
+ # checked in less than +minimum_idle+ seconds ago, are unaffected.
616
+ def flush(minimum_idle = @idle_timeout)
617
+ return if minimum_idle.nil?
618
+
619
+ idle_connections = synchronize do
620
+ @connections.select do |conn|
621
+ !conn.in_use? && conn.seconds_idle >= minimum_idle
622
+ end.each do |conn|
623
+ conn.lease
624
+
625
+ @available.delete conn
626
+ @connections.delete conn
627
+ end
628
+ end
629
+
630
+ idle_connections.each do |conn|
631
+ conn.disconnect!
632
+ end
633
+ end
634
+
635
+ # Disconnect all currently idle connections. Connections currently checked
636
+ # out are unaffected.
637
+ def flush!
638
+ reap
639
+ flush(-1)
640
+ end
641
+
577
642
  def num_waiting_in_queue # :nodoc:
578
643
  @available.num_waiting
579
644
  end
@@ -618,6 +683,10 @@ module ActiveRecord
618
683
  thread
619
684
  end
620
685
 
686
+ def current_thread
687
+ @lock_thread || Thread.current
688
+ end
689
+
621
690
  # Take control of all existing connections so a "group" action such as
622
691
  # reload/disconnect can be performed safely. It is no longer enough to
623
692
  # wrap it in +synchronize+ because some pool's actions are allowed
@@ -681,7 +750,7 @@ module ActiveRecord
681
750
  # this block can't be easily moved into attempt_to_checkout_all_existing_connections's
682
751
  # rescue block, because doing so would put it outside of synchronize section, without
683
752
  # being in a critical section thread_report might become inaccurate
684
- msg = "could not obtain ownership of all database connections in #{checkout_timeout} seconds"
753
+ msg = "could not obtain ownership of all database connections in #{checkout_timeout} seconds".dup
685
754
 
686
755
  thread_report = []
687
756
  @connections.each do |conn|
@@ -736,10 +805,10 @@ module ActiveRecord
736
805
  # Implementation detail: the connection returned by +acquire_connection+
737
806
  # will already be "+connection.lease+ -ed" to the current thread.
738
807
  def acquire_connection(checkout_timeout)
739
- # NOTE: we rely on +@available.poll+ and +try_to_checkout_new_connection+ to
808
+ # NOTE: we rely on <tt>@available.poll</tt> and +try_to_checkout_new_connection+ to
740
809
  # +conn.lease+ the returned connection (and to do this in a +synchronized+
741
810
  # section). This is not the cleanest implementation, as ideally we would
742
- # <tt>synchronize { conn.lease }</tt> in this method, but by leaving it to +@available.poll+
811
+ # <tt>synchronize { conn.lease }</tt> in this method, but by leaving it to <tt>@available.poll</tt>
743
812
  # and +try_to_checkout_new_connection+ we can piggyback on +synchronize+ sections
744
813
  # of the said methods and avoid an additional +synchronize+ overhead.
745
814
  if conn = @available.poll || try_to_checkout_new_connection
@@ -763,7 +832,7 @@ module ActiveRecord
763
832
  end
764
833
  end
765
834
 
766
- # If the pool is not at a +@size+ limit, establish new connection. Connecting
835
+ # If the pool is not at a <tt>@size</tt> limit, establish new connection. Connecting
767
836
  # to the DB is done outside main synchronized section.
768
837
  #--
769
838
  # Implementation constraint: a newly established connection returned by this
@@ -829,7 +898,7 @@ module ActiveRecord
829
898
  # end
830
899
  #
831
900
  # class Book < ActiveRecord::Base
832
- # establish_connection "library_db"
901
+ # establish_connection :library_db
833
902
  # end
834
903
  #
835
904
  # class ScaryBook < Book
@@ -861,17 +930,41 @@ module ActiveRecord
861
930
  # All Active Record models use this handler to determine the connection pool that they
862
931
  # should use.
863
932
  #
864
- # The ConnectionHandler class is not coupled with the Active models, as it has no knowlodge
933
+ # The ConnectionHandler class is not coupled with the Active models, as it has no knowledge
865
934
  # about the model. The model needs to pass a specification name to the handler,
866
- # in order to lookup the correct connection pool.
935
+ # in order to look up the correct connection pool.
867
936
  class ConnectionHandler
868
- def initialize
869
- # These caches are keyed by spec.name (ConnectionSpecification#name).
870
- @owner_to_pool = Concurrent::Map.new(initial_capacity: 2) do |h, k|
937
+ def self.create_owner_to_pool # :nodoc:
938
+ Concurrent::Map.new(initial_capacity: 2) do |h, k|
939
+ # Discard the parent's connection pools immediately; we have no need
940
+ # of them
941
+ discard_unowned_pools(h)
942
+
871
943
  h[k] = Concurrent::Map.new(initial_capacity: 2)
872
944
  end
873
945
  end
874
946
 
947
+ def self.unowned_pool_finalizer(pid_map) # :nodoc:
948
+ lambda do |_|
949
+ discard_unowned_pools(pid_map)
950
+ end
951
+ end
952
+
953
+ def self.discard_unowned_pools(pid_map) # :nodoc:
954
+ pid_map.each do |pid, pools|
955
+ pools.values.compact.each(&:discard!) unless pid == Process.pid
956
+ end
957
+ end
958
+
959
+ def initialize
960
+ # These caches are keyed by spec.name (ConnectionSpecification#name).
961
+ @owner_to_pool = ConnectionHandler.create_owner_to_pool
962
+
963
+ # Backup finalizer: if the forked child never needed a pool, the above
964
+ # early discard has not occurred
965
+ ObjectSpace.define_finalizer self, ConnectionHandler.unowned_pool_finalizer(@owner_to_pool)
966
+ end
967
+
875
968
  def connection_pool_list
876
969
  owner_to_pool.values.compact
877
970
  end
@@ -923,6 +1016,13 @@ module ActiveRecord
923
1016
  connection_pool_list.each(&:disconnect!)
924
1017
  end
925
1018
 
1019
+ # Disconnects all currently idle connections.
1020
+ #
1021
+ # See ConnectionPool#flush! for details.
1022
+ def flush_idle_connections!
1023
+ connection_pool_list.each(&:flush!)
1024
+ end
1025
+
926
1026
  # Locate the connection of the nearest super class. This can be an
927
1027
  # active or defined connection: if it is the latter, it will be
928
1028
  # opened and set as the active connection for the class it was defined
@@ -930,9 +1030,7 @@ module ActiveRecord
930
1030
  def retrieve_connection(spec_name) #:nodoc:
931
1031
  pool = retrieve_connection_pool(spec_name)
932
1032
  raise ConnectionNotEstablished, "No connection pool with '#{spec_name}' found." unless pool
933
- conn = pool.connection
934
- raise ConnectionNotEstablished, "No connection for '#{spec_name}' in connection pool" unless conn
935
- conn
1033
+ pool.connection
936
1034
  end
937
1035
 
938
1036
  # Returns true if a connection that's accessible to this class has