activerecord 5.0.6 → 6.0.1

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 (358) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +638 -2023
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +8 -6
  5. data/examples/performance.rb +31 -29
  6. data/examples/simple.rb +5 -3
  7. data/lib/active_record/aggregations.rb +249 -246
  8. data/lib/active_record/association_relation.rb +24 -13
  9. data/lib/active_record/associations/alias_tracker.rb +24 -33
  10. data/lib/active_record/associations/association.rb +119 -56
  11. data/lib/active_record/associations/association_scope.rb +94 -94
  12. data/lib/active_record/associations/belongs_to_association.rb +58 -42
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
  14. data/lib/active_record/associations/builder/association.rb +18 -25
  15. data/lib/active_record/associations/builder/belongs_to.rb +43 -54
  16. data/lib/active_record/associations/builder/collection_association.rb +7 -18
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +42 -61
  18. data/lib/active_record/associations/builder/has_many.rb +4 -0
  19. data/lib/active_record/associations/builder/has_one.rb +37 -1
  20. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  21. data/lib/active_record/associations/collection_association.rb +80 -252
  22. data/lib/active_record/associations/collection_proxy.rb +158 -121
  23. data/lib/active_record/associations/foreign_association.rb +9 -0
  24. data/lib/active_record/associations/has_many_association.rb +23 -29
  25. data/lib/active_record/associations/has_many_through_association.rb +58 -44
  26. data/lib/active_record/associations/has_one_association.rb +59 -54
  27. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  28. data/lib/active_record/associations/join_dependency/join_association.rb +38 -90
  29. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +12 -12
  31. data/lib/active_record/associations/join_dependency.rb +134 -176
  32. data/lib/active_record/associations/preloader/association.rb +84 -125
  33. data/lib/active_record/associations/preloader/through_association.rb +82 -75
  34. data/lib/active_record/associations/preloader.rb +90 -102
  35. data/lib/active_record/associations/singular_association.rb +12 -45
  36. data/lib/active_record/associations/through_association.rb +26 -14
  37. data/lib/active_record/associations.rb +1603 -1592
  38. data/lib/active_record/attribute_assignment.rb +54 -60
  39. data/lib/active_record/attribute_decorators.rb +38 -15
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +12 -7
  41. data/lib/active_record/attribute_methods/dirty.rb +179 -109
  42. data/lib/active_record/attribute_methods/primary_key.rb +86 -91
  43. data/lib/active_record/attribute_methods/query.rb +4 -3
  44. data/lib/active_record/attribute_methods/read.rb +21 -49
  45. data/lib/active_record/attribute_methods/serialization.rb +30 -7
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -64
  47. data/lib/active_record/attribute_methods/write.rb +35 -33
  48. data/lib/active_record/attribute_methods.rb +66 -106
  49. data/lib/active_record/attributes.rb +38 -24
  50. data/lib/active_record/autosave_association.rb +53 -32
  51. data/lib/active_record/base.rb +27 -24
  52. data/lib/active_record/callbacks.rb +63 -33
  53. data/lib/active_record/coders/json.rb +2 -0
  54. data/lib/active_record/coders/yaml_column.rb +11 -11
  55. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +553 -321
  56. data/lib/active_record/connection_adapters/abstract/database_limits.rb +23 -5
  57. data/lib/active_record/connection_adapters/abstract/database_statements.rb +213 -94
  58. data/lib/active_record/connection_adapters/abstract/query_cache.rb +59 -28
  59. data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -75
  60. data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
  61. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +33 -27
  62. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +207 -126
  63. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +68 -80
  64. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +369 -199
  65. data/lib/active_record/connection_adapters/abstract/transaction.rb +169 -78
  66. data/lib/active_record/connection_adapters/abstract_adapter.rb +363 -202
  67. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +405 -551
  68. data/lib/active_record/connection_adapters/column.rb +41 -13
  69. data/lib/active_record/connection_adapters/connection_specification.rb +172 -138
  70. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +11 -4
  71. data/lib/active_record/connection_adapters/mysql/column.rb +8 -31
  72. data/lib/active_record/connection_adapters/mysql/database_statements.rb +143 -49
  73. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +24 -22
  74. data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -20
  75. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +50 -45
  76. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +58 -56
  77. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +70 -36
  78. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
  79. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +12 -13
  80. data/lib/active_record/connection_adapters/mysql2_adapter.rb +49 -30
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +22 -7
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +60 -54
  83. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +5 -3
  84. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +22 -10
  85. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +6 -5
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
  89. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +4 -2
  91. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -3
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +19 -17
  94. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  96. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -5
  98. data/lib/active_record/connection_adapters/postgresql/oid/{json.rb → oid.rb} +6 -1
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +31 -9
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +34 -30
  101. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
  102. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
  103. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +9 -4
  104. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid.rb +24 -21
  107. data/lib/active_record/connection_adapters/postgresql/quoting.rb +95 -35
  108. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
  109. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +147 -105
  111. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +35 -32
  112. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +380 -300
  113. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +26 -25
  114. data/lib/active_record/connection_adapters/postgresql/utils.rb +10 -6
  115. data/lib/active_record/connection_adapters/postgresql_adapter.rb +382 -275
  116. data/lib/active_record/connection_adapters/schema_cache.rb +46 -12
  117. data/lib/active_record/connection_adapters/sql_type_metadata.rb +13 -8
  118. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +3 -1
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +74 -19
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +3 -8
  122. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +254 -262
  126. data/lib/active_record/connection_adapters/statement_pool.rb +9 -7
  127. data/lib/active_record/connection_handling.rb +159 -40
  128. data/lib/active_record/core.rb +202 -162
  129. data/lib/active_record/counter_cache.rb +57 -28
  130. data/lib/active_record/database_configurations/database_config.rb +37 -0
  131. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  132. data/lib/active_record/database_configurations/url_config.rb +79 -0
  133. data/lib/active_record/database_configurations.rb +233 -0
  134. data/lib/active_record/define_callbacks.rb +22 -0
  135. data/lib/active_record/dynamic_matchers.rb +87 -86
  136. data/lib/active_record/enum.rb +60 -23
  137. data/lib/active_record/errors.rb +114 -18
  138. data/lib/active_record/explain.rb +4 -3
  139. data/lib/active_record/explain_registry.rb +3 -1
  140. data/lib/active_record/explain_subscriber.rb +9 -4
  141. data/lib/active_record/fixture_set/file.rb +13 -8
  142. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  143. data/lib/active_record/fixture_set/render_context.rb +17 -0
  144. data/lib/active_record/fixture_set/table_row.rb +153 -0
  145. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  146. data/lib/active_record/fixtures.rb +195 -502
  147. data/lib/active_record/gem_version.rb +4 -2
  148. data/lib/active_record/inheritance.rb +151 -97
  149. data/lib/active_record/insert_all.rb +179 -0
  150. data/lib/active_record/integration.rb +116 -25
  151. data/lib/active_record/internal_metadata.rb +15 -18
  152. data/lib/active_record/legacy_yaml_adapter.rb +4 -2
  153. data/lib/active_record/locking/optimistic.rb +78 -87
  154. data/lib/active_record/locking/pessimistic.rb +18 -6
  155. data/lib/active_record/log_subscriber.rb +48 -29
  156. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  157. data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
  158. data/lib/active_record/middleware/database_selector.rb +75 -0
  159. data/lib/active_record/migration/command_recorder.rb +143 -97
  160. data/lib/active_record/migration/compatibility.rb +174 -56
  161. data/lib/active_record/migration/join_table.rb +8 -6
  162. data/lib/active_record/migration.rb +367 -300
  163. data/lib/active_record/model_schema.rb +145 -139
  164. data/lib/active_record/nested_attributes.rb +214 -201
  165. data/lib/active_record/no_touching.rb +10 -1
  166. data/lib/active_record/null_relation.rb +13 -34
  167. data/lib/active_record/persistence.rb +442 -72
  168. data/lib/active_record/query_cache.rb +15 -14
  169. data/lib/active_record/querying.rb +36 -23
  170. data/lib/active_record/railtie.rb +128 -36
  171. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  172. data/lib/active_record/railties/console_sandbox.rb +2 -0
  173. data/lib/active_record/railties/controller_runtime.rb +34 -33
  174. data/lib/active_record/railties/databases.rake +309 -177
  175. data/lib/active_record/readonly_attributes.rb +5 -4
  176. data/lib/active_record/reflection.rb +211 -249
  177. data/lib/active_record/relation/batches/batch_enumerator.rb +3 -1
  178. data/lib/active_record/relation/batches.rb +99 -52
  179. data/lib/active_record/relation/calculations.rb +211 -172
  180. data/lib/active_record/relation/delegation.rb +67 -65
  181. data/lib/active_record/relation/finder_methods.rb +208 -247
  182. data/lib/active_record/relation/from_clause.rb +2 -8
  183. data/lib/active_record/relation/merger.rb +78 -61
  184. data/lib/active_record/relation/predicate_builder/array_handler.rb +20 -14
  185. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  186. data/lib/active_record/relation/predicate_builder/base_handler.rb +4 -3
  187. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +6 -4
  188. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  189. data/lib/active_record/relation/predicate_builder/range_handler.rb +7 -18
  190. data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
  191. data/lib/active_record/relation/predicate_builder.rb +86 -104
  192. data/lib/active_record/relation/query_attribute.rb +33 -2
  193. data/lib/active_record/relation/query_methods.rb +458 -329
  194. data/lib/active_record/relation/record_fetch_warning.rb +5 -3
  195. data/lib/active_record/relation/spawn_methods.rb +8 -7
  196. data/lib/active_record/relation/where_clause.rb +111 -95
  197. data/lib/active_record/relation/where_clause_factory.rb +6 -11
  198. data/lib/active_record/relation.rb +429 -318
  199. data/lib/active_record/result.rb +69 -39
  200. data/lib/active_record/runtime_registry.rb +5 -3
  201. data/lib/active_record/sanitization.rb +83 -99
  202. data/lib/active_record/schema.rb +7 -14
  203. data/lib/active_record/schema_dumper.rb +71 -69
  204. data/lib/active_record/schema_migration.rb +15 -5
  205. data/lib/active_record/scoping/default.rb +93 -95
  206. data/lib/active_record/scoping/named.rb +45 -25
  207. data/lib/active_record/scoping.rb +20 -19
  208. data/lib/active_record/secure_token.rb +4 -2
  209. data/lib/active_record/serialization.rb +2 -0
  210. data/lib/active_record/statement_cache.rb +63 -28
  211. data/lib/active_record/store.rb +121 -41
  212. data/lib/active_record/suppressor.rb +4 -1
  213. data/lib/active_record/table_metadata.rb +26 -20
  214. data/lib/active_record/tasks/database_tasks.rb +276 -85
  215. data/lib/active_record/tasks/mysql_database_tasks.rb +54 -90
  216. data/lib/active_record/tasks/postgresql_database_tasks.rb +78 -47
  217. data/lib/active_record/tasks/sqlite_database_tasks.rb +34 -16
  218. data/lib/active_record/test_databases.rb +23 -0
  219. data/lib/active_record/test_fixtures.rb +224 -0
  220. data/lib/active_record/timestamp.rb +70 -35
  221. data/lib/active_record/touch_later.rb +7 -4
  222. data/lib/active_record/transactions.rb +133 -149
  223. data/lib/active_record/translation.rb +3 -1
  224. data/lib/active_record/type/adapter_specific_registry.rb +44 -45
  225. data/lib/active_record/type/date.rb +2 -0
  226. data/lib/active_record/type/date_time.rb +2 -0
  227. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  228. data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
  229. data/lib/active_record/type/internal/timezone.rb +2 -0
  230. data/lib/active_record/type/json.rb +30 -0
  231. data/lib/active_record/type/serialized.rb +16 -8
  232. data/lib/active_record/type/text.rb +11 -0
  233. data/lib/active_record/type/time.rb +2 -1
  234. data/lib/active_record/type/type_map.rb +13 -15
  235. data/lib/active_record/type/unsigned_integer.rb +17 -0
  236. data/lib/active_record/type.rb +23 -17
  237. data/lib/active_record/type_caster/connection.rb +17 -12
  238. data/lib/active_record/type_caster/map.rb +5 -4
  239. data/lib/active_record/type_caster.rb +4 -2
  240. data/lib/active_record/validations/absence.rb +2 -0
  241. data/lib/active_record/validations/associated.rb +3 -1
  242. data/lib/active_record/validations/length.rb +2 -0
  243. data/lib/active_record/validations/presence.rb +4 -2
  244. data/lib/active_record/validations/uniqueness.rb +29 -42
  245. data/lib/active_record/validations.rb +7 -4
  246. data/lib/active_record/version.rb +3 -1
  247. data/lib/active_record.rb +36 -22
  248. data/lib/arel/alias_predication.rb +9 -0
  249. data/lib/arel/attributes/attribute.rb +37 -0
  250. data/lib/arel/attributes.rb +22 -0
  251. data/lib/arel/collectors/bind.rb +24 -0
  252. data/lib/arel/collectors/composite.rb +31 -0
  253. data/lib/arel/collectors/plain_string.rb +20 -0
  254. data/lib/arel/collectors/sql_string.rb +20 -0
  255. data/lib/arel/collectors/substitute_binds.rb +28 -0
  256. data/lib/arel/crud.rb +42 -0
  257. data/lib/arel/delete_manager.rb +18 -0
  258. data/lib/arel/errors.rb +9 -0
  259. data/lib/arel/expressions.rb +29 -0
  260. data/lib/arel/factory_methods.rb +49 -0
  261. data/lib/arel/insert_manager.rb +49 -0
  262. data/lib/arel/math.rb +45 -0
  263. data/lib/arel/nodes/and.rb +32 -0
  264. data/lib/arel/nodes/ascending.rb +23 -0
  265. data/lib/arel/nodes/binary.rb +52 -0
  266. data/lib/arel/nodes/bind_param.rb +36 -0
  267. data/lib/arel/nodes/case.rb +55 -0
  268. data/lib/arel/nodes/casted.rb +50 -0
  269. data/lib/arel/nodes/comment.rb +29 -0
  270. data/lib/arel/nodes/count.rb +12 -0
  271. data/lib/arel/nodes/delete_statement.rb +45 -0
  272. data/lib/arel/nodes/descending.rb +23 -0
  273. data/lib/arel/nodes/equality.rb +18 -0
  274. data/lib/arel/nodes/extract.rb +24 -0
  275. data/lib/arel/nodes/false.rb +16 -0
  276. data/lib/arel/nodes/full_outer_join.rb +8 -0
  277. data/lib/arel/nodes/function.rb +44 -0
  278. data/lib/arel/nodes/grouping.rb +8 -0
  279. data/lib/arel/nodes/in.rb +8 -0
  280. data/lib/arel/nodes/infix_operation.rb +80 -0
  281. data/lib/arel/nodes/inner_join.rb +8 -0
  282. data/lib/arel/nodes/insert_statement.rb +37 -0
  283. data/lib/arel/nodes/join_source.rb +20 -0
  284. data/lib/arel/nodes/matches.rb +18 -0
  285. data/lib/arel/nodes/named_function.rb +23 -0
  286. data/lib/arel/nodes/node.rb +50 -0
  287. data/lib/arel/nodes/node_expression.rb +13 -0
  288. data/lib/arel/nodes/outer_join.rb +8 -0
  289. data/lib/arel/nodes/over.rb +15 -0
  290. data/lib/arel/nodes/regexp.rb +16 -0
  291. data/lib/arel/nodes/right_outer_join.rb +8 -0
  292. data/lib/arel/nodes/select_core.rb +67 -0
  293. data/lib/arel/nodes/select_statement.rb +41 -0
  294. data/lib/arel/nodes/sql_literal.rb +16 -0
  295. data/lib/arel/nodes/string_join.rb +11 -0
  296. data/lib/arel/nodes/table_alias.rb +27 -0
  297. data/lib/arel/nodes/terminal.rb +16 -0
  298. data/lib/arel/nodes/true.rb +16 -0
  299. data/lib/arel/nodes/unary.rb +45 -0
  300. data/lib/arel/nodes/unary_operation.rb +20 -0
  301. data/lib/arel/nodes/unqualified_column.rb +22 -0
  302. data/lib/arel/nodes/update_statement.rb +41 -0
  303. data/lib/arel/nodes/values_list.rb +9 -0
  304. data/lib/arel/nodes/window.rb +126 -0
  305. data/lib/arel/nodes/with.rb +11 -0
  306. data/lib/arel/nodes.rb +68 -0
  307. data/lib/arel/order_predications.rb +13 -0
  308. data/lib/arel/predications.rb +257 -0
  309. data/lib/arel/select_manager.rb +271 -0
  310. data/lib/arel/table.rb +110 -0
  311. data/lib/arel/tree_manager.rb +72 -0
  312. data/lib/arel/update_manager.rb +34 -0
  313. data/lib/arel/visitors/depth_first.rb +204 -0
  314. data/lib/arel/visitors/dot.rb +297 -0
  315. data/lib/arel/visitors/ibm_db.rb +34 -0
  316. data/lib/arel/visitors/informix.rb +62 -0
  317. data/lib/arel/visitors/mssql.rb +157 -0
  318. data/lib/arel/visitors/mysql.rb +83 -0
  319. data/lib/arel/visitors/oracle.rb +159 -0
  320. data/lib/arel/visitors/oracle12.rb +66 -0
  321. data/lib/arel/visitors/postgresql.rb +110 -0
  322. data/lib/arel/visitors/sqlite.rb +39 -0
  323. data/lib/arel/visitors/to_sql.rb +889 -0
  324. data/lib/arel/visitors/visitor.rb +46 -0
  325. data/lib/arel/visitors/where_sql.rb +23 -0
  326. data/lib/arel/visitors.rb +20 -0
  327. data/lib/arel/window_predications.rb +9 -0
  328. data/lib/arel.rb +58 -0
  329. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  330. data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
  331. data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -35
  332. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +1 -1
  333. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +4 -2
  334. data/lib/rails/generators/active_record/migration.rb +17 -2
  335. data/lib/rails/generators/active_record/model/model_generator.rb +9 -29
  336. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +10 -1
  337. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  338. data/lib/rails/generators/active_record.rb +7 -5
  339. metadata +133 -50
  340. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  341. data/lib/active_record/associations/preloader/collection_association.rb +0 -17
  342. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  343. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  344. data/lib/active_record/associations/preloader/has_one.rb +0 -15
  345. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  346. data/lib/active_record/associations/preloader/singular_association.rb +0 -20
  347. data/lib/active_record/attribute/user_provided_default.rb +0 -28
  348. data/lib/active_record/attribute.rb +0 -213
  349. data/lib/active_record/attribute_mutation_tracker.rb +0 -70
  350. data/lib/active_record/attribute_set/builder.rb +0 -130
  351. data/lib/active_record/attribute_set.rb +0 -110
  352. data/lib/active_record/collection_cache_key.rb +0 -50
  353. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +0 -50
  354. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  355. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
  356. data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
  357. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -57
  358. data/lib/active_record/type/internal/abstract_json.rb +0 -33
@@ -1,6 +1,9 @@
1
- require 'thread'
2
- require 'concurrent/map'
3
- require 'monitor'
1
+ # frozen_string_literal: true
2
+
3
+ require "thread"
4
+ require "concurrent/map"
5
+ require "monitor"
6
+ require "weakref"
4
7
 
5
8
  module ActiveRecord
6
9
  # Raised when a connection could not be obtained within the connection
@@ -17,6 +20,26 @@ module ActiveRecord
17
20
  end
18
21
 
19
22
  module ConnectionAdapters
23
+ module AbstractPool # :nodoc:
24
+ def get_schema_cache(connection)
25
+ @schema_cache ||= SchemaCache.new(connection)
26
+ @schema_cache.connection = connection
27
+ @schema_cache
28
+ end
29
+
30
+ def set_schema_cache(cache)
31
+ @schema_cache = cache
32
+ end
33
+ end
34
+
35
+ class NullPool # :nodoc:
36
+ include ConnectionAdapters::AbstractPool
37
+
38
+ def initialize
39
+ @schema_cache = nil
40
+ end
41
+ end
42
+
20
43
  # Connection pool base class for managing Active Record database
21
44
  # connections.
22
45
  #
@@ -61,30 +84,25 @@ module ActiveRecord
61
84
  # There are several connection-pooling-related options that you can add to
62
85
  # your database connection configuration:
63
86
  #
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).
87
+ # * +pool+: maximum number of connections the pool may manage (default 5).
88
+ # * +idle_timeout+: number of seconds that a connection will be kept
89
+ # unused in the pool before it is automatically disconnected (default
90
+ # 300 seconds). Set this to zero to keep connections forever.
91
+ # * +checkout_timeout+: number of seconds to wait for a connection to
92
+ # become available before giving up and raising a timeout error (default
93
+ # 5 seconds).
73
94
  #
74
95
  #--
75
96
  # Synchronization policy:
76
97
  # * all public methods can be called outside +synchronize+
77
- # * access to these i-vars needs to be in +synchronize+:
98
+ # * access to these instance variables needs to be in +synchronize+:
78
99
  # * @connections
79
100
  # * @now_connecting
80
101
  # * private methods that require being called in a +synchronize+ blocks
81
102
  # are now explicitly documented
82
103
  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.
104
+ # Threadsafe, fair, LIFO queue. Meant to be used by ConnectionPool
105
+ # with which it shares a Monitor.
88
106
  class Queue
89
107
  def initialize(lock = Monitor.new)
90
108
  @lock = lock
@@ -116,7 +134,7 @@ module ActiveRecord
116
134
  end
117
135
  end
118
136
 
119
- # If +element+ is in the queue, remove and return it, or nil.
137
+ # If +element+ is in the queue, remove and return it, or +nil+.
120
138
  def delete(element)
121
139
  synchronize do
122
140
  @queue.delete(element)
@@ -135,7 +153,7 @@ module ActiveRecord
135
153
  # If +timeout+ is not given, remove and return the head the
136
154
  # queue if the number of available elements is strictly
137
155
  # greater than the number of threads currently waiting (that
138
- # is, don't jump ahead in line). Otherwise, return nil.
156
+ # is, don't jump ahead in line). Otherwise, return +nil+.
139
157
  #
140
158
  # If +timeout+ is given, block if there is no element
141
159
  # available, waiting up to +timeout+ seconds for an element to
@@ -150,61 +168,63 @@ module ActiveRecord
150
168
 
151
169
  private
152
170
 
153
- def internal_poll(timeout)
154
- no_wait_poll || (timeout && wait_poll(timeout))
155
- end
171
+ def internal_poll(timeout)
172
+ no_wait_poll || (timeout && wait_poll(timeout))
173
+ end
156
174
 
157
- def synchronize(&block)
158
- @lock.synchronize(&block)
159
- end
175
+ def synchronize(&block)
176
+ @lock.synchronize(&block)
177
+ end
160
178
 
161
- # Test if the queue currently contains any elements.
162
- def any?
163
- !@queue.empty?
164
- end
179
+ # Test if the queue currently contains any elements.
180
+ def any?
181
+ !@queue.empty?
182
+ end
165
183
 
166
- # A thread can remove an element from the queue without
167
- # waiting if and only if the number of currently available
168
- # connections is strictly greater than the number of waiting
169
- # threads.
170
- def can_remove_no_wait?
171
- @queue.size > @num_waiting
172
- end
184
+ # A thread can remove an element from the queue without
185
+ # waiting if and only if the number of currently available
186
+ # connections is strictly greater than the number of waiting
187
+ # threads.
188
+ def can_remove_no_wait?
189
+ @queue.size > @num_waiting
190
+ end
173
191
 
174
- # Removes and returns the head of the queue if possible, or nil.
175
- def remove
176
- @queue.shift
177
- end
192
+ # Removes and returns the head of the queue if possible, or +nil+.
193
+ def remove
194
+ @queue.pop
195
+ end
178
196
 
179
- # Remove and return the head the queue if the number of
180
- # available elements is strictly greater than the number of
181
- # threads currently waiting. Otherwise, return nil.
182
- def no_wait_poll
183
- remove if can_remove_no_wait?
184
- end
197
+ # Remove and return the head the queue if the number of
198
+ # available elements is strictly greater than the number of
199
+ # threads currently waiting. Otherwise, return +nil+.
200
+ def no_wait_poll
201
+ remove if can_remove_no_wait?
202
+ end
185
203
 
186
- # Waits on the queue up to +timeout+ seconds, then removes and
187
- # returns the head of the queue.
188
- def wait_poll(timeout)
189
- @num_waiting += 1
204
+ # Waits on the queue up to +timeout+ seconds, then removes and
205
+ # returns the head of the queue.
206
+ def wait_poll(timeout)
207
+ @num_waiting += 1
190
208
 
191
- t0 = Time.now
192
- elapsed = 0
193
- loop do
194
- @cond.wait(timeout - elapsed)
209
+ t0 = Concurrent.monotonic_time
210
+ elapsed = 0
211
+ loop do
212
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
213
+ @cond.wait(timeout - elapsed)
214
+ end
195
215
 
196
- return remove if any?
216
+ return remove if any?
197
217
 
198
- elapsed = Time.now - t0
199
- if elapsed >= timeout
200
- msg = 'could not obtain a connection from the pool within %0.3f seconds (waited %0.3f seconds); all pooled connections were in use' %
201
- [timeout, elapsed]
202
- raise ConnectionTimeoutError, msg
218
+ elapsed = Concurrent.monotonic_time - t0
219
+ if elapsed >= timeout
220
+ msg = "could not obtain a connection from the pool within %0.3f seconds (waited %0.3f seconds); all pooled connections were in use" %
221
+ [timeout, elapsed]
222
+ raise ConnectionTimeoutError, msg
223
+ end
203
224
  end
225
+ ensure
226
+ @num_waiting -= 1
204
227
  end
205
- ensure
206
- @num_waiting -= 1
207
- end
208
228
  end
209
229
 
210
230
  # Adds the ability to turn a basic fair FIFO queue into one
@@ -268,25 +288,25 @@ module ActiveRecord
268
288
  # Connections must be leased while holding the main pool mutex. This is
269
289
  # an internal subclass that also +.leases+ returned connections while
270
290
  # still in queue's critical section (queue synchronizes with the same
271
- # +@lock+ as the main pool) so that a returned connection is already
291
+ # <tt>@lock</tt> as the main pool) so that a returned connection is already
272
292
  # leased and there is no need to re-enter synchronized block.
273
293
  class ConnectionLeasingQueue < Queue # :nodoc:
274
294
  include BiasableQueue
275
295
 
276
296
  private
277
- def internal_poll(timeout)
278
- conn = super
279
- conn.lease if conn
280
- conn
281
- end
297
+ def internal_poll(timeout)
298
+ conn = super
299
+ conn.lease if conn
300
+ conn
301
+ end
282
302
  end
283
303
 
284
- # Every +frequency+ seconds, the reaper will call +reap+ on +pool+.
285
- # A reaper instantiated with a nil frequency will never reap the
286
- # connection pool.
304
+ # Every +frequency+ seconds, the reaper will call +reap+ and +flush+ on
305
+ # +pool+. A reaper instantiated with a zero frequency will never reap
306
+ # the connection pool.
287
307
  #
288
- # Configure the frequency by setting "reaping_frequency" in your
289
- # database yaml file.
308
+ # Configure the frequency by setting +reaping_frequency+ in your database
309
+ # yaml file (default 60 seconds).
290
310
  class Reaper
291
311
  attr_reader :pool, :frequency
292
312
 
@@ -295,22 +315,59 @@ module ActiveRecord
295
315
  @frequency = frequency
296
316
  end
297
317
 
298
- def run
299
- return unless frequency
300
- Thread.new(frequency, pool) { |t, p|
301
- while true
302
- sleep t
303
- p.reap
318
+ @mutex = Mutex.new
319
+ @pools = {}
320
+ @threads = {}
321
+
322
+ class << self
323
+ def register_pool(pool, frequency) # :nodoc:
324
+ @mutex.synchronize do
325
+ unless @threads[frequency]&.alive?
326
+ @threads[frequency] = spawn_thread(frequency)
327
+ end
328
+ @pools[frequency] ||= []
329
+ @pools[frequency] << WeakRef.new(pool)
304
330
  end
305
- }
331
+ end
332
+
333
+ private
334
+
335
+ def spawn_thread(frequency)
336
+ Thread.new(frequency) do |t|
337
+ running = true
338
+ while running
339
+ sleep t
340
+ @mutex.synchronize do
341
+ @pools[frequency].select!(&:weakref_alive?)
342
+ @pools[frequency].each do |p|
343
+ p.reap
344
+ p.flush
345
+ rescue WeakRef::RefError
346
+ end
347
+
348
+ if @pools[frequency].empty?
349
+ @pools.delete(frequency)
350
+ @threads.delete(frequency)
351
+ running = false
352
+ end
353
+ end
354
+ end
355
+ end
356
+ end
357
+ end
358
+
359
+ def run
360
+ return unless frequency && frequency > 0
361
+ self.class.register_pool(pool, frequency)
306
362
  end
307
363
  end
308
364
 
309
365
  include MonitorMixin
310
366
  include QueryCache::ConnectionPoolConfiguration
367
+ include ConnectionAdapters::AbstractPool
311
368
 
312
369
  attr_accessor :automatic_reconnect, :checkout_timeout, :schema_cache
313
- attr_reader :spec, :connections, :size, :reaper
370
+ attr_reader :spec, :size, :reaper
314
371
 
315
372
  # Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
316
373
  # object which describes database connection information (e.g. adapter,
@@ -324,23 +381,25 @@ module ActiveRecord
324
381
  @spec = spec
325
382
 
326
383
  @checkout_timeout = (spec.config[:checkout_timeout] && spec.config[:checkout_timeout].to_f) || 5
327
- @reaper = Reaper.new(self, (spec.config[:reaping_frequency] && spec.config[:reaping_frequency].to_f))
328
- @reaper.run
384
+ if @idle_timeout = spec.config.fetch(:idle_timeout, 300)
385
+ @idle_timeout = @idle_timeout.to_f
386
+ @idle_timeout = nil if @idle_timeout <= 0
387
+ end
329
388
 
330
389
  # default max pool size to 5
331
390
  @size = (spec.config[:pool] && spec.config[:pool].to_i) || 5
332
391
 
333
- # The cache of threads mapped to reserved connections, the sole purpose
334
- # of the cache is to speed-up +connection+ method, it is not the authoritative
335
- # registry of which thread owns which connection, that is tracked by
336
- # +connection.owner+ attr on each +connection+ instance.
392
+ # This variable tracks the cache of threads mapped to reserved connections, with the
393
+ # sole purpose of speeding up the +connection+ method. It is not the authoritative
394
+ # registry of which thread owns which connection. Connection ownership is tracked by
395
+ # the +connection.owner+ attr on each +connection+ instance.
337
396
  # The invariant works like this: if there is mapping of <tt>thread => conn</tt>,
338
- # then that +thread+ does indeed own that +conn+, however an absence of a such
339
- # mapping does not mean that the +thread+ doesn't own the said connection, in
397
+ # then that +thread+ does indeed own that +conn+. However, an absence of a such
398
+ # mapping does not mean that the +thread+ doesn't own the said connection. In
340
399
  # that case +conn.owner+ attr should be consulted.
341
- # Access and modification of +@thread_cached_conns+ does not require
400
+ # Access and modification of <tt>@thread_cached_conns</tt> does not require
342
401
  # synchronization.
343
- @thread_cached_conns = Concurrent::Map.new(:initial_capacity => @size)
402
+ @thread_cached_conns = Concurrent::Map.new(initial_capacity: @size)
344
403
 
345
404
  @connections = []
346
405
  @automatic_reconnect = true
@@ -353,6 +412,22 @@ module ActiveRecord
353
412
  @threads_blocking_new_connections = 0
354
413
 
355
414
  @available = ConnectionLeasingQueue.new self
415
+
416
+ @lock_thread = false
417
+
418
+ # +reaping_frequency+ is configurable mostly for historical reasons, but it could
419
+ # also be useful if someone wants a very low +idle_timeout+.
420
+ reaping_frequency = spec.config.fetch(:reaping_frequency, 60)
421
+ @reaper = Reaper.new(self, reaping_frequency && reaping_frequency.to_f)
422
+ @reaper.run
423
+ end
424
+
425
+ def lock_thread=(lock_thread)
426
+ if lock_thread
427
+ @lock_thread = Thread.current
428
+ else
429
+ @lock_thread = nil
430
+ end
356
431
  end
357
432
 
358
433
  # Retrieve the connection associated with the current thread, or call
@@ -361,16 +436,16 @@ module ActiveRecord
361
436
  # #connection can be called any number of times; the connection is
362
437
  # held in a cache keyed by a thread.
363
438
  def connection
364
- @thread_cached_conns[connection_cache_key(Thread.current)] ||= checkout
439
+ @thread_cached_conns[connection_cache_key(current_thread)] ||= checkout
365
440
  end
366
441
 
367
- # Is there an open connection that is being used for the current thread?
442
+ # Returns true if there is an open connection being used for the current thread.
368
443
  #
369
444
  # This method only works for connections that have been obtained through
370
- # #connection or #with_connection methods, connections obtained through
445
+ # #connection or #with_connection methods. Connections obtained through
371
446
  # #checkout will not be detected by #active_connection?
372
447
  def active_connection?
373
- @thread_cached_conns[connection_cache_key(Thread.current)]
448
+ @thread_cached_conns[connection_cache_key(current_thread)]
374
449
  end
375
450
 
376
451
  # Signal that the thread is finished with the current connection.
@@ -405,6 +480,21 @@ module ActiveRecord
405
480
  synchronize { @connections.any? }
406
481
  end
407
482
 
483
+ # Returns an array containing the connections currently in the pool.
484
+ # Access to the array does not require synchronization on the pool because
485
+ # the array is newly created and not retained by the pool.
486
+ #
487
+ # However; this method bypasses the ConnectionPool's thread-safe connection
488
+ # access pattern. A returned connection may be owned by another thread,
489
+ # unowned, or by happen-stance owned by the calling thread.
490
+ #
491
+ # Calling methods on a connection without ownership is subject to the
492
+ # thread-safety guarantees of the underlying method. Many of the methods
493
+ # on connection adapter classes are inherently multi-thread unsafe.
494
+ def connections
495
+ synchronize { @connections.dup }
496
+ end
497
+
408
498
  # Disconnects all connections in the pool, and clears the pool.
409
499
  #
410
500
  # Raises:
@@ -429,14 +519,29 @@ module ActiveRecord
429
519
 
430
520
  # Disconnects all connections in the pool, and clears the pool.
431
521
  #
432
- # The pool first tries to gain ownership of all connections, if unable to
522
+ # The pool first tries to gain ownership of all connections. If unable to
433
523
  # do so within a timeout interval (default duration is
434
- # <tt>spec.config[:checkout_timeout] * 2</tt> seconds), the pool is forcefully
524
+ # <tt>spec.config[:checkout_timeout] * 2</tt> seconds), then the pool is forcefully
435
525
  # disconnected without any regard for other connection owning threads.
436
526
  def disconnect!
437
527
  disconnect(false)
438
528
  end
439
529
 
530
+ # Discards all connections in the pool (even if they're currently
531
+ # leased!), along with the pool itself. Any further interaction with the
532
+ # pool (except #spec and #schema_cache) is undefined.
533
+ #
534
+ # See AbstractAdapter#discard!
535
+ def discard! # :nodoc:
536
+ synchronize do
537
+ return if @connections.nil? # already discarded
538
+ @connections.each do |conn|
539
+ conn.discard!
540
+ end
541
+ @connections = @available = @thread_cached_conns = nil
542
+ end
543
+ end
544
+
440
545
  # Clears the cache which maps classes and re-connects connections that
441
546
  # require reloading.
442
547
  #
@@ -463,9 +568,9 @@ module ActiveRecord
463
568
  # Clears the cache which maps classes and re-connects connections that
464
569
  # require reloading.
465
570
  #
466
- # The pool first tries to gain ownership of all connections, if unable to
571
+ # The pool first tries to gain ownership of all connections. If unable to
467
572
  # do so within a timeout interval (default duration is
468
- # <tt>spec.config[:checkout_timeout] * 2</tt> seconds), the pool forcefully
573
+ # <tt>spec.config[:checkout_timeout] * 2</tt> seconds), then the pool forcefully
469
574
  # clears the cache and reloads connections without any regard for other
470
575
  # connection owning threads.
471
576
  def clear_reloadable_connections!
@@ -496,14 +601,16 @@ module ActiveRecord
496
601
  # +conn+: an AbstractAdapter object, which was obtained by earlier by
497
602
  # calling #checkout on this pool.
498
603
  def checkin(conn)
499
- synchronize do
500
- remove_connection_from_thread_cache conn
604
+ conn.lock.synchronize do
605
+ synchronize do
606
+ remove_connection_from_thread_cache conn
501
607
 
502
- conn._run_checkin_callbacks do
503
- conn.expire
504
- end
608
+ conn._run_checkin_callbacks do
609
+ conn.expire
610
+ end
505
611
 
506
- @available.add conn
612
+ @available.add conn
613
+ end
507
614
  end
508
615
  end
509
616
 
@@ -519,20 +626,20 @@ module ActiveRecord
519
626
  @available.delete conn
520
627
 
521
628
  # @available.any_waiting? => true means that prior to removing this
522
- # conn, the pool was at its max size (@connections.size == @size)
523
- # this would mean that any threads stuck waiting in the queue wouldn't
629
+ # conn, the pool was at its max size (@connections.size == @size).
630
+ # This would mean that any threads stuck waiting in the queue wouldn't
524
631
  # know they could checkout_new_connection, so let's do it for them.
525
632
  # Because condition-wait loop is encapsulated in the Queue class
526
633
  # (that in turn is oblivious to ConnectionPool implementation), threads
527
- # that are "stuck" there are helpless, they have no way of creating
634
+ # that are "stuck" there are helpless. They have no way of creating
528
635
  # new connections and are completely reliant on us feeding available
529
636
  # connections into the Queue.
530
637
  needs_new_connection = @available.any_waiting?
531
638
  end
532
639
 
533
640
  # This is intentionally done outside of the synchronized section as we
534
- # would like not to hold the main mutex while checking out new connections,
535
- # thus there is some chance that needs_new_connection information is now
641
+ # would like not to hold the main mutex while checking out new connections.
642
+ # Thus there is some chance that needs_new_connection information is now
536
643
  # stale, we can live with that (bulk_make_new_connections will make
537
644
  # sure not to exceed the pool's @size limit).
538
645
  bulk_make_new_connections(1) if needs_new_connection
@@ -543,6 +650,7 @@ module ActiveRecord
543
650
  # or a thread dies unexpectedly.
544
651
  def reap
545
652
  stale_connections = synchronize do
653
+ return unless @connections
546
654
  @connections.select do |conn|
547
655
  conn.in_use? && !conn.owner.alive?
548
656
  end.each do |conn|
@@ -560,229 +668,281 @@ module ActiveRecord
560
668
  end
561
669
  end
562
670
 
671
+ # Disconnect all connections that have been idle for at least
672
+ # +minimum_idle+ seconds. Connections currently checked out, or that were
673
+ # checked in less than +minimum_idle+ seconds ago, are unaffected.
674
+ def flush(minimum_idle = @idle_timeout)
675
+ return if minimum_idle.nil?
676
+
677
+ idle_connections = synchronize do
678
+ return unless @connections
679
+ @connections.select do |conn|
680
+ !conn.in_use? && conn.seconds_idle >= minimum_idle
681
+ end.each do |conn|
682
+ conn.lease
683
+
684
+ @available.delete conn
685
+ @connections.delete conn
686
+ end
687
+ end
688
+
689
+ idle_connections.each do |conn|
690
+ conn.disconnect!
691
+ end
692
+ end
693
+
694
+ # Disconnect all currently idle connections. Connections currently checked
695
+ # out are unaffected.
696
+ def flush!
697
+ reap
698
+ flush(-1)
699
+ end
700
+
563
701
  def num_waiting_in_queue # :nodoc:
564
702
  @available.num_waiting
565
703
  end
566
704
 
567
- private
568
- #--
569
- # this is unfortunately not concurrent
570
- def bulk_make_new_connections(num_new_conns_needed)
571
- num_new_conns_needed.times do
572
- # try_to_checkout_new_connection will not exceed pool's @size limit
573
- if new_conn = try_to_checkout_new_connection
574
- # make the new_conn available to the starving threads stuck @available Queue
575
- checkin(new_conn)
576
- end
577
- end
578
- end
579
-
580
- #--
581
- # From the discussion on GitHub:
582
- # https://github.com/rails/rails/pull/14938#commitcomment-6601951
583
- # This hook-in method allows for easier monkey-patching fixes needed by
584
- # JRuby users that use Fibers.
585
- def connection_cache_key(thread)
586
- thread
587
- end
588
-
589
- # Take control of all existing connections so a "group" action such as
590
- # reload/disconnect can be performed safely. It is no longer enough to
591
- # wrap it in +synchronize+ because some pool's actions are allowed
592
- # to be performed outside of the main +synchronize+ block.
593
- def with_exclusively_acquired_all_connections(raise_on_acquisition_timeout = true)
594
- with_new_connections_blocked do
595
- attempt_to_checkout_all_existing_connections(raise_on_acquisition_timeout)
596
- yield
705
+ # Return connection pool's usage statistic
706
+ # Example:
707
+ #
708
+ # ActiveRecord::Base.connection_pool.stat # => { size: 15, connections: 1, busy: 1, dead: 0, idle: 0, waiting: 0, checkout_timeout: 5 }
709
+ def stat
710
+ synchronize do
711
+ {
712
+ size: size,
713
+ connections: @connections.size,
714
+ busy: @connections.count { |c| c.in_use? && c.owner.alive? },
715
+ dead: @connections.count { |c| c.in_use? && !c.owner.alive? },
716
+ idle: @connections.count { |c| !c.in_use? },
717
+ waiting: num_waiting_in_queue,
718
+ checkout_timeout: checkout_timeout
719
+ }
597
720
  end
598
721
  end
599
722
 
600
- def attempt_to_checkout_all_existing_connections(raise_on_acquisition_timeout = true)
601
- collected_conns = synchronize do
602
- # account for our own connections
603
- @connections.select {|conn| conn.owner == Thread.current}
604
- end
605
-
606
- newly_checked_out = []
607
- timeout_time = Time.now + (@checkout_timeout * 2)
608
-
609
- @available.with_a_bias_for(Thread.current) do
610
- while true
611
- synchronize do
612
- return if collected_conns.size == @connections.size && @now_connecting == 0
613
- remaining_timeout = timeout_time - Time.now
614
- remaining_timeout = 0 if remaining_timeout < 0
615
- conn = checkout_for_exclusive_access(remaining_timeout)
616
- collected_conns << conn
617
- newly_checked_out << conn
723
+ private
724
+ #--
725
+ # this is unfortunately not concurrent
726
+ def bulk_make_new_connections(num_new_conns_needed)
727
+ num_new_conns_needed.times do
728
+ # try_to_checkout_new_connection will not exceed pool's @size limit
729
+ if new_conn = try_to_checkout_new_connection
730
+ # make the new_conn available to the starving threads stuck @available Queue
731
+ checkin(new_conn)
618
732
  end
619
733
  end
620
734
  end
621
- rescue ExclusiveConnectionTimeoutError
622
- # <tt>raise_on_acquisition_timeout == false</tt> means we are directed to ignore any
623
- # timeouts and are expected to just give up: we've obtained as many connections
624
- # as possible, note that in a case like that we don't return any of the
625
- # +newly_checked_out+ connections.
626
735
 
627
- if raise_on_acquisition_timeout
628
- release_newly_checked_out = true
629
- raise
736
+ #--
737
+ # From the discussion on GitHub:
738
+ # https://github.com/rails/rails/pull/14938#commitcomment-6601951
739
+ # This hook-in method allows for easier monkey-patching fixes needed by
740
+ # JRuby users that use Fibers.
741
+ def connection_cache_key(thread)
742
+ thread
630
743
  end
631
- rescue Exception # if something else went wrong
632
- # this can't be a "naked" rescue, because we have should return conns
633
- # even for non-StandardErrors
634
- release_newly_checked_out = true
635
- raise
636
- ensure
637
- if release_newly_checked_out && newly_checked_out
638
- # releasing only those conns that were checked out in this method, conns
639
- # checked outside this method (before it was called) are not for us to release
640
- newly_checked_out.each {|conn| checkin(conn)}
744
+
745
+ def current_thread
746
+ @lock_thread || Thread.current
747
+ end
748
+
749
+ # Take control of all existing connections so a "group" action such as
750
+ # reload/disconnect can be performed safely. It is no longer enough to
751
+ # wrap it in +synchronize+ because some pool's actions are allowed
752
+ # to be performed outside of the main +synchronize+ block.
753
+ def with_exclusively_acquired_all_connections(raise_on_acquisition_timeout = true)
754
+ with_new_connections_blocked do
755
+ attempt_to_checkout_all_existing_connections(raise_on_acquisition_timeout)
756
+ yield
757
+ end
641
758
  end
642
- end
643
759
 
644
- #--
645
- # Must be called in a synchronize block.
646
- def checkout_for_exclusive_access(checkout_timeout)
647
- checkout(checkout_timeout)
648
- rescue ConnectionTimeoutError
649
- # this block can't be easily moved into attempt_to_checkout_all_existing_connections's
650
- # rescue block, because doing so would put it outside of synchronize section, without
651
- # being in a critical section thread_report might become inaccurate
652
- msg = "could not obtain ownership of all database connections in #{checkout_timeout} seconds"
760
+ def attempt_to_checkout_all_existing_connections(raise_on_acquisition_timeout = true)
761
+ collected_conns = synchronize do
762
+ # account for our own connections
763
+ @connections.select { |conn| conn.owner == Thread.current }
764
+ end
653
765
 
654
- thread_report = []
655
- @connections.each do |conn|
656
- unless conn.owner == Thread.current
657
- thread_report << "#{conn} is owned by #{conn.owner}"
766
+ newly_checked_out = []
767
+ timeout_time = Concurrent.monotonic_time + (@checkout_timeout * 2)
768
+
769
+ @available.with_a_bias_for(Thread.current) do
770
+ loop do
771
+ synchronize do
772
+ return if collected_conns.size == @connections.size && @now_connecting == 0
773
+ remaining_timeout = timeout_time - Concurrent.monotonic_time
774
+ remaining_timeout = 0 if remaining_timeout < 0
775
+ conn = checkout_for_exclusive_access(remaining_timeout)
776
+ collected_conns << conn
777
+ newly_checked_out << conn
778
+ end
779
+ end
780
+ end
781
+ rescue ExclusiveConnectionTimeoutError
782
+ # <tt>raise_on_acquisition_timeout == false</tt> means we are directed to ignore any
783
+ # timeouts and are expected to just give up: we've obtained as many connections
784
+ # as possible, note that in a case like that we don't return any of the
785
+ # +newly_checked_out+ connections.
786
+
787
+ if raise_on_acquisition_timeout
788
+ release_newly_checked_out = true
789
+ raise
790
+ end
791
+ rescue Exception # if something else went wrong
792
+ # this can't be a "naked" rescue, because we have should return conns
793
+ # even for non-StandardErrors
794
+ release_newly_checked_out = true
795
+ raise
796
+ ensure
797
+ if release_newly_checked_out && newly_checked_out
798
+ # releasing only those conns that were checked out in this method, conns
799
+ # checked outside this method (before it was called) are not for us to release
800
+ newly_checked_out.each { |conn| checkin(conn) }
658
801
  end
659
802
  end
660
803
 
661
- msg << " (#{thread_report.join(', ')})" if thread_report.any?
804
+ #--
805
+ # Must be called in a synchronize block.
806
+ def checkout_for_exclusive_access(checkout_timeout)
807
+ checkout(checkout_timeout)
808
+ rescue ConnectionTimeoutError
809
+ # this block can't be easily moved into attempt_to_checkout_all_existing_connections's
810
+ # rescue block, because doing so would put it outside of synchronize section, without
811
+ # being in a critical section thread_report might become inaccurate
812
+ msg = +"could not obtain ownership of all database connections in #{checkout_timeout} seconds"
813
+
814
+ thread_report = []
815
+ @connections.each do |conn|
816
+ unless conn.owner == Thread.current
817
+ thread_report << "#{conn} is owned by #{conn.owner}"
818
+ end
819
+ end
662
820
 
663
- raise ExclusiveConnectionTimeoutError, msg
664
- end
821
+ msg << " (#{thread_report.join(', ')})" if thread_report.any?
665
822
 
666
- def with_new_connections_blocked
667
- synchronize do
668
- @threads_blocking_new_connections += 1
823
+ raise ExclusiveConnectionTimeoutError, msg
669
824
  end
670
825
 
671
- yield
672
- ensure
673
- num_new_conns_required = 0
826
+ def with_new_connections_blocked
827
+ synchronize do
828
+ @threads_blocking_new_connections += 1
829
+ end
674
830
 
675
- synchronize do
676
- @threads_blocking_new_connections -= 1
831
+ yield
832
+ ensure
833
+ num_new_conns_required = 0
677
834
 
678
- if @threads_blocking_new_connections.zero?
679
- @available.clear
835
+ synchronize do
836
+ @threads_blocking_new_connections -= 1
680
837
 
681
- num_new_conns_required = num_waiting_in_queue
838
+ if @threads_blocking_new_connections.zero?
839
+ @available.clear
682
840
 
683
- @connections.each do |conn|
684
- next if conn.in_use?
841
+ num_new_conns_required = num_waiting_in_queue
685
842
 
686
- @available.add conn
687
- num_new_conns_required -= 1
843
+ @connections.each do |conn|
844
+ next if conn.in_use?
845
+
846
+ @available.add conn
847
+ num_new_conns_required -= 1
848
+ end
688
849
  end
689
850
  end
690
- end
691
851
 
692
- bulk_make_new_connections(num_new_conns_required) if num_new_conns_required > 0
693
- end
694
-
695
- # Acquire a connection by one of 1) immediately removing one
696
- # from the queue of available connections, 2) creating a new
697
- # connection if the pool is not at capacity, 3) waiting on the
698
- # queue for a connection to become available.
699
- #
700
- # Raises:
701
- # - ActiveRecord::ConnectionTimeoutError if a connection could not be acquired
702
- #
703
- #--
704
- # Implementation detail: the connection returned by +acquire_connection+
705
- # will already be "+connection.lease+ -ed" to the current thread.
706
- def acquire_connection(checkout_timeout)
707
- # NOTE: we rely on +@available.poll+ and +try_to_checkout_new_connection+ to
708
- # +conn.lease+ the returned connection (and to do this in a +synchronized+
709
- # section), this is not the cleanest implementation, as ideally we would
710
- # <tt>synchronize { conn.lease }</tt> in this method, but by leaving it to +@available.poll+
711
- # and +try_to_checkout_new_connection+ we can piggyback on +synchronize+ sections
712
- # of the said methods and avoid an additional +synchronize+ overhead.
713
- if conn = @available.poll || try_to_checkout_new_connection
714
- conn
715
- else
716
- reap
717
- @available.poll(checkout_timeout)
852
+ bulk_make_new_connections(num_new_conns_required) if num_new_conns_required > 0
718
853
  end
719
- end
720
854
 
721
- #--
722
- # if owner_thread param is omitted, this must be called in synchronize block
723
- def remove_connection_from_thread_cache(conn, owner_thread = conn.owner)
724
- @thread_cached_conns.delete_pair(connection_cache_key(owner_thread), conn)
725
- end
726
- alias_method :release, :remove_connection_from_thread_cache
855
+ # Acquire a connection by one of 1) immediately removing one
856
+ # from the queue of available connections, 2) creating a new
857
+ # connection if the pool is not at capacity, 3) waiting on the
858
+ # queue for a connection to become available.
859
+ #
860
+ # Raises:
861
+ # - ActiveRecord::ConnectionTimeoutError if a connection could not be acquired
862
+ #
863
+ #--
864
+ # Implementation detail: the connection returned by +acquire_connection+
865
+ # will already be "+connection.lease+ -ed" to the current thread.
866
+ def acquire_connection(checkout_timeout)
867
+ # NOTE: we rely on <tt>@available.poll</tt> and +try_to_checkout_new_connection+ to
868
+ # +conn.lease+ the returned connection (and to do this in a +synchronized+
869
+ # section). This is not the cleanest implementation, as ideally we would
870
+ # <tt>synchronize { conn.lease }</tt> in this method, but by leaving it to <tt>@available.poll</tt>
871
+ # and +try_to_checkout_new_connection+ we can piggyback on +synchronize+ sections
872
+ # of the said methods and avoid an additional +synchronize+ overhead.
873
+ if conn = @available.poll || try_to_checkout_new_connection
874
+ conn
875
+ else
876
+ reap
877
+ @available.poll(checkout_timeout)
878
+ end
879
+ end
727
880
 
728
- def new_connection
729
- Base.send(spec.adapter_method, spec.config).tap do |conn|
730
- conn.schema_cache = schema_cache.dup if schema_cache
881
+ #--
882
+ # if owner_thread param is omitted, this must be called in synchronize block
883
+ def remove_connection_from_thread_cache(conn, owner_thread = conn.owner)
884
+ @thread_cached_conns.delete_pair(connection_cache_key(owner_thread), conn)
731
885
  end
732
- end
886
+ alias_method :release, :remove_connection_from_thread_cache
733
887
 
734
- # If the pool is not at a +@size+ limit, establish new connection. Connecting
735
- # to the DB is done outside main synchronized section.
736
- #--
737
- # Implementation constraint: a newly established connection returned by this
738
- # method must be in the +.leased+ state.
739
- def try_to_checkout_new_connection
740
- # first in synchronized section check if establishing new conns is allowed
741
- # and increment @now_connecting, to prevent overstepping this pool's @size
742
- # constraint
743
- do_checkout = synchronize do
744
- if @threads_blocking_new_connections.zero? && (@connections.size + @now_connecting) < @size
745
- @now_connecting += 1
888
+ def new_connection
889
+ Base.send(spec.adapter_method, spec.config).tap do |conn|
890
+ conn.check_version
746
891
  end
747
892
  end
748
- if do_checkout
749
- begin
750
- # if successfully incremented @now_connecting establish new connection
751
- # outside of synchronized section
752
- conn = checkout_new_connection
753
- ensure
754
- synchronize do
755
- if conn
756
- adopt_connection(conn)
757
- # returned conn needs to be already leased
758
- conn.lease
893
+
894
+ # If the pool is not at a <tt>@size</tt> limit, establish new connection. Connecting
895
+ # to the DB is done outside main synchronized section.
896
+ #--
897
+ # Implementation constraint: a newly established connection returned by this
898
+ # method must be in the +.leased+ state.
899
+ def try_to_checkout_new_connection
900
+ # first in synchronized section check if establishing new conns is allowed
901
+ # and increment @now_connecting, to prevent overstepping this pool's @size
902
+ # constraint
903
+ do_checkout = synchronize do
904
+ if @threads_blocking_new_connections.zero? && (@connections.size + @now_connecting) < @size
905
+ @now_connecting += 1
906
+ end
907
+ end
908
+ if do_checkout
909
+ begin
910
+ # if successfully incremented @now_connecting establish new connection
911
+ # outside of synchronized section
912
+ conn = checkout_new_connection
913
+ ensure
914
+ synchronize do
915
+ if conn
916
+ adopt_connection(conn)
917
+ # returned conn needs to be already leased
918
+ conn.lease
919
+ end
920
+ @now_connecting -= 1
759
921
  end
760
- @now_connecting -= 1
761
922
  end
762
923
  end
763
924
  end
764
- end
765
925
 
766
- def adopt_connection(conn)
767
- conn.pool = self
768
- @connections << conn
769
- end
926
+ def adopt_connection(conn)
927
+ conn.pool = self
928
+ @connections << conn
929
+ end
770
930
 
771
- def checkout_new_connection
772
- raise ConnectionNotEstablished unless @automatic_reconnect
773
- new_connection
774
- end
931
+ def checkout_new_connection
932
+ raise ConnectionNotEstablished unless @automatic_reconnect
933
+ new_connection
934
+ end
775
935
 
776
- def checkout_and_verify(c)
777
- c._run_checkout_callbacks do
778
- c.verify!
936
+ def checkout_and_verify(c)
937
+ c._run_checkout_callbacks do
938
+ c.verify!
939
+ end
940
+ c
941
+ rescue
942
+ remove c
943
+ c.disconnect!
944
+ raise
779
945
  end
780
- c
781
- rescue
782
- remove c
783
- c.disconnect!
784
- raise
785
- end
786
946
  end
787
947
 
788
948
  # ConnectionHandler is a collection of ConnectionPool objects. It is used
@@ -797,7 +957,7 @@ module ActiveRecord
797
957
  # end
798
958
  #
799
959
  # class Book < ActiveRecord::Base
800
- # establish_connection "library_db"
960
+ # establish_connection :library_db
801
961
  # end
802
962
  #
803
963
  # class ScaryBook < Book
@@ -829,15 +989,59 @@ module ActiveRecord
829
989
  # All Active Record models use this handler to determine the connection pool that they
830
990
  # should use.
831
991
  #
832
- # The ConnectionHandler class is not coupled with the Active models, as it has no knowlodge
833
- # about the model. The model, needs to pass a specification name to the handler,
834
- # in order to lookup the correct connection pool.
992
+ # The ConnectionHandler class is not coupled with the Active models, as it has no knowledge
993
+ # about the model. The model needs to pass a specification name to the handler,
994
+ # in order to look up the correct connection pool.
835
995
  class ConnectionHandler
996
+ def self.create_owner_to_pool # :nodoc:
997
+ Concurrent::Map.new(initial_capacity: 2) do |h, k|
998
+ # Discard the parent's connection pools immediately; we have no need
999
+ # of them
1000
+ discard_unowned_pools(h)
1001
+
1002
+ h[k] = Concurrent::Map.new(initial_capacity: 2)
1003
+ end
1004
+ end
1005
+
1006
+ def self.unowned_pool_finalizer(pid_map) # :nodoc:
1007
+ lambda do |_|
1008
+ discard_unowned_pools(pid_map)
1009
+ end
1010
+ end
1011
+
1012
+ def self.discard_unowned_pools(pid_map) # :nodoc:
1013
+ pid_map.each do |pid, pools|
1014
+ pools.values.compact.each(&:discard!) unless pid == Process.pid
1015
+ end
1016
+ end
1017
+
836
1018
  def initialize
837
1019
  # These caches are keyed by spec.name (ConnectionSpecification#name).
838
- @owner_to_pool = Concurrent::Map.new(:initial_capacity => 2) do |h,k|
839
- h[k] = Concurrent::Map.new(:initial_capacity => 2)
840
- end
1020
+ @owner_to_pool = ConnectionHandler.create_owner_to_pool
1021
+
1022
+ # Backup finalizer: if the forked child never needed a pool, the above
1023
+ # early discard has not occurred
1024
+ ObjectSpace.define_finalizer self, ConnectionHandler.unowned_pool_finalizer(@owner_to_pool)
1025
+ end
1026
+
1027
+ def prevent_writes # :nodoc:
1028
+ Thread.current[:prevent_writes]
1029
+ end
1030
+
1031
+ def prevent_writes=(prevent_writes) # :nodoc:
1032
+ Thread.current[:prevent_writes] = prevent_writes
1033
+ end
1034
+
1035
+ # Prevent writing to the database regardless of role.
1036
+ #
1037
+ # In some cases you may want to prevent writes to the database
1038
+ # even if you are on a database that can write. `while_preventing_writes`
1039
+ # will prevent writes to the database for the duration of the block.
1040
+ def while_preventing_writes(enabled = true)
1041
+ original, self.prevent_writes = self.prevent_writes, enabled
1042
+ yield
1043
+ ensure
1044
+ self.prevent_writes = original
841
1045
  end
842
1046
 
843
1047
  def connection_pool_list
@@ -845,8 +1049,26 @@ module ActiveRecord
845
1049
  end
846
1050
  alias :connection_pools :connection_pool_list
847
1051
 
848
- def establish_connection(spec)
849
- owner_to_pool[spec.name] = ConnectionAdapters::ConnectionPool.new(spec)
1052
+ def establish_connection(config)
1053
+ resolver = ConnectionSpecification::Resolver.new(Base.configurations)
1054
+ spec = resolver.spec(config)
1055
+
1056
+ remove_connection(spec.name)
1057
+
1058
+ message_bus = ActiveSupport::Notifications.instrumenter
1059
+ payload = {
1060
+ connection_id: object_id
1061
+ }
1062
+ if spec
1063
+ payload[:spec_name] = spec.name
1064
+ payload[:config] = spec.config
1065
+ end
1066
+
1067
+ message_bus.instrument("!connection.active_record", payload) do
1068
+ owner_to_pool[spec.name] = ConnectionAdapters::ConnectionPool.new(spec)
1069
+ end
1070
+
1071
+ owner_to_pool[spec.name]
850
1072
  end
851
1073
 
852
1074
  # Returns true if there are any active connections among the connection
@@ -873,28 +1095,42 @@ module ActiveRecord
873
1095
  connection_pool_list.each(&:disconnect!)
874
1096
  end
875
1097
 
1098
+ # Disconnects all currently idle connections.
1099
+ #
1100
+ # See ConnectionPool#flush! for details.
1101
+ def flush_idle_connections!
1102
+ connection_pool_list.each(&:flush!)
1103
+ end
1104
+
876
1105
  # Locate the connection of the nearest super class. This can be an
877
1106
  # active or defined connection: if it is the latter, it will be
878
1107
  # opened and set as the active connection for the class it was defined
879
1108
  # for (not necessarily the current class).
880
1109
  def retrieve_connection(spec_name) #:nodoc:
881
1110
  pool = retrieve_connection_pool(spec_name)
882
- raise ConnectionNotEstablished, "No connection pool with id #{spec_name} found." unless pool
883
- conn = pool.connection
884
- raise ConnectionNotEstablished, "No connection for #{spec_name} in connection pool" unless conn
885
- conn
1111
+
1112
+ unless pool
1113
+ # multiple database application
1114
+ if ActiveRecord::Base.connection_handler != ActiveRecord::Base.default_connection_handler
1115
+ raise ConnectionNotEstablished, "No connection pool with '#{spec_name}' found for the '#{ActiveRecord::Base.current_role}' role."
1116
+ else
1117
+ raise ConnectionNotEstablished, "No connection pool with '#{spec_name}' found."
1118
+ end
1119
+ end
1120
+
1121
+ pool.connection
886
1122
  end
887
1123
 
888
1124
  # Returns true if a connection that's accessible to this class has
889
1125
  # already been opened.
890
1126
  def connected?(spec_name)
891
- conn = retrieve_connection_pool(spec_name)
892
- conn && conn.connected?
1127
+ pool = retrieve_connection_pool(spec_name)
1128
+ pool && pool.connected?
893
1129
  end
894
1130
 
895
1131
  # Remove the connection for this class. This will close the active
896
1132
  # connection and the defined connection (if they exist). The result
897
- # can be used as an argument for establish_connection, for easily
1133
+ # can be used as an argument for #establish_connection, for easily
898
1134
  # re-establishing the connection.
899
1135
  def remove_connection(spec_name)
900
1136
  if pool = owner_to_pool.delete(spec_name)
@@ -904,22 +1140,18 @@ module ActiveRecord
904
1140
  end
905
1141
  end
906
1142
 
907
- # Retrieving the connection pool happens a lot so we cache it in @class_to_pool.
1143
+ # Retrieving the connection pool happens a lot, so we cache it in @owner_to_pool.
908
1144
  # This makes retrieving the connection pool O(1) once the process is warm.
909
1145
  # When a connection is established or removed, we invalidate the cache.
910
- #
911
- # Ideally we would use #fetch here, as class_to_pool[klass] may sometimes be nil.
912
- # However, benchmarking (https://gist.github.com/jonleighton/3552829) showed that
913
- # #fetch is significantly slower than #[]. So in the nil case, no caching will
914
- # take place, but that's ok since the nil case is not the common one that we wish
915
- # to optimise for.
916
1146
  def retrieve_connection_pool(spec_name)
917
1147
  owner_to_pool.fetch(spec_name) do
1148
+ # Check if a connection was previously established in an ancestor process,
1149
+ # which may have been forked.
918
1150
  if ancestor_pool = pool_from_any_process_for(spec_name)
919
1151
  # A connection was established in an ancestor process that must have
920
1152
  # subsequently forked. We can't reuse the connection, but we can copy
921
1153
  # the specification and establish a new connection with it.
922
- establish_connection(ancestor_pool.spec).tap do |pool|
1154
+ establish_connection(ancestor_pool.spec.to_hash).tap do |pool|
923
1155
  pool.schema_cache = ancestor_pool.schema_cache if ancestor_pool.schema_cache
924
1156
  end
925
1157
  else
@@ -930,14 +1162,14 @@ module ActiveRecord
930
1162
 
931
1163
  private
932
1164
 
933
- def owner_to_pool
934
- @owner_to_pool[Process.pid]
935
- end
1165
+ def owner_to_pool
1166
+ @owner_to_pool[Process.pid]
1167
+ end
936
1168
 
937
- def pool_from_any_process_for(spec_name)
938
- owner_to_pool = @owner_to_pool.values.reverse.find { |v| v[spec_name] }
939
- owner_to_pool && owner_to_pool[spec_name]
940
- end
1169
+ def pool_from_any_process_for(spec_name)
1170
+ owner_to_pool = @owner_to_pool.values.reverse.find { |v| v[spec_name] }
1171
+ owner_to_pool && owner_to_pool[spec_name]
1172
+ end
941
1173
  end
942
1174
  end
943
1175
  end