activerecord 6.1.7 → 7.2.2

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 (333) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +616 -1290
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +31 -31
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +17 -14
  7. data/lib/active_record/association_relation.rb +2 -12
  8. data/lib/active_record/associations/alias_tracker.rb +25 -19
  9. data/lib/active_record/associations/association.rb +60 -21
  10. data/lib/active_record/associations/association_scope.rb +17 -12
  11. data/lib/active_record/associations/belongs_to_association.rb +37 -11
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -4
  13. data/lib/active_record/associations/builder/association.rb +11 -5
  14. data/lib/active_record/associations/builder/belongs_to.rb +41 -14
  15. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
  17. data/lib/active_record/associations/builder/has_many.rb +4 -4
  18. data/lib/active_record/associations/builder/has_one.rb +4 -4
  19. data/lib/active_record/associations/builder/singular_association.rb +6 -2
  20. data/lib/active_record/associations/collection_association.rb +46 -36
  21. data/lib/active_record/associations/collection_proxy.rb +44 -16
  22. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  23. data/lib/active_record/associations/errors.rb +265 -0
  24. data/lib/active_record/associations/foreign_association.rb +10 -3
  25. data/lib/active_record/associations/has_many_association.rb +29 -19
  26. data/lib/active_record/associations/has_many_through_association.rb +19 -8
  27. data/lib/active_record/associations/has_one_association.rb +20 -10
  28. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  29. data/lib/active_record/associations/join_dependency/join_association.rb +30 -27
  30. data/lib/active_record/associations/join_dependency.rb +28 -20
  31. data/lib/active_record/associations/nested_error.rb +47 -0
  32. data/lib/active_record/associations/preloader/association.rb +212 -53
  33. data/lib/active_record/associations/preloader/batch.rb +48 -0
  34. data/lib/active_record/associations/preloader/branch.rb +153 -0
  35. data/lib/active_record/associations/preloader/through_association.rb +50 -16
  36. data/lib/active_record/associations/preloader.rb +50 -121
  37. data/lib/active_record/associations/singular_association.rb +15 -3
  38. data/lib/active_record/associations/through_association.rb +25 -14
  39. data/lib/active_record/associations.rb +429 -522
  40. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  41. data/lib/active_record/attribute_assignment.rb +1 -5
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
  43. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  44. data/lib/active_record/attribute_methods/dirty.rb +73 -22
  45. data/lib/active_record/attribute_methods/primary_key.rb +47 -27
  46. data/lib/active_record/attribute_methods/query.rb +31 -19
  47. data/lib/active_record/attribute_methods/read.rb +14 -11
  48. data/lib/active_record/attribute_methods/serialization.rb +174 -37
  49. data/lib/active_record/attribute_methods/time_zone_conversion.rb +15 -9
  50. data/lib/active_record/attribute_methods/write.rb +12 -15
  51. data/lib/active_record/attribute_methods.rb +164 -52
  52. data/lib/active_record/attributes.rb +57 -54
  53. data/lib/active_record/autosave_association.rb +74 -57
  54. data/lib/active_record/base.rb +27 -5
  55. data/lib/active_record/callbacks.rb +19 -35
  56. data/lib/active_record/coders/column_serializer.rb +61 -0
  57. data/lib/active_record/coders/json.rb +1 -1
  58. data/lib/active_record/coders/yaml_column.rb +70 -46
  59. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +284 -0
  60. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  61. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +79 -0
  62. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +325 -604
  63. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
  64. data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -60
  65. data/lib/active_record/connection_adapters/abstract/query_cache.rb +230 -64
  66. data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -131
  67. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  68. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
  69. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
  70. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  71. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +378 -143
  72. data/lib/active_record/connection_adapters/abstract/transaction.rb +361 -76
  73. data/lib/active_record/connection_adapters/abstract_adapter.rb +624 -163
  74. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +348 -165
  75. data/lib/active_record/connection_adapters/column.rb +13 -0
  76. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  77. data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -130
  78. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -55
  79. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  80. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  81. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  82. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +45 -14
  83. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  84. data/lib/active_record/connection_adapters/mysql2_adapter.rb +107 -68
  85. data/lib/active_record/connection_adapters/pool_config.rb +26 -16
  86. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  87. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
  88. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +114 -54
  89. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  94. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  96. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
  97. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  100. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  101. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  102. data/lib/active_record/connection_adapters/postgresql/quoting.rb +137 -104
  103. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
  104. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
  105. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +173 -3
  106. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
  107. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +403 -77
  108. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  109. data/lib/active_record/connection_adapters/postgresql_adapter.rb +520 -253
  110. data/lib/active_record/connection_adapters/schema_cache.rb +326 -102
  111. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  112. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +78 -55
  113. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +68 -54
  114. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  115. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
  116. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  117. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +66 -22
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +372 -130
  119. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  120. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  121. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  122. data/lib/active_record/connection_adapters.rb +130 -6
  123. data/lib/active_record/connection_handling.rb +132 -146
  124. data/lib/active_record/core.rb +310 -253
  125. data/lib/active_record/counter_cache.rb +68 -34
  126. data/lib/active_record/database_configurations/connection_url_resolver.rb +10 -4
  127. data/lib/active_record/database_configurations/database_config.rb +34 -10
  128. data/lib/active_record/database_configurations/hash_config.rb +107 -31
  129. data/lib/active_record/database_configurations/url_config.rb +38 -13
  130. data/lib/active_record/database_configurations.rb +96 -60
  131. data/lib/active_record/delegated_type.rb +90 -20
  132. data/lib/active_record/deprecator.rb +7 -0
  133. data/lib/active_record/destroy_association_async_job.rb +4 -2
  134. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  135. data/lib/active_record/dynamic_matchers.rb +3 -3
  136. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  137. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  138. data/lib/active_record/encryption/cipher.rb +53 -0
  139. data/lib/active_record/encryption/config.rb +68 -0
  140. data/lib/active_record/encryption/configurable.rb +60 -0
  141. data/lib/active_record/encryption/context.rb +42 -0
  142. data/lib/active_record/encryption/contexts.rb +76 -0
  143. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  144. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  145. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  146. data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -0
  147. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  148. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  149. data/lib/active_record/encryption/encryptor.rb +170 -0
  150. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  151. data/lib/active_record/encryption/errors.rb +15 -0
  152. data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
  153. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  154. data/lib/active_record/encryption/key.rb +28 -0
  155. data/lib/active_record/encryption/key_generator.rb +53 -0
  156. data/lib/active_record/encryption/key_provider.rb +46 -0
  157. data/lib/active_record/encryption/message.rb +33 -0
  158. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  159. data/lib/active_record/encryption/message_serializer.rb +96 -0
  160. data/lib/active_record/encryption/null_encryptor.rb +25 -0
  161. data/lib/active_record/encryption/properties.rb +76 -0
  162. data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
  163. data/lib/active_record/encryption/scheme.rb +100 -0
  164. data/lib/active_record/encryption.rb +58 -0
  165. data/lib/active_record/enum.rb +170 -62
  166. data/lib/active_record/errors.rb +210 -27
  167. data/lib/active_record/explain.rb +21 -12
  168. data/lib/active_record/explain_registry.rb +11 -6
  169. data/lib/active_record/explain_subscriber.rb +1 -1
  170. data/lib/active_record/fixture_set/file.rb +15 -1
  171. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  172. data/lib/active_record/fixture_set/render_context.rb +2 -0
  173. data/lib/active_record/fixture_set/table_row.rb +70 -14
  174. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  175. data/lib/active_record/fixtures.rb +179 -112
  176. data/lib/active_record/future_result.rb +178 -0
  177. data/lib/active_record/gem_version.rb +4 -4
  178. data/lib/active_record/inheritance.rb +85 -31
  179. data/lib/active_record/insert_all.rb +148 -32
  180. data/lib/active_record/integration.rb +14 -10
  181. data/lib/active_record/internal_metadata.rb +123 -23
  182. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  183. data/lib/active_record/locking/optimistic.rb +43 -27
  184. data/lib/active_record/locking/pessimistic.rb +15 -6
  185. data/lib/active_record/log_subscriber.rb +41 -29
  186. data/lib/active_record/marshalling.rb +59 -0
  187. data/lib/active_record/message_pack.rb +124 -0
  188. data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
  189. data/lib/active_record/middleware/database_selector.rb +23 -13
  190. data/lib/active_record/middleware/shard_selector.rb +62 -0
  191. data/lib/active_record/migration/command_recorder.rb +113 -16
  192. data/lib/active_record/migration/compatibility.rb +235 -46
  193. data/lib/active_record/migration/default_strategy.rb +22 -0
  194. data/lib/active_record/migration/execution_strategy.rb +19 -0
  195. data/lib/active_record/migration/join_table.rb +1 -1
  196. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  197. data/lib/active_record/migration.rb +374 -177
  198. data/lib/active_record/model_schema.rb +145 -158
  199. data/lib/active_record/nested_attributes.rb +61 -23
  200. data/lib/active_record/no_touching.rb +3 -3
  201. data/lib/active_record/normalization.rb +163 -0
  202. data/lib/active_record/persistence.rb +282 -283
  203. data/lib/active_record/promise.rb +84 -0
  204. data/lib/active_record/query_cache.rb +18 -25
  205. data/lib/active_record/query_logs.rb +189 -0
  206. data/lib/active_record/query_logs_formatter.rb +41 -0
  207. data/lib/active_record/querying.rb +44 -9
  208. data/lib/active_record/railtie.rb +229 -71
  209. data/lib/active_record/railties/controller_runtime.rb +25 -11
  210. data/lib/active_record/railties/databases.rake +189 -256
  211. data/lib/active_record/railties/job_runtime.rb +23 -0
  212. data/lib/active_record/readonly_attributes.rb +41 -3
  213. data/lib/active_record/reflection.rb +332 -103
  214. data/lib/active_record/relation/batches/batch_enumerator.rb +38 -9
  215. data/lib/active_record/relation/batches.rb +200 -65
  216. data/lib/active_record/relation/calculations.rb +301 -112
  217. data/lib/active_record/relation/delegation.rb +33 -22
  218. data/lib/active_record/relation/finder_methods.rb +123 -52
  219. data/lib/active_record/relation/merger.rb +26 -19
  220. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  221. data/lib/active_record/relation/predicate_builder/association_query_value.rb +38 -4
  222. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  223. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  224. data/lib/active_record/relation/predicate_builder.rb +29 -22
  225. data/lib/active_record/relation/query_attribute.rb +30 -12
  226. data/lib/active_record/relation/query_methods.rb +870 -163
  227. data/lib/active_record/relation/record_fetch_warning.rb +10 -9
  228. data/lib/active_record/relation/spawn_methods.rb +7 -6
  229. data/lib/active_record/relation/where_clause.rb +15 -36
  230. data/lib/active_record/relation.rb +736 -145
  231. data/lib/active_record/result.rb +67 -54
  232. data/lib/active_record/runtime_registry.rb +71 -13
  233. data/lib/active_record/sanitization.rb +84 -34
  234. data/lib/active_record/schema.rb +39 -23
  235. data/lib/active_record/schema_dumper.rb +90 -31
  236. data/lib/active_record/schema_migration.rb +74 -23
  237. data/lib/active_record/scoping/default.rb +72 -15
  238. data/lib/active_record/scoping/named.rb +6 -13
  239. data/lib/active_record/scoping.rb +65 -34
  240. data/lib/active_record/secure_password.rb +60 -0
  241. data/lib/active_record/secure_token.rb +21 -3
  242. data/lib/active_record/serialization.rb +6 -1
  243. data/lib/active_record/signed_id.rb +30 -9
  244. data/lib/active_record/statement_cache.rb +7 -7
  245. data/lib/active_record/store.rb +10 -10
  246. data/lib/active_record/suppressor.rb +13 -15
  247. data/lib/active_record/table_metadata.rb +7 -3
  248. data/lib/active_record/tasks/database_tasks.rb +288 -149
  249. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
  250. data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
  251. data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
  252. data/lib/active_record/test_databases.rb +1 -1
  253. data/lib/active_record/test_fixtures.rb +173 -155
  254. data/lib/active_record/testing/query_assertions.rb +121 -0
  255. data/lib/active_record/timestamp.rb +32 -19
  256. data/lib/active_record/token_for.rb +123 -0
  257. data/lib/active_record/touch_later.rb +12 -7
  258. data/lib/active_record/transaction.rb +132 -0
  259. data/lib/active_record/transactions.rb +118 -41
  260. data/lib/active_record/translation.rb +3 -5
  261. data/lib/active_record/type/adapter_specific_registry.rb +32 -14
  262. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  263. data/lib/active_record/type/internal/timezone.rb +7 -2
  264. data/lib/active_record/type/serialized.rb +9 -7
  265. data/lib/active_record/type/time.rb +4 -0
  266. data/lib/active_record/type/type_map.rb +17 -20
  267. data/lib/active_record/type.rb +1 -2
  268. data/lib/active_record/type_caster/connection.rb +4 -4
  269. data/lib/active_record/validations/absence.rb +1 -1
  270. data/lib/active_record/validations/associated.rb +13 -7
  271. data/lib/active_record/validations/numericality.rb +5 -4
  272. data/lib/active_record/validations/presence.rb +5 -28
  273. data/lib/active_record/validations/uniqueness.rb +65 -15
  274. data/lib/active_record/validations.rb +12 -5
  275. data/lib/active_record/version.rb +1 -1
  276. data/lib/active_record.rb +444 -32
  277. data/lib/arel/alias_predication.rb +1 -1
  278. data/lib/arel/attributes/attribute.rb +0 -8
  279. data/lib/arel/collectors/bind.rb +2 -0
  280. data/lib/arel/collectors/composite.rb +7 -0
  281. data/lib/arel/collectors/sql_string.rb +1 -1
  282. data/lib/arel/collectors/substitute_binds.rb +1 -1
  283. data/lib/arel/crud.rb +28 -22
  284. data/lib/arel/delete_manager.rb +18 -4
  285. data/lib/arel/errors.rb +10 -0
  286. data/lib/arel/factory_methods.rb +4 -0
  287. data/lib/arel/filter_predications.rb +9 -0
  288. data/lib/arel/insert_manager.rb +2 -3
  289. data/lib/arel/nodes/binary.rb +6 -7
  290. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  291. data/lib/arel/nodes/casted.rb +1 -1
  292. data/lib/arel/nodes/cte.rb +36 -0
  293. data/lib/arel/nodes/delete_statement.rb +12 -13
  294. data/lib/arel/nodes/filter.rb +10 -0
  295. data/lib/arel/nodes/fragments.rb +35 -0
  296. data/lib/arel/nodes/function.rb +1 -0
  297. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  298. data/lib/arel/nodes/insert_statement.rb +2 -2
  299. data/lib/arel/nodes/leading_join.rb +8 -0
  300. data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
  301. data/lib/arel/nodes/node.rb +115 -5
  302. data/lib/arel/nodes/select_core.rb +2 -2
  303. data/lib/arel/nodes/select_statement.rb +2 -2
  304. data/lib/arel/nodes/sql_literal.rb +13 -0
  305. data/lib/arel/nodes/table_alias.rb +4 -0
  306. data/lib/arel/nodes/update_statement.rb +8 -3
  307. data/lib/arel/nodes.rb +7 -2
  308. data/lib/arel/predications.rb +14 -4
  309. data/lib/arel/select_manager.rb +11 -5
  310. data/lib/arel/table.rb +9 -6
  311. data/lib/arel/tree_manager.rb +8 -15
  312. data/lib/arel/update_manager.rb +20 -5
  313. data/lib/arel/visitors/dot.rb +81 -90
  314. data/lib/arel/visitors/mysql.rb +23 -5
  315. data/lib/arel/visitors/postgresql.rb +1 -22
  316. data/lib/arel/visitors/sqlite.rb +25 -0
  317. data/lib/arel/visitors/to_sql.rb +170 -36
  318. data/lib/arel/visitors/visitor.rb +2 -2
  319. data/lib/arel.rb +23 -4
  320. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  321. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  322. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  323. data/lib/rails/generators/active_record/migration.rb +3 -1
  324. data/lib/rails/generators/active_record/model/USAGE +113 -0
  325. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  326. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  327. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  328. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  329. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  330. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  331. metadata +103 -17
  332. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  333. data/lib/active_record/null_relation.rb +0 -67
@@ -5,6 +5,11 @@ module ActiveRecord
5
5
  module CounterCache
6
6
  extend ActiveSupport::Concern
7
7
 
8
+ included do
9
+ class_attribute :_counter_cache_columns, instance_accessor: false, default: []
10
+ class_attribute :counter_cached_association_names, instance_writer: false, default: []
11
+ end
12
+
8
13
  module ClassMethods
9
14
  # Resets one or more counter caches to their correct value using an SQL
10
15
  # count query. This is useful when adding new counter caches, or if the
@@ -29,6 +34,7 @@ module ActiveRecord
29
34
  def reset_counters(id, *counters, touch: nil)
30
35
  object = find(id)
31
36
 
37
+ updates = {}
32
38
  counters.each do |counter_association|
33
39
  has_many_association = _reflect_on_association(counter_association)
34
40
  unless has_many_association
@@ -47,19 +53,21 @@ module ActiveRecord
47
53
  reflection = child_class._reflections.values.find { |e| e.belongs_to? && e.foreign_key.to_s == foreign_key && e.options[:counter_cache].present? }
48
54
  counter_name = reflection.counter_cache_column
49
55
 
50
- updates = { counter_name => object.send(counter_association).count(:all) }
51
-
52
- if touch
53
- names = touch if touch != true
54
- names = Array.wrap(names)
55
- options = names.extract_options!
56
- touch_updates = touch_attributes_with_time(*names, **options)
57
- updates.merge!(touch_updates)
58
- end
56
+ count_was = object.send(counter_name)
57
+ count = object.send(counter_association).count(:all)
58
+ updates[counter_name] = count if count != count_was
59
+ end
59
60
 
60
- unscoped.where(primary_key => object.id).update_all(updates)
61
+ if touch
62
+ names = touch if touch != true
63
+ names = Array.wrap(names)
64
+ options = names.extract_options!
65
+ touch_updates = touch_attributes_with_time(*names, **options)
66
+ updates.merge!(touch_updates)
61
67
  end
62
68
 
69
+ unscoped.where(primary_key => [object.id]).update_all(updates) if updates.any?
70
+
63
71
  true
64
72
  end
65
73
 
@@ -80,31 +88,32 @@ module ActiveRecord
80
88
  #
81
89
  # ==== Examples
82
90
  #
83
- # # For the Post with id of 5, decrement the comment_count by 1, and
84
- # # increment the action_count by 1
85
- # Post.update_counters 5, comment_count: -1, action_count: 1
91
+ # # For the Post with id of 5, decrement the comments_count by 1, and
92
+ # # increment the actions_count by 1
93
+ # Post.update_counters 5, comments_count: -1, actions_count: 1
86
94
  # # Executes the following SQL:
87
95
  # # UPDATE posts
88
- # # SET comment_count = COALESCE(comment_count, 0) - 1,
89
- # # action_count = COALESCE(action_count, 0) + 1
96
+ # # SET comments_count = COALESCE(comments_count, 0) - 1,
97
+ # # actions_count = COALESCE(actions_count, 0) + 1
90
98
  # # WHERE id = 5
91
99
  #
92
- # # For the Posts with id of 10 and 15, increment the comment_count by 1
93
- # Post.update_counters [10, 15], comment_count: 1
100
+ # # For the Posts with id of 10 and 15, increment the comments_count by 1
101
+ # Post.update_counters [10, 15], comments_count: 1
94
102
  # # Executes the following SQL:
95
103
  # # UPDATE posts
96
- # # SET comment_count = COALESCE(comment_count, 0) + 1
104
+ # # SET comments_count = COALESCE(comments_count, 0) + 1
97
105
  # # WHERE id IN (10, 15)
98
106
  #
99
- # # For the Posts with id of 10 and 15, increment the comment_count by 1
107
+ # # For the Posts with id of 10 and 15, increment the comments_count by 1
100
108
  # # and update the updated_at value for each counter.
101
- # Post.update_counters [10, 15], comment_count: 1, touch: true
109
+ # Post.update_counters [10, 15], comments_count: 1, touch: true
102
110
  # # Executes the following SQL:
103
111
  # # UPDATE posts
104
- # # SET comment_count = COALESCE(comment_count, 0) + 1,
112
+ # # SET comments_count = COALESCE(comments_count, 0) + 1,
105
113
  # # `updated_at` = '2016-10-13T09:59:23-05:00'
106
114
  # # WHERE id IN (10, 15)
107
115
  def update_counters(id, counters)
116
+ id = [id] if composite_primary_key? && id.is_a?(Array) && !id[0].is_a?(Array)
108
117
  unscoped.where!(primary_key => id).update_counters(counters)
109
118
  end
110
119
 
@@ -119,6 +128,7 @@ module ActiveRecord
119
128
  #
120
129
  # * +counter_name+ - The name of the field that should be incremented.
121
130
  # * +id+ - The id of the object that should be incremented or an array of ids.
131
+ # * <tt>:by</tt> - The amount by which to increment the value. Defaults to +1+.
122
132
  # * <tt>:touch</tt> - Touch timestamp columns when updating.
123
133
  # Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to
124
134
  # touch that column or an array of symbols to touch just those ones.
@@ -129,10 +139,14 @@ module ActiveRecord
129
139
  # DiscussionBoard.increment_counter(:posts_count, 5)
130
140
  #
131
141
  # # Increment the posts_count column for the record with an id of 5
142
+ # # by a specific amount.
143
+ # DiscussionBoard.increment_counter(:posts_count, 5, by: 3)
144
+ #
145
+ # # Increment the posts_count column for the record with an id of 5
132
146
  # # and update the updated_at value.
133
147
  # DiscussionBoard.increment_counter(:posts_count, 5, touch: true)
134
- def increment_counter(counter_name, id, touch: nil)
135
- update_counters(id, counter_name => 1, touch: touch)
148
+ def increment_counter(counter_name, id, by: 1, touch: nil)
149
+ update_counters(id, counter_name => by, touch: touch)
136
150
  end
137
151
 
138
152
  # Decrement a numeric field by one, via a direct SQL update.
@@ -144,6 +158,7 @@ module ActiveRecord
144
158
  #
145
159
  # * +counter_name+ - The name of the field that should be decremented.
146
160
  # * +id+ - The id of the object that should be decremented or an array of ids.
161
+ # * <tt>:by</tt> - The amount by which to decrement the value. Defaults to +1+.
147
162
  # * <tt>:touch</tt> - Touch timestamp columns when updating.
148
163
  # Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to
149
164
  # touch that column or an array of symbols to touch just those ones.
@@ -154,10 +169,30 @@ module ActiveRecord
154
169
  # DiscussionBoard.decrement_counter(:posts_count, 5)
155
170
  #
156
171
  # # Decrement the posts_count column for the record with an id of 5
172
+ # by a specific amount.
173
+ # DiscussionBoard.decrement_counter(:posts_count, 5, by: 3)
174
+ #
175
+ # # Decrement the posts_count column for the record with an id of 5
157
176
  # # and update the updated_at value.
158
177
  # DiscussionBoard.decrement_counter(:posts_count, 5, touch: true)
159
- def decrement_counter(counter_name, id, touch: nil)
160
- update_counters(id, counter_name => -1, touch: touch)
178
+ def decrement_counter(counter_name, id, by: 1, touch: nil)
179
+ update_counters(id, counter_name => -by, touch: touch)
180
+ end
181
+
182
+ def counter_cache_column?(name) # :nodoc:
183
+ _counter_cache_columns.include?(name)
184
+ end
185
+
186
+ def load_schema! # :nodoc:
187
+ super
188
+
189
+ association_names = _reflections.filter_map do |name, reflection|
190
+ next unless reflection.belongs_to? && reflection.counter_cache_column
191
+
192
+ name.to_sym
193
+ end
194
+
195
+ self.counter_cached_association_names |= association_names
161
196
  end
162
197
  end
163
198
 
@@ -165,8 +200,8 @@ module ActiveRecord
165
200
  def _create_record(attribute_names = self.attribute_names)
166
201
  id = super
167
202
 
168
- each_counter_cached_associations do |association|
169
- association.increment_counters
203
+ counter_cached_association_names.each do |association_name|
204
+ association(association_name).increment_counters
170
205
  end
171
206
 
172
207
  id
@@ -176,9 +211,10 @@ module ActiveRecord
176
211
  affected_rows = super
177
212
 
178
213
  if affected_rows > 0
179
- each_counter_cached_associations do |association|
180
- foreign_key = association.reflection.foreign_key.to_sym
181
- unless destroyed_by_association && destroyed_by_association.foreign_key.to_sym == foreign_key
214
+ counter_cached_association_names.each do |association_name|
215
+ association = association(association_name)
216
+
217
+ unless destroyed_by_association && _foreign_keys_equal?(destroyed_by_association.foreign_key, association.reflection.foreign_key)
182
218
  association.decrement_counters
183
219
  end
184
220
  end
@@ -187,10 +223,8 @@ module ActiveRecord
187
223
  affected_rows
188
224
  end
189
225
 
190
- def each_counter_cached_associations
191
- _reflections.each do |name, reflection|
192
- yield association(name.to_sym) if reflection.belongs_to? && reflection.counter_cache_column
193
- end
226
+ def _foreign_keys_equal?(fkey1, fkey2)
227
+ fkey1 == fkey2 || Array(fkey1).map(&:to_sym) == Array(fkey2).map(&:to_sym)
194
228
  end
195
229
  end
196
230
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "uri"
4
4
  require "active_support/core_ext/enumerable"
5
+ require "active_support/core_ext/hash/reverse_merge"
5
6
 
6
7
  module ActiveRecord
7
8
  class DatabaseConfigurations
@@ -24,8 +25,7 @@ module ActiveRecord
24
25
  def initialize(url)
25
26
  raise "Database URL cannot be empty" if url.blank?
26
27
  @uri = uri_parser.parse(url)
27
- @adapter = @uri.scheme && @uri.scheme.tr("-", "_")
28
- @adapter = "postgresql" if @adapter == "postgres"
28
+ @adapter = resolved_adapter
29
29
 
30
30
  if @uri.opaque
31
31
  @uri.opaque, @query = @uri.opaque.split("?", 2)
@@ -45,7 +45,7 @@ module ActiveRecord
45
45
  attr_reader :uri
46
46
 
47
47
  def uri_parser
48
- @uri_parser ||= URI::Parser.new
48
+ @uri_parser ||= URI::RFC2396_Parser.new
49
49
  end
50
50
 
51
51
  # Converts the query parameters of the URI into a hash.
@@ -68,7 +68,7 @@ module ActiveRecord
68
68
  database: uri.opaque
69
69
  )
70
70
  else
71
- query_hash.merge(
71
+ query_hash.reverse_merge(
72
72
  adapter: @adapter,
73
73
  username: uri.user,
74
74
  password: uri.password,
@@ -79,6 +79,12 @@ module ActiveRecord
79
79
  end
80
80
  end
81
81
 
82
+ def resolved_adapter
83
+ adapter = uri.scheme && @uri.scheme.tr("-", "_")
84
+ adapter = ActiveRecord.protocol_adapters[adapter] || adapter
85
+ adapter
86
+ end
87
+
82
88
  # Returns name of the database.
83
89
  def database_from_path
84
90
  if @adapter == "sqlite3"
@@ -3,29 +3,33 @@
3
3
  module ActiveRecord
4
4
  class DatabaseConfigurations
5
5
  # ActiveRecord::Base.configurations will return either a HashConfig or
6
- # UrlConfig respectively. It will never return a DatabaseConfig object,
6
+ # UrlConfig respectively. It will never return a +DatabaseConfig+ object,
7
7
  # as this is the parent class for the types of database configuration objects.
8
8
  class DatabaseConfig # :nodoc:
9
9
  attr_reader :env_name, :name
10
10
 
11
- attr_accessor :owner_name
12
-
13
11
  def initialize(env_name, name)
14
12
  @env_name = env_name
15
13
  @name = name
14
+ @adapter_class = nil
16
15
  end
17
16
 
18
- def spec_name
19
- @name
17
+ def adapter_class
18
+ @adapter_class ||= ActiveRecord::ConnectionAdapters.resolve(adapter)
20
19
  end
21
- deprecate spec_name: "please use name instead"
22
20
 
23
- def config
24
- raise NotImplementedError
21
+ def inspect # :nodoc:
22
+ "#<#{self.class.name} env_name=#{@env_name} name=#{@name} adapter_class=#{adapter_class}>"
25
23
  end
26
24
 
27
- def adapter_method
28
- "#{adapter}_connection"
25
+ def new_connection
26
+ adapter_class.new(configuration_hash)
27
+ end
28
+
29
+ def validate!
30
+ adapter_class if adapter
31
+
32
+ true
29
33
  end
30
34
 
31
35
  def host
@@ -48,6 +52,22 @@ module ActiveRecord
48
52
  raise NotImplementedError
49
53
  end
50
54
 
55
+ def min_threads
56
+ raise NotImplementedError
57
+ end
58
+
59
+ def max_threads
60
+ raise NotImplementedError
61
+ end
62
+
63
+ def max_queue
64
+ raise NotImplementedError
65
+ end
66
+
67
+ def query_cache
68
+ raise NotImplementedError
69
+ end
70
+
51
71
  def checkout_timeout
52
72
  raise NotImplementedError
53
73
  end
@@ -75,6 +95,10 @@ module ActiveRecord
75
95
  def schema_cache_path
76
96
  raise NotImplementedError
77
97
  end
98
+
99
+ def use_metadata_table?
100
+ raise NotImplementedError
101
+ end
78
102
  end
79
103
  end
80
104
  end
@@ -1,51 +1,54 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActiveRecord
4
6
  class DatabaseConfigurations
5
- # A HashConfig object is created for each database configuration entry that
6
- # is created from a hash.
7
+ # # Active Record Database Hash Config
8
+ #
9
+ # A `HashConfig` object is created for each database configuration entry that is
10
+ # created from a hash.
7
11
  #
8
12
  # A hash config:
9
13
  #
10
- # { "development" => { "database" => "db_name" } }
14
+ # { "development" => { "database" => "db_name" } }
11
15
  #
12
16
  # Becomes:
13
17
  #
14
- # #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10
15
- # @env_name="development", @name="primary", @config={database: "db_name"}>
18
+ # #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10
19
+ # @env_name="development", @name="primary", @config={database: "db_name"}>
16
20
  #
17
- # ==== Options
18
- #
19
- # * <tt>:env_name</tt> - The Rails environment, i.e. "development".
20
- # * <tt>:name</tt> - The db config name. In a standard two-tier
21
- # database configuration this will default to "primary". In a multiple
22
- # database three-tier database configuration this corresponds to the name
23
- # used in the second tier, for example "primary_readonly".
24
- # * <tt>:config</tt> - The config hash. This is the hash that contains the
25
- # database adapter, name, and other important information for database
26
- # connections.
21
+ # See ActiveRecord::DatabaseConfigurations for more info.
27
22
  class HashConfig < DatabaseConfig
28
23
  attr_reader :configuration_hash
24
+
25
+ # Initialize a new `HashConfig` object
26
+ #
27
+ # #### Parameters
28
+ #
29
+ # * `env_name` - The Rails environment, i.e. "development".
30
+ # * `name` - The db config name. In a standard two-tier database configuration
31
+ # this will default to "primary". In a multiple database three-tier database
32
+ # configuration this corresponds to the name used in the second tier, for
33
+ # example "primary_readonly".
34
+ # * `configuration_hash` - The config hash. This is the hash that contains the
35
+ # database adapter, name, and other important information for database
36
+ # connections.
37
+ #
29
38
  def initialize(env_name, name, configuration_hash)
30
39
  super(env_name, name)
31
40
  @configuration_hash = configuration_hash.symbolize_keys.freeze
32
41
  end
33
42
 
34
- def config
35
- ActiveSupport::Deprecation.warn("DatabaseConfig#config will be removed in 7.0.0 in favor of DatabaseConfig#configuration_hash which returns a hash with symbol keys")
36
- configuration_hash.stringify_keys
37
- end
38
-
39
43
  # Determines whether a database configuration is for a replica / readonly
40
- # connection. If the +replica+ key is present in the config, +replica?+ will
41
- # return +true+.
44
+ # connection. If the `replica` key is present in the config, `replica?` will
45
+ # return `true`.
42
46
  def replica?
43
47
  configuration_hash[:replica]
44
48
  end
45
49
 
46
- # The migrations paths for a database configuration. If the
47
- # +migrations_paths+ key is present in the config, +migrations_paths+
48
- # will return its value.
50
+ # The migrations paths for a database configuration. If the `migrations_paths`
51
+ # key is present in the config, `migrations_paths` will return its value.
49
52
  def migrations_paths
50
53
  configuration_hash[:migrations_paths]
51
54
  end
@@ -54,6 +57,10 @@ module ActiveRecord
54
57
  configuration_hash[:host]
55
58
  end
56
59
 
60
+ def socket # :nodoc:
61
+ configuration_hash[:socket]
62
+ end
63
+
57
64
  def database
58
65
  configuration_hash[:database]
59
66
  end
@@ -66,12 +73,28 @@ module ActiveRecord
66
73
  (configuration_hash[:pool] || 5).to_i
67
74
  end
68
75
 
76
+ def min_threads
77
+ (configuration_hash[:min_threads] || 0).to_i
78
+ end
79
+
80
+ def max_threads
81
+ (configuration_hash[:max_threads] || pool).to_i
82
+ end
83
+
84
+ def query_cache
85
+ configuration_hash[:query_cache]
86
+ end
87
+
88
+ def max_queue
89
+ max_threads * 4
90
+ end
91
+
69
92
  def checkout_timeout
70
93
  (configuration_hash[:checkout_timeout] || 5).to_f
71
94
  end
72
95
 
73
- # +reaping_frequency+ is configurable mostly for historical reasons, but it could
74
- # also be useful if someone wants a very low +idle_timeout+.
96
+ # `reaping_frequency` is configurable mostly for historical reasons, but it
97
+ # could also be useful if someone wants a very low `idle_timeout`.
75
98
  def reaping_frequency
76
99
  configuration_hash.fetch(:reaping_frequency, 60)&.to_f
77
100
  end
@@ -82,15 +105,68 @@ module ActiveRecord
82
105
  end
83
106
 
84
107
  def adapter
85
- configuration_hash[:adapter]
108
+ configuration_hash[:adapter]&.to_s
86
109
  end
87
110
 
88
- # The path to the schema cache dump file for a database.
89
- # If omitted, the filename will be read from ENV or a
90
- # default will be derived.
111
+ # The path to the schema cache dump file for a database. If omitted, the
112
+ # filename will be read from ENV or a default will be derived.
91
113
  def schema_cache_path
92
114
  configuration_hash[:schema_cache_path]
93
115
  end
116
+
117
+ def default_schema_cache_path(db_dir = "db")
118
+ if primary?
119
+ File.join(db_dir, "schema_cache.yml")
120
+ else
121
+ File.join(db_dir, "#{name}_schema_cache.yml")
122
+ end
123
+ end
124
+
125
+ def lazy_schema_cache_path
126
+ schema_cache_path || default_schema_cache_path
127
+ end
128
+
129
+ def primary? # :nodoc:
130
+ Base.configurations.primary?(name)
131
+ end
132
+
133
+ # Determines whether to dump the schema/structure files and the filename that
134
+ # should be used.
135
+ #
136
+ # If `configuration_hash[:schema_dump]` is set to `false` or `nil` the schema
137
+ # will not be dumped.
138
+ #
139
+ # If the config option is set that will be used. Otherwise Rails will generate
140
+ # the filename from the database config name.
141
+ def schema_dump(format = ActiveRecord.schema_format)
142
+ if configuration_hash.key?(:schema_dump)
143
+ if config = configuration_hash[:schema_dump]
144
+ config
145
+ end
146
+ elsif primary?
147
+ schema_file_type(format)
148
+ else
149
+ "#{name}_#{schema_file_type(format)}"
150
+ end
151
+ end
152
+
153
+ def database_tasks? # :nodoc:
154
+ !replica? && !!configuration_hash.fetch(:database_tasks, true)
155
+ end
156
+
157
+ def use_metadata_table? # :nodoc:
158
+ configuration_hash.fetch(:use_metadata_table, true)
159
+ end
160
+
161
+ private
162
+ def schema_file_type(format)
163
+ case format
164
+ when :ruby
165
+ "schema.rb"
166
+ when :sql
167
+ "structure.sql"
168
+ end
169
+ end
94
170
  end
95
171
  end
96
172
  end
@@ -2,7 +2,9 @@
2
2
 
3
3
  module ActiveRecord
4
4
  class DatabaseConfigurations
5
- # A UrlConfig object is created for each database configuration
5
+ # = Active Record Database Url Config
6
+ #
7
+ # A +UrlConfig+ object is created for each database configuration
6
8
  # entry that is created from a URL. This can either be a URL string
7
9
  # or a hash with a URL in place of the config hash.
8
10
  #
@@ -17,32 +19,55 @@ module ActiveRecord
17
19
  # @config={adapter: "postgresql", database: "foo", host: "localhost"},
18
20
  # @url="postgres://localhost/foo">
19
21
  #
20
- # ==== Options
22
+ # See ActiveRecord::DatabaseConfigurations for more info.
21
23
  #
22
- # * <tt>:env_name</tt> - The Rails environment, ie "development".
23
- # * <tt>:name</tt> - The db config name. In a standard two-tier
24
- # database configuration this will default to "primary". In a multiple
25
- # database three-tier database configuration this corresponds to the name
26
- # used in the second tier, for example "primary_readonly".
27
- # * <tt>:url</tt> - The database URL.
28
- # * <tt>:config</tt> - The config hash. This is the hash that contains the
29
- # database adapter, name, and other important information for database
30
- # connections.
31
24
  class UrlConfig < HashConfig
32
25
  attr_reader :url
33
26
 
27
+ # Initialize a new +UrlConfig+ object
28
+ #
29
+ # ==== Options
30
+ #
31
+ # * <tt>:env_name</tt> - The \Rails environment, i.e. "development".
32
+ # * <tt>:name</tt> - The db config name. In a standard two-tier
33
+ # database configuration this will default to "primary". In a multiple
34
+ # database three-tier database configuration this corresponds to the name
35
+ # used in the second tier, for example "primary_readonly".
36
+ # * <tt>:url</tt> - The database URL.
37
+ # * <tt>:config</tt> - The config hash. This is the hash that contains the
38
+ # database adapter, name, and other important information for database
39
+ # connections.
34
40
  def initialize(env_name, name, url, configuration_hash = {})
35
41
  super(env_name, name, configuration_hash)
36
42
 
37
43
  @url = url
38
- @configuration_hash = @configuration_hash.merge(build_url_hash).freeze
44
+ @configuration_hash = @configuration_hash.merge(build_url_hash)
45
+
46
+ if @configuration_hash[:schema_dump] == "false"
47
+ @configuration_hash[:schema_dump] = false
48
+ end
49
+
50
+ if @configuration_hash[:query_cache] == "false"
51
+ @configuration_hash[:query_cache] = false
52
+ end
53
+
54
+ to_boolean!(@configuration_hash, :replica)
55
+ to_boolean!(@configuration_hash, :database_tasks)
56
+
57
+ @configuration_hash.freeze
39
58
  end
40
59
 
41
60
  private
61
+ def to_boolean!(configuration_hash, key)
62
+ if configuration_hash[key].is_a?(String)
63
+ configuration_hash[key] = configuration_hash[key] != "false"
64
+ end
65
+ end
66
+
42
67
  # Return a Hash that can be merged into the main config that represents
43
68
  # the passed in url
44
69
  def build_url_hash
45
- if url.nil? || %w(jdbc: http: https:).any? { |protocol| url.start_with?(protocol) }
70
+ if url.nil? || url.start_with?("jdbc:", "http:", "https:")
46
71
  { url: url }
47
72
  else
48
73
  ConnectionUrlResolver.new(url).to_hash