activerecord 7.0.8 → 7.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (228) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1401 -1513
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +15 -16
  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 +15 -9
  15. data/lib/active_record/associations/collection_proxy.rb +15 -10
  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 +13 -10
  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 +295 -199
  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 +40 -26
  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 +63 -43
  46. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  47. data/lib/active_record/connection_adapters/abstract/database_statements.rb +128 -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 +289 -122
  54. data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
  55. data/lib/active_record/connection_adapters/abstract_adapter.rb +502 -91
  56. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +207 -108
  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 -13
  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 +71 -40
  71. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  72. data/lib/active_record/connection_adapters/postgresql/quoting.rb +10 -6
  73. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
  74. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  75. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
  76. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
  77. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +347 -54
  78. data/lib/active_record/connection_adapters/postgresql_adapter.rb +337 -176
  79. data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
  80. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  81. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +45 -39
  82. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +4 -3
  83. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +1 -0
  84. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +26 -7
  85. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +209 -79
  86. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  87. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
  88. data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
  89. data/lib/active_record/connection_adapters.rb +3 -1
  90. data/lib/active_record/connection_handling.rb +71 -94
  91. data/lib/active_record/core.rb +134 -146
  92. data/lib/active_record/counter_cache.rb +46 -25
  93. data/lib/active_record/database_configurations/database_config.rb +9 -3
  94. data/lib/active_record/database_configurations/hash_config.rb +22 -12
  95. data/lib/active_record/database_configurations/url_config.rb +17 -11
  96. data/lib/active_record/database_configurations.rb +86 -33
  97. data/lib/active_record/delegated_type.rb +8 -3
  98. data/lib/active_record/deprecator.rb +7 -0
  99. data/lib/active_record/destroy_association_async_job.rb +2 -0
  100. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  101. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  102. data/lib/active_record/encryption/config.rb +25 -1
  103. data/lib/active_record/encryption/configurable.rb +12 -19
  104. data/lib/active_record/encryption/context.rb +10 -3
  105. data/lib/active_record/encryption/contexts.rb +5 -1
  106. data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
  107. data/lib/active_record/encryption/encryptable_record.rb +36 -18
  108. data/lib/active_record/encryption/encrypted_attribute_type.rb +17 -6
  109. data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -54
  110. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  111. data/lib/active_record/encryption/key_generator.rb +12 -1
  112. data/lib/active_record/encryption/message_serializer.rb +2 -0
  113. data/lib/active_record/encryption/properties.rb +3 -3
  114. data/lib/active_record/encryption/scheme.rb +19 -22
  115. data/lib/active_record/encryption.rb +1 -0
  116. data/lib/active_record/enum.rb +113 -26
  117. data/lib/active_record/errors.rb +108 -15
  118. data/lib/active_record/explain.rb +23 -3
  119. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  120. data/lib/active_record/fixture_set/render_context.rb +2 -0
  121. data/lib/active_record/fixture_set/table_row.rb +29 -8
  122. data/lib/active_record/fixtures.rb +119 -71
  123. data/lib/active_record/future_result.rb +30 -5
  124. data/lib/active_record/gem_version.rb +3 -3
  125. data/lib/active_record/inheritance.rb +30 -16
  126. data/lib/active_record/insert_all.rb +55 -8
  127. data/lib/active_record/integration.rb +8 -8
  128. data/lib/active_record/internal_metadata.rb +118 -30
  129. data/lib/active_record/locking/pessimistic.rb +5 -2
  130. data/lib/active_record/log_subscriber.rb +29 -12
  131. data/lib/active_record/marshalling.rb +56 -0
  132. data/lib/active_record/message_pack.rb +124 -0
  133. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  134. data/lib/active_record/middleware/database_selector.rb +5 -7
  135. data/lib/active_record/middleware/shard_selector.rb +3 -1
  136. data/lib/active_record/migration/command_recorder.rb +100 -4
  137. data/lib/active_record/migration/compatibility.rb +131 -5
  138. data/lib/active_record/migration/default_strategy.rb +23 -0
  139. data/lib/active_record/migration/execution_strategy.rb +19 -0
  140. data/lib/active_record/migration.rb +213 -109
  141. data/lib/active_record/model_schema.rb +60 -40
  142. data/lib/active_record/nested_attributes.rb +23 -3
  143. data/lib/active_record/normalization.rb +159 -0
  144. data/lib/active_record/persistence.rb +184 -34
  145. data/lib/active_record/promise.rb +84 -0
  146. data/lib/active_record/query_cache.rb +3 -21
  147. data/lib/active_record/query_logs.rb +77 -52
  148. data/lib/active_record/query_logs_formatter.rb +41 -0
  149. data/lib/active_record/querying.rb +15 -2
  150. data/lib/active_record/railtie.rb +108 -46
  151. data/lib/active_record/railties/controller_runtime.rb +10 -5
  152. data/lib/active_record/railties/databases.rake +139 -145
  153. data/lib/active_record/railties/job_runtime.rb +23 -0
  154. data/lib/active_record/readonly_attributes.rb +32 -5
  155. data/lib/active_record/reflection.rb +162 -44
  156. data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
  157. data/lib/active_record/relation/batches.rb +190 -61
  158. data/lib/active_record/relation/calculations.rb +152 -63
  159. data/lib/active_record/relation/delegation.rb +22 -8
  160. data/lib/active_record/relation/finder_methods.rb +77 -16
  161. data/lib/active_record/relation/merger.rb +2 -0
  162. data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -2
  163. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
  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 +76 -35
  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 +46 -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 +15 -7
  189. data/lib/active_record/test_fixtures.rb +113 -96
  190. data/lib/active_record/timestamp.rb +26 -14
  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 +0 -8
  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 +46 -11
  227. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  228. data/lib/active_record/null_relation.rb +0 -63
@@ -1,14 +1,15 @@
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
  ##
@@ -19,11 +20,30 @@ module ActiveRecord
19
20
  # retrieved on both a class and instance level by calling +logger+.
20
21
  class_attribute :logger, instance_writer: false
21
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
+
22
38
  ##
23
39
  # :singleton-method:
24
40
  #
25
- # Specifies the job used to destroy associations in the background
26
- 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
27
47
 
28
48
  ##
29
49
  # Contains the database configuration - as is typically stored in config/database.yml -
@@ -33,19 +53,19 @@ module ActiveRecord
33
53
  #
34
54
  # development:
35
55
  # adapter: sqlite3
36
- # database: db/development.sqlite3
56
+ # database: storage/development.sqlite3
37
57
  #
38
58
  # production:
39
59
  # adapter: sqlite3
40
- # database: db/production.sqlite3
60
+ # database: storage/production.sqlite3
41
61
  #
42
62
  # ...would result in ActiveRecord::Base.configurations to look like this:
43
63
  #
44
64
  # #<ActiveRecord::DatabaseConfigurations:0x00007fd1acbdf800 @configurations=[
45
65
  # #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10 @env_name="development",
46
- # @name="primary", @config={adapter: "sqlite3", database: "db/development.sqlite3"}>,
66
+ # @name="primary", @config={adapter: "sqlite3", database: "storage/development.sqlite3"}>,
47
67
  # #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbdea90 @env_name="production",
48
- # @name="primary", @config={adapter: "sqlite3", database: "db/production.sqlite3"}>
68
+ # @name="primary", @config={adapter: "sqlite3", database: "storage/production.sqlite3"}>
49
69
  # ]>
50
70
  def self.configurations=(config)
51
71
  @@configurations = ActiveRecord::DatabaseConfigurations.new(config)
@@ -71,6 +91,8 @@ module ActiveRecord
71
91
 
72
92
  class_attribute :has_many_inversing, instance_accessor: false, default: false
73
93
 
94
+ class_attribute :run_commit_callbacks_on_first_saved_instances_in_transaction, instance_accessor: false, default: true
95
+
74
96
  class_attribute :default_connection_handler, instance_writer: false
75
97
 
76
98
  class_attribute :default_role, instance_writer: false
@@ -99,33 +121,6 @@ module ActiveRecord
99
121
  ActiveSupport::IsolatedExecutionState[:active_record_connection_handler] = handler
100
122
  end
101
123
 
102
- def self.connection_handlers
103
- if ActiveRecord.legacy_connection_handling
104
- else
105
- raise NotImplementedError, "The new connection handling does not support accessing multiple connection handlers."
106
- end
107
-
108
- @@connection_handlers ||= {}
109
- end
110
-
111
- def self.connection_handlers=(handlers)
112
- if ActiveRecord.legacy_connection_handling
113
- ActiveSupport::Deprecation.warn(<<~MSG)
114
- Using legacy connection handling is deprecated. Please set
115
- `legacy_connection_handling` to `false` in your application.
116
-
117
- The new connection handling does not support `connection_handlers`
118
- getter and setter.
119
-
120
- Read more about how to migrate at: https://guides.rubyonrails.org/active_record_multiple_databases.html#migrate-to-the-new-connection-handling
121
- MSG
122
- else
123
- raise NotImplementedError, "The new connection handling does not support multiple connection handlers."
124
- end
125
-
126
- @@connection_handlers = handlers
127
- end
128
-
129
124
  def self.asynchronous_queries_session # :nodoc:
130
125
  asynchronous_queries_tracker.current_session
131
126
  end
@@ -145,16 +140,12 @@ module ActiveRecord
145
140
  # ActiveRecord::Base.current_role #=> :reading
146
141
  # end
147
142
  def self.current_role
148
- if ActiveRecord.legacy_connection_handling
149
- connection_handlers.key(connection_handler) || default_role
150
- else
151
- connected_to_stack.reverse_each do |hash|
152
- return hash[:role] if hash[:role] && hash[:klasses].include?(Base)
153
- return hash[:role] if hash[:role] && hash[:klasses].include?(connection_class_for_self)
154
- end
155
-
156
- 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)
157
146
  end
147
+
148
+ default_role
158
149
  end
159
150
 
160
151
  # Returns the symbol representing the current connected shard.
@@ -186,16 +177,12 @@ module ActiveRecord
186
177
  # ActiveRecord::Base.current_preventing_writes #=> false
187
178
  # end
188
179
  def self.current_preventing_writes
189
- if ActiveRecord.legacy_connection_handling
190
- connection_handler.prevent_writes
191
- else
192
- connected_to_stack.reverse_each do |hash|
193
- return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(Base)
194
- return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(connection_class_for_self)
195
- end
196
-
197
- 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)
198
183
  end
184
+
185
+ false
199
186
  end
200
187
 
201
188
  def self.connected_to_stack # :nodoc:
@@ -252,19 +239,6 @@ module ActiveRecord
252
239
  @find_by_statement_cache = { true => Concurrent::Map.new, false => Concurrent::Map.new }
253
240
  end
254
241
 
255
- def inherited(child_class) # :nodoc:
256
- # initialize cache at class definition for thread safety
257
- child_class.initialize_find_by_cache
258
- unless child_class.base_class?
259
- klass = self
260
- until klass.base_class?
261
- klass.initialize_find_by_cache
262
- klass = klass.superclass
263
- end
264
- end
265
- super
266
- end
267
-
268
242
  def find(*ids) # :nodoc:
269
243
  # We don't have cache keys for this stuff yet
270
244
  return super unless ids.length == 1
@@ -274,14 +248,8 @@ module ActiveRecord
274
248
 
275
249
  return super if StatementCache.unsupported_value?(id)
276
250
 
277
- key = primary_key
278
-
279
- statement = cached_find_by_statement(key) { |params|
280
- where(key => params.bind).limit(1)
281
- }
282
-
283
- statement.execute([id], connection).first ||
284
- 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))
285
253
  end
286
254
 
287
255
  def find_by(*args) # :nodoc:
@@ -313,48 +281,13 @@ module ActiveRecord
313
281
  h[key] = value
314
282
  end
315
283
 
316
- keys = hash.keys
317
- statement = cached_find_by_statement(keys) { |params|
318
- wheres = keys.index_with { params.bind }
319
- where(wheres).limit(1)
320
- }
321
-
322
- begin
323
- statement.execute(hash.values, connection).first
324
- rescue TypeError
325
- raise ActiveRecord::StatementInvalid
326
- end
284
+ cached_find_by(hash.keys, hash.values)
327
285
  end
328
286
 
329
287
  def find_by!(*args) # :nodoc:
330
288
  find_by(*args) || where(*args).raise_record_not_found_exception!
331
289
  end
332
290
 
333
- %w(
334
- reading_role writing_role legacy_connection_handling default_timezone index_nested_attribute_errors
335
- verbose_query_logs queues warn_on_records_fetched_greater_than maintain_test_schema
336
- application_record_class action_on_strict_loading_violation schema_format error_on_ignored_order
337
- timestamped_migrations dump_schema_after_migration dump_schemas suppress_multiple_database_warning
338
- ).each do |attr|
339
- module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
340
- def #{attr}
341
- ActiveSupport::Deprecation.warn(<<~MSG)
342
- ActiveRecord::Base.#{attr} is deprecated and will be removed in Rails 7.1.
343
- Use `ActiveRecord.#{attr}` instead.
344
- MSG
345
- ActiveRecord.#{attr}
346
- end
347
-
348
- def #{attr}=(value)
349
- ActiveSupport::Deprecation.warn(<<~MSG)
350
- ActiveRecord::Base.#{attr}= is deprecated and will be removed in Rails 7.1.
351
- Use `ActiveRecord.#{attr}=` instead.
352
- MSG
353
- ActiveRecord.#{attr} = value
354
- end
355
- RUBY
356
- end
357
-
358
291
  def initialize_generated_modules # :nodoc:
359
292
  generated_association_methods
360
293
  end
@@ -371,10 +304,10 @@ module ActiveRecord
371
304
 
372
305
  # Returns columns which shouldn't be exposed while calling +#inspect+.
373
306
  def filter_attributes
374
- if defined?(@filter_attributes)
375
- @filter_attributes
376
- else
307
+ if @filter_attributes.nil?
377
308
  superclass.filter_attributes
309
+ else
310
+ @filter_attributes
378
311
  end
379
312
  end
380
313
 
@@ -385,13 +318,13 @@ module ActiveRecord
385
318
  end
386
319
 
387
320
  def inspection_filter # :nodoc:
388
- if defined?(@filter_attributes)
321
+ if @filter_attributes.nil?
322
+ superclass.inspection_filter
323
+ else
389
324
  @inspection_filter ||= begin
390
325
  mask = InspectionMask.new(ActiveSupport::ParameterFilter::FILTERED)
391
326
  ActiveSupport::ParameterFilter.new(@filter_attributes, mask: mask)
392
327
  end
393
- else
394
- superclass.inspection_filter
395
328
  end
396
329
  end
397
330
 
@@ -411,12 +344,7 @@ module ActiveRecord
411
344
  end
412
345
  end
413
346
 
414
- # Override the default class equality method to provide support for decorated models.
415
- def ===(object) # :nodoc:
416
- object.is_a?(self)
417
- end
418
-
419
- # Returns an instance of <tt>Arel::Table</tt> loaded with the current table name.
347
+ # Returns an instance of +Arel::Table+ loaded with the current table name.
420
348
  def arel_table # :nodoc:
421
349
  @arel_table ||= Arel::Table.new(table_name, klass: self)
422
350
  end
@@ -435,6 +363,28 @@ module ActiveRecord
435
363
  end
436
364
 
437
365
  private
366
+ def inherited(subclass)
367
+ super
368
+
369
+ # initialize cache at class definition for thread safety
370
+ subclass.initialize_find_by_cache
371
+ unless subclass.base_class?
372
+ klass = self
373
+ until klass.base_class?
374
+ klass.initialize_find_by_cache
375
+ klass = klass.superclass
376
+ end
377
+ end
378
+
379
+ subclass.class_eval do
380
+ @arel_table = nil
381
+ @predicate_builder = nil
382
+ @inspection_filter = nil
383
+ @filter_attributes = nil
384
+ @generated_association_methods = nil
385
+ end
386
+ end
387
+
438
388
  def relation
439
389
  relation = Relation.create(self)
440
390
 
@@ -448,6 +398,19 @@ module ActiveRecord
448
398
  def table_metadata
449
399
  TableMetadata.new(self, arel_table)
450
400
  end
401
+
402
+ def cached_find_by(keys, values)
403
+ statement = cached_find_by_statement(keys) { |params|
404
+ wheres = keys.index_with { params.bind }
405
+ where(wheres).limit(1)
406
+ }
407
+
408
+ begin
409
+ statement.execute(values, connection).first
410
+ rescue TypeError
411
+ raise ActiveRecord::StatementInvalid
412
+ end
413
+ end
451
414
  end
452
415
 
453
416
  # New objects can be instantiated as either empty (pass no construction parameter) or pre-set with
@@ -455,7 +418,7 @@ module ActiveRecord
455
418
  # In both instances, valid attribute keys are determined by the column names of the associated table --
456
419
  # hence you can't have attributes that aren't part of the table columns.
457
420
  #
458
- # ==== Example:
421
+ # ==== Example
459
422
  # # Instantiates a single new object
460
423
  # User.new(first_name: 'Jamie')
461
424
  def initialize(attributes = nil)
@@ -533,12 +496,17 @@ module ActiveRecord
533
496
  # only, not its associations. The extent of a "deep" copy is application
534
497
  # specific and is therefore left to the application to implement according
535
498
  # to its need.
536
- # The dup method does not preserve the timestamps (created|updated)_(at|on).
499
+ # The dup method does not preserve the timestamps (created|updated)_(at|on)
500
+ # and locking column.
537
501
 
538
502
  ##
539
503
  def initialize_dup(other) # :nodoc:
540
504
  @attributes = @attributes.deep_dup
541
- @attributes.reset(@primary_key)
505
+ if self.class.composite_primary_key?
506
+ @primary_key.each { |key| @attributes.reset(key) }
507
+ else
508
+ @attributes.reset(@primary_key)
509
+ end
542
510
 
543
511
  _run_initialize_callbacks
544
512
 
@@ -580,7 +548,7 @@ module ActiveRecord
580
548
  def ==(comparison_object)
581
549
  super ||
582
550
  comparison_object.instance_of?(self.class) &&
583
- !id.nil? &&
551
+ primary_key_values_present? &&
584
552
  comparison_object.id == id
585
553
  end
586
554
  alias :eql? :==
@@ -590,7 +558,7 @@ module ActiveRecord
590
558
  def hash
591
559
  id = self.id
592
560
 
593
- if id
561
+ if primary_key_values_present?
594
562
  self.class.hash ^ id.hash
595
563
  else
596
564
  super
@@ -642,25 +610,33 @@ module ActiveRecord
642
610
  #
643
611
  # user = User.first
644
612
  # user.strict_loading! # => true
645
- # user.comments
613
+ # user.address.city
614
+ # => ActiveRecord::StrictLoadingViolationError
615
+ # user.comments.to_a
646
616
  # => ActiveRecord::StrictLoadingViolationError
647
617
  #
648
- # === Parameters:
618
+ # ==== Parameters
649
619
  #
650
- # * value - Boolean specifying whether to enable or disable strict loading.
651
- # * mode - Symbol specifying strict loading mode. Defaults to :all. Using
652
- # :n_plus_one_only mode will only raise an error if an association
653
- # that will lead to an n plus one query is lazily loaded.
620
+ # * +value+ - Boolean specifying whether to enable or disable strict loading.
621
+ # * <tt>:mode</tt> - Symbol specifying strict loading mode. Defaults to :all. Using
622
+ # :n_plus_one_only mode will only raise an error if an association that
623
+ # will lead to an n plus one query is lazily loaded.
654
624
  #
655
- # === Example:
625
+ # ==== Examples
656
626
  #
657
627
  # user = User.first
658
628
  # user.strict_loading!(false) # => false
659
- # user.comments
660
- # => #<ActiveRecord::Associations::CollectionProxy>
629
+ # user.address.city # => "Tatooine"
630
+ # user.comments.to_a # => [#<Comment:0x00...]
631
+ #
632
+ # user.strict_loading!(mode: :n_plus_one_only)
633
+ # user.address.city # => "Tatooine"
634
+ # user.comments.to_a # => [#<Comment:0x00...]
635
+ # user.comments.first.ratings.to_a
636
+ # => ActiveRecord::StrictLoadingViolationError
661
637
  def strict_loading!(value = true, mode: :all)
662
638
  unless [:all, :n_plus_one_only].include?(mode)
663
- raise ArgumentError, "The :mode option must be one of [:all, :n_plus_one_only]."
639
+ raise ArgumentError, "The :mode option must be one of [:all, :n_plus_one_only] but #{mode.inspect} was provided."
664
640
  end
665
641
 
666
642
  @strict_loading_mode = mode
@@ -688,7 +664,7 @@ module ActiveRecord
688
664
  # We check defined?(@attributes) not to issue warnings if the object is
689
665
  # allocated but not initialized.
690
666
  inspection = if defined?(@attributes) && @attributes
691
- self.class.attribute_names.filter_map do |name|
667
+ attribute_names.filter_map do |name|
692
668
  if _has_attribute?(name)
693
669
  "#{name}: #{attribute_for_inspect(name)}"
694
670
  end
@@ -725,15 +701,26 @@ module ActiveRecord
725
701
  end
726
702
  end
727
703
 
728
- # Returns a hash of the given methods with their names as keys and returned values as values.
729
- def slice(*methods)
730
- methods.flatten.index_with { |method| public_send(method) }.with_indifferent_access
731
- end
732
-
704
+ ##
705
+ # :method: values_at
706
+ #
707
+ # :call-seq: values_at(*methods)
708
+ #
733
709
  # Returns an array of the values returned by the given methods.
734
- def values_at(*methods)
735
- methods.flatten.map! { |method| public_send(method) }
736
- end
710
+ #
711
+ #--
712
+ # Implemented by ActiveModel::Access#values_at.
713
+
714
+ ##
715
+ # :method: slice
716
+ #
717
+ # :call-seq: slice(*methods)
718
+ #
719
+ # Returns a hash of the given methods with their names as keys and returned
720
+ # values as values.
721
+ #
722
+ #--
723
+ # Implemented by ActiveModel::Access#slice.
737
724
 
738
725
  private
739
726
  # +Array#flatten+ will call +#to_ary+ (recursively) on each of the elements of
@@ -763,6 +750,7 @@ module ActiveRecord
763
750
  @strict_loading_mode = :all
764
751
 
765
752
  klass.define_attribute_methods
753
+ klass.generate_alias_attributes
766
754
  end
767
755
 
768
756
  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