activerecord 7.0.8.1 → 7.2.2.1

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 (279) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +642 -1925
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +29 -29
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +16 -13
  7. data/lib/active_record/association_relation.rb +2 -2
  8. data/lib/active_record/associations/alias_tracker.rb +25 -19
  9. data/lib/active_record/associations/association.rb +35 -12
  10. data/lib/active_record/associations/association_scope.rb +16 -9
  11. data/lib/active_record/associations/belongs_to_association.rb +23 -8
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  13. data/lib/active_record/associations/builder/association.rb +3 -3
  14. data/lib/active_record/associations/builder/belongs_to.rb +22 -8
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
  16. data/lib/active_record/associations/builder/has_many.rb +3 -4
  17. data/lib/active_record/associations/builder/has_one.rb +3 -4
  18. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  19. data/lib/active_record/associations/collection_association.rb +26 -14
  20. data/lib/active_record/associations/collection_proxy.rb +29 -11
  21. data/lib/active_record/associations/errors.rb +265 -0
  22. data/lib/active_record/associations/foreign_association.rb +10 -3
  23. data/lib/active_record/associations/has_many_association.rb +21 -14
  24. data/lib/active_record/associations/has_many_through_association.rb +17 -7
  25. data/lib/active_record/associations/has_one_association.rb +10 -3
  26. data/lib/active_record/associations/join_dependency/join_association.rb +30 -27
  27. data/lib/active_record/associations/join_dependency.rb +10 -10
  28. data/lib/active_record/associations/nested_error.rb +47 -0
  29. data/lib/active_record/associations/preloader/association.rb +33 -8
  30. data/lib/active_record/associations/preloader/branch.rb +7 -1
  31. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  32. data/lib/active_record/associations/preloader.rb +13 -10
  33. data/lib/active_record/associations/singular_association.rb +7 -1
  34. data/lib/active_record/associations/through_association.rb +22 -11
  35. data/lib/active_record/associations.rb +354 -485
  36. data/lib/active_record/attribute_assignment.rb +0 -4
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  38. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  39. data/lib/active_record/attribute_methods/dirty.rb +53 -35
  40. data/lib/active_record/attribute_methods/primary_key.rb +45 -25
  41. data/lib/active_record/attribute_methods/query.rb +28 -16
  42. data/lib/active_record/attribute_methods/read.rb +8 -7
  43. data/lib/active_record/attribute_methods/serialization.rb +131 -32
  44. data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -6
  45. data/lib/active_record/attribute_methods/write.rb +6 -6
  46. data/lib/active_record/attribute_methods.rb +148 -33
  47. data/lib/active_record/attributes.rb +64 -50
  48. data/lib/active_record/autosave_association.rb +69 -37
  49. data/lib/active_record/base.rb +9 -5
  50. data/lib/active_record/callbacks.rb +11 -25
  51. data/lib/active_record/coders/column_serializer.rb +61 -0
  52. data/lib/active_record/coders/json.rb +1 -1
  53. data/lib/active_record/coders/yaml_column.rb +70 -42
  54. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +123 -131
  55. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +4 -1
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +323 -88
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +160 -45
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +217 -63
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -63
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +307 -129
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +510 -111
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +278 -125
  69. data/lib/active_record/connection_adapters/column.rb +9 -0
  70. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  71. data/lib/active_record/connection_adapters/mysql/database_statements.rb +26 -139
  72. data/lib/active_record/connection_adapters/mysql/quoting.rb +53 -54
  73. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  76. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +25 -13
  77. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  78. data/lib/active_record/connection_adapters/mysql2_adapter.rb +101 -68
  79. data/lib/active_record/connection_adapters/pool_config.rb +20 -10
  80. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +14 -3
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +100 -43
  83. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  87. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
  88. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  89. data/lib/active_record/connection_adapters/postgresql/quoting.rb +65 -61
  90. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
  91. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  92. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +151 -2
  93. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
  94. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +370 -63
  95. data/lib/active_record/connection_adapters/postgresql_adapter.rb +367 -201
  96. data/lib/active_record/connection_adapters/schema_cache.rb +302 -79
  97. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  98. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +60 -43
  99. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +45 -46
  100. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  101. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +14 -0
  102. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  103. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +50 -8
  104. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +290 -110
  105. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  106. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  107. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  108. data/lib/active_record/connection_adapters.rb +124 -1
  109. data/lib/active_record/connection_handling.rb +96 -104
  110. data/lib/active_record/core.rb +251 -176
  111. data/lib/active_record/counter_cache.rb +68 -34
  112. data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -3
  113. data/lib/active_record/database_configurations/database_config.rb +26 -5
  114. data/lib/active_record/database_configurations/hash_config.rb +52 -34
  115. data/lib/active_record/database_configurations/url_config.rb +37 -12
  116. data/lib/active_record/database_configurations.rb +87 -34
  117. data/lib/active_record/delegated_type.rb +39 -10
  118. data/lib/active_record/deprecator.rb +7 -0
  119. data/lib/active_record/destroy_association_async_job.rb +3 -1
  120. data/lib/active_record/dynamic_matchers.rb +2 -2
  121. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  122. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  123. data/lib/active_record/encryption/config.rb +25 -1
  124. data/lib/active_record/encryption/configurable.rb +12 -19
  125. data/lib/active_record/encryption/context.rb +10 -3
  126. data/lib/active_record/encryption/contexts.rb +5 -1
  127. data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
  128. data/lib/active_record/encryption/encryptable_record.rb +45 -21
  129. data/lib/active_record/encryption/encrypted_attribute_type.rb +47 -12
  130. data/lib/active_record/encryption/encryptor.rb +18 -3
  131. data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
  132. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  133. data/lib/active_record/encryption/key_generator.rb +12 -1
  134. data/lib/active_record/encryption/key_provider.rb +1 -1
  135. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  136. data/lib/active_record/encryption/message_serializer.rb +6 -0
  137. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  138. data/lib/active_record/encryption/properties.rb +3 -3
  139. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  140. data/lib/active_record/encryption/scheme.rb +22 -21
  141. data/lib/active_record/encryption.rb +3 -0
  142. data/lib/active_record/enum.rb +129 -28
  143. data/lib/active_record/errors.rb +151 -31
  144. data/lib/active_record/explain.rb +21 -12
  145. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  146. data/lib/active_record/fixture_set/render_context.rb +2 -0
  147. data/lib/active_record/fixture_set/table_row.rb +29 -8
  148. data/lib/active_record/fixtures.rb +167 -97
  149. data/lib/active_record/future_result.rb +47 -8
  150. data/lib/active_record/gem_version.rb +3 -3
  151. data/lib/active_record/inheritance.rb +34 -18
  152. data/lib/active_record/insert_all.rb +72 -22
  153. data/lib/active_record/integration.rb +11 -8
  154. data/lib/active_record/internal_metadata.rb +124 -20
  155. data/lib/active_record/locking/optimistic.rb +8 -7
  156. data/lib/active_record/locking/pessimistic.rb +5 -2
  157. data/lib/active_record/log_subscriber.rb +18 -22
  158. data/lib/active_record/marshalling.rb +59 -0
  159. data/lib/active_record/message_pack.rb +124 -0
  160. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  161. data/lib/active_record/middleware/database_selector.rb +6 -8
  162. data/lib/active_record/middleware/shard_selector.rb +3 -1
  163. data/lib/active_record/migration/command_recorder.rb +106 -8
  164. data/lib/active_record/migration/compatibility.rb +147 -5
  165. data/lib/active_record/migration/default_strategy.rb +22 -0
  166. data/lib/active_record/migration/execution_strategy.rb +19 -0
  167. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  168. data/lib/active_record/migration.rb +234 -117
  169. data/lib/active_record/model_schema.rb +90 -102
  170. data/lib/active_record/nested_attributes.rb +48 -11
  171. data/lib/active_record/normalization.rb +163 -0
  172. data/lib/active_record/persistence.rb +168 -339
  173. data/lib/active_record/promise.rb +84 -0
  174. data/lib/active_record/query_cache.rb +18 -25
  175. data/lib/active_record/query_logs.rb +92 -52
  176. data/lib/active_record/query_logs_formatter.rb +41 -0
  177. data/lib/active_record/querying.rb +33 -8
  178. data/lib/active_record/railtie.rb +129 -85
  179. data/lib/active_record/railties/controller_runtime.rb +22 -7
  180. data/lib/active_record/railties/databases.rake +145 -154
  181. data/lib/active_record/railties/job_runtime.rb +23 -0
  182. data/lib/active_record/readonly_attributes.rb +32 -5
  183. data/lib/active_record/reflection.rb +267 -69
  184. data/lib/active_record/relation/batches/batch_enumerator.rb +20 -5
  185. data/lib/active_record/relation/batches.rb +198 -63
  186. data/lib/active_record/relation/calculations.rb +250 -93
  187. data/lib/active_record/relation/delegation.rb +30 -19
  188. data/lib/active_record/relation/finder_methods.rb +93 -18
  189. data/lib/active_record/relation/merger.rb +6 -6
  190. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  191. data/lib/active_record/relation/predicate_builder/association_query_value.rb +18 -3
  192. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  193. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  194. data/lib/active_record/relation/predicate_builder.rb +28 -16
  195. data/lib/active_record/relation/query_attribute.rb +2 -1
  196. data/lib/active_record/relation/query_methods.rb +576 -107
  197. data/lib/active_record/relation/record_fetch_warning.rb +3 -0
  198. data/lib/active_record/relation/spawn_methods.rb +5 -4
  199. data/lib/active_record/relation/where_clause.rb +7 -19
  200. data/lib/active_record/relation.rb +580 -90
  201. data/lib/active_record/result.rb +49 -48
  202. data/lib/active_record/runtime_registry.rb +63 -1
  203. data/lib/active_record/sanitization.rb +70 -25
  204. data/lib/active_record/schema.rb +8 -7
  205. data/lib/active_record/schema_dumper.rb +63 -14
  206. data/lib/active_record/schema_migration.rb +75 -24
  207. data/lib/active_record/scoping/default.rb +15 -5
  208. data/lib/active_record/scoping/named.rb +3 -2
  209. data/lib/active_record/scoping.rb +2 -1
  210. data/lib/active_record/secure_password.rb +60 -0
  211. data/lib/active_record/secure_token.rb +21 -3
  212. data/lib/active_record/signed_id.rb +27 -6
  213. data/lib/active_record/statement_cache.rb +7 -7
  214. data/lib/active_record/store.rb +8 -8
  215. data/lib/active_record/suppressor.rb +3 -1
  216. data/lib/active_record/table_metadata.rb +1 -1
  217. data/lib/active_record/tasks/database_tasks.rb +190 -118
  218. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  219. data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
  220. data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
  221. data/lib/active_record/test_fixtures.rb +170 -155
  222. data/lib/active_record/testing/query_assertions.rb +121 -0
  223. data/lib/active_record/timestamp.rb +31 -17
  224. data/lib/active_record/token_for.rb +123 -0
  225. data/lib/active_record/touch_later.rb +12 -7
  226. data/lib/active_record/transaction.rb +132 -0
  227. data/lib/active_record/transactions.rb +106 -24
  228. data/lib/active_record/translation.rb +0 -2
  229. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  230. data/lib/active_record/type/internal/timezone.rb +7 -2
  231. data/lib/active_record/type/serialized.rb +1 -3
  232. data/lib/active_record/type/time.rb +4 -0
  233. data/lib/active_record/type_caster/connection.rb +4 -4
  234. data/lib/active_record/validations/absence.rb +1 -1
  235. data/lib/active_record/validations/associated.rb +9 -3
  236. data/lib/active_record/validations/numericality.rb +5 -4
  237. data/lib/active_record/validations/presence.rb +5 -28
  238. data/lib/active_record/validations/uniqueness.rb +61 -11
  239. data/lib/active_record/validations.rb +12 -5
  240. data/lib/active_record/version.rb +1 -1
  241. data/lib/active_record.rb +247 -33
  242. data/lib/arel/alias_predication.rb +1 -1
  243. data/lib/arel/collectors/bind.rb +2 -0
  244. data/lib/arel/collectors/composite.rb +7 -0
  245. data/lib/arel/collectors/sql_string.rb +1 -1
  246. data/lib/arel/collectors/substitute_binds.rb +1 -1
  247. data/lib/arel/errors.rb +10 -0
  248. data/lib/arel/factory_methods.rb +4 -0
  249. data/lib/arel/nodes/binary.rb +6 -7
  250. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  251. data/lib/arel/nodes/cte.rb +36 -0
  252. data/lib/arel/nodes/fragments.rb +35 -0
  253. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  254. data/lib/arel/nodes/leading_join.rb +8 -0
  255. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  256. data/lib/arel/nodes/node.rb +115 -5
  257. data/lib/arel/nodes/sql_literal.rb +13 -0
  258. data/lib/arel/nodes/table_alias.rb +4 -0
  259. data/lib/arel/nodes.rb +6 -2
  260. data/lib/arel/predications.rb +3 -1
  261. data/lib/arel/select_manager.rb +1 -1
  262. data/lib/arel/table.rb +9 -5
  263. data/lib/arel/tree_manager.rb +8 -3
  264. data/lib/arel/update_manager.rb +2 -1
  265. data/lib/arel/visitors/dot.rb +1 -0
  266. data/lib/arel/visitors/mysql.rb +17 -5
  267. data/lib/arel/visitors/postgresql.rb +1 -12
  268. data/lib/arel/visitors/sqlite.rb +25 -0
  269. data/lib/arel/visitors/to_sql.rb +112 -34
  270. data/lib/arel/visitors/visitor.rb +2 -2
  271. data/lib/arel.rb +21 -3
  272. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  273. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  274. data/lib/rails/generators/active_record/migration.rb +3 -1
  275. data/lib/rails/generators/active_record/model/USAGE +113 -0
  276. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  277. metadata +59 -17
  278. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  279. data/lib/active_record/null_relation.rb +0 -63
@@ -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
@@ -25,8 +25,7 @@ module ActiveRecord
25
25
  def initialize(url)
26
26
  raise "Database URL cannot be empty" if url.blank?
27
27
  @uri = uri_parser.parse(url)
28
- @adapter = @uri.scheme && @uri.scheme.tr("-", "_")
29
- @adapter = "postgresql" if @adapter == "postgres"
28
+ @adapter = resolved_adapter
30
29
 
31
30
  if @uri.opaque
32
31
  @uri.opaque, @query = @uri.opaque.split("?", 2)
@@ -46,7 +45,7 @@ module ActiveRecord
46
45
  attr_reader :uri
47
46
 
48
47
  def uri_parser
49
- @uri_parser ||= URI::Parser.new
48
+ @uri_parser ||= URI::RFC2396_Parser.new
50
49
  end
51
50
 
52
51
  # Converts the query parameters of the URI into a hash.
@@ -80,6 +79,12 @@ module ActiveRecord
80
79
  end
81
80
  end
82
81
 
82
+ def resolved_adapter
83
+ adapter = uri.scheme && @uri.scheme.tr("-", "_")
84
+ adapter = ActiveRecord.protocol_adapters[adapter] || adapter
85
+ adapter
86
+ end
87
+
83
88
  # Returns name of the database.
84
89
  def database_from_path
85
90
  if @adapter == "sqlite3"
@@ -3,20 +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
15
+ end
16
+
17
+ def adapter_class
18
+ @adapter_class ||= ActiveRecord::ConnectionAdapters.resolve(adapter)
19
+ end
20
+
21
+ def inspect # :nodoc:
22
+ "#<#{self.class.name} env_name=#{@env_name} name=#{@name} adapter_class=#{adapter_class}>"
23
+ end
24
+
25
+ def new_connection
26
+ adapter_class.new(configuration_hash)
16
27
  end
17
28
 
18
- def adapter_method
19
- "#{adapter}_connection"
29
+ def validate!
30
+ adapter_class if adapter
31
+
32
+ true
20
33
  end
21
34
 
22
35
  def host
@@ -51,6 +64,10 @@ module ActiveRecord
51
64
  raise NotImplementedError
52
65
  end
53
66
 
67
+ def query_cache
68
+ raise NotImplementedError
69
+ end
70
+
54
71
  def checkout_timeout
55
72
  raise NotImplementedError
56
73
  end
@@ -78,6 +95,10 @@ module ActiveRecord
78
95
  def schema_cache_path
79
96
  raise NotImplementedError
80
97
  end
98
+
99
+ def use_metadata_table?
100
+ raise NotImplementedError
101
+ end
81
102
  end
82
103
  end
83
104
  end
@@ -1,47 +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"}>
16
- #
17
- # ==== Options
18
+ # #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10
19
+ # @env_name="development", @name="primary", @config={database: "db_name"}>
18
20
  #
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
29
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
+ #
30
38
  def initialize(env_name, name, configuration_hash)
31
39
  super(env_name, name)
32
40
  @configuration_hash = configuration_hash.symbolize_keys.freeze
33
41
  end
34
42
 
35
43
  # Determines whether a database configuration is for a replica / readonly
36
- # connection. If the +replica+ key is present in the config, +replica?+ will
37
- # return +true+.
44
+ # connection. If the `replica` key is present in the config, `replica?` will
45
+ # return `true`.
38
46
  def replica?
39
47
  configuration_hash[:replica]
40
48
  end
41
49
 
42
- # The migrations paths for a database configuration. If the
43
- # +migrations_paths+ key is present in the config, +migrations_paths+
44
- # 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.
45
52
  def migrations_paths
46
53
  configuration_hash[:migrations_paths]
47
54
  end
@@ -74,6 +81,10 @@ module ActiveRecord
74
81
  (configuration_hash[:max_threads] || pool).to_i
75
82
  end
76
83
 
84
+ def query_cache
85
+ configuration_hash[:query_cache]
86
+ end
87
+
77
88
  def max_queue
78
89
  max_threads * 4
79
90
  end
@@ -82,8 +93,8 @@ module ActiveRecord
82
93
  (configuration_hash[:checkout_timeout] || 5).to_f
83
94
  end
84
95
 
85
- # +reaping_frequency+ is configurable mostly for historical reasons, but it could
86
- # 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`.
87
98
  def reaping_frequency
88
99
  configuration_hash.fetch(:reaping_frequency, 60)&.to_f
89
100
  end
@@ -94,18 +105,21 @@ module ActiveRecord
94
105
  end
95
106
 
96
107
  def adapter
97
- configuration_hash[:adapter]
108
+ configuration_hash[:adapter]&.to_s
98
109
  end
99
110
 
100
- # The path to the schema cache dump file for a database.
101
- # If omitted, the filename will be read from ENV or a
102
- # 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.
103
113
  def schema_cache_path
104
114
  configuration_hash[:schema_cache_path]
105
115
  end
106
116
 
107
- def default_schema_cache_path
108
- "db/schema_cache.yml"
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
109
123
  end
110
124
 
111
125
  def lazy_schema_cache_path
@@ -116,14 +130,14 @@ module ActiveRecord
116
130
  Base.configurations.primary?(name)
117
131
  end
118
132
 
119
- # Determines whether to dump the schema/structure files and the
120
- # filename that should be used.
133
+ # Determines whether to dump the schema/structure files and the filename that
134
+ # should be used.
121
135
  #
122
- # If +configuration_hash[:schema_dump]+ is set to +false+ or +nil+
123
- # the schema will not be dumped.
136
+ # If `configuration_hash[:schema_dump]` is set to `false` or `nil` the schema
137
+ # will not be dumped.
124
138
  #
125
- # If the config option is set that will be used. Otherwise Rails
126
- # will generate the filename from the database config name.
139
+ # If the config option is set that will be used. Otherwise Rails will generate
140
+ # the filename from the database config name.
127
141
  def schema_dump(format = ActiveRecord.schema_format)
128
142
  if configuration_hash.key?(:schema_dump)
129
143
  if config = configuration_hash[:schema_dump]
@@ -140,6 +154,10 @@ module ActiveRecord
140
154
  !replica? && !!configuration_hash.fetch(:database_tasks, true)
141
155
  end
142
156
 
157
+ def use_metadata_table? # :nodoc:
158
+ configuration_hash.fetch(:use_metadata_table, true)
159
+ end
160
+
143
161
  private
144
162
  def schema_file_type(format)
145
163
  case format
@@ -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,28 +19,51 @@ 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, i.e. "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