omg-activerecord 8.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (412) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +355 -0
  3. data/MIT-LICENSE +22 -0
  4. data/README.rdoc +219 -0
  5. data/examples/performance.rb +185 -0
  6. data/examples/simple.rb +15 -0
  7. data/lib/active_record/aggregations.rb +287 -0
  8. data/lib/active_record/association_relation.rb +50 -0
  9. data/lib/active_record/associations/alias_tracker.rb +90 -0
  10. data/lib/active_record/associations/association.rb +417 -0
  11. data/lib/active_record/associations/association_scope.rb +175 -0
  12. data/lib/active_record/associations/belongs_to_association.rb +163 -0
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +50 -0
  14. data/lib/active_record/associations/builder/association.rb +170 -0
  15. data/lib/active_record/associations/builder/belongs_to.rb +160 -0
  16. data/lib/active_record/associations/builder/collection_association.rb +80 -0
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +107 -0
  18. data/lib/active_record/associations/builder/has_many.rb +23 -0
  19. data/lib/active_record/associations/builder/has_one.rb +61 -0
  20. data/lib/active_record/associations/builder/singular_association.rb +48 -0
  21. data/lib/active_record/associations/collection_association.rb +535 -0
  22. data/lib/active_record/associations/collection_proxy.rb +1163 -0
  23. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  24. data/lib/active_record/associations/errors.rb +265 -0
  25. data/lib/active_record/associations/foreign_association.rb +40 -0
  26. data/lib/active_record/associations/has_many_association.rb +167 -0
  27. data/lib/active_record/associations/has_many_through_association.rb +232 -0
  28. data/lib/active_record/associations/has_one_association.rb +142 -0
  29. data/lib/active_record/associations/has_one_through_association.rb +45 -0
  30. data/lib/active_record/associations/join_dependency/join_association.rb +106 -0
  31. data/lib/active_record/associations/join_dependency/join_base.rb +23 -0
  32. data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
  33. data/lib/active_record/associations/join_dependency.rb +301 -0
  34. data/lib/active_record/associations/nested_error.rb +47 -0
  35. data/lib/active_record/associations/preloader/association.rb +316 -0
  36. data/lib/active_record/associations/preloader/batch.rb +48 -0
  37. data/lib/active_record/associations/preloader/branch.rb +153 -0
  38. data/lib/active_record/associations/preloader/through_association.rb +150 -0
  39. data/lib/active_record/associations/preloader.rb +135 -0
  40. data/lib/active_record/associations/singular_association.rb +76 -0
  41. data/lib/active_record/associations/through_association.rb +132 -0
  42. data/lib/active_record/associations.rb +1897 -0
  43. data/lib/active_record/asynchronous_queries_tracker.rb +64 -0
  44. data/lib/active_record/attribute_assignment.rb +82 -0
  45. data/lib/active_record/attribute_methods/before_type_cast.rb +106 -0
  46. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  47. data/lib/active_record/attribute_methods/dirty.rb +262 -0
  48. data/lib/active_record/attribute_methods/primary_key.rb +158 -0
  49. data/lib/active_record/attribute_methods/query.rb +50 -0
  50. data/lib/active_record/attribute_methods/read.rb +46 -0
  51. data/lib/active_record/attribute_methods/serialization.rb +232 -0
  52. data/lib/active_record/attribute_methods/time_zone_conversion.rb +94 -0
  53. data/lib/active_record/attribute_methods/write.rb +49 -0
  54. data/lib/active_record/attribute_methods.rb +542 -0
  55. data/lib/active_record/attributes.rb +307 -0
  56. data/lib/active_record/autosave_association.rb +586 -0
  57. data/lib/active_record/base.rb +338 -0
  58. data/lib/active_record/callbacks.rb +452 -0
  59. data/lib/active_record/coders/column_serializer.rb +61 -0
  60. data/lib/active_record/coders/json.rb +15 -0
  61. data/lib/active_record/coders/yaml_column.rb +95 -0
  62. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +290 -0
  63. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +210 -0
  64. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
  65. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +923 -0
  66. data/lib/active_record/connection_adapters/abstract/database_limits.rb +31 -0
  67. data/lib/active_record/connection_adapters/abstract/database_statements.rb +747 -0
  68. data/lib/active_record/connection_adapters/abstract/query_cache.rb +319 -0
  69. data/lib/active_record/connection_adapters/abstract/quoting.rb +239 -0
  70. data/lib/active_record/connection_adapters/abstract/savepoints.rb +24 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +190 -0
  72. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +961 -0
  73. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +106 -0
  74. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1883 -0
  75. data/lib/active_record/connection_adapters/abstract/transaction.rb +676 -0
  76. data/lib/active_record/connection_adapters/abstract_adapter.rb +1218 -0
  77. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +1016 -0
  78. data/lib/active_record/connection_adapters/column.rb +122 -0
  79. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  80. data/lib/active_record/connection_adapters/mysql/column.rb +28 -0
  81. data/lib/active_record/connection_adapters/mysql/database_statements.rb +95 -0
  82. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
  83. data/lib/active_record/connection_adapters/mysql/quoting.rb +114 -0
  84. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +106 -0
  85. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +106 -0
  86. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +97 -0
  87. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +300 -0
  88. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +40 -0
  89. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +96 -0
  90. data/lib/active_record/connection_adapters/mysql2_adapter.rb +196 -0
  91. data/lib/active_record/connection_adapters/pool_config.rb +83 -0
  92. data/lib/active_record/connection_adapters/pool_manager.rb +57 -0
  93. data/lib/active_record/connection_adapters/postgresql/column.rb +82 -0
  94. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +231 -0
  95. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +91 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +53 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +54 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +31 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +20 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +109 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
  110. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  111. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +42 -0
  112. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  113. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +74 -0
  114. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +124 -0
  115. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  116. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  117. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  118. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +125 -0
  119. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +45 -0
  120. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  121. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  122. data/lib/active_record/connection_adapters/postgresql/oid.rb +38 -0
  123. data/lib/active_record/connection_adapters/postgresql/quoting.rb +238 -0
  124. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +71 -0
  125. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +169 -0
  126. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +392 -0
  127. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +127 -0
  128. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +1162 -0
  129. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +44 -0
  130. data/lib/active_record/connection_adapters/postgresql/utils.rb +79 -0
  131. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1182 -0
  132. data/lib/active_record/connection_adapters/schema_cache.rb +478 -0
  133. data/lib/active_record/connection_adapters/sql_type_metadata.rb +45 -0
  134. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  135. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +145 -0
  136. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  137. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +116 -0
  138. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +37 -0
  139. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +39 -0
  140. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +47 -0
  141. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +221 -0
  142. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +843 -0
  143. data/lib/active_record/connection_adapters/statement_pool.rb +67 -0
  144. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +69 -0
  145. data/lib/active_record/connection_adapters/trilogy_adapter.rb +212 -0
  146. data/lib/active_record/connection_adapters.rb +176 -0
  147. data/lib/active_record/connection_handling.rb +413 -0
  148. data/lib/active_record/core.rb +836 -0
  149. data/lib/active_record/counter_cache.rb +230 -0
  150. data/lib/active_record/database_configurations/connection_url_resolver.rb +105 -0
  151. data/lib/active_record/database_configurations/database_config.rb +104 -0
  152. data/lib/active_record/database_configurations/hash_config.rb +172 -0
  153. data/lib/active_record/database_configurations/url_config.rb +78 -0
  154. data/lib/active_record/database_configurations.rb +309 -0
  155. data/lib/active_record/delegated_type.rb +289 -0
  156. data/lib/active_record/deprecator.rb +7 -0
  157. data/lib/active_record/destroy_association_async_job.rb +38 -0
  158. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  159. data/lib/active_record/dynamic_matchers.rb +121 -0
  160. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  161. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  162. data/lib/active_record/encryption/cipher.rb +53 -0
  163. data/lib/active_record/encryption/config.rb +70 -0
  164. data/lib/active_record/encryption/configurable.rb +60 -0
  165. data/lib/active_record/encryption/context.rb +42 -0
  166. data/lib/active_record/encryption/contexts.rb +76 -0
  167. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  168. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  169. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  170. data/lib/active_record/encryption/encrypted_attribute_type.rb +184 -0
  171. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  172. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  173. data/lib/active_record/encryption/encryptor.rb +177 -0
  174. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  175. data/lib/active_record/encryption/errors.rb +15 -0
  176. data/lib/active_record/encryption/extended_deterministic_queries.rb +159 -0
  177. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  178. data/lib/active_record/encryption/key.rb +28 -0
  179. data/lib/active_record/encryption/key_generator.rb +53 -0
  180. data/lib/active_record/encryption/key_provider.rb +46 -0
  181. data/lib/active_record/encryption/message.rb +33 -0
  182. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  183. data/lib/active_record/encryption/message_serializer.rb +96 -0
  184. data/lib/active_record/encryption/null_encryptor.rb +25 -0
  185. data/lib/active_record/encryption/properties.rb +76 -0
  186. data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
  187. data/lib/active_record/encryption/scheme.rb +107 -0
  188. data/lib/active_record/encryption.rb +58 -0
  189. data/lib/active_record/enum.rb +424 -0
  190. data/lib/active_record/errors.rb +614 -0
  191. data/lib/active_record/explain.rb +63 -0
  192. data/lib/active_record/explain_registry.rb +37 -0
  193. data/lib/active_record/explain_subscriber.rb +34 -0
  194. data/lib/active_record/fixture_set/file.rb +89 -0
  195. data/lib/active_record/fixture_set/model_metadata.rb +42 -0
  196. data/lib/active_record/fixture_set/render_context.rb +19 -0
  197. data/lib/active_record/fixture_set/table_row.rb +208 -0
  198. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  199. data/lib/active_record/fixtures.rb +850 -0
  200. data/lib/active_record/future_result.rb +182 -0
  201. data/lib/active_record/gem_version.rb +17 -0
  202. data/lib/active_record/inheritance.rb +366 -0
  203. data/lib/active_record/insert_all.rb +328 -0
  204. data/lib/active_record/integration.rb +209 -0
  205. data/lib/active_record/internal_metadata.rb +164 -0
  206. data/lib/active_record/legacy_yaml_adapter.rb +15 -0
  207. data/lib/active_record/locale/en.yml +48 -0
  208. data/lib/active_record/locking/optimistic.rb +228 -0
  209. data/lib/active_record/locking/pessimistic.rb +102 -0
  210. data/lib/active_record/log_subscriber.rb +149 -0
  211. data/lib/active_record/marshalling.rb +56 -0
  212. data/lib/active_record/message_pack.rb +124 -0
  213. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  214. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  215. data/lib/active_record/middleware/database_selector.rb +87 -0
  216. data/lib/active_record/middleware/shard_selector.rb +62 -0
  217. data/lib/active_record/migration/command_recorder.rb +406 -0
  218. data/lib/active_record/migration/compatibility.rb +490 -0
  219. data/lib/active_record/migration/default_strategy.rb +22 -0
  220. data/lib/active_record/migration/execution_strategy.rb +19 -0
  221. data/lib/active_record/migration/join_table.rb +16 -0
  222. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  223. data/lib/active_record/migration.rb +1626 -0
  224. data/lib/active_record/model_schema.rb +635 -0
  225. data/lib/active_record/nested_attributes.rb +633 -0
  226. data/lib/active_record/no_touching.rb +65 -0
  227. data/lib/active_record/normalization.rb +163 -0
  228. data/lib/active_record/persistence.rb +968 -0
  229. data/lib/active_record/promise.rb +84 -0
  230. data/lib/active_record/query_cache.rb +56 -0
  231. data/lib/active_record/query_logs.rb +247 -0
  232. data/lib/active_record/query_logs_formatter.rb +30 -0
  233. data/lib/active_record/querying.rb +122 -0
  234. data/lib/active_record/railtie.rb +440 -0
  235. data/lib/active_record/railties/console_sandbox.rb +5 -0
  236. data/lib/active_record/railties/controller_runtime.rb +65 -0
  237. data/lib/active_record/railties/databases.rake +641 -0
  238. data/lib/active_record/railties/job_runtime.rb +23 -0
  239. data/lib/active_record/readonly_attributes.rb +66 -0
  240. data/lib/active_record/reflection.rb +1287 -0
  241. data/lib/active_record/relation/batches/batch_enumerator.rb +115 -0
  242. data/lib/active_record/relation/batches.rb +491 -0
  243. data/lib/active_record/relation/calculations.rb +679 -0
  244. data/lib/active_record/relation/delegation.rb +154 -0
  245. data/lib/active_record/relation/finder_methods.rb +661 -0
  246. data/lib/active_record/relation/from_clause.rb +30 -0
  247. data/lib/active_record/relation/merger.rb +192 -0
  248. data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
  249. data/lib/active_record/relation/predicate_builder/association_query_value.rb +76 -0
  250. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  251. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +60 -0
  252. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  253. data/lib/active_record/relation/predicate_builder/relation_handler.rb +24 -0
  254. data/lib/active_record/relation/predicate_builder.rb +181 -0
  255. data/lib/active_record/relation/query_attribute.rb +68 -0
  256. data/lib/active_record/relation/query_methods.rb +2235 -0
  257. data/lib/active_record/relation/record_fetch_warning.rb +52 -0
  258. data/lib/active_record/relation/spawn_methods.rb +78 -0
  259. data/lib/active_record/relation/where_clause.rb +218 -0
  260. data/lib/active_record/relation.rb +1495 -0
  261. data/lib/active_record/result.rb +249 -0
  262. data/lib/active_record/runtime_registry.rb +82 -0
  263. data/lib/active_record/sanitization.rb +254 -0
  264. data/lib/active_record/schema.rb +77 -0
  265. data/lib/active_record/schema_dumper.rb +364 -0
  266. data/lib/active_record/schema_migration.rb +106 -0
  267. data/lib/active_record/scoping/default.rb +205 -0
  268. data/lib/active_record/scoping/named.rb +202 -0
  269. data/lib/active_record/scoping.rb +136 -0
  270. data/lib/active_record/secure_password.rb +60 -0
  271. data/lib/active_record/secure_token.rb +66 -0
  272. data/lib/active_record/serialization.rb +29 -0
  273. data/lib/active_record/signed_id.rb +137 -0
  274. data/lib/active_record/statement_cache.rb +164 -0
  275. data/lib/active_record/store.rb +299 -0
  276. data/lib/active_record/suppressor.rb +59 -0
  277. data/lib/active_record/table_metadata.rb +85 -0
  278. data/lib/active_record/tasks/database_tasks.rb +681 -0
  279. data/lib/active_record/tasks/mysql_database_tasks.rb +120 -0
  280. data/lib/active_record/tasks/postgresql_database_tasks.rb +147 -0
  281. data/lib/active_record/tasks/sqlite_database_tasks.rb +89 -0
  282. data/lib/active_record/test_databases.rb +24 -0
  283. data/lib/active_record/test_fixtures.rb +321 -0
  284. data/lib/active_record/testing/query_assertions.rb +121 -0
  285. data/lib/active_record/timestamp.rb +177 -0
  286. data/lib/active_record/token_for.rb +123 -0
  287. data/lib/active_record/touch_later.rb +70 -0
  288. data/lib/active_record/transaction.rb +132 -0
  289. data/lib/active_record/transactions.rb +523 -0
  290. data/lib/active_record/translation.rb +22 -0
  291. data/lib/active_record/type/adapter_specific_registry.rb +144 -0
  292. data/lib/active_record/type/date.rb +9 -0
  293. data/lib/active_record/type/date_time.rb +9 -0
  294. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  295. data/lib/active_record/type/hash_lookup_type_map.rb +57 -0
  296. data/lib/active_record/type/internal/timezone.rb +22 -0
  297. data/lib/active_record/type/json.rb +30 -0
  298. data/lib/active_record/type/serialized.rb +76 -0
  299. data/lib/active_record/type/text.rb +11 -0
  300. data/lib/active_record/type/time.rb +35 -0
  301. data/lib/active_record/type/type_map.rb +58 -0
  302. data/lib/active_record/type/unsigned_integer.rb +16 -0
  303. data/lib/active_record/type.rb +83 -0
  304. data/lib/active_record/type_caster/connection.rb +33 -0
  305. data/lib/active_record/type_caster/map.rb +23 -0
  306. data/lib/active_record/type_caster.rb +9 -0
  307. data/lib/active_record/validations/absence.rb +25 -0
  308. data/lib/active_record/validations/associated.rb +65 -0
  309. data/lib/active_record/validations/length.rb +26 -0
  310. data/lib/active_record/validations/numericality.rb +36 -0
  311. data/lib/active_record/validations/presence.rb +45 -0
  312. data/lib/active_record/validations/uniqueness.rb +295 -0
  313. data/lib/active_record/validations.rb +101 -0
  314. data/lib/active_record/version.rb +10 -0
  315. data/lib/active_record.rb +616 -0
  316. data/lib/arel/alias_predication.rb +9 -0
  317. data/lib/arel/attributes/attribute.rb +33 -0
  318. data/lib/arel/collectors/bind.rb +31 -0
  319. data/lib/arel/collectors/composite.rb +46 -0
  320. data/lib/arel/collectors/plain_string.rb +20 -0
  321. data/lib/arel/collectors/sql_string.rb +27 -0
  322. data/lib/arel/collectors/substitute_binds.rb +35 -0
  323. data/lib/arel/crud.rb +48 -0
  324. data/lib/arel/delete_manager.rb +32 -0
  325. data/lib/arel/errors.rb +19 -0
  326. data/lib/arel/expressions.rb +29 -0
  327. data/lib/arel/factory_methods.rb +53 -0
  328. data/lib/arel/filter_predications.rb +9 -0
  329. data/lib/arel/insert_manager.rb +48 -0
  330. data/lib/arel/math.rb +45 -0
  331. data/lib/arel/nodes/ascending.rb +23 -0
  332. data/lib/arel/nodes/binary.rb +125 -0
  333. data/lib/arel/nodes/bind_param.rb +44 -0
  334. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  335. data/lib/arel/nodes/case.rb +55 -0
  336. data/lib/arel/nodes/casted.rb +62 -0
  337. data/lib/arel/nodes/comment.rb +29 -0
  338. data/lib/arel/nodes/count.rb +12 -0
  339. data/lib/arel/nodes/cte.rb +36 -0
  340. data/lib/arel/nodes/delete_statement.rb +44 -0
  341. data/lib/arel/nodes/descending.rb +23 -0
  342. data/lib/arel/nodes/equality.rb +15 -0
  343. data/lib/arel/nodes/extract.rb +24 -0
  344. data/lib/arel/nodes/false.rb +16 -0
  345. data/lib/arel/nodes/filter.rb +10 -0
  346. data/lib/arel/nodes/fragments.rb +35 -0
  347. data/lib/arel/nodes/full_outer_join.rb +8 -0
  348. data/lib/arel/nodes/function.rb +45 -0
  349. data/lib/arel/nodes/grouping.rb +11 -0
  350. data/lib/arel/nodes/homogeneous_in.rb +68 -0
  351. data/lib/arel/nodes/in.rb +15 -0
  352. data/lib/arel/nodes/infix_operation.rb +92 -0
  353. data/lib/arel/nodes/inner_join.rb +8 -0
  354. data/lib/arel/nodes/insert_statement.rb +37 -0
  355. data/lib/arel/nodes/join_source.rb +20 -0
  356. data/lib/arel/nodes/leading_join.rb +8 -0
  357. data/lib/arel/nodes/matches.rb +18 -0
  358. data/lib/arel/nodes/named_function.rb +23 -0
  359. data/lib/arel/nodes/nary.rb +39 -0
  360. data/lib/arel/nodes/node.rb +161 -0
  361. data/lib/arel/nodes/node_expression.rb +13 -0
  362. data/lib/arel/nodes/ordering.rb +27 -0
  363. data/lib/arel/nodes/outer_join.rb +8 -0
  364. data/lib/arel/nodes/over.rb +15 -0
  365. data/lib/arel/nodes/regexp.rb +16 -0
  366. data/lib/arel/nodes/right_outer_join.rb +8 -0
  367. data/lib/arel/nodes/select_core.rb +67 -0
  368. data/lib/arel/nodes/select_statement.rb +41 -0
  369. data/lib/arel/nodes/sql_literal.rb +32 -0
  370. data/lib/arel/nodes/string_join.rb +11 -0
  371. data/lib/arel/nodes/table_alias.rb +35 -0
  372. data/lib/arel/nodes/terminal.rb +16 -0
  373. data/lib/arel/nodes/true.rb +16 -0
  374. data/lib/arel/nodes/unary.rb +44 -0
  375. data/lib/arel/nodes/unary_operation.rb +20 -0
  376. data/lib/arel/nodes/unqualified_column.rb +22 -0
  377. data/lib/arel/nodes/update_statement.rb +46 -0
  378. data/lib/arel/nodes/values_list.rb +9 -0
  379. data/lib/arel/nodes/window.rb +126 -0
  380. data/lib/arel/nodes/with.rb +11 -0
  381. data/lib/arel/nodes.rb +75 -0
  382. data/lib/arel/order_predications.rb +13 -0
  383. data/lib/arel/predications.rb +260 -0
  384. data/lib/arel/select_manager.rb +276 -0
  385. data/lib/arel/table.rb +121 -0
  386. data/lib/arel/tree_manager.rb +65 -0
  387. data/lib/arel/update_manager.rb +49 -0
  388. data/lib/arel/visitors/dot.rb +299 -0
  389. data/lib/arel/visitors/mysql.rb +111 -0
  390. data/lib/arel/visitors/postgresql.rb +99 -0
  391. data/lib/arel/visitors/sqlite.rb +38 -0
  392. data/lib/arel/visitors/to_sql.rb +1033 -0
  393. data/lib/arel/visitors/visitor.rb +45 -0
  394. data/lib/arel/visitors.rb +13 -0
  395. data/lib/arel/window_predications.rb +9 -0
  396. data/lib/arel.rb +73 -0
  397. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  398. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
  399. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  400. data/lib/rails/generators/active_record/migration/migration_generator.rb +76 -0
  401. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +29 -0
  402. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +48 -0
  403. data/lib/rails/generators/active_record/migration.rb +54 -0
  404. data/lib/rails/generators/active_record/model/USAGE +113 -0
  405. data/lib/rails/generators/active_record/model/model_generator.rb +94 -0
  406. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  407. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  408. data/lib/rails/generators/active_record/model/templates/module.rb.tt +7 -0
  409. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  410. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  411. data/lib/rails/generators/active_record.rb +19 -0
  412. metadata +505 -0
@@ -0,0 +1,923 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "concurrent/map"
4
+ require "monitor"
5
+
6
+ require "active_record/connection_adapters/abstract/connection_pool/queue"
7
+ require "active_record/connection_adapters/abstract/connection_pool/reaper"
8
+
9
+ module ActiveRecord
10
+ module ConnectionAdapters
11
+ module AbstractPool # :nodoc:
12
+ end
13
+
14
+ class NullPool # :nodoc:
15
+ include ConnectionAdapters::AbstractPool
16
+
17
+ class NullConfig # :nodoc:
18
+ def method_missing(...)
19
+ nil
20
+ end
21
+ end
22
+ NULL_CONFIG = NullConfig.new # :nodoc:
23
+
24
+ def initialize
25
+ super()
26
+ @mutex = Mutex.new
27
+ @server_version = nil
28
+ end
29
+
30
+ def server_version(connection) # :nodoc:
31
+ @server_version || @mutex.synchronize { @server_version ||= connection.get_database_version }
32
+ end
33
+
34
+ def schema_reflection
35
+ SchemaReflection.new(nil)
36
+ end
37
+
38
+ def schema_cache; end
39
+ def connection_class; end
40
+ def checkin(_); end
41
+ def remove(_); end
42
+ def async_executor; end
43
+
44
+ def db_config
45
+ NULL_CONFIG
46
+ end
47
+
48
+ def dirties_query_cache
49
+ true
50
+ end
51
+ end
52
+
53
+ # = Active Record Connection Pool
54
+ #
55
+ # Connection pool base class for managing Active Record database
56
+ # connections.
57
+ #
58
+ # == Introduction
59
+ #
60
+ # A connection pool synchronizes thread access to a limited number of
61
+ # database connections. The basic idea is that each thread checks out a
62
+ # database connection from the pool, uses that connection, and checks the
63
+ # connection back in. ConnectionPool is completely thread-safe, and will
64
+ # ensure that a connection cannot be used by two threads at the same time,
65
+ # as long as ConnectionPool's contract is correctly followed. It will also
66
+ # handle cases in which there are more threads than connections: if all
67
+ # connections have been checked out, and a thread tries to checkout a
68
+ # connection anyway, then ConnectionPool will wait until some other thread
69
+ # has checked in a connection, or the +checkout_timeout+ has expired.
70
+ #
71
+ # == Obtaining (checking out) a connection
72
+ #
73
+ # Connections can be obtained and used from a connection pool in several
74
+ # ways:
75
+ #
76
+ # 1. Simply use {ActiveRecord::Base.lease_connection}[rdoc-ref:ConnectionHandling#lease_connection].
77
+ # When you're done with the connection(s) and wish it to be returned to the pool, you call
78
+ # {ActiveRecord::Base.connection_handler.clear_active_connections!}[rdoc-ref:ConnectionAdapters::ConnectionHandler#clear_active_connections!].
79
+ # This is the default behavior for Active Record when used in conjunction with
80
+ # Action Pack's request handling cycle.
81
+ # 2. Manually check out a connection from the pool with
82
+ # {ActiveRecord::Base.connection_pool.checkout}[rdoc-ref:#checkout]. You are responsible for
83
+ # returning this connection to the pool when finished by calling
84
+ # {ActiveRecord::Base.connection_pool.checkin(connection)}[rdoc-ref:#checkin].
85
+ # 3. Use {ActiveRecord::Base.connection_pool.with_connection(&block)}[rdoc-ref:#with_connection], which
86
+ # obtains a connection, yields it as the sole argument to the block,
87
+ # and returns it to the pool after the block completes.
88
+ #
89
+ # Connections in the pool are actually AbstractAdapter objects (or objects
90
+ # compatible with AbstractAdapter's interface).
91
+ #
92
+ # While a thread has a connection checked out from the pool using one of the
93
+ # above three methods, that connection will automatically be the one used
94
+ # by ActiveRecord queries executing on that thread. It is not required to
95
+ # explicitly pass the checked out connection to \Rails models or queries, for
96
+ # example.
97
+ #
98
+ # == Options
99
+ #
100
+ # There are several connection-pooling-related options that you can add to
101
+ # your database connection configuration:
102
+ #
103
+ # * +pool+: maximum number of connections the pool may manage (default 5).
104
+ # * +idle_timeout+: number of seconds that a connection will be kept
105
+ # unused in the pool before it is automatically disconnected (default
106
+ # 300 seconds). Set this to zero to keep connections forever.
107
+ # * +checkout_timeout+: number of seconds to wait for a connection to
108
+ # become available before giving up and raising a timeout error (default
109
+ # 5 seconds).
110
+ #
111
+ #--
112
+ # Synchronization policy:
113
+ # * all public methods can be called outside +synchronize+
114
+ # * access to these instance variables needs to be in +synchronize+:
115
+ # * @connections
116
+ # * @now_connecting
117
+ # * private methods that require being called in a +synchronize+ blocks
118
+ # are now explicitly documented
119
+ class ConnectionPool
120
+ class WeakThreadKeyMap # :nodoc:
121
+ # FIXME: On 3.3 we could use ObjectSpace::WeakKeyMap
122
+ # but it currently cause GC crashes: https://github.com/byroot/rails/pull/3
123
+ def initialize
124
+ @map = {}
125
+ end
126
+
127
+ def clear
128
+ @map.clear
129
+ end
130
+
131
+ def [](key)
132
+ @map[key]
133
+ end
134
+
135
+ def []=(key, value)
136
+ @map.select! { |c, _| c.alive? }
137
+ @map[key] = value
138
+ end
139
+ end
140
+
141
+ class Lease # :nodoc:
142
+ attr_accessor :connection, :sticky
143
+
144
+ def initialize
145
+ @connection = nil
146
+ @sticky = nil
147
+ end
148
+
149
+ def release
150
+ conn = @connection
151
+ @connection = nil
152
+ @sticky = nil
153
+ conn
154
+ end
155
+
156
+ def clear(connection)
157
+ if @connection == connection
158
+ @connection = nil
159
+ @sticky = nil
160
+ true
161
+ else
162
+ false
163
+ end
164
+ end
165
+ end
166
+
167
+ class LeaseRegistry # :nodoc:
168
+ def initialize
169
+ @mutex = Mutex.new
170
+ @map = WeakThreadKeyMap.new
171
+ end
172
+
173
+ def [](context)
174
+ @mutex.synchronize do
175
+ @map[context] ||= Lease.new
176
+ end
177
+ end
178
+
179
+ def clear
180
+ @mutex.synchronize do
181
+ @map.clear
182
+ end
183
+ end
184
+ end
185
+
186
+ include MonitorMixin
187
+ prepend QueryCache::ConnectionPoolConfiguration
188
+ include ConnectionAdapters::AbstractPool
189
+
190
+ attr_accessor :automatic_reconnect, :checkout_timeout
191
+ attr_reader :db_config, :size, :reaper, :pool_config, :async_executor, :role, :shard
192
+
193
+ delegate :schema_reflection, :server_version, to: :pool_config
194
+
195
+ # Creates a new ConnectionPool object. +pool_config+ is a PoolConfig
196
+ # object which describes database connection information (e.g. adapter,
197
+ # host name, username, password, etc), as well as the maximum size for
198
+ # this ConnectionPool.
199
+ #
200
+ # The default ConnectionPool maximum size is 5.
201
+ def initialize(pool_config)
202
+ super()
203
+
204
+ @pool_config = pool_config
205
+ @db_config = pool_config.db_config
206
+ @role = pool_config.role
207
+ @shard = pool_config.shard
208
+
209
+ @checkout_timeout = db_config.checkout_timeout
210
+ @idle_timeout = db_config.idle_timeout
211
+ @size = db_config.pool
212
+
213
+ # This variable tracks the cache of threads mapped to reserved connections, with the
214
+ # sole purpose of speeding up the +connection+ method. It is not the authoritative
215
+ # registry of which thread owns which connection. Connection ownership is tracked by
216
+ # the +connection.owner+ attr on each +connection+ instance.
217
+ # The invariant works like this: if there is mapping of <tt>thread => conn</tt>,
218
+ # then that +thread+ does indeed own that +conn+. However, an absence of such
219
+ # mapping does not mean that the +thread+ doesn't own the said connection. In
220
+ # that case +conn.owner+ attr should be consulted.
221
+ # Access and modification of <tt>@leases</tt> does not require
222
+ # synchronization.
223
+ @leases = LeaseRegistry.new
224
+
225
+ @connections = []
226
+ @automatic_reconnect = true
227
+
228
+ # Connection pool allows for concurrent (outside the main +synchronize+ section)
229
+ # establishment of new connections. This variable tracks the number of threads
230
+ # currently in the process of independently establishing connections to the DB.
231
+ @now_connecting = 0
232
+
233
+ @threads_blocking_new_connections = 0
234
+
235
+ @available = ConnectionLeasingQueue.new self
236
+ @pinned_connection = nil
237
+ @pinned_connections_depth = 0
238
+
239
+ @async_executor = build_async_executor
240
+
241
+ @schema_cache = nil
242
+
243
+ @reaper = Reaper.new(self, db_config.reaping_frequency)
244
+ @reaper.run
245
+ end
246
+
247
+ def inspect # :nodoc:
248
+ name_field = " name=#{db_config.name.inspect}" unless db_config.name == "primary"
249
+ shard_field = " shard=#{@shard.inspect}" unless @shard == :default
250
+
251
+ "#<#{self.class.name} env_name=#{db_config.env_name.inspect}#{name_field} role=#{role.inspect}#{shard_field}>"
252
+ end
253
+
254
+ def schema_cache
255
+ @schema_cache ||= BoundSchemaReflection.new(schema_reflection, self)
256
+ end
257
+
258
+ def schema_reflection=(schema_reflection)
259
+ pool_config.schema_reflection = schema_reflection
260
+ @schema_cache = nil
261
+ end
262
+
263
+ def migration_context # :nodoc:
264
+ MigrationContext.new(migrations_paths, schema_migration, internal_metadata)
265
+ end
266
+
267
+ def migrations_paths # :nodoc:
268
+ db_config.migrations_paths || Migrator.migrations_paths
269
+ end
270
+
271
+ def schema_migration # :nodoc:
272
+ SchemaMigration.new(self)
273
+ end
274
+
275
+ def internal_metadata # :nodoc:
276
+ InternalMetadata.new(self)
277
+ end
278
+
279
+ # Retrieve the connection associated with the current thread, or call
280
+ # #checkout to obtain one if necessary.
281
+ #
282
+ # #lease_connection can be called any number of times; the connection is
283
+ # held in a cache keyed by a thread.
284
+ def lease_connection
285
+ lease = connection_lease
286
+ lease.sticky = true
287
+ lease.connection ||= checkout
288
+ end
289
+
290
+ def permanent_lease? # :nodoc:
291
+ connection_lease.sticky.nil?
292
+ end
293
+
294
+ def connection
295
+ ActiveRecord.deprecator.warn(<<~MSG)
296
+ ActiveRecord::ConnectionAdapters::ConnectionPool#connection is deprecated
297
+ and will be removed in Rails 8.0. Use #lease_connection instead.
298
+ MSG
299
+ lease_connection
300
+ end
301
+
302
+ def pin_connection!(lock_thread) # :nodoc:
303
+ @pinned_connection ||= (connection_lease&.connection || checkout)
304
+ @pinned_connections_depth += 1
305
+
306
+ # Any leased connection must be in @connections otherwise
307
+ # some methods like #connected? won't behave correctly
308
+ unless @connections.include?(@pinned_connection)
309
+ @connections << @pinned_connection
310
+ end
311
+
312
+ @pinned_connection.lock_thread = ActiveSupport::IsolatedExecutionState.context if lock_thread
313
+ @pinned_connection.verify! # eagerly validate the connection
314
+ @pinned_connection.begin_transaction joinable: false, _lazy: false
315
+ end
316
+
317
+ def unpin_connection! # :nodoc:
318
+ raise "There isn't a pinned connection #{object_id}" unless @pinned_connection
319
+
320
+ clean = true
321
+ @pinned_connection.lock.synchronize do
322
+ @pinned_connections_depth -= 1
323
+ connection = @pinned_connection
324
+ @pinned_connection = nil if @pinned_connections_depth.zero?
325
+
326
+ if connection.transaction_open?
327
+ connection.rollback_transaction
328
+ else
329
+ # Something committed or rolled back the transaction
330
+ clean = false
331
+ connection.reset!
332
+ end
333
+
334
+ if @pinned_connection.nil?
335
+ connection.lock_thread = nil
336
+ checkin(connection)
337
+ end
338
+ end
339
+
340
+ clean
341
+ end
342
+
343
+ def connection_class # :nodoc:
344
+ pool_config.connection_class
345
+ end
346
+
347
+ # Returns true if there is an open connection being used for the current thread.
348
+ #
349
+ # This method only works for connections that have been obtained through
350
+ # #lease_connection or #with_connection methods. Connections obtained through
351
+ # #checkout will not be detected by #active_connection?
352
+ def active_connection?
353
+ connection_lease.connection
354
+ end
355
+ alias_method :active_connection, :active_connection? # :nodoc:
356
+
357
+ # Signal that the thread is finished with the current connection.
358
+ # #release_connection releases the connection-thread association
359
+ # and returns the connection to the pool.
360
+ #
361
+ # This method only works for connections that have been obtained through
362
+ # #lease_connection or #with_connection methods, connections obtained through
363
+ # #checkout will not be automatically released.
364
+ def release_connection(existing_lease = nil)
365
+ if conn = connection_lease.release
366
+ checkin conn
367
+ return true
368
+ end
369
+ false
370
+ end
371
+
372
+ # Yields a connection from the connection pool to the block. If no connection
373
+ # is already checked out by the current thread, a connection will be checked
374
+ # out from the pool, yielded to the block, and then returned to the pool when
375
+ # the block is finished. If a connection has already been checked out on the
376
+ # current thread, such as via #lease_connection or #with_connection, that existing
377
+ # connection will be the one yielded and it will not be returned to the pool
378
+ # automatically at the end of the block; it is expected that such an existing
379
+ # connection will be properly returned to the pool by the code that checked
380
+ # it out.
381
+ def with_connection(prevent_permanent_checkout: false)
382
+ lease = connection_lease
383
+ sticky_was = lease.sticky
384
+ lease.sticky = false if prevent_permanent_checkout
385
+
386
+ if lease.connection
387
+ begin
388
+ yield lease.connection
389
+ ensure
390
+ lease.sticky = sticky_was if prevent_permanent_checkout && !sticky_was
391
+ end
392
+ else
393
+ begin
394
+ yield lease.connection = checkout
395
+ ensure
396
+ lease.sticky = sticky_was if prevent_permanent_checkout && !sticky_was
397
+ release_connection(lease) unless lease.sticky
398
+ end
399
+ end
400
+ end
401
+
402
+ # Returns true if a connection has already been opened.
403
+ def connected?
404
+ synchronize { @connections.any?(&:connected?) }
405
+ end
406
+
407
+ # Returns an array containing the connections currently in the pool.
408
+ # Access to the array does not require synchronization on the pool because
409
+ # the array is newly created and not retained by the pool.
410
+ #
411
+ # However; this method bypasses the ConnectionPool's thread-safe connection
412
+ # access pattern. A returned connection may be owned by another thread,
413
+ # unowned, or by happen-stance owned by the calling thread.
414
+ #
415
+ # Calling methods on a connection without ownership is subject to the
416
+ # thread-safety guarantees of the underlying method. Many of the methods
417
+ # on connection adapter classes are inherently multi-thread unsafe.
418
+ def connections
419
+ synchronize { @connections.dup }
420
+ end
421
+
422
+ # Disconnects all connections in the pool, and clears the pool.
423
+ #
424
+ # Raises:
425
+ # - ActiveRecord::ExclusiveConnectionTimeoutError if unable to gain ownership of all
426
+ # connections in the pool within a timeout interval (default duration is
427
+ # <tt>spec.db_config.checkout_timeout * 2</tt> seconds).
428
+ def disconnect(raise_on_acquisition_timeout = true)
429
+ with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
430
+ synchronize do
431
+ @connections.each do |conn|
432
+ if conn.in_use?
433
+ conn.steal!
434
+ checkin conn
435
+ end
436
+ conn.disconnect!
437
+ end
438
+ @connections = []
439
+ @leases.clear
440
+ @available.clear
441
+ end
442
+ end
443
+ end
444
+
445
+ # Disconnects all connections in the pool, and clears the pool.
446
+ #
447
+ # The pool first tries to gain ownership of all connections. If unable to
448
+ # do so within a timeout interval (default duration is
449
+ # <tt>spec.db_config.checkout_timeout * 2</tt> seconds), then the pool is forcefully
450
+ # disconnected without any regard for other connection owning threads.
451
+ def disconnect!
452
+ disconnect(false)
453
+ end
454
+
455
+ # Discards all connections in the pool (even if they're currently
456
+ # leased!), along with the pool itself. Any further interaction with the
457
+ # pool (except #spec and #schema_cache) is undefined.
458
+ #
459
+ # See AbstractAdapter#discard!
460
+ def discard! # :nodoc:
461
+ synchronize do
462
+ return if self.discarded?
463
+ @connections.each do |conn|
464
+ conn.discard!
465
+ end
466
+ @connections = @available = @leases = nil
467
+ end
468
+ end
469
+
470
+ def discarded? # :nodoc:
471
+ @connections.nil?
472
+ end
473
+
474
+ # Clears the cache which maps classes and re-connects connections that
475
+ # require reloading.
476
+ #
477
+ # Raises:
478
+ # - ActiveRecord::ExclusiveConnectionTimeoutError if unable to gain ownership of all
479
+ # connections in the pool within a timeout interval (default duration is
480
+ # <tt>spec.db_config.checkout_timeout * 2</tt> seconds).
481
+ def clear_reloadable_connections(raise_on_acquisition_timeout = true)
482
+ with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
483
+ synchronize do
484
+ @connections.each do |conn|
485
+ if conn.in_use?
486
+ conn.steal!
487
+ checkin conn
488
+ end
489
+ conn.disconnect! if conn.requires_reloading?
490
+ end
491
+ @connections.delete_if(&:requires_reloading?)
492
+ @available.clear
493
+ end
494
+ end
495
+ end
496
+
497
+ # Clears the cache which maps classes and re-connects connections that
498
+ # require reloading.
499
+ #
500
+ # The pool first tries to gain ownership of all connections. If unable to
501
+ # do so within a timeout interval (default duration is
502
+ # <tt>spec.db_config.checkout_timeout * 2</tt> seconds), then the pool forcefully
503
+ # clears the cache and reloads connections without any regard for other
504
+ # connection owning threads.
505
+ def clear_reloadable_connections!
506
+ clear_reloadable_connections(false)
507
+ end
508
+
509
+ # Check-out a database connection from the pool, indicating that you want
510
+ # to use it. You should call #checkin when you no longer need this.
511
+ #
512
+ # This is done by either returning and leasing existing connection, or by
513
+ # creating a new connection and leasing it.
514
+ #
515
+ # If all connections are leased and the pool is at capacity (meaning the
516
+ # number of currently leased connections is greater than or equal to the
517
+ # size limit set), an ActiveRecord::ConnectionTimeoutError exception will be raised.
518
+ #
519
+ # Returns: an AbstractAdapter object.
520
+ #
521
+ # Raises:
522
+ # - ActiveRecord::ConnectionTimeoutError no connection can be obtained from the pool.
523
+ def checkout(checkout_timeout = @checkout_timeout)
524
+ if @pinned_connection
525
+ @pinned_connection.lock.synchronize do
526
+ synchronize do
527
+ @pinned_connection.verify!
528
+ # Any leased connection must be in @connections otherwise
529
+ # some methods like #connected? won't behave correctly
530
+ unless @connections.include?(@pinned_connection)
531
+ @connections << @pinned_connection
532
+ end
533
+ end
534
+ end
535
+ @pinned_connection
536
+ else
537
+ checkout_and_verify(acquire_connection(checkout_timeout))
538
+ end
539
+ end
540
+
541
+ # Check-in a database connection back into the pool, indicating that you
542
+ # no longer need this connection.
543
+ #
544
+ # +conn+: an AbstractAdapter object, which was obtained by earlier by
545
+ # calling #checkout on this pool.
546
+ def checkin(conn)
547
+ return if @pinned_connection.equal?(conn)
548
+
549
+ conn.lock.synchronize do
550
+ synchronize do
551
+ connection_lease.clear(conn)
552
+
553
+ conn._run_checkin_callbacks do
554
+ conn.expire
555
+ end
556
+
557
+ @available.add conn
558
+ end
559
+ end
560
+ end
561
+
562
+ # Remove a connection from the connection pool. The connection will
563
+ # remain open and active but will no longer be managed by this pool.
564
+ def remove(conn)
565
+ needs_new_connection = false
566
+
567
+ synchronize do
568
+ remove_connection_from_thread_cache conn
569
+
570
+ @connections.delete conn
571
+ @available.delete conn
572
+
573
+ # @available.any_waiting? => true means that prior to removing this
574
+ # conn, the pool was at its max size (@connections.size == @size).
575
+ # This would mean that any threads stuck waiting in the queue wouldn't
576
+ # know they could checkout_new_connection, so let's do it for them.
577
+ # Because condition-wait loop is encapsulated in the Queue class
578
+ # (that in turn is oblivious to ConnectionPool implementation), threads
579
+ # that are "stuck" there are helpless. They have no way of creating
580
+ # new connections and are completely reliant on us feeding available
581
+ # connections into the Queue.
582
+ needs_new_connection = @available.any_waiting?
583
+ end
584
+
585
+ # This is intentionally done outside of the synchronized section as we
586
+ # would like not to hold the main mutex while checking out new connections.
587
+ # Thus there is some chance that needs_new_connection information is now
588
+ # stale, we can live with that (bulk_make_new_connections will make
589
+ # sure not to exceed the pool's @size limit).
590
+ bulk_make_new_connections(1) if needs_new_connection
591
+ end
592
+
593
+ # Recover lost connections for the pool. A lost connection can occur if
594
+ # a programmer forgets to checkin a connection at the end of a thread
595
+ # or a thread dies unexpectedly.
596
+ def reap
597
+ stale_connections = synchronize do
598
+ return if self.discarded?
599
+ @connections.select do |conn|
600
+ conn.in_use? && !conn.owner.alive?
601
+ end.each do |conn|
602
+ conn.steal!
603
+ end
604
+ end
605
+
606
+ stale_connections.each do |conn|
607
+ if conn.active?
608
+ conn.reset!
609
+ checkin conn
610
+ else
611
+ remove conn
612
+ end
613
+ end
614
+ end
615
+
616
+ # Disconnect all connections that have been idle for at least
617
+ # +minimum_idle+ seconds. Connections currently checked out, or that were
618
+ # checked in less than +minimum_idle+ seconds ago, are unaffected.
619
+ def flush(minimum_idle = @idle_timeout)
620
+ return if minimum_idle.nil?
621
+
622
+ idle_connections = synchronize do
623
+ return if self.discarded?
624
+ @connections.select do |conn|
625
+ !conn.in_use? && conn.seconds_idle >= minimum_idle
626
+ end.each do |conn|
627
+ conn.lease
628
+
629
+ @available.delete conn
630
+ @connections.delete conn
631
+ end
632
+ end
633
+
634
+ idle_connections.each do |conn|
635
+ conn.disconnect!
636
+ end
637
+ end
638
+
639
+ # Disconnect all currently idle connections. Connections currently checked
640
+ # out are unaffected.
641
+ def flush!
642
+ reap
643
+ flush(-1)
644
+ end
645
+
646
+ def num_waiting_in_queue # :nodoc:
647
+ @available.num_waiting
648
+ end
649
+
650
+ # Returns the connection pool's usage statistic.
651
+ #
652
+ # ActiveRecord::Base.connection_pool.stat # => { size: 15, connections: 1, busy: 1, dead: 0, idle: 0, waiting: 0, checkout_timeout: 5 }
653
+ def stat
654
+ synchronize do
655
+ {
656
+ size: size,
657
+ connections: @connections.size,
658
+ busy: @connections.count { |c| c.in_use? && c.owner.alive? },
659
+ dead: @connections.count { |c| c.in_use? && !c.owner.alive? },
660
+ idle: @connections.count { |c| !c.in_use? },
661
+ waiting: num_waiting_in_queue,
662
+ checkout_timeout: checkout_timeout
663
+ }
664
+ end
665
+ end
666
+
667
+ def schedule_query(future_result) # :nodoc:
668
+ @async_executor.post { future_result.execute_or_skip }
669
+ Thread.pass
670
+ end
671
+
672
+ private
673
+ def connection_lease
674
+ @leases[ActiveSupport::IsolatedExecutionState.context]
675
+ end
676
+
677
+ def build_async_executor
678
+ case ActiveRecord.async_query_executor
679
+ when :multi_thread_pool
680
+ if @db_config.max_threads > 0
681
+ Concurrent::ThreadPoolExecutor.new(
682
+ min_threads: @db_config.min_threads,
683
+ max_threads: @db_config.max_threads,
684
+ max_queue: @db_config.max_queue,
685
+ fallback_policy: :caller_runs
686
+ )
687
+ end
688
+ when :global_thread_pool
689
+ ActiveRecord.global_thread_pool_async_query_executor
690
+ end
691
+ end
692
+
693
+ #--
694
+ # this is unfortunately not concurrent
695
+ def bulk_make_new_connections(num_new_conns_needed)
696
+ num_new_conns_needed.times do
697
+ # try_to_checkout_new_connection will not exceed pool's @size limit
698
+ if new_conn = try_to_checkout_new_connection
699
+ # make the new_conn available to the starving threads stuck @available Queue
700
+ checkin(new_conn)
701
+ end
702
+ end
703
+ end
704
+
705
+ # Take control of all existing connections so a "group" action such as
706
+ # reload/disconnect can be performed safely. It is no longer enough to
707
+ # wrap it in +synchronize+ because some pool's actions are allowed
708
+ # to be performed outside of the main +synchronize+ block.
709
+ def with_exclusively_acquired_all_connections(raise_on_acquisition_timeout = true)
710
+ with_new_connections_blocked do
711
+ attempt_to_checkout_all_existing_connections(raise_on_acquisition_timeout)
712
+ yield
713
+ end
714
+ end
715
+
716
+ def attempt_to_checkout_all_existing_connections(raise_on_acquisition_timeout = true)
717
+ collected_conns = synchronize do
718
+ reap # No need to wait for dead owners
719
+
720
+ # account for our own connections
721
+ @connections.select { |conn| conn.owner == ActiveSupport::IsolatedExecutionState.context }
722
+ end
723
+
724
+ newly_checked_out = []
725
+ timeout_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) + (@checkout_timeout * 2)
726
+
727
+ @available.with_a_bias_for(ActiveSupport::IsolatedExecutionState.context) do
728
+ loop do
729
+ synchronize do
730
+ return if collected_conns.size == @connections.size && @now_connecting == 0
731
+
732
+ remaining_timeout = timeout_time - Process.clock_gettime(Process::CLOCK_MONOTONIC)
733
+ remaining_timeout = 0 if remaining_timeout < 0
734
+ conn = checkout_for_exclusive_access(remaining_timeout)
735
+ collected_conns << conn
736
+ newly_checked_out << conn
737
+ end
738
+ end
739
+ end
740
+ rescue ExclusiveConnectionTimeoutError
741
+ # <tt>raise_on_acquisition_timeout == false</tt> means we are directed to ignore any
742
+ # timeouts and are expected to just give up: we've obtained as many connections
743
+ # as possible, note that in a case like that we don't return any of the
744
+ # +newly_checked_out+ connections.
745
+
746
+ if raise_on_acquisition_timeout
747
+ release_newly_checked_out = true
748
+ raise
749
+ end
750
+ rescue Exception # if something else went wrong
751
+ # this can't be a "naked" rescue, because we have should return conns
752
+ # even for non-StandardErrors
753
+ release_newly_checked_out = true
754
+ raise
755
+ ensure
756
+ if release_newly_checked_out && newly_checked_out
757
+ # releasing only those conns that were checked out in this method, conns
758
+ # checked outside this method (before it was called) are not for us to release
759
+ newly_checked_out.each { |conn| checkin(conn) }
760
+ end
761
+ end
762
+
763
+ #--
764
+ # Must be called in a synchronize block.
765
+ def checkout_for_exclusive_access(checkout_timeout)
766
+ checkout(checkout_timeout)
767
+ rescue ConnectionTimeoutError
768
+ # this block can't be easily moved into attempt_to_checkout_all_existing_connections's
769
+ # rescue block, because doing so would put it outside of synchronize section, without
770
+ # being in a critical section thread_report might become inaccurate
771
+ msg = +"could not obtain ownership of all database connections in #{checkout_timeout} seconds"
772
+
773
+ thread_report = []
774
+ @connections.each do |conn|
775
+ unless conn.owner == ActiveSupport::IsolatedExecutionState.context
776
+ thread_report << "#{conn} is owned by #{conn.owner}"
777
+ end
778
+ end
779
+
780
+ msg << " (#{thread_report.join(', ')})" if thread_report.any?
781
+
782
+ raise ExclusiveConnectionTimeoutError.new(msg, connection_pool: self)
783
+ end
784
+
785
+ def with_new_connections_blocked
786
+ synchronize do
787
+ @threads_blocking_new_connections += 1
788
+ end
789
+
790
+ yield
791
+ ensure
792
+ num_new_conns_required = 0
793
+
794
+ synchronize do
795
+ @threads_blocking_new_connections -= 1
796
+
797
+ if @threads_blocking_new_connections.zero?
798
+ @available.clear
799
+
800
+ num_new_conns_required = num_waiting_in_queue
801
+
802
+ @connections.each do |conn|
803
+ next if conn.in_use?
804
+
805
+ @available.add conn
806
+ num_new_conns_required -= 1
807
+ end
808
+ end
809
+ end
810
+
811
+ bulk_make_new_connections(num_new_conns_required) if num_new_conns_required > 0
812
+ end
813
+
814
+ # Acquire a connection by one of 1) immediately removing one
815
+ # from the queue of available connections, 2) creating a new
816
+ # connection if the pool is not at capacity, 3) waiting on the
817
+ # queue for a connection to become available.
818
+ #
819
+ # Raises:
820
+ # - ActiveRecord::ConnectionTimeoutError if a connection could not be acquired
821
+ #
822
+ #--
823
+ # Implementation detail: the connection returned by +acquire_connection+
824
+ # will already be "+connection.lease+ -ed" to the current thread.
825
+ def acquire_connection(checkout_timeout)
826
+ # NOTE: we rely on <tt>@available.poll</tt> and +try_to_checkout_new_connection+ to
827
+ # +conn.lease+ the returned connection (and to do this in a +synchronized+
828
+ # section). This is not the cleanest implementation, as ideally we would
829
+ # <tt>synchronize { conn.lease }</tt> in this method, but by leaving it to <tt>@available.poll</tt>
830
+ # and +try_to_checkout_new_connection+ we can piggyback on +synchronize+ sections
831
+ # of the said methods and avoid an additional +synchronize+ overhead.
832
+ if conn = @available.poll || try_to_checkout_new_connection
833
+ conn
834
+ else
835
+ reap
836
+ # Retry after reaping, which may return an available connection,
837
+ # remove an inactive connection, or both
838
+ if conn = @available.poll || try_to_checkout_new_connection
839
+ conn
840
+ else
841
+ @available.poll(checkout_timeout)
842
+ end
843
+ end
844
+ rescue ConnectionTimeoutError => ex
845
+ raise ex.set_pool(self)
846
+ end
847
+
848
+ #--
849
+ # if owner_thread param is omitted, this must be called in synchronize block
850
+ def remove_connection_from_thread_cache(conn, owner_thread = conn.owner)
851
+ @leases[owner_thread].clear(conn)
852
+ end
853
+ alias_method :release, :remove_connection_from_thread_cache
854
+
855
+ def new_connection
856
+ connection = db_config.new_connection
857
+ connection.pool = self
858
+ connection
859
+ rescue ConnectionNotEstablished => ex
860
+ raise ex.set_pool(self)
861
+ end
862
+
863
+ # If the pool is not at a <tt>@size</tt> limit, establish new connection. Connecting
864
+ # to the DB is done outside main synchronized section.
865
+ #--
866
+ # Implementation constraint: a newly established connection returned by this
867
+ # method must be in the +.leased+ state.
868
+ def try_to_checkout_new_connection
869
+ # first in synchronized section check if establishing new conns is allowed
870
+ # and increment @now_connecting, to prevent overstepping this pool's @size
871
+ # constraint
872
+ do_checkout = synchronize do
873
+ if @threads_blocking_new_connections.zero? && (@connections.size + @now_connecting) < @size
874
+ @now_connecting += 1
875
+ end
876
+ end
877
+ if do_checkout
878
+ begin
879
+ # if successfully incremented @now_connecting establish new connection
880
+ # outside of synchronized section
881
+ conn = checkout_new_connection
882
+ ensure
883
+ synchronize do
884
+ if conn
885
+ adopt_connection(conn)
886
+ # returned conn needs to be already leased
887
+ conn.lease
888
+ end
889
+ @now_connecting -= 1
890
+ end
891
+ end
892
+ end
893
+ end
894
+
895
+ def adopt_connection(conn)
896
+ conn.pool = self
897
+ @connections << conn
898
+
899
+ # We just created the first connection, it's time to load the schema
900
+ # cache if that wasn't eagerly done before
901
+ if @schema_cache.nil? && ActiveRecord.lazily_load_schema_cache
902
+ schema_cache.load!
903
+ end
904
+ end
905
+
906
+ def checkout_new_connection
907
+ raise ConnectionNotEstablished unless @automatic_reconnect
908
+ new_connection
909
+ end
910
+
911
+ def checkout_and_verify(c)
912
+ c._run_checkout_callbacks do
913
+ c.clean!
914
+ end
915
+ c
916
+ rescue Exception
917
+ remove c
918
+ c.disconnect!
919
+ raise
920
+ end
921
+ end
922
+ end
923
+ end