activerecord 7.0.0 → 7.1.0

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 (249) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1607 -1040
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +17 -18
  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 -12
  15. data/lib/active_record/associations/collection_proxy.rb +22 -12
  16. data/lib/active_record/associations/foreign_association.rb +10 -3
  17. data/lib/active_record/associations/has_many_association.rb +27 -17
  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 +20 -14
  21. data/lib/active_record/associations/preloader/association.rb +27 -6
  22. data/lib/active_record/associations/preloader/through_association.rb +1 -1
  23. data/lib/active_record/associations/preloader.rb +13 -10
  24. data/lib/active_record/associations/singular_association.rb +1 -1
  25. data/lib/active_record/associations/through_association.rb +22 -11
  26. data/lib/active_record/associations.rb +345 -219
  27. data/lib/active_record/attribute_assignment.rb +0 -2
  28. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  29. data/lib/active_record/attribute_methods/dirty.rb +40 -26
  30. data/lib/active_record/attribute_methods/primary_key.rb +76 -24
  31. data/lib/active_record/attribute_methods/query.rb +28 -16
  32. data/lib/active_record/attribute_methods/read.rb +18 -5
  33. data/lib/active_record/attribute_methods/serialization.rb +172 -69
  34. data/lib/active_record/attribute_methods/write.rb +3 -3
  35. data/lib/active_record/attribute_methods.rb +110 -28
  36. data/lib/active_record/attributes.rb +3 -3
  37. data/lib/active_record/autosave_association.rb +56 -10
  38. data/lib/active_record/base.rb +10 -5
  39. data/lib/active_record/callbacks.rb +16 -32
  40. data/lib/active_record/coders/column_serializer.rb +61 -0
  41. data/lib/active_record/coders/json.rb +1 -1
  42. data/lib/active_record/coders/yaml_column.rb +70 -34
  43. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +164 -89
  44. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  45. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
  46. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +63 -43
  47. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  48. data/lib/active_record/connection_adapters/abstract/database_statements.rb +128 -32
  49. data/lib/active_record/connection_adapters/abstract/query_cache.rb +60 -22
  50. data/lib/active_record/connection_adapters/abstract/quoting.rb +52 -8
  51. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  52. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  53. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +163 -29
  54. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  55. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +302 -129
  56. data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
  57. data/lib/active_record/connection_adapters/abstract_adapter.rb +504 -106
  58. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +217 -104
  59. data/lib/active_record/connection_adapters/column.rb +9 -0
  60. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  61. data/lib/active_record/connection_adapters/mysql/database_statements.rb +23 -144
  62. data/lib/active_record/connection_adapters/mysql/quoting.rb +29 -12
  63. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  64. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  65. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  66. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +38 -14
  67. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -0
  68. data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
  69. data/lib/active_record/connection_adapters/pool_config.rb +14 -5
  70. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  71. data/lib/active_record/connection_adapters/postgresql/column.rb +3 -2
  72. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +72 -45
  73. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  74. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -2
  75. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  76. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +3 -1
  77. data/lib/active_record/connection_adapters/postgresql/quoting.rb +41 -8
  78. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +6 -10
  79. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  80. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
  81. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
  82. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +358 -57
  83. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  84. data/lib/active_record/connection_adapters/postgresql_adapter.rb +343 -181
  85. data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
  86. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  87. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +45 -39
  88. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +22 -5
  89. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
  90. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +41 -22
  91. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +242 -81
  92. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  93. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
  94. data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
  95. data/lib/active_record/connection_adapters.rb +3 -1
  96. data/lib/active_record/connection_handling.rb +73 -96
  97. data/lib/active_record/core.rb +136 -148
  98. data/lib/active_record/counter_cache.rb +46 -25
  99. data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -0
  100. data/lib/active_record/database_configurations/database_config.rb +9 -3
  101. data/lib/active_record/database_configurations/hash_config.rb +22 -12
  102. data/lib/active_record/database_configurations/url_config.rb +17 -11
  103. data/lib/active_record/database_configurations.rb +87 -34
  104. data/lib/active_record/delegated_type.rb +9 -4
  105. data/lib/active_record/deprecator.rb +7 -0
  106. data/lib/active_record/destroy_association_async_job.rb +2 -0
  107. data/lib/active_record/disable_joins_association_relation.rb +1 -1
  108. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  109. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  110. data/lib/active_record/encryption/config.rb +25 -1
  111. data/lib/active_record/encryption/configurable.rb +13 -14
  112. data/lib/active_record/encryption/context.rb +10 -3
  113. data/lib/active_record/encryption/contexts.rb +8 -4
  114. data/lib/active_record/encryption/derived_secret_key_provider.rb +9 -3
  115. data/lib/active_record/encryption/deterministic_key_provider.rb +1 -1
  116. data/lib/active_record/encryption/encryptable_record.rb +38 -22
  117. data/lib/active_record/encryption/encrypted_attribute_type.rb +19 -8
  118. data/lib/active_record/encryption/encryptor.rb +7 -7
  119. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +3 -3
  120. data/lib/active_record/encryption/extended_deterministic_queries.rb +83 -71
  121. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  122. data/lib/active_record/encryption/key_generator.rb +12 -1
  123. data/lib/active_record/encryption/message.rb +1 -1
  124. data/lib/active_record/encryption/message_serializer.rb +2 -0
  125. data/lib/active_record/encryption/properties.rb +4 -4
  126. data/lib/active_record/encryption/scheme.rb +20 -23
  127. data/lib/active_record/encryption.rb +1 -0
  128. data/lib/active_record/enum.rb +114 -27
  129. data/lib/active_record/errors.rb +108 -15
  130. data/lib/active_record/explain.rb +23 -3
  131. data/lib/active_record/explain_subscriber.rb +1 -1
  132. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  133. data/lib/active_record/fixture_set/render_context.rb +2 -0
  134. data/lib/active_record/fixture_set/table_row.rb +29 -8
  135. data/lib/active_record/fixtures.rb +121 -73
  136. data/lib/active_record/future_result.rb +30 -5
  137. data/lib/active_record/gem_version.rb +2 -2
  138. data/lib/active_record/inheritance.rb +30 -16
  139. data/lib/active_record/insert_all.rb +55 -8
  140. data/lib/active_record/integration.rb +10 -10
  141. data/lib/active_record/internal_metadata.rb +118 -30
  142. data/lib/active_record/locking/optimistic.rb +32 -18
  143. data/lib/active_record/locking/pessimistic.rb +8 -5
  144. data/lib/active_record/log_subscriber.rb +39 -17
  145. data/lib/active_record/marshalling.rb +56 -0
  146. data/lib/active_record/message_pack.rb +124 -0
  147. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  148. data/lib/active_record/middleware/database_selector.rb +18 -13
  149. data/lib/active_record/middleware/shard_selector.rb +7 -5
  150. data/lib/active_record/migration/command_recorder.rb +104 -9
  151. data/lib/active_record/migration/compatibility.rb +158 -64
  152. data/lib/active_record/migration/default_strategy.rb +23 -0
  153. data/lib/active_record/migration/execution_strategy.rb +19 -0
  154. data/lib/active_record/migration.rb +271 -117
  155. data/lib/active_record/model_schema.rb +82 -50
  156. data/lib/active_record/nested_attributes.rb +23 -3
  157. data/lib/active_record/normalization.rb +159 -0
  158. data/lib/active_record/persistence.rb +200 -47
  159. data/lib/active_record/promise.rb +84 -0
  160. data/lib/active_record/query_cache.rb +3 -21
  161. data/lib/active_record/query_logs.rb +87 -51
  162. data/lib/active_record/query_logs_formatter.rb +41 -0
  163. data/lib/active_record/querying.rb +16 -3
  164. data/lib/active_record/railtie.rb +127 -61
  165. data/lib/active_record/railties/controller_runtime.rb +12 -8
  166. data/lib/active_record/railties/databases.rake +142 -143
  167. data/lib/active_record/railties/job_runtime.rb +23 -0
  168. data/lib/active_record/readonly_attributes.rb +32 -5
  169. data/lib/active_record/reflection.rb +177 -45
  170. data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
  171. data/lib/active_record/relation/batches.rb +190 -61
  172. data/lib/active_record/relation/calculations.rb +200 -83
  173. data/lib/active_record/relation/delegation.rb +23 -9
  174. data/lib/active_record/relation/finder_methods.rb +77 -16
  175. data/lib/active_record/relation/merger.rb +2 -0
  176. data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
  177. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  179. data/lib/active_record/relation/predicate_builder.rb +26 -14
  180. data/lib/active_record/relation/query_attribute.rb +25 -1
  181. data/lib/active_record/relation/query_methods.rb +429 -76
  182. data/lib/active_record/relation/spawn_methods.rb +18 -1
  183. data/lib/active_record/relation.rb +98 -41
  184. data/lib/active_record/result.rb +25 -9
  185. data/lib/active_record/runtime_registry.rb +10 -1
  186. data/lib/active_record/sanitization.rb +57 -16
  187. data/lib/active_record/schema.rb +36 -22
  188. data/lib/active_record/schema_dumper.rb +65 -23
  189. data/lib/active_record/schema_migration.rb +68 -33
  190. data/lib/active_record/scoping/default.rb +20 -12
  191. data/lib/active_record/scoping/named.rb +2 -2
  192. data/lib/active_record/scoping.rb +2 -1
  193. data/lib/active_record/secure_password.rb +60 -0
  194. data/lib/active_record/secure_token.rb +21 -3
  195. data/lib/active_record/serialization.rb +5 -0
  196. data/lib/active_record/signed_id.rb +9 -7
  197. data/lib/active_record/store.rb +16 -11
  198. data/lib/active_record/suppressor.rb +3 -1
  199. data/lib/active_record/table_metadata.rb +16 -3
  200. data/lib/active_record/tasks/database_tasks.rb +138 -107
  201. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  202. data/lib/active_record/tasks/postgresql_database_tasks.rb +17 -15
  203. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  204. data/lib/active_record/test_fixtures.rb +123 -99
  205. data/lib/active_record/timestamp.rb +26 -14
  206. data/lib/active_record/token_for.rb +113 -0
  207. data/lib/active_record/touch_later.rb +11 -6
  208. data/lib/active_record/transactions.rb +39 -13
  209. data/lib/active_record/translation.rb +1 -1
  210. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  211. data/lib/active_record/type/internal/timezone.rb +7 -2
  212. data/lib/active_record/type/serialized.rb +8 -4
  213. data/lib/active_record/type/time.rb +4 -0
  214. data/lib/active_record/validations/absence.rb +1 -1
  215. data/lib/active_record/validations/associated.rb +3 -3
  216. data/lib/active_record/validations/numericality.rb +5 -4
  217. data/lib/active_record/validations/presence.rb +5 -28
  218. data/lib/active_record/validations/uniqueness.rb +50 -5
  219. data/lib/active_record/validations.rb +8 -4
  220. data/lib/active_record/version.rb +1 -1
  221. data/lib/active_record.rb +143 -16
  222. data/lib/arel/errors.rb +10 -0
  223. data/lib/arel/factory_methods.rb +4 -0
  224. data/lib/arel/filter_predications.rb +1 -1
  225. data/lib/arel/nodes/and.rb +4 -0
  226. data/lib/arel/nodes/binary.rb +6 -1
  227. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  228. data/lib/arel/nodes/cte.rb +36 -0
  229. data/lib/arel/nodes/filter.rb +1 -1
  230. data/lib/arel/nodes/fragments.rb +35 -0
  231. data/lib/arel/nodes/homogeneous_in.rb +0 -8
  232. data/lib/arel/nodes/leading_join.rb +8 -0
  233. data/lib/arel/nodes/node.rb +111 -2
  234. data/lib/arel/nodes/sql_literal.rb +6 -0
  235. data/lib/arel/nodes/table_alias.rb +4 -0
  236. data/lib/arel/nodes.rb +4 -0
  237. data/lib/arel/predications.rb +2 -0
  238. data/lib/arel/table.rb +9 -5
  239. data/lib/arel/visitors/mysql.rb +8 -1
  240. data/lib/arel/visitors/to_sql.rb +81 -17
  241. data/lib/arel/visitors/visitor.rb +2 -2
  242. data/lib/arel.rb +16 -2
  243. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  244. data/lib/rails/generators/active_record/migration.rb +3 -1
  245. data/lib/rails/generators/active_record/model/USAGE +113 -0
  246. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  247. metadata +50 -15
  248. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  249. data/lib/active_record/null_relation.rb +0 -63
@@ -57,12 +57,42 @@ module ActiveRecord
57
57
  end
58
58
  end
59
59
 
60
+ # Builds an object (or multiple objects) and returns either the built object or a list of built
61
+ # objects.
62
+ #
63
+ # The +attributes+ parameter can be either a Hash or an Array of Hashes. These Hashes describe the
64
+ # attributes on the objects that are to be built.
65
+ #
66
+ # ==== Examples
67
+ # # Build a single new object
68
+ # User.build(first_name: 'Jamie')
69
+ #
70
+ # # Build an Array of new objects
71
+ # User.build([{ first_name: 'Jamie' }, { first_name: 'Jeremy' }])
72
+ #
73
+ # # Build a single object and pass it into a block to set other attributes.
74
+ # User.build(first_name: 'Jamie') do |u|
75
+ # u.is_admin = false
76
+ # end
77
+ #
78
+ # # Building an Array of new objects using a block, where the block is executed for each object:
79
+ # User.build([{ first_name: 'Jamie' }, { first_name: 'Jeremy' }]) do |u|
80
+ # u.is_admin = false
81
+ # end
82
+ def build(attributes = nil, &block)
83
+ if attributes.is_a?(Array)
84
+ attributes.collect { |attr| build(attr, &block) }
85
+ else
86
+ new(attributes, &block)
87
+ end
88
+ end
89
+
60
90
  # Inserts a single record into the database in a single SQL INSERT
61
91
  # statement. It does not instantiate any models nor does it trigger
62
92
  # Active Record callbacks or validations. Though passed values
63
93
  # go through Active Record's type casting and serialization.
64
94
  #
65
- # See <tt>ActiveRecord::Persistence#insert_all</tt> for documentation.
95
+ # See #insert_all for documentation.
66
96
  def insert(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
67
97
  insert_all([ attributes ], returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
68
98
  end
@@ -79,7 +109,7 @@ module ActiveRecord
79
109
  # duplicate rows are skipped.
80
110
  # Override with <tt>:unique_by</tt> (see below).
81
111
  #
82
- # Returns an <tt>ActiveRecord::Result</tt> with its contents based on
112
+ # Returns an ActiveRecord::Result with its contents based on
83
113
  # <tt>:returning</tt> (see below).
84
114
  #
85
115
  # ==== Options
@@ -92,7 +122,7 @@ module ActiveRecord
92
122
  # clause entirely.
93
123
  #
94
124
  # You can also pass an SQL string if you need more control on the return values
95
- # (for example, <tt>returning: "id, name as new_name"</tt>).
125
+ # (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
96
126
  #
97
127
  # [:unique_by]
98
128
  # (PostgreSQL and SQLite only) By default rows are considered to be unique
@@ -102,7 +132,7 @@ module ActiveRecord
102
132
  #
103
133
  # Consider a Book model where no duplicate ISBNs make sense, but if any
104
134
  # row has an existing id, or is not unique by another unique index,
105
- # <tt>ActiveRecord::RecordNotUnique</tt> is raised.
135
+ # ActiveRecord::RecordNotUnique is raised.
106
136
  #
107
137
  # Unique indexes can be identified by columns or name:
108
138
  #
@@ -151,7 +181,7 @@ module ActiveRecord
151
181
  # Active Record callbacks or validations. Though passed values
152
182
  # go through Active Record's type casting and serialization.
153
183
  #
154
- # See <tt>ActiveRecord::Persistence#insert_all!</tt> for more.
184
+ # See #insert_all! for more.
155
185
  def insert!(attributes, returning: nil, record_timestamps: nil)
156
186
  insert_all!([ attributes ], returning: returning, record_timestamps: record_timestamps)
157
187
  end
@@ -164,13 +194,12 @@ module ActiveRecord
164
194
  # The +attributes+ parameter is an Array of Hashes. Every Hash determines
165
195
  # the attributes for a single row and must have the same keys.
166
196
  #
167
- # Raises <tt>ActiveRecord::RecordNotUnique</tt> if any rows violate a
197
+ # Raises ActiveRecord::RecordNotUnique if any rows violate a
168
198
  # unique index on the table. In that case, no rows are inserted.
169
199
  #
170
- # To skip duplicate rows, see <tt>ActiveRecord::Persistence#insert_all</tt>.
171
- # To replace them, see <tt>ActiveRecord::Persistence#upsert_all</tt>.
200
+ # To skip duplicate rows, see #insert_all. To replace them, see #upsert_all.
172
201
  #
173
- # Returns an <tt>ActiveRecord::Result</tt> with its contents based on
202
+ # Returns an ActiveRecord::Result with its contents based on
174
203
  # <tt>:returning</tt> (see below).
175
204
  #
176
205
  # ==== Options
@@ -183,7 +212,7 @@ module ActiveRecord
183
212
  # clause entirely.
184
213
  #
185
214
  # You can also pass an SQL string if you need more control on the return values
186
- # (for example, <tt>returning: "id, name as new_name"</tt>).
215
+ # (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
187
216
  #
188
217
  # [:record_timestamps]
189
218
  # By default, automatic setting of timestamp columns is controlled by
@@ -219,9 +248,9 @@ module ActiveRecord
219
248
  # it trigger Active Record callbacks or validations. Though passed values
220
249
  # go through Active Record's type casting and serialization.
221
250
  #
222
- # See <tt>ActiveRecord::Persistence#upsert_all</tt> for documentation.
223
- def upsert(attributes, on_duplicate: :update, returning: nil, unique_by: nil, record_timestamps: nil)
224
- upsert_all([ attributes ], on_duplicate: on_duplicate, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
251
+ # See #upsert_all for documentation.
252
+ def upsert(attributes, **kwargs)
253
+ upsert_all([ attributes ], **kwargs)
225
254
  end
226
255
 
227
256
  # Updates or inserts (upserts) multiple records into the database in a
@@ -232,7 +261,7 @@ module ActiveRecord
232
261
  # The +attributes+ parameter is an Array of Hashes. Every Hash determines
233
262
  # the attributes for a single row and must have the same keys.
234
263
  #
235
- # Returns an <tt>ActiveRecord::Result</tt> with its contents based on
264
+ # Returns an ActiveRecord::Result with its contents based on
236
265
  # <tt>:returning</tt> (see below).
237
266
  #
238
267
  # By default, +upsert_all+ will update all the columns that can be updated when
@@ -249,7 +278,7 @@ module ActiveRecord
249
278
  # clause entirely.
250
279
  #
251
280
  # You can also pass an SQL string if you need more control on the return values
252
- # (for example, <tt>returning: "id, name as new_name"</tt>).
281
+ # (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
253
282
  #
254
283
  # [:unique_by]
255
284
  # (PostgreSQL and SQLite only) By default rows are considered to be unique
@@ -259,7 +288,7 @@ module ActiveRecord
259
288
  #
260
289
  # Consider a Book model where no duplicate ISBNs make sense, but if any
261
290
  # row has an existing id, or is not unique by another unique index,
262
- # <tt>ActiveRecord::RecordNotUnique</tt> is raised.
291
+ # ActiveRecord::RecordNotUnique is raised.
263
292
  #
264
293
  # Unique indexes can be identified by columns or name:
265
294
  #
@@ -426,6 +455,62 @@ module ActiveRecord
426
455
  end
427
456
  end
428
457
 
458
+ # Accepts a list of attribute names to be used in the WHERE clause
459
+ # of SELECT / UPDATE / DELETE queries and in the ORDER BY clause for `#first` and `#last` finder methods.
460
+ #
461
+ # class Developer < ActiveRecord::Base
462
+ # query_constraints :company_id, :id
463
+ # end
464
+ #
465
+ # developer = Developer.first
466
+ # # SELECT "developers".* FROM "developers" ORDER BY "developers"."company_id" ASC, "developers"."id" ASC LIMIT 1
467
+ # developer.inspect # => #<Developer id: 1, company_id: 1, ...>
468
+ #
469
+ # developer.update!(name: "Nikita")
470
+ # # UPDATE "developers" SET "name" = 'Nikita' WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
471
+ #
472
+ # It is possible to update attribute used in the query_by clause:
473
+ # developer.update!(company_id: 2)
474
+ # # UPDATE "developers" SET "company_id" = 2 WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
475
+ #
476
+ # developer.name = "Bob"
477
+ # developer.save!
478
+ # # UPDATE "developers" SET "name" = 'Bob' WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
479
+ #
480
+ # developer.destroy!
481
+ # # DELETE FROM "developers" WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
482
+ #
483
+ # developer.delete
484
+ # # DELETE FROM "developers" WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
485
+ #
486
+ # developer.reload
487
+ # # SELECT "developers".* FROM "developers" WHERE "developers"."company_id" = 1 AND "developers"."id" = 1 LIMIT 1
488
+ def query_constraints(*columns_list)
489
+ raise ArgumentError, "You must specify at least one column to be used in querying" if columns_list.empty?
490
+
491
+ @query_constraints_list = columns_list.map(&:to_s)
492
+ @has_query_constraints = @query_constraints_list
493
+ end
494
+
495
+ def has_query_constraints? # :nodoc:
496
+ @has_query_constraints
497
+ end
498
+
499
+ def query_constraints_list # :nodoc:
500
+ @query_constraints_list ||= if base_class? || primary_key != base_class.primary_key
501
+ primary_key if primary_key.is_a?(Array)
502
+ else
503
+ base_class.query_constraints_list
504
+ end
505
+ end
506
+
507
+ # Returns an array of column names to be used in queries. The source of column
508
+ # names is derived from +query_constraints_list+ or +primary_key+. This method
509
+ # is for internal use when the primary key is to be treated as an array.
510
+ def composite_query_constraints_list # :nodoc:
511
+ @composite_query_constraints_list ||= query_constraints_list || Array(primary_key)
512
+ end
513
+
429
514
  # Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
430
515
  # therefore all callbacks and filters are fired off before the object is deleted. This method is
431
516
  # less efficient than #delete but allows cleanup methods and other actions to be run.
@@ -446,7 +531,13 @@ module ActiveRecord
446
531
  # todos = [1,2,3]
447
532
  # Todo.destroy(todos)
448
533
  def destroy(id)
449
- if id.is_a?(Array)
534
+ multiple_ids = if composite_primary_key?
535
+ id.first.is_a?(Array)
536
+ else
537
+ id.is_a?(Array)
538
+ end
539
+
540
+ if multiple_ids
450
541
  find(id).each(&:destroy)
451
542
  else
452
543
  find(id).destroy
@@ -475,7 +566,7 @@ module ActiveRecord
475
566
  delete_by(primary_key => id_or_array)
476
567
  end
477
568
 
478
- def _insert_record(values) # :nodoc:
569
+ def _insert_record(values, returning) # :nodoc:
479
570
  primary_key = self.primary_key
480
571
  primary_key_value = nil
481
572
 
@@ -494,7 +585,10 @@ module ActiveRecord
494
585
  im.insert(values.transform_keys { |name| arel_table[name] })
495
586
  end
496
587
 
497
- connection.insert(im, "#{self} Create", primary_key || false, primary_key_value)
588
+ connection.insert(
589
+ im, "#{self} Create", primary_key || false, primary_key_value,
590
+ returning: returning
591
+ )
498
592
  end
499
593
 
500
594
  def _update_record(values, constraints) # :nodoc:
@@ -531,6 +625,14 @@ module ActiveRecord
531
625
  end
532
626
 
533
627
  private
628
+ def inherited(subclass)
629
+ super
630
+ subclass.class_eval do
631
+ @_query_constraints_list = nil
632
+ @has_query_constraints = false
633
+ end
634
+ end
635
+
534
636
  # Given a class, an attributes hash, +instantiate_instance_of+ returns a
535
637
  # new instance of the class. Accepts only keys as strings.
536
638
  def instantiate_instance_of(klass, attributes, column_types = {}, &block)
@@ -565,7 +667,7 @@ module ActiveRecord
565
667
  end
566
668
 
567
669
  # Returns true if this object was just created -- that is, prior to the last
568
- # save, the object didn't exist in the database and new_record? would have
670
+ # update or delete, the object didn't exist in the database and new_record? would have
569
671
  # returned true.
570
672
  def previously_new_record?
571
673
  @previously_new_record
@@ -664,6 +766,7 @@ module ActiveRecord
664
766
  def delete
665
767
  _delete_row if persisted?
666
768
  @destroyed = true
769
+ @previously_new_record = false
667
770
  freeze
668
771
  end
669
772
 
@@ -677,12 +780,9 @@ module ActiveRecord
677
780
  def destroy
678
781
  _raise_readonly_record_error if readonly?
679
782
  destroy_associations
680
- @_trigger_destroy_callback = if persisted?
681
- destroy_row > 0
682
- else
683
- true
684
- end
783
+ @_trigger_destroy_callback ||= persisted? && destroy_row > 0
685
784
  @destroyed = true
785
+ @previously_new_record = false
686
786
  freeze
687
787
  end
688
788
 
@@ -708,11 +808,14 @@ module ActiveRecord
708
808
  # Note: The new instance will share a link to the same attributes as the original class.
709
809
  # Therefore the STI column value will still be the same.
710
810
  # Any change to the attributes on either instance will affect both instances.
811
+ # This includes any attribute initialization done by the new instance.
812
+ #
711
813
  # If you want to change the STI column as well, use #becomes! instead.
712
814
  def becomes(klass)
713
815
  became = klass.allocate
714
816
 
715
817
  became.send(:initialize) do |becoming|
818
+ @attributes.reverse_merge!(becoming.instance_variable_get(:@attributes))
716
819
  becoming.instance_variable_set(:@attributes, @attributes)
717
820
  becoming.instance_variable_set(:@mutations_from_database, @mutations_from_database ||= nil)
718
821
  becoming.instance_variable_set(:@new_record, new_record?)
@@ -747,7 +850,7 @@ module ActiveRecord
747
850
  # * updated_at/updated_on column is updated if that column is available.
748
851
  # * Updates all the attributes that are dirty in this object.
749
852
  #
750
- # This method raises an ActiveRecord::ActiveRecordError if the
853
+ # This method raises an ActiveRecord::ActiveRecordError if the
751
854
  # attribute is marked as readonly.
752
855
  #
753
856
  # Also see #update_column.
@@ -759,6 +862,28 @@ module ActiveRecord
759
862
  save(validate: false)
760
863
  end
761
864
 
865
+ # Updates a single attribute and saves the record.
866
+ # This is especially useful for boolean flags on existing records. Also note that
867
+ #
868
+ # * Validation is skipped.
869
+ # * \Callbacks are invoked.
870
+ # * updated_at/updated_on column is updated if that column is available.
871
+ # * Updates all the attributes that are dirty in this object.
872
+ #
873
+ # This method raises an ActiveRecord::ActiveRecordError if the
874
+ # attribute is marked as readonly.
875
+ #
876
+ # If any of the <tt>before_*</tt> callbacks throws +:abort+ the action is cancelled
877
+ # and #update_attribute! raises ActiveRecord::RecordNotSaved. See
878
+ # ActiveRecord::Callbacks for further details.
879
+ def update_attribute!(name, value)
880
+ name = name.to_s
881
+ verify_readonly_attribute(name)
882
+ public_send("#{name}=", value)
883
+
884
+ save!(validate: false)
885
+ end
886
+
762
887
  # Updates the attributes of the model from the passed-in hash and saves the
763
888
  # record, all wrapped in a transaction. If the object is invalid, the saving
764
889
  # will fail and false will be returned.
@@ -806,6 +931,7 @@ module ActiveRecord
806
931
  def update_columns(attributes)
807
932
  raise ActiveRecordError, "cannot update a new record" if new_record?
808
933
  raise ActiveRecordError, "cannot update a destroyed record" if destroyed?
934
+ _raise_readonly_record_error if readonly?
809
935
 
810
936
  attributes = attributes.transform_keys do |key|
811
937
  name = key.to_s
@@ -813,7 +939,7 @@ module ActiveRecord
813
939
  verify_readonly_attribute(name) || name
814
940
  end
815
941
 
816
- update_constraints = _primary_key_constraints_hash
942
+ update_constraints = _query_constraints_hash
817
943
  attributes = attributes.each_with_object({}) do |(k, v), h|
818
944
  h[k] = @attributes.write_cast_value(k, v)
819
945
  clear_attribute_change(k)
@@ -944,7 +1070,7 @@ module ActiveRecord
944
1070
  self.class.connection.clear_query_cache
945
1071
 
946
1072
  fresh_object = if apply_scoping?(options)
947
- _find_record(options)
1073
+ _find_record((options || {}).merge(all_queries: true))
948
1074
  else
949
1075
  self.class.unscoped { _find_record(options) }
950
1076
  end
@@ -992,12 +1118,15 @@ module ActiveRecord
992
1118
  #
993
1119
  def touch(*names, time: nil)
994
1120
  _raise_record_not_touched_error unless persisted?
1121
+ _raise_readonly_record_error if readonly?
995
1122
 
996
1123
  attribute_names = timestamp_attributes_for_update_in_model
997
- attribute_names |= names.map! do |name|
1124
+ attribute_names = (attribute_names | names).map! do |name|
998
1125
  name = name.to_s
999
- self.class.attribute_aliases[name] || name
1000
- end unless names.empty?
1126
+ name = self.class.attribute_aliases[name] || name
1127
+ verify_readonly_attribute(name)
1128
+ name
1129
+ end
1001
1130
 
1002
1131
  unless attribute_names.empty?
1003
1132
  affected_rows = _touch_row(attribute_names, time)
@@ -1008,6 +1137,12 @@ module ActiveRecord
1008
1137
  end
1009
1138
 
1010
1139
  private
1140
+ def init_internals
1141
+ super
1142
+ @_trigger_destroy_callback = @_trigger_update_callback = nil
1143
+ @previously_new_record = false
1144
+ end
1145
+
1011
1146
  def strict_loaded_associations
1012
1147
  @association_cache.find_all do |_, assoc|
1013
1148
  assoc.owner.strict_loading? && !assoc.owner.strict_loading_n_plus_one_only?
@@ -1015,10 +1150,23 @@ module ActiveRecord
1015
1150
  end
1016
1151
 
1017
1152
  def _find_record(options)
1153
+ all_queries = options ? options[:all_queries] : nil
1154
+ base = self.class.all(all_queries: all_queries).preload(strict_loaded_associations)
1155
+
1018
1156
  if options && options[:lock]
1019
- self.class.preload(strict_loaded_associations).lock(options[:lock]).find(id)
1157
+ base.lock(options[:lock]).find_by!(_in_memory_query_constraints_hash)
1020
1158
  else
1021
- self.class.preload(strict_loaded_associations).find(id)
1159
+ base.find_by!(_in_memory_query_constraints_hash)
1160
+ end
1161
+ end
1162
+
1163
+ def _in_memory_query_constraints_hash
1164
+ if self.class.query_constraints_list.nil?
1165
+ { @primary_key => id }
1166
+ else
1167
+ self.class.query_constraints_list.index_with do |column_name|
1168
+ attribute(column_name)
1169
+ end
1022
1170
  end
1023
1171
  end
1024
1172
 
@@ -1027,8 +1175,14 @@ module ActiveRecord
1027
1175
  (self.class.default_scopes?(all_queries: true) || self.class.global_current_scope)
1028
1176
  end
1029
1177
 
1030
- def _primary_key_constraints_hash
1031
- { @primary_key => id_in_database }
1178
+ def _query_constraints_hash
1179
+ if self.class.query_constraints_list.nil?
1180
+ { @primary_key => id_in_database }
1181
+ else
1182
+ self.class.query_constraints_list.index_with do |column_name|
1183
+ attribute_in_database(column_name)
1184
+ end
1185
+ end
1032
1186
  end
1033
1187
 
1034
1188
  # A hook to be overridden by association modules.
@@ -1040,7 +1194,7 @@ module ActiveRecord
1040
1194
  end
1041
1195
 
1042
1196
  def _delete_row
1043
- self.class._delete_record(_primary_key_constraints_hash)
1197
+ self.class._delete_record(_query_constraints_hash)
1044
1198
  end
1045
1199
 
1046
1200
  def _touch_row(attribute_names, time)
@@ -1056,7 +1210,7 @@ module ActiveRecord
1056
1210
  def _update_row(attribute_names, attempted_action = "update")
1057
1211
  self.class._update_record(
1058
1212
  attributes_with_values(attribute_names),
1059
- _primary_key_constraints_hash
1213
+ _query_constraints_hash
1060
1214
  )
1061
1215
  end
1062
1216
 
@@ -1092,11 +1246,16 @@ module ActiveRecord
1092
1246
  def _create_record(attribute_names = self.attribute_names)
1093
1247
  attribute_names = attributes_for_create(attribute_names)
1094
1248
 
1095
- new_id = self.class._insert_record(
1096
- attributes_with_values(attribute_names)
1249
+ returning_columns = self.class._returning_columns_for_insert
1250
+
1251
+ returning_values = self.class._insert_record(
1252
+ attributes_with_values(attribute_names),
1253
+ returning_columns
1097
1254
  )
1098
1255
 
1099
- self.id ||= new_id if @primary_key
1256
+ returning_columns.zip(returning_values).each do |column, value|
1257
+ _write_attribute(column, value) if !_read_attribute(column)
1258
+ end if returning_values
1100
1259
 
1101
1260
  @new_record = false
1102
1261
  @previously_new_record = true
@@ -1113,7 +1272,7 @@ module ActiveRecord
1113
1272
  def _raise_record_not_destroyed
1114
1273
  @_association_destroy_exception ||= nil
1115
1274
  key = self.class.primary_key
1116
- raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy #{self.class} with #{key}=#{send(key)}", self)
1275
+ raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy #{self.class} with #{key}=#{id}", self)
1117
1276
  ensure
1118
1277
  @_association_destroy_exception = nil
1119
1278
  end
@@ -1128,11 +1287,5 @@ module ActiveRecord
1128
1287
  persisted?, new_record?, or destroyed? before touching.
1129
1288
  MSG
1130
1289
  end
1131
-
1132
- # The name of the method used to touch a +belongs_to+ association when the
1133
- # +:touch+ option is used.
1134
- def belongs_to_touch_method
1135
- :touch
1136
- end
1137
1290
  end
1138
1291
  end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ class Promise < BasicObject
5
+ undef_method :==, :!, :!=
6
+
7
+ def initialize(future_result, block) # :nodoc:
8
+ @future_result = future_result
9
+ @block = block
10
+ end
11
+
12
+ # Returns whether the associated query is still being executed or not.
13
+ def pending?
14
+ @future_result.pending?
15
+ end
16
+
17
+ # Returns the query result.
18
+ # If the query wasn't completed yet, accessing +#value+ will block until the query completes.
19
+ # If the query failed, +#value+ will raise the corresponding error.
20
+ def value
21
+ return @value if defined? @value
22
+
23
+ result = @future_result.result
24
+ @value = if @block
25
+ @block.call(result)
26
+ else
27
+ result
28
+ end
29
+ end
30
+
31
+ # Returns a new +ActiveRecord::Promise+ that will apply the passed block
32
+ # when the value is accessed:
33
+ #
34
+ # Post.async_pluck(:title).then { |title| title.upcase }.value
35
+ # # => "POST TITLE"
36
+ def then(&block)
37
+ Promise.new(@future_result, @block ? @block >> block : block)
38
+ end
39
+
40
+ [:class, :respond_to?, :is_a?].each do |method|
41
+ define_method(method, ::Object.instance_method(method))
42
+ end
43
+
44
+ def inspect # :nodoc:
45
+ "#<ActiveRecord::Promise status=#{status}>"
46
+ end
47
+
48
+ def pretty_print(q) # :nodoc:
49
+ q.text(inspect)
50
+ end
51
+
52
+ private
53
+ def status
54
+ if @future_result.pending?
55
+ :pending
56
+ elsif @future_result.canceled?
57
+ :canceled
58
+ else
59
+ :complete
60
+ end
61
+ end
62
+
63
+ class Complete < self # :nodoc:
64
+ attr_reader :value
65
+
66
+ def initialize(value)
67
+ @value = value
68
+ end
69
+
70
+ def then
71
+ Complete.new(yield @value)
72
+ end
73
+
74
+ def pending?
75
+ false
76
+ end
77
+
78
+ private
79
+ def status
80
+ :complete
81
+ end
82
+ end
83
+ end
84
+ end
@@ -26,32 +26,14 @@ module ActiveRecord
26
26
  end
27
27
 
28
28
  def self.run
29
- pools = []
30
-
31
- if ActiveRecord.legacy_connection_handling
32
- ActiveRecord::Base.connection_handlers.each do |key, handler|
33
- pools.concat(handler.connection_pool_list.reject { |p| p.query_cache_enabled }.each { |p| p.enable_query_cache! })
34
- end
35
- else
36
- pools.concat(ActiveRecord::Base.connection_handler.all_connection_pools.reject { |p| p.query_cache_enabled }.each { |p| p.enable_query_cache! })
37
- end
38
-
39
- pools
29
+ ActiveRecord::Base.connection_handler.each_connection_pool.reject { |p| p.query_cache_enabled }.each { |p| p.enable_query_cache! }
40
30
  end
41
31
 
42
32
  def self.complete(pools)
43
33
  pools.each { |pool| pool.disable_query_cache! }
44
34
 
45
- if ActiveRecord.legacy_connection_handling
46
- ActiveRecord::Base.connection_handlers.each do |_, handler|
47
- handler.connection_pool_list.each do |pool|
48
- pool.release_connection if pool.active_connection? && !pool.connection.transaction_open?
49
- end
50
- end
51
- else
52
- ActiveRecord::Base.connection_handler.all_connection_pools.each do |pool|
53
- pool.release_connection if pool.active_connection? && !pool.connection.transaction_open?
54
- end
35
+ ActiveRecord::Base.connection_handler.each_connection_pool do |pool|
36
+ pool.release_connection if pool.active_connection? && !pool.connection.transaction_open?
55
37
  end
56
38
  end
57
39