activerecord 6.1.6 → 7.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (309) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1627 -983
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +18 -18
  5. data/lib/active_record/aggregations.rb +17 -14
  6. data/lib/active_record/association_relation.rb +1 -11
  7. data/lib/active_record/associations/association.rb +50 -19
  8. data/lib/active_record/associations/association_scope.rb +17 -12
  9. data/lib/active_record/associations/belongs_to_association.rb +28 -9
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  11. data/lib/active_record/associations/builder/association.rb +11 -5
  12. data/lib/active_record/associations/builder/belongs_to.rb +40 -14
  13. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  14. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
  15. data/lib/active_record/associations/builder/has_many.rb +3 -2
  16. data/lib/active_record/associations/builder/has_one.rb +2 -1
  17. data/lib/active_record/associations/builder/singular_association.rb +6 -2
  18. data/lib/active_record/associations/collection_association.rb +35 -31
  19. data/lib/active_record/associations/collection_proxy.rb +30 -15
  20. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  21. data/lib/active_record/associations/foreign_association.rb +10 -3
  22. data/lib/active_record/associations/has_many_association.rb +28 -18
  23. data/lib/active_record/associations/has_many_through_association.rb +12 -7
  24. data/lib/active_record/associations/has_one_association.rb +20 -10
  25. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  26. data/lib/active_record/associations/join_dependency.rb +26 -16
  27. data/lib/active_record/associations/preloader/association.rb +207 -52
  28. data/lib/active_record/associations/preloader/batch.rb +48 -0
  29. data/lib/active_record/associations/preloader/branch.rb +147 -0
  30. data/lib/active_record/associations/preloader/through_association.rb +50 -14
  31. data/lib/active_record/associations/preloader.rb +50 -121
  32. data/lib/active_record/associations/singular_association.rb +9 -3
  33. data/lib/active_record/associations/through_association.rb +25 -14
  34. data/lib/active_record/associations.rb +439 -305
  35. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  36. data/lib/active_record/attribute_assignment.rb +1 -3
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
  38. data/lib/active_record/attribute_methods/dirty.rb +73 -22
  39. data/lib/active_record/attribute_methods/primary_key.rb +78 -26
  40. data/lib/active_record/attribute_methods/query.rb +31 -19
  41. data/lib/active_record/attribute_methods/read.rb +25 -10
  42. data/lib/active_record/attribute_methods/serialization.rb +194 -37
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
  44. data/lib/active_record/attribute_methods/write.rb +10 -13
  45. data/lib/active_record/attribute_methods.rb +121 -40
  46. data/lib/active_record/attributes.rb +27 -38
  47. data/lib/active_record/autosave_association.rb +61 -30
  48. data/lib/active_record/base.rb +25 -2
  49. data/lib/active_record/callbacks.rb +18 -34
  50. data/lib/active_record/coders/column_serializer.rb +61 -0
  51. data/lib/active_record/coders/json.rb +1 -1
  52. data/lib/active_record/coders/yaml_column.rb +70 -34
  53. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +367 -0
  54. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  55. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +96 -590
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +172 -50
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +77 -27
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +87 -73
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +360 -138
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +281 -59
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +631 -149
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +285 -156
  69. data/lib/active_record/connection_adapters/column.rb +13 -0
  70. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  71. data/lib/active_record/connection_adapters/mysql/database_statements.rb +25 -134
  72. data/lib/active_record/connection_adapters/mysql/quoting.rb +56 -25
  73. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  75. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  76. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +38 -14
  77. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
  78. data/lib/active_record/connection_adapters/mysql2_adapter.rb +104 -53
  79. data/lib/active_record/connection_adapters/pool_config.rb +20 -11
  80. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +89 -52
  83. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  84. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  87. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
  89. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  92. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  93. data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -56
  94. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
  95. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
  96. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +153 -3
  97. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
  98. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +394 -74
  99. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  100. data/lib/active_record/connection_adapters/postgresql_adapter.rb +509 -247
  101. data/lib/active_record/connection_adapters/schema_cache.rb +319 -90
  102. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  103. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +72 -53
  104. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +37 -21
  105. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
  106. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -22
  107. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +294 -102
  108. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  109. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  110. data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
  111. data/lib/active_record/connection_adapters.rb +9 -6
  112. data/lib/active_record/connection_handling.rb +107 -136
  113. data/lib/active_record/core.rb +202 -223
  114. data/lib/active_record/counter_cache.rb +46 -25
  115. data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
  116. data/lib/active_record/database_configurations/database_config.rb +21 -12
  117. data/lib/active_record/database_configurations/hash_config.rb +84 -16
  118. data/lib/active_record/database_configurations/url_config.rb +18 -12
  119. data/lib/active_record/database_configurations.rb +95 -59
  120. data/lib/active_record/delegated_type.rb +61 -15
  121. data/lib/active_record/deprecator.rb +7 -0
  122. data/lib/active_record/destroy_association_async_job.rb +3 -1
  123. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  124. data/lib/active_record/dynamic_matchers.rb +1 -1
  125. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  126. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  127. data/lib/active_record/encryption/cipher.rb +53 -0
  128. data/lib/active_record/encryption/config.rb +68 -0
  129. data/lib/active_record/encryption/configurable.rb +60 -0
  130. data/lib/active_record/encryption/context.rb +42 -0
  131. data/lib/active_record/encryption/contexts.rb +76 -0
  132. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  133. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  134. data/lib/active_record/encryption/encryptable_record.rb +224 -0
  135. data/lib/active_record/encryption/encrypted_attribute_type.rb +151 -0
  136. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  137. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  138. data/lib/active_record/encryption/encryptor.rb +155 -0
  139. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  140. data/lib/active_record/encryption/errors.rb +15 -0
  141. data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
  142. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  143. data/lib/active_record/encryption/key.rb +28 -0
  144. data/lib/active_record/encryption/key_generator.rb +53 -0
  145. data/lib/active_record/encryption/key_provider.rb +46 -0
  146. data/lib/active_record/encryption/message.rb +33 -0
  147. data/lib/active_record/encryption/message_serializer.rb +92 -0
  148. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  149. data/lib/active_record/encryption/properties.rb +76 -0
  150. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  151. data/lib/active_record/encryption/scheme.rb +96 -0
  152. data/lib/active_record/encryption.rb +56 -0
  153. data/lib/active_record/enum.rb +154 -63
  154. data/lib/active_record/errors.rb +171 -15
  155. data/lib/active_record/explain.rb +23 -3
  156. data/lib/active_record/explain_registry.rb +11 -6
  157. data/lib/active_record/explain_subscriber.rb +1 -1
  158. data/lib/active_record/fixture_set/file.rb +15 -1
  159. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  160. data/lib/active_record/fixture_set/render_context.rb +2 -0
  161. data/lib/active_record/fixture_set/table_row.rb +70 -14
  162. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  163. data/lib/active_record/fixtures.rb +131 -86
  164. data/lib/active_record/future_result.rb +164 -0
  165. data/lib/active_record/gem_version.rb +3 -3
  166. data/lib/active_record/inheritance.rb +81 -29
  167. data/lib/active_record/insert_all.rb +135 -22
  168. data/lib/active_record/integration.rb +11 -10
  169. data/lib/active_record/internal_metadata.rb +119 -33
  170. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  171. data/lib/active_record/locking/optimistic.rb +36 -21
  172. data/lib/active_record/locking/pessimistic.rb +15 -6
  173. data/lib/active_record/log_subscriber.rb +52 -19
  174. data/lib/active_record/marshalling.rb +56 -0
  175. data/lib/active_record/message_pack.rb +124 -0
  176. data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
  177. data/lib/active_record/middleware/database_selector.rb +23 -13
  178. data/lib/active_record/middleware/shard_selector.rb +62 -0
  179. data/lib/active_record/migration/command_recorder.rb +112 -14
  180. data/lib/active_record/migration/compatibility.rb +221 -48
  181. data/lib/active_record/migration/default_strategy.rb +23 -0
  182. data/lib/active_record/migration/execution_strategy.rb +19 -0
  183. data/lib/active_record/migration/join_table.rb +1 -1
  184. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  185. data/lib/active_record/migration.rb +358 -171
  186. data/lib/active_record/model_schema.rb +120 -101
  187. data/lib/active_record/nested_attributes.rb +37 -18
  188. data/lib/active_record/no_touching.rb +3 -3
  189. data/lib/active_record/normalization.rb +167 -0
  190. data/lib/active_record/persistence.rb +405 -85
  191. data/lib/active_record/promise.rb +84 -0
  192. data/lib/active_record/query_cache.rb +3 -21
  193. data/lib/active_record/query_logs.rb +174 -0
  194. data/lib/active_record/query_logs_formatter.rb +41 -0
  195. data/lib/active_record/querying.rb +29 -6
  196. data/lib/active_record/railtie.rb +219 -43
  197. data/lib/active_record/railties/controller_runtime.rb +13 -9
  198. data/lib/active_record/railties/databases.rake +188 -252
  199. data/lib/active_record/railties/job_runtime.rb +23 -0
  200. data/lib/active_record/readonly_attributes.rb +41 -3
  201. data/lib/active_record/reflection.rb +241 -80
  202. data/lib/active_record/relation/batches/batch_enumerator.rb +23 -7
  203. data/lib/active_record/relation/batches.rb +192 -63
  204. data/lib/active_record/relation/calculations.rb +219 -90
  205. data/lib/active_record/relation/delegation.rb +27 -13
  206. data/lib/active_record/relation/finder_methods.rb +108 -51
  207. data/lib/active_record/relation/merger.rb +22 -13
  208. data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
  209. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
  210. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  211. data/lib/active_record/relation/predicate_builder.rb +27 -20
  212. data/lib/active_record/relation/query_attribute.rb +30 -12
  213. data/lib/active_record/relation/query_methods.rb +654 -127
  214. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  215. data/lib/active_record/relation/spawn_methods.rb +20 -3
  216. data/lib/active_record/relation/where_clause.rb +10 -19
  217. data/lib/active_record/relation.rb +262 -120
  218. data/lib/active_record/result.rb +37 -11
  219. data/lib/active_record/runtime_registry.rb +18 -13
  220. data/lib/active_record/sanitization.rb +65 -20
  221. data/lib/active_record/schema.rb +36 -22
  222. data/lib/active_record/schema_dumper.rb +73 -24
  223. data/lib/active_record/schema_migration.rb +68 -33
  224. data/lib/active_record/scoping/default.rb +72 -15
  225. data/lib/active_record/scoping/named.rb +5 -13
  226. data/lib/active_record/scoping.rb +65 -34
  227. data/lib/active_record/secure_password.rb +60 -0
  228. data/lib/active_record/secure_token.rb +21 -3
  229. data/lib/active_record/serialization.rb +6 -1
  230. data/lib/active_record/signed_id.rb +10 -8
  231. data/lib/active_record/store.rb +16 -11
  232. data/lib/active_record/suppressor.rb +13 -15
  233. data/lib/active_record/table_metadata.rb +16 -3
  234. data/lib/active_record/tasks/database_tasks.rb +225 -136
  235. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
  236. data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
  237. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  238. data/lib/active_record/test_databases.rb +1 -1
  239. data/lib/active_record/test_fixtures.rb +123 -99
  240. data/lib/active_record/timestamp.rb +29 -18
  241. data/lib/active_record/token_for.rb +113 -0
  242. data/lib/active_record/touch_later.rb +11 -6
  243. data/lib/active_record/transactions.rb +48 -27
  244. data/lib/active_record/translation.rb +3 -3
  245. data/lib/active_record/type/adapter_specific_registry.rb +32 -14
  246. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  247. data/lib/active_record/type/internal/timezone.rb +7 -2
  248. data/lib/active_record/type/serialized.rb +9 -5
  249. data/lib/active_record/type/time.rb +4 -0
  250. data/lib/active_record/type/type_map.rb +17 -20
  251. data/lib/active_record/type.rb +1 -2
  252. data/lib/active_record/validations/absence.rb +1 -1
  253. data/lib/active_record/validations/associated.rb +4 -4
  254. data/lib/active_record/validations/numericality.rb +5 -4
  255. data/lib/active_record/validations/presence.rb +5 -28
  256. data/lib/active_record/validations/uniqueness.rb +51 -6
  257. data/lib/active_record/validations.rb +8 -4
  258. data/lib/active_record/version.rb +1 -1
  259. data/lib/active_record.rb +335 -32
  260. data/lib/arel/attributes/attribute.rb +0 -8
  261. data/lib/arel/crud.rb +28 -22
  262. data/lib/arel/delete_manager.rb +18 -4
  263. data/lib/arel/errors.rb +10 -0
  264. data/lib/arel/factory_methods.rb +4 -0
  265. data/lib/arel/filter_predications.rb +9 -0
  266. data/lib/arel/insert_manager.rb +2 -3
  267. data/lib/arel/nodes/and.rb +4 -0
  268. data/lib/arel/nodes/binary.rb +6 -1
  269. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  270. data/lib/arel/nodes/casted.rb +1 -1
  271. data/lib/arel/nodes/cte.rb +36 -0
  272. data/lib/arel/nodes/delete_statement.rb +12 -13
  273. data/lib/arel/nodes/filter.rb +10 -0
  274. data/lib/arel/nodes/fragments.rb +35 -0
  275. data/lib/arel/nodes/function.rb +1 -0
  276. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  277. data/lib/arel/nodes/insert_statement.rb +2 -2
  278. data/lib/arel/nodes/leading_join.rb +8 -0
  279. data/lib/arel/nodes/node.rb +111 -2
  280. data/lib/arel/nodes/select_core.rb +2 -2
  281. data/lib/arel/nodes/select_statement.rb +2 -2
  282. data/lib/arel/nodes/sql_literal.rb +6 -0
  283. data/lib/arel/nodes/table_alias.rb +4 -0
  284. data/lib/arel/nodes/update_statement.rb +8 -3
  285. data/lib/arel/nodes.rb +5 -0
  286. data/lib/arel/predications.rb +13 -3
  287. data/lib/arel/select_manager.rb +10 -4
  288. data/lib/arel/table.rb +9 -6
  289. data/lib/arel/tree_manager.rb +0 -12
  290. data/lib/arel/update_manager.rb +18 -4
  291. data/lib/arel/visitors/dot.rb +80 -90
  292. data/lib/arel/visitors/mysql.rb +16 -3
  293. data/lib/arel/visitors/postgresql.rb +0 -10
  294. data/lib/arel/visitors/to_sql.rb +139 -19
  295. data/lib/arel/visitors/visitor.rb +2 -2
  296. data/lib/arel.rb +18 -3
  297. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  298. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  299. data/lib/rails/generators/active_record/migration.rb +3 -1
  300. data/lib/rails/generators/active_record/model/USAGE +113 -0
  301. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  302. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  303. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  304. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  305. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  306. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  307. metadata +93 -13
  308. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  309. data/lib/active_record/null_relation.rb +0 -67
@@ -5,6 +5,10 @@ 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
+ end
11
+
8
12
  module ClassMethods
9
13
  # Resets one or more counter caches to their correct value using an SQL
10
14
  # count query. This is useful when adding new counter caches, or if the
@@ -29,6 +33,7 @@ module ActiveRecord
29
33
  def reset_counters(id, *counters, touch: nil)
30
34
  object = find(id)
31
35
 
36
+ updates = {}
32
37
  counters.each do |counter_association|
33
38
  has_many_association = _reflect_on_association(counter_association)
34
39
  unless has_many_association
@@ -47,19 +52,21 @@ module ActiveRecord
47
52
  reflection = child_class._reflections.values.find { |e| e.belongs_to? && e.foreign_key.to_s == foreign_key && e.options[:counter_cache].present? }
48
53
  counter_name = reflection.counter_cache_column
49
54
 
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
55
+ count_was = object.send(counter_name)
56
+ count = object.send(counter_association).count(:all)
57
+ updates[counter_name] = count if count != count_was
58
+ end
59
59
 
60
- unscoped.where(primary_key => object.id).update_all(updates)
60
+ if touch
61
+ names = touch if touch != true
62
+ names = Array.wrap(names)
63
+ options = names.extract_options!
64
+ touch_updates = touch_attributes_with_time(*names, **options)
65
+ updates.merge!(touch_updates)
61
66
  end
62
67
 
68
+ unscoped.where(primary_key => object.id).update_all(updates) if updates.any?
69
+
63
70
  true
64
71
  end
65
72
 
@@ -80,28 +87,28 @@ module ActiveRecord
80
87
  #
81
88
  # ==== Examples
82
89
  #
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
90
+ # # For the Post with id of 5, decrement the comments_count by 1, and
91
+ # # increment the actions_count by 1
92
+ # Post.update_counters 5, comments_count: -1, actions_count: 1
86
93
  # # Executes the following SQL:
87
94
  # # UPDATE posts
88
- # # SET comment_count = COALESCE(comment_count, 0) - 1,
89
- # # action_count = COALESCE(action_count, 0) + 1
95
+ # # SET comments_count = COALESCE(comments_count, 0) - 1,
96
+ # # actions_count = COALESCE(actions_count, 0) + 1
90
97
  # # WHERE id = 5
91
98
  #
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
99
+ # # For the Posts with id of 10 and 15, increment the comments_count by 1
100
+ # Post.update_counters [10, 15], comments_count: 1
94
101
  # # Executes the following SQL:
95
102
  # # UPDATE posts
96
- # # SET comment_count = COALESCE(comment_count, 0) + 1
103
+ # # SET comments_count = COALESCE(comments_count, 0) + 1
97
104
  # # WHERE id IN (10, 15)
98
105
  #
99
- # # For the Posts with id of 10 and 15, increment the comment_count by 1
106
+ # # For the Posts with id of 10 and 15, increment the comments_count by 1
100
107
  # # and update the updated_at value for each counter.
101
- # Post.update_counters [10, 15], comment_count: 1, touch: true
108
+ # Post.update_counters [10, 15], comments_count: 1, touch: true
102
109
  # # Executes the following SQL:
103
110
  # # UPDATE posts
104
- # # SET comment_count = COALESCE(comment_count, 0) + 1,
111
+ # # SET comments_count = COALESCE(comments_count, 0) + 1,
105
112
  # # `updated_at` = '2016-10-13T09:59:23-05:00'
106
113
  # # WHERE id IN (10, 15)
107
114
  def update_counters(id, counters)
@@ -119,6 +126,7 @@ module ActiveRecord
119
126
  #
120
127
  # * +counter_name+ - The name of the field that should be incremented.
121
128
  # * +id+ - The id of the object that should be incremented or an array of ids.
129
+ # * <tt>:by</tt> - The amount by which to increment the value. Defaults to +1+.
122
130
  # * <tt>:touch</tt> - Touch timestamp columns when updating.
123
131
  # Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to
124
132
  # touch that column or an array of symbols to touch just those ones.
@@ -129,10 +137,14 @@ module ActiveRecord
129
137
  # DiscussionBoard.increment_counter(:posts_count, 5)
130
138
  #
131
139
  # # Increment the posts_count column for the record with an id of 5
140
+ # # by a specific amount.
141
+ # DiscussionBoard.increment_counter(:posts_count, 5, by: 3)
142
+ #
143
+ # # Increment the posts_count column for the record with an id of 5
132
144
  # # and update the updated_at value.
133
145
  # 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)
146
+ def increment_counter(counter_name, id, by: 1, touch: nil)
147
+ update_counters(id, counter_name => by, touch: touch)
136
148
  end
137
149
 
138
150
  # Decrement a numeric field by one, via a direct SQL update.
@@ -144,6 +156,7 @@ module ActiveRecord
144
156
  #
145
157
  # * +counter_name+ - The name of the field that should be decremented.
146
158
  # * +id+ - The id of the object that should be decremented or an array of ids.
159
+ # * <tt>:by</tt> - The amount by which to decrement the value. Defaults to +1+.
147
160
  # * <tt>:touch</tt> - Touch timestamp columns when updating.
148
161
  # Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to
149
162
  # touch that column or an array of symbols to touch just those ones.
@@ -154,10 +167,18 @@ module ActiveRecord
154
167
  # DiscussionBoard.decrement_counter(:posts_count, 5)
155
168
  #
156
169
  # # Decrement the posts_count column for the record with an id of 5
170
+ # by a specific amount.
171
+ # DiscussionBoard.decrement_counter(:posts_count, 5, by: 3)
172
+ #
173
+ # # Decrement the posts_count column for the record with an id of 5
157
174
  # # and update the updated_at value.
158
175
  # 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)
176
+ def decrement_counter(counter_name, id, by: 1, touch: nil)
177
+ update_counters(id, counter_name => -by, touch: touch)
178
+ end
179
+
180
+ def counter_cache_column?(name) # :nodoc:
181
+ _counter_cache_columns.include?(name)
161
182
  end
162
183
  end
163
184
 
@@ -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
@@ -68,7 +69,7 @@ module ActiveRecord
68
69
  database: uri.opaque
69
70
  )
70
71
  else
71
- query_hash.merge(
72
+ query_hash.reverse_merge(
72
73
  adapter: @adapter,
73
74
  username: uri.user,
74
75
  password: uri.password,
@@ -3,31 +3,24 @@
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
16
14
  end
17
15
 
18
- def spec_name
19
- @name
20
- end
21
- deprecate spec_name: "please use name instead"
22
-
23
- def config
24
- raise NotImplementedError
25
- end
26
-
27
16
  def adapter_method
28
17
  "#{adapter}_connection"
29
18
  end
30
19
 
20
+ def adapter_class_method
21
+ "#{adapter}_adapter_class"
22
+ end
23
+
31
24
  def host
32
25
  raise NotImplementedError
33
26
  end
@@ -48,6 +41,22 @@ module ActiveRecord
48
41
  raise NotImplementedError
49
42
  end
50
43
 
44
+ def min_threads
45
+ raise NotImplementedError
46
+ end
47
+
48
+ def max_threads
49
+ raise NotImplementedError
50
+ end
51
+
52
+ def max_queue
53
+ raise NotImplementedError
54
+ end
55
+
56
+ def query_cache
57
+ raise NotImplementedError
58
+ end
59
+
51
60
  def checkout_timeout
52
61
  raise NotImplementedError
53
62
  end
@@ -2,7 +2,9 @@
2
2
 
3
3
  module ActiveRecord
4
4
  class DatabaseConfigurations
5
- # A HashConfig object is created for each database configuration entry that
5
+ # = Active Record Database Hash Config
6
+ #
7
+ # A +HashConfig+ object is created for each database configuration entry that
6
8
  # is created from a hash.
7
9
  #
8
10
  # A hash config:
@@ -14,28 +16,28 @@ module ActiveRecord
14
16
  # #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10
15
17
  # @env_name="development", @name="primary", @config={database: "db_name"}>
16
18
  #
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.
19
+ # See ActiveRecord::DatabaseConfigurations for more info.
27
20
  class HashConfig < DatabaseConfig
28
21
  attr_reader :configuration_hash
22
+
23
+
24
+ # Initialize a new +HashConfig+ object
25
+ #
26
+ # ==== Options
27
+ #
28
+ # * <tt>:env_name</tt> - The \Rails environment, i.e. "development".
29
+ # * <tt>:name</tt> - The db config name. In a standard two-tier
30
+ # database configuration this will default to "primary". In a multiple
31
+ # database three-tier database configuration this corresponds to the name
32
+ # used in the second tier, for example "primary_readonly".
33
+ # * <tt>:config</tt> - The config hash. This is the hash that contains the
34
+ # database adapter, name, and other important information for database
35
+ # connections.
29
36
  def initialize(env_name, name, configuration_hash)
30
37
  super(env_name, name)
31
38
  @configuration_hash = configuration_hash.symbolize_keys.freeze
32
39
  end
33
40
 
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
41
  # Determines whether a database configuration is for a replica / readonly
40
42
  # connection. If the +replica+ key is present in the config, +replica?+ will
41
43
  # return +true+.
@@ -54,6 +56,10 @@ module ActiveRecord
54
56
  configuration_hash[:host]
55
57
  end
56
58
 
59
+ def socket # :nodoc:
60
+ configuration_hash[:socket]
61
+ end
62
+
57
63
  def database
58
64
  configuration_hash[:database]
59
65
  end
@@ -66,6 +72,22 @@ module ActiveRecord
66
72
  (configuration_hash[:pool] || 5).to_i
67
73
  end
68
74
 
75
+ def min_threads
76
+ (configuration_hash[:min_threads] || 0).to_i
77
+ end
78
+
79
+ def max_threads
80
+ (configuration_hash[:max_threads] || pool).to_i
81
+ end
82
+
83
+ def query_cache
84
+ configuration_hash[:query_cache]
85
+ end
86
+
87
+ def max_queue
88
+ max_threads * 4
89
+ end
90
+
69
91
  def checkout_timeout
70
92
  (configuration_hash[:checkout_timeout] || 5).to_f
71
93
  end
@@ -91,6 +113,52 @@ module ActiveRecord
91
113
  def schema_cache_path
92
114
  configuration_hash[:schema_cache_path]
93
115
  end
116
+
117
+ def default_schema_cache_path
118
+ "db/schema_cache.yml"
119
+ end
120
+
121
+ def lazy_schema_cache_path
122
+ schema_cache_path || default_schema_cache_path
123
+ end
124
+
125
+ def primary? # :nodoc:
126
+ Base.configurations.primary?(name)
127
+ end
128
+
129
+ # Determines whether to dump the schema/structure files and the
130
+ # filename that should be used.
131
+ #
132
+ # If +configuration_hash[:schema_dump]+ is set to +false+ or +nil+
133
+ # the schema will not be dumped.
134
+ #
135
+ # If the config option is set that will be used. Otherwise \Rails
136
+ # will generate the filename from the database config name.
137
+ def schema_dump(format = ActiveRecord.schema_format)
138
+ if configuration_hash.key?(:schema_dump)
139
+ if config = configuration_hash[:schema_dump]
140
+ config
141
+ end
142
+ elsif primary?
143
+ schema_file_type(format)
144
+ else
145
+ "#{name}_#{schema_file_type(format)}"
146
+ end
147
+ end
148
+
149
+ def database_tasks? # :nodoc:
150
+ !replica? && !!configuration_hash.fetch(:database_tasks, true)
151
+ end
152
+
153
+ private
154
+ def schema_file_type(format)
155
+ case format
156
+ when :ruby
157
+ "schema.rb"
158
+ when :sql
159
+ "structure.sql"
160
+ end
161
+ end
94
162
  end
95
163
  end
96
164
  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,20 +19,24 @@ 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
 
@@ -42,7 +48,7 @@ module ActiveRecord
42
48
  # Return a Hash that can be merged into the main config that represents
43
49
  # the passed in url
44
50
  def build_url_hash
45
- if url.nil? || %w(jdbc: http: https:).any? { |protocol| url.start_with?(protocol) }
51
+ if url.nil? || url.start_with?("jdbc:", "http:", "https:")
46
52
  { url: url }
47
53
  else
48
54
  ConnectionUrlResolver.new(url).to_hash
@@ -7,24 +7,78 @@ require "active_record/database_configurations/url_config"
7
7
  require "active_record/database_configurations/connection_url_resolver"
8
8
 
9
9
  module ActiveRecord
10
- # ActiveRecord::DatabaseConfigurations returns an array of DatabaseConfig
11
- # objects (either a HashConfig or UrlConfig) that are constructed from the
12
- # application's database configuration hash or URL string.
10
+ # = Active Record Database Configurations
11
+ #
12
+ # +ActiveRecord::DatabaseConfigurations+ returns an array of +DatabaseConfig+
13
+ # objects that are constructed from the application's database
14
+ # configuration hash or URL string.
15
+ #
16
+ # The array of +DatabaseConfig+ objects in an application default to either a
17
+ # HashConfig or UrlConfig. You can retrieve your application's config by using
18
+ # ActiveRecord::Base.configurations.
19
+ #
20
+ # If you register a custom handler, objects will be created according to the
21
+ # conditions of the handler. See ::register_db_config_handler for more on
22
+ # registering custom handlers.
13
23
  class DatabaseConfigurations
14
24
  class InvalidConfigurationError < StandardError; end
15
25
 
16
26
  attr_reader :configurations
17
27
  delegate :any?, to: :configurations
18
28
 
29
+ singleton_class.attr_accessor :db_config_handlers # :nodoc:
30
+ self.db_config_handlers = [] # :nodoc:
31
+
32
+ # Allows an application to register a custom handler for database configuration
33
+ # objects. This is useful for creating a custom handler that responds to
34
+ # methods your application needs but Active Record doesn't implement. For
35
+ # example if you are using Vitess, you may want your Vitess configurations
36
+ # to respond to `sharded?`. To implement this define the following in an
37
+ # initializer:
38
+ #
39
+ # ActiveRecord::DatabaseConfigurations.register_db_config_handler do |env_name, name, url, config|
40
+ # next unless config.key?(:vitess)
41
+ # VitessConfig.new(env_name, name, config)
42
+ # end
43
+ #
44
+ # Note: applications must handle the condition in which custom config should be
45
+ # created in your handler registration otherwise all objects will use the custom
46
+ # handler.
47
+ #
48
+ # Then define your +VitessConfig+ to respond to the methods your application
49
+ # needs. It is recommended that you inherit from one of the existing
50
+ # database config classes to avoid having to reimplement all methods. Custom
51
+ # config handlers should only implement methods Active Record does not.
52
+ #
53
+ # class VitessConfig < ActiveRecord::DatabaseConfigurations::UrlConfig
54
+ # def sharded?
55
+ # configuration_hash.fetch("sharded", false)
56
+ # end
57
+ # end
58
+ #
59
+ # For configs that have a +:vitess+ key, a +VitessConfig+ object will be
60
+ # created instead of a +UrlConfig+.
61
+ def self.register_db_config_handler(&block)
62
+ db_config_handlers << block
63
+ end
64
+
65
+ register_db_config_handler do |env_name, name, url, config|
66
+ if url
67
+ UrlConfig.new(env_name, name, url, config)
68
+ else
69
+ HashConfig.new(env_name, name, config)
70
+ end
71
+ end
72
+
19
73
  def initialize(configurations = {})
20
74
  @configurations = build_configs(configurations)
21
75
  end
22
76
 
23
77
  # Collects the configs for the environment and optionally the specification
24
- # name passed in. To include replica configurations pass <tt>include_replicas: true</tt>.
78
+ # name passed in. To include replica configurations pass <tt>include_hidden: true</tt>.
25
79
  #
26
- # If a name is provided a single DatabaseConfig object will be
27
- # returned, otherwise an array of DatabaseConfig objects will be
80
+ # If a name is provided a single +DatabaseConfig+ object will be
81
+ # returned, otherwise an array of +DatabaseConfig+ objects will be
28
82
  # returned that corresponds with the environment and type requested.
29
83
  #
30
84
  # ==== Options
@@ -34,22 +88,26 @@ module ActiveRecord
34
88
  # * <tt>name:</tt> The db config name (i.e. primary, animals, etc.). Defaults
35
89
  # to +nil+. If no +env_name+ is specified the config for the default env and the
36
90
  # passed +name+ will be returned.
37
- # * <tt>include_replicas:</tt> Determines whether to include replicas in
38
- # the returned list. Most of the time we're only iterating over the write
39
- # connection (i.e. migrations don't need to run for the write and read connection).
40
- # Defaults to +false+.
41
- def configs_for(env_name: nil, spec_name: nil, name: nil, include_replicas: false)
42
- if spec_name
43
- name = spec_name
44
- ActiveSupport::Deprecation.warn("The kwarg `spec_name` is deprecated in favor of `name`. `spec_name` will be removed in Rails 7.0")
45
- end
46
-
91
+ # * <tt>config_key:</tt> Selects configs that contain a particular key in the configuration
92
+ # hash. Useful for selecting configs that use a custom db config handler or finding
93
+ # configs with hashes that contain a particular key.
94
+ # * <tt>include_hidden:</tt> Determines whether to include replicas and configurations
95
+ # hidden by <tt>database_tasks: false</tt> in the returned list. Most of the time we're only
96
+ # iterating over the primary connections (i.e. migrations don't need to run for the
97
+ # write and read connection). Defaults to +false+.
98
+ def configs_for(env_name: nil, name: nil, config_key: nil, include_hidden: false)
47
99
  env_name ||= default_env if name
48
100
  configs = env_with_configs(env_name)
49
101
 
50
- unless include_replicas
102
+ unless include_hidden
103
+ configs = configs.select do |db_config|
104
+ db_config.database_tasks?
105
+ end
106
+ end
107
+
108
+ if config_key
51
109
  configs = configs.select do |db_config|
52
- !db_config.replica?
110
+ db_config.configuration_hash.key?(config_key)
53
111
  end
54
112
  end
55
113
 
@@ -62,30 +120,17 @@ module ActiveRecord
62
120
  end
63
121
  end
64
122
 
65
- # Returns the config hash that corresponds with the environment
66
- #
67
- # If the application has multiple databases +default_hash+ will
68
- # return the first config hash for the environment.
69
- #
70
- # { database: "my_db", adapter: "mysql2" }
71
- def default_hash(env = default_env)
72
- default = find_db_config(env)
73
- default.configuration_hash if default
74
- end
75
- alias :[] :default_hash
76
- deprecate "[]": "Use configs_for", default_hash: "Use configs_for"
77
-
78
- # Returns a single DatabaseConfig object based on the requested environment.
123
+ # Returns a single +DatabaseConfig+ object based on the requested environment.
79
124
  #
80
125
  # If the application has multiple databases +find_db_config+ will return
81
- # the first DatabaseConfig for the environment.
126
+ # the first +DatabaseConfig+ for the environment.
82
127
  def find_db_config(env)
83
- configurations
84
- .sort_by.with_index { |db_config, i| db_config.for_current_env? ? [0, i] : [1, i] }
85
- .find do |db_config|
86
- db_config.env_name == env.to_s ||
87
- (db_config.for_current_env? && db_config.name == env.to_s)
88
- end
128
+ env = env.to_s
129
+ configurations.find do |db_config|
130
+ db_config.for_current_env? && (db_config.env_name == env || db_config.name == env)
131
+ end || configurations.find do |db_config|
132
+ db_config.env_name == env
133
+ end
89
134
  end
90
135
 
91
136
  # A primary configuration is one that is named primary or if there is
@@ -101,17 +146,7 @@ module ActiveRecord
101
146
  first_config && name == first_config.name
102
147
  end
103
148
 
104
- # Returns the DatabaseConfigurations object as a Hash.
105
- def to_h
106
- configurations.inject({}) do |memo, db_config|
107
- memo.merge(db_config.env_name => db_config.configuration_hash.stringify_keys)
108
- end
109
- end
110
- deprecate to_h: "You can use `ActiveRecord::Base.configurations.configs_for(env_name: 'env', name: 'primary').configuration_hash` to get the configuration hashes."
111
-
112
149
  # Checks if the application's configurations are empty.
113
- #
114
- # Aliased to blank?
115
150
  def empty?
116
151
  configurations.empty?
117
152
  end
@@ -167,7 +202,7 @@ module ActiveRecord
167
202
  return configs if configs.is_a?(Array)
168
203
 
169
204
  db_configs = configs.flat_map do |env_name, config|
170
- if config.is_a?(Hash) && config.all? { |_, v| v.is_a?(Hash) }
205
+ if config.is_a?(Hash) && config.values.all?(Hash)
171
206
  walk_configs(env_name.to_s, config)
172
207
  else
173
208
  build_db_config_from_raw_config(env_name.to_s, "primary", config)
@@ -194,7 +229,7 @@ module ActiveRecord
194
229
  raise AdapterNotSpecified, <<~MSG
195
230
  The `#{name}` database is not configured for the `#{default_env}` environment.
196
231
 
197
- Available databases configurations are:
232
+ Available database configurations are:
198
233
 
199
234
  #{build_configuration_sentence}
200
235
  MSG
@@ -202,7 +237,7 @@ module ActiveRecord
202
237
  end
203
238
 
204
239
  def build_configuration_sentence
205
- configs = configs_for(include_replicas: true)
240
+ configs = configs_for(include_hidden: true)
206
241
 
207
242
  configs.group_by(&:env_name).map do |env, config|
208
243
  names = config.map(&:name)
@@ -236,15 +271,16 @@ module ActiveRecord
236
271
  end
237
272
 
238
273
  def build_db_config_from_hash(env_name, name, config)
239
- if config.has_key?(:url)
240
- url = config[:url]
241
- config_without_url = config.dup
242
- config_without_url.delete :url
274
+ url = config[:url]
275
+ config_without_url = config.dup
276
+ config_without_url.delete :url
243
277
 
244
- UrlConfig.new(env_name, name, url, config_without_url)
245
- else
246
- HashConfig.new(env_name, name, config)
278
+ DatabaseConfigurations.db_config_handlers.reverse_each do |handler|
279
+ config = handler.call(env_name, name, url, config_without_url)
280
+ return config if config
247
281
  end
282
+
283
+ nil
248
284
  end
249
285
 
250
286
  def merge_db_environment_variables(current_env, configs)