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