activerecord 7.0.10 → 7.1.0.beta1

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.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (228) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1348 -1672
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +15 -17
  5. data/lib/active_record/aggregations.rb +16 -13
  6. data/lib/active_record/association_relation.rb +1 -1
  7. data/lib/active_record/associations/association.rb +18 -3
  8. data/lib/active_record/associations/association_scope.rb +16 -9
  9. data/lib/active_record/associations/belongs_to_association.rb +14 -6
  10. data/lib/active_record/associations/builder/association.rb +3 -3
  11. data/lib/active_record/associations/builder/belongs_to.rb +21 -8
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
  13. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  14. data/lib/active_record/associations/collection_association.rb +17 -9
  15. data/lib/active_record/associations/collection_proxy.rb +16 -11
  16. data/lib/active_record/associations/foreign_association.rb +10 -3
  17. data/lib/active_record/associations/has_many_association.rb +20 -13
  18. data/lib/active_record/associations/has_many_through_association.rb +10 -6
  19. data/lib/active_record/associations/has_one_association.rb +10 -3
  20. data/lib/active_record/associations/join_dependency.rb +10 -8
  21. data/lib/active_record/associations/preloader/association.rb +27 -6
  22. data/lib/active_record/associations/preloader.rb +12 -9
  23. data/lib/active_record/associations/singular_association.rb +1 -1
  24. data/lib/active_record/associations/through_association.rb +22 -11
  25. data/lib/active_record/associations.rb +313 -217
  26. data/lib/active_record/attribute_assignment.rb +0 -2
  27. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  28. data/lib/active_record/attribute_methods/dirty.rb +48 -38
  29. data/lib/active_record/attribute_methods/primary_key.rb +76 -24
  30. data/lib/active_record/attribute_methods/query.rb +28 -16
  31. data/lib/active_record/attribute_methods/read.rb +18 -5
  32. data/lib/active_record/attribute_methods/serialization.rb +150 -31
  33. data/lib/active_record/attribute_methods/write.rb +3 -3
  34. data/lib/active_record/attribute_methods.rb +105 -21
  35. data/lib/active_record/attributes.rb +3 -3
  36. data/lib/active_record/autosave_association.rb +55 -9
  37. data/lib/active_record/base.rb +7 -2
  38. data/lib/active_record/callbacks.rb +10 -24
  39. data/lib/active_record/coders/column_serializer.rb +61 -0
  40. data/lib/active_record/coders/json.rb +1 -1
  41. data/lib/active_record/coders/yaml_column.rb +70 -42
  42. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +163 -88
  43. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  44. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
  45. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +65 -49
  46. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  47. data/lib/active_record/connection_adapters/abstract/database_statements.rb +109 -32
  48. data/lib/active_record/connection_adapters/abstract/query_cache.rb +60 -22
  49. data/lib/active_record/connection_adapters/abstract/quoting.rb +41 -6
  50. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  51. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  52. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
  53. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +291 -122
  54. data/lib/active_record/connection_adapters/abstract/transaction.rb +280 -58
  55. data/lib/active_record/connection_adapters/abstract_adapter.rb +502 -91
  56. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +200 -115
  57. data/lib/active_record/connection_adapters/column.rb +9 -0
  58. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  59. data/lib/active_record/connection_adapters/mysql/database_statements.rb +22 -143
  60. data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -12
  61. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  62. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
  63. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  64. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +18 -14
  65. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -0
  66. data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
  67. data/lib/active_record/connection_adapters/pool_config.rb +14 -5
  68. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  69. data/lib/active_record/connection_adapters/postgresql/column.rb +1 -2
  70. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +76 -29
  71. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -3
  72. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  73. data/lib/active_record/connection_adapters/postgresql/quoting.rb +9 -6
  74. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
  75. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  76. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
  77. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +42 -0
  78. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +352 -55
  79. data/lib/active_record/connection_adapters/postgresql_adapter.rb +336 -168
  80. data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
  81. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  82. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +42 -36
  83. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +4 -3
  84. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +1 -0
  85. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +26 -7
  86. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +162 -77
  87. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  88. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
  89. data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
  90. data/lib/active_record/connection_adapters.rb +3 -1
  91. data/lib/active_record/connection_handling.rb +72 -95
  92. data/lib/active_record/core.rb +131 -142
  93. data/lib/active_record/counter_cache.rb +46 -25
  94. data/lib/active_record/database_configurations/database_config.rb +9 -3
  95. data/lib/active_record/database_configurations/hash_config.rb +22 -12
  96. data/lib/active_record/database_configurations/url_config.rb +17 -11
  97. data/lib/active_record/database_configurations.rb +86 -33
  98. data/lib/active_record/delegated_type.rb +11 -6
  99. data/lib/active_record/deprecator.rb +7 -0
  100. data/lib/active_record/destroy_association_async_job.rb +2 -0
  101. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  102. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  103. data/lib/active_record/encryption/config.rb +25 -1
  104. data/lib/active_record/encryption/configurable.rb +12 -19
  105. data/lib/active_record/encryption/context.rb +10 -3
  106. data/lib/active_record/encryption/contexts.rb +5 -1
  107. data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
  108. data/lib/active_record/encryption/encryptable_record.rb +36 -18
  109. data/lib/active_record/encryption/encrypted_attribute_type.rb +17 -6
  110. data/lib/active_record/encryption/extended_deterministic_queries.rb +52 -12
  111. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +2 -2
  112. data/lib/active_record/encryption/key_generator.rb +12 -1
  113. data/lib/active_record/encryption/message_serializer.rb +2 -0
  114. data/lib/active_record/encryption/properties.rb +3 -3
  115. data/lib/active_record/encryption/scheme.rb +19 -22
  116. data/lib/active_record/encryption.rb +1 -0
  117. data/lib/active_record/enum.rb +113 -26
  118. data/lib/active_record/errors.rb +89 -15
  119. data/lib/active_record/explain.rb +23 -3
  120. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  121. data/lib/active_record/fixture_set/render_context.rb +2 -0
  122. data/lib/active_record/fixture_set/table_row.rb +29 -8
  123. data/lib/active_record/fixtures.rb +119 -83
  124. data/lib/active_record/future_result.rb +30 -5
  125. data/lib/active_record/gem_version.rb +4 -4
  126. data/lib/active_record/inheritance.rb +30 -16
  127. data/lib/active_record/insert_all.rb +58 -11
  128. data/lib/active_record/integration.rb +8 -8
  129. data/lib/active_record/internal_metadata.rb +118 -30
  130. data/lib/active_record/locking/pessimistic.rb +5 -2
  131. data/lib/active_record/log_subscriber.rb +29 -12
  132. data/lib/active_record/marshalling.rb +56 -0
  133. data/lib/active_record/message_pack.rb +124 -0
  134. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  135. data/lib/active_record/middleware/database_selector.rb +6 -8
  136. data/lib/active_record/middleware/shard_selector.rb +3 -1
  137. data/lib/active_record/migration/command_recorder.rb +100 -4
  138. data/lib/active_record/migration/compatibility.rb +131 -5
  139. data/lib/active_record/migration/default_strategy.rb +23 -0
  140. data/lib/active_record/migration/execution_strategy.rb +19 -0
  141. data/lib/active_record/migration.rb +215 -112
  142. data/lib/active_record/model_schema.rb +50 -32
  143. data/lib/active_record/nested_attributes.rb +31 -6
  144. data/lib/active_record/normalization.rb +158 -0
  145. data/lib/active_record/persistence.rb +183 -33
  146. data/lib/active_record/promise.rb +84 -0
  147. data/lib/active_record/query_cache.rb +3 -21
  148. data/lib/active_record/query_logs.rb +77 -52
  149. data/lib/active_record/query_logs_formatter.rb +41 -0
  150. data/lib/active_record/querying.rb +15 -2
  151. data/lib/active_record/railtie.rb +108 -46
  152. data/lib/active_record/railties/controller_runtime.rb +10 -5
  153. data/lib/active_record/railties/databases.rake +139 -145
  154. data/lib/active_record/railties/job_runtime.rb +23 -0
  155. data/lib/active_record/readonly_attributes.rb +32 -5
  156. data/lib/active_record/reflection.rb +169 -45
  157. data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
  158. data/lib/active_record/relation/batches.rb +190 -61
  159. data/lib/active_record/relation/calculations.rb +152 -63
  160. data/lib/active_record/relation/delegation.rb +23 -9
  161. data/lib/active_record/relation/finder_methods.rb +85 -15
  162. data/lib/active_record/relation/merger.rb +2 -0
  163. data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -2
  164. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  165. data/lib/active_record/relation/predicate_builder.rb +26 -14
  166. data/lib/active_record/relation/query_attribute.rb +2 -1
  167. data/lib/active_record/relation/query_methods.rb +351 -62
  168. data/lib/active_record/relation/spawn_methods.rb +18 -1
  169. data/lib/active_record/relation.rb +75 -34
  170. data/lib/active_record/result.rb +19 -5
  171. data/lib/active_record/runtime_registry.rb +10 -1
  172. data/lib/active_record/sanitization.rb +51 -11
  173. data/lib/active_record/schema.rb +2 -3
  174. data/lib/active_record/schema_dumper.rb +41 -7
  175. data/lib/active_record/schema_migration.rb +68 -33
  176. data/lib/active_record/scoping/default.rb +15 -5
  177. data/lib/active_record/scoping/named.rb +2 -2
  178. data/lib/active_record/scoping.rb +2 -1
  179. data/lib/active_record/secure_password.rb +60 -0
  180. data/lib/active_record/secure_token.rb +21 -3
  181. data/lib/active_record/signed_id.rb +7 -5
  182. data/lib/active_record/store.rb +8 -8
  183. data/lib/active_record/suppressor.rb +3 -1
  184. data/lib/active_record/table_metadata.rb +10 -1
  185. data/lib/active_record/tasks/database_tasks.rb +127 -105
  186. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  187. data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
  188. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -7
  189. data/lib/active_record/test_fixtures.rb +113 -96
  190. data/lib/active_record/timestamp.rb +27 -15
  191. data/lib/active_record/token_for.rb +113 -0
  192. data/lib/active_record/touch_later.rb +11 -6
  193. data/lib/active_record/transactions.rb +36 -10
  194. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  195. data/lib/active_record/type/internal/timezone.rb +7 -2
  196. data/lib/active_record/type/time.rb +4 -0
  197. data/lib/active_record/validations/absence.rb +1 -1
  198. data/lib/active_record/validations/numericality.rb +5 -4
  199. data/lib/active_record/validations/presence.rb +5 -28
  200. data/lib/active_record/validations/uniqueness.rb +47 -2
  201. data/lib/active_record/validations.rb +8 -4
  202. data/lib/active_record/version.rb +1 -1
  203. data/lib/active_record.rb +121 -16
  204. data/lib/arel/errors.rb +10 -0
  205. data/lib/arel/factory_methods.rb +4 -0
  206. data/lib/arel/nodes/binary.rb +6 -1
  207. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  208. data/lib/arel/nodes/cte.rb +36 -0
  209. data/lib/arel/nodes/fragments.rb +35 -0
  210. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  211. data/lib/arel/nodes/leading_join.rb +8 -0
  212. data/lib/arel/nodes/node.rb +111 -2
  213. data/lib/arel/nodes/sql_literal.rb +6 -0
  214. data/lib/arel/nodes/table_alias.rb +4 -0
  215. data/lib/arel/nodes.rb +4 -0
  216. data/lib/arel/predications.rb +2 -0
  217. data/lib/arel/table.rb +9 -5
  218. data/lib/arel/visitors/mysql.rb +8 -1
  219. data/lib/arel/visitors/to_sql.rb +81 -17
  220. data/lib/arel/visitors/visitor.rb +2 -2
  221. data/lib/arel.rb +16 -2
  222. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  223. data/lib/rails/generators/active_record/migration.rb +3 -1
  224. data/lib/rails/generators/active_record/model/USAGE +113 -0
  225. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  226. metadata +52 -14
  227. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  228. data/lib/active_record/null_relation.rb +0 -63
@@ -1,30 +1,49 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/core_ext/enumerable"
4
- require "active_support/core_ext/hash/indifferent_access"
5
- require "active_support/core_ext/string/filters"
4
+ require "active_support/core_ext/module/delegation"
6
5
  require "active_support/parameter_filter"
7
6
  require "concurrent/map"
8
7
 
9
8
  module ActiveRecord
9
+ # = Active Record \Core
10
10
  module Core
11
11
  extend ActiveSupport::Concern
12
+ include ActiveModel::Access
12
13
 
13
14
  included do
14
15
  ##
15
16
  # :singleton-method:
16
17
  #
17
- # Accepts a logger conforming to the interface of Log4r or the default
18
- # Ruby +Logger+ class, which is then passed on to any new database
19
- # connections made. You can retrieve this logger by calling +logger+ on
20
- # either an Active Record model class or an Active Record model instance.
18
+ # Accepts a logger conforming to the interface of Log4r which is then
19
+ # passed on to any new database connections made and which can be
20
+ # retrieved on both a class and instance level by calling +logger+.
21
21
  class_attribute :logger, instance_writer: false
22
22
 
23
+ class_attribute :_destroy_association_async_job, instance_accessor: false, default: "ActiveRecord::DestroyAssociationAsyncJob"
24
+
25
+ # The job class used to destroy associations in the background.
26
+ def self.destroy_association_async_job
27
+ if _destroy_association_async_job.is_a?(String)
28
+ self._destroy_association_async_job = _destroy_association_async_job.constantize
29
+ end
30
+ _destroy_association_async_job
31
+ rescue NameError => error
32
+ raise NameError, "Unable to load destroy_association_async_job: #{error.message}"
33
+ end
34
+
35
+ singleton_class.alias_method :destroy_association_async_job=, :_destroy_association_async_job=
36
+ delegate :destroy_association_async_job, to: :class
37
+
23
38
  ##
24
39
  # :singleton-method:
25
40
  #
26
- # Specifies the job used to destroy associations in the background
27
- class_attribute :destroy_association_async_job, instance_writer: false, instance_predicate: false, default: false
41
+ # Specifies the maximum number of records that will be destroyed in a
42
+ # single background job by the <tt>dependent: :destroy_async</tt>
43
+ # association option. When +nil+ (default), all dependent records will be
44
+ # destroyed in a single background job. If specified, the records to be
45
+ # destroyed will be split into multiple background jobs.
46
+ class_attribute :destroy_association_async_batch_size, instance_writer: false, instance_predicate: false, default: nil
28
47
 
29
48
  ##
30
49
  # Contains the database configuration - as is typically stored in config/database.yml -
@@ -34,19 +53,19 @@ module ActiveRecord
34
53
  #
35
54
  # development:
36
55
  # adapter: sqlite3
37
- # database: db/development.sqlite3
56
+ # database: storage/development.sqlite3
38
57
  #
39
58
  # production:
40
59
  # adapter: sqlite3
41
- # database: db/production.sqlite3
60
+ # database: storage/production.sqlite3
42
61
  #
43
62
  # ...would result in ActiveRecord::Base.configurations to look like this:
44
63
  #
45
64
  # #<ActiveRecord::DatabaseConfigurations:0x00007fd1acbdf800 @configurations=[
46
65
  # #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10 @env_name="development",
47
- # @name="primary", @config={adapter: "sqlite3", database: "db/development.sqlite3"}>,
66
+ # @name="primary", @config={adapter: "sqlite3", database: "storage/development.sqlite3"}>,
48
67
  # #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbdea90 @env_name="production",
49
- # @name="primary", @config={adapter: "sqlite3", database: "db/production.sqlite3"}>
68
+ # @name="primary", @config={adapter: "sqlite3", database: "storage/production.sqlite3"}>
50
69
  # ]>
51
70
  def self.configurations=(config)
52
71
  @@configurations = ActiveRecord::DatabaseConfigurations.new(config)
@@ -72,6 +91,8 @@ module ActiveRecord
72
91
 
73
92
  class_attribute :has_many_inversing, instance_accessor: false, default: false
74
93
 
94
+ class_attribute :run_commit_callbacks_on_first_saved_instances_in_transaction, instance_accessor: false, default: true
95
+
75
96
  class_attribute :default_connection_handler, instance_writer: false
76
97
 
77
98
  class_attribute :default_role, instance_writer: false
@@ -100,33 +121,6 @@ module ActiveRecord
100
121
  ActiveSupport::IsolatedExecutionState[:active_record_connection_handler] = handler
101
122
  end
102
123
 
103
- def self.connection_handlers
104
- if ActiveRecord.legacy_connection_handling
105
- else
106
- raise NotImplementedError, "The new connection handling does not support accessing multiple connection handlers."
107
- end
108
-
109
- @@connection_handlers ||= {}
110
- end
111
-
112
- def self.connection_handlers=(handlers)
113
- if ActiveRecord.legacy_connection_handling
114
- ActiveSupport::Deprecation.warn(<<~MSG)
115
- Using legacy connection handling is deprecated. Please set
116
- `legacy_connection_handling` to `false` in your application.
117
-
118
- The new connection handling does not support `connection_handlers`
119
- getter and setter.
120
-
121
- Read more about how to migrate at: https://guides.rubyonrails.org/v7.0/active_record_multiple_databases.html#migrate-to-the-new-connection-handling
122
- MSG
123
- else
124
- raise NotImplementedError, "The new connection handling does not support multiple connection handlers."
125
- end
126
-
127
- @@connection_handlers = handlers
128
- end
129
-
130
124
  def self.asynchronous_queries_session # :nodoc:
131
125
  asynchronous_queries_tracker.current_session
132
126
  end
@@ -146,16 +140,12 @@ module ActiveRecord
146
140
  # ActiveRecord::Base.current_role #=> :reading
147
141
  # end
148
142
  def self.current_role
149
- if ActiveRecord.legacy_connection_handling
150
- connection_handlers.key(connection_handler) || default_role
151
- else
152
- connected_to_stack.reverse_each do |hash|
153
- return hash[:role] if hash[:role] && hash[:klasses].include?(Base)
154
- return hash[:role] if hash[:role] && hash[:klasses].include?(connection_class_for_self)
155
- end
156
-
157
- default_role
143
+ connected_to_stack.reverse_each do |hash|
144
+ return hash[:role] if hash[:role] && hash[:klasses].include?(Base)
145
+ return hash[:role] if hash[:role] && hash[:klasses].include?(connection_class_for_self)
158
146
  end
147
+
148
+ default_role
159
149
  end
160
150
 
161
151
  # Returns the symbol representing the current connected shard.
@@ -187,16 +177,12 @@ module ActiveRecord
187
177
  # ActiveRecord::Base.current_preventing_writes #=> false
188
178
  # end
189
179
  def self.current_preventing_writes
190
- if ActiveRecord.legacy_connection_handling
191
- connection_handler.prevent_writes
192
- else
193
- connected_to_stack.reverse_each do |hash|
194
- return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(Base)
195
- return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(connection_class_for_self)
196
- end
197
-
198
- false
180
+ connected_to_stack.reverse_each do |hash|
181
+ return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(Base)
182
+ return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(connection_class_for_self)
199
183
  end
184
+
185
+ false
200
186
  end
201
187
 
202
188
  def self.connected_to_stack # :nodoc:
@@ -253,19 +239,6 @@ module ActiveRecord
253
239
  @find_by_statement_cache = { true => Concurrent::Map.new, false => Concurrent::Map.new }
254
240
  end
255
241
 
256
- def inherited(child_class) # :nodoc:
257
- # initialize cache at class definition for thread safety
258
- child_class.initialize_find_by_cache
259
- unless child_class.base_class?
260
- klass = self
261
- until klass.base_class?
262
- klass.initialize_find_by_cache
263
- klass = klass.superclass
264
- end
265
- end
266
- super
267
- end
268
-
269
242
  def find(*ids) # :nodoc:
270
243
  # We don't have cache keys for this stuff yet
271
244
  return super unless ids.length == 1
@@ -275,14 +248,8 @@ module ActiveRecord
275
248
 
276
249
  return super if StatementCache.unsupported_value?(id)
277
250
 
278
- key = primary_key
279
-
280
- statement = cached_find_by_statement(key) { |params|
281
- where(key => params.bind).limit(1)
282
- }
283
-
284
- statement.execute([id], connection).first ||
285
- raise(RecordNotFound.new("Couldn't find #{name} with '#{key}'=#{id}", name, key, id))
251
+ cached_find_by([primary_key], [id]) ||
252
+ raise(RecordNotFound.new("Couldn't find #{name} with '#{primary_key}'=#{id}", name, primary_key, id))
286
253
  end
287
254
 
288
255
  def find_by(*args) # :nodoc:
@@ -314,48 +281,13 @@ module ActiveRecord
314
281
  h[key] = value
315
282
  end
316
283
 
317
- keys = hash.keys
318
- statement = cached_find_by_statement(keys) { |params|
319
- wheres = keys.index_with { params.bind }
320
- where(wheres).limit(1)
321
- }
322
-
323
- begin
324
- statement.execute(hash.values, connection).first
325
- rescue TypeError
326
- raise ActiveRecord::StatementInvalid
327
- end
284
+ cached_find_by(hash.keys, hash.values)
328
285
  end
329
286
 
330
287
  def find_by!(*args) # :nodoc:
331
288
  find_by(*args) || where(*args).raise_record_not_found_exception!
332
289
  end
333
290
 
334
- %w(
335
- reading_role writing_role legacy_connection_handling default_timezone index_nested_attribute_errors
336
- verbose_query_logs queues warn_on_records_fetched_greater_than maintain_test_schema
337
- application_record_class action_on_strict_loading_violation schema_format error_on_ignored_order
338
- timestamped_migrations dump_schema_after_migration dump_schemas suppress_multiple_database_warning
339
- ).each do |attr|
340
- module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
341
- def #{attr}
342
- ActiveSupport::Deprecation.warn(<<~MSG)
343
- ActiveRecord::Base.#{attr} is deprecated and will be removed in Rails 7.1.
344
- Use `ActiveRecord.#{attr}` instead.
345
- MSG
346
- ActiveRecord.#{attr}
347
- end
348
-
349
- def #{attr}=(value)
350
- ActiveSupport::Deprecation.warn(<<~MSG)
351
- ActiveRecord::Base.#{attr}= is deprecated and will be removed in Rails 7.1.
352
- Use `ActiveRecord.#{attr}=` instead.
353
- MSG
354
- ActiveRecord.#{attr} = value
355
- end
356
- RUBY
357
- end
358
-
359
291
  def initialize_generated_modules # :nodoc:
360
292
  generated_association_methods
361
293
  end
@@ -372,10 +304,10 @@ module ActiveRecord
372
304
 
373
305
  # Returns columns which shouldn't be exposed while calling +#inspect+.
374
306
  def filter_attributes
375
- if defined?(@filter_attributes)
376
- @filter_attributes
377
- else
307
+ if @filter_attributes.nil?
378
308
  superclass.filter_attributes
309
+ else
310
+ @filter_attributes
379
311
  end
380
312
  end
381
313
 
@@ -386,13 +318,13 @@ module ActiveRecord
386
318
  end
387
319
 
388
320
  def inspection_filter # :nodoc:
389
- if defined?(@filter_attributes)
321
+ if @filter_attributes.nil?
322
+ superclass.inspection_filter
323
+ else
390
324
  @inspection_filter ||= begin
391
325
  mask = InspectionMask.new(ActiveSupport::ParameterFilter::FILTERED)
392
326
  ActiveSupport::ParameterFilter.new(@filter_attributes, mask: mask)
393
327
  end
394
- else
395
- superclass.inspection_filter
396
328
  end
397
329
  end
398
330
 
@@ -417,7 +349,7 @@ module ActiveRecord
417
349
  object.is_a?(self)
418
350
  end
419
351
 
420
- # Returns an instance of <tt>Arel::Table</tt> loaded with the current table name.
352
+ # Returns an instance of +Arel::Table+ loaded with the current table name.
421
353
  def arel_table # :nodoc:
422
354
  @arel_table ||= Arel::Table.new(table_name, klass: self)
423
355
  end
@@ -436,6 +368,28 @@ module ActiveRecord
436
368
  end
437
369
 
438
370
  private
371
+ def inherited(subclass)
372
+ super
373
+
374
+ # initialize cache at class definition for thread safety
375
+ subclass.initialize_find_by_cache
376
+ unless subclass.base_class?
377
+ klass = self
378
+ until klass.base_class?
379
+ klass.initialize_find_by_cache
380
+ klass = klass.superclass
381
+ end
382
+ end
383
+
384
+ subclass.class_eval do
385
+ @arel_table = nil
386
+ @predicate_builder = nil
387
+ @inspection_filter = nil
388
+ @filter_attributes = nil
389
+ @generated_association_methods = nil
390
+ end
391
+ end
392
+
439
393
  def relation
440
394
  relation = Relation.create(self)
441
395
 
@@ -449,6 +403,19 @@ module ActiveRecord
449
403
  def table_metadata
450
404
  TableMetadata.new(self, arel_table)
451
405
  end
406
+
407
+ def cached_find_by(keys, values)
408
+ statement = cached_find_by_statement(keys) { |params|
409
+ wheres = keys.index_with { params.bind }
410
+ where(wheres).limit(1)
411
+ }
412
+
413
+ begin
414
+ statement.execute(values, connection).first
415
+ rescue TypeError
416
+ raise ActiveRecord::StatementInvalid
417
+ end
418
+ end
452
419
  end
453
420
 
454
421
  # New objects can be instantiated as either empty (pass no construction parameter) or pre-set with
@@ -456,7 +423,7 @@ module ActiveRecord
456
423
  # In both instances, valid attribute keys are determined by the column names of the associated table --
457
424
  # hence you can't have attributes that aren't part of the table columns.
458
425
  #
459
- # ==== Example:
426
+ # ==== Example
460
427
  # # Instantiates a single new object
461
428
  # User.new(first_name: 'Jamie')
462
429
  def initialize(attributes = nil)
@@ -534,12 +501,17 @@ module ActiveRecord
534
501
  # only, not its associations. The extent of a "deep" copy is application
535
502
  # specific and is therefore left to the application to implement according
536
503
  # to its need.
537
- # The dup method does not preserve the timestamps (created|updated)_(at|on).
504
+ # The dup method does not preserve the timestamps (created|updated)_(at|on)
505
+ # and locking column.
538
506
 
539
507
  ##
540
508
  def initialize_dup(other) # :nodoc:
541
509
  @attributes = @attributes.deep_dup
542
- @attributes.reset(@primary_key)
510
+ if self.class.composite_primary_key?
511
+ @primary_key.each { |key| @attributes.reset(key) }
512
+ else
513
+ @attributes.reset(@primary_key)
514
+ end
543
515
 
544
516
  _run_initialize_callbacks
545
517
 
@@ -581,7 +553,7 @@ module ActiveRecord
581
553
  def ==(comparison_object)
582
554
  super ||
583
555
  comparison_object.instance_of?(self.class) &&
584
- !id.nil? &&
556
+ primary_key_values_present? &&
585
557
  comparison_object.id == id
586
558
  end
587
559
  alias :eql? :==
@@ -591,7 +563,7 @@ module ActiveRecord
591
563
  def hash
592
564
  id = self.id
593
565
 
594
- if id
566
+ if primary_key_values_present?
595
567
  self.class.hash ^ id.hash
596
568
  else
597
569
  super
@@ -646,22 +618,27 @@ module ActiveRecord
646
618
  # user.comments
647
619
  # => ActiveRecord::StrictLoadingViolationError
648
620
  #
649
- # === Parameters:
621
+ # ==== Parameters
650
622
  #
651
- # * value - Boolean specifying whether to enable or disable strict loading.
652
- # * mode - Symbol specifying strict loading mode. Defaults to :all. Using
653
- # :n_plus_one_only mode will only raise an error if an association
654
- # that will lead to an n plus one query is lazily loaded.
623
+ # * +value+ - Boolean specifying whether to enable or disable strict loading.
624
+ # * <tt>:mode</tt> - Symbol specifying strict loading mode. Defaults to :all. Using
625
+ # :n_plus_one_only mode will only raise an error if an association that
626
+ # will lead to an n plus one query is lazily loaded.
655
627
  #
656
- # === Example:
628
+ # ==== Examples
657
629
  #
658
630
  # user = User.first
659
631
  # user.strict_loading!(false) # => false
660
632
  # user.comments
661
633
  # => #<ActiveRecord::Associations::CollectionProxy>
634
+ #
635
+ # user.strict_loading!(mode: :n_plus_one_only)
636
+ # user.address.city # => "Tatooine"
637
+ # user.comments
638
+ # => ActiveRecord::StrictLoadingViolationError
662
639
  def strict_loading!(value = true, mode: :all)
663
640
  unless [:all, :n_plus_one_only].include?(mode)
664
- raise ArgumentError, "The :mode option must be one of [:all, :n_plus_one_only]."
641
+ raise ArgumentError, "The :mode option must be one of [:all, :n_plus_one_only] but #{mode.inspect} was provided."
665
642
  end
666
643
 
667
644
  @strict_loading_mode = mode
@@ -689,7 +666,7 @@ module ActiveRecord
689
666
  # We check defined?(@attributes) not to issue warnings if the object is
690
667
  # allocated but not initialized.
691
668
  inspection = if defined?(@attributes) && @attributes
692
- self.class.attribute_names.filter_map do |name|
669
+ attribute_names.filter_map do |name|
693
670
  if _has_attribute?(name)
694
671
  "#{name}: #{attribute_for_inspect(name)}"
695
672
  end
@@ -726,15 +703,26 @@ module ActiveRecord
726
703
  end
727
704
  end
728
705
 
729
- # Returns a hash of the given methods with their names as keys and returned values as values.
730
- def slice(*methods)
731
- methods.flatten.index_with { |method| public_send(method) }.with_indifferent_access
732
- end
733
-
706
+ ##
707
+ # :method: values_at
708
+ #
709
+ # :call-seq: values_at(*methods)
710
+ #
734
711
  # Returns an array of the values returned by the given methods.
735
- def values_at(*methods)
736
- methods.flatten.map! { |method| public_send(method) }
737
- end
712
+ #
713
+ #--
714
+ # Implemented by ActiveModel::Access#values_at.
715
+
716
+ ##
717
+ # :method: slice
718
+ #
719
+ # :call-seq: slice(*methods)
720
+ #
721
+ # Returns a hash of the given methods with their names as keys and returned
722
+ # values as values.
723
+ #
724
+ #--
725
+ # Implemented by ActiveModel::Access#slice.
738
726
 
739
727
  private
740
728
  # +Array#flatten+ will call +#to_ary+ (recursively) on each of the elements of
@@ -764,6 +752,7 @@ module ActiveRecord
764
752
  @strict_loading_mode = :all
765
753
 
766
754
  klass.define_attribute_methods
755
+ klass.generate_alias_attributes
767
756
  end
768
757
 
769
758
  def initialize_internals_callback
@@ -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
 
@@ -3,13 +3,11 @@
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
@@ -19,6 +17,10 @@ module ActiveRecord
19
17
  "#{adapter}_connection"
20
18
  end
21
19
 
20
+ def adapter_class_method
21
+ "#{adapter}_adapter_class"
22
+ end
23
+
22
24
  def host
23
25
  raise NotImplementedError
24
26
  end
@@ -51,6 +53,10 @@ module ActiveRecord
51
53
  raise NotImplementedError
52
54
  end
53
55
 
56
+ def query_cache
57
+ raise NotImplementedError
58
+ end
59
+
54
60
  def checkout_timeout
55
61
  raise NotImplementedError
56
62
  end