omg-activerecord 8.0.0.alpha1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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