activerecord 6.1.4.1 → 7.0.0.rc2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


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

Files changed (234) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1050 -981
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/active_record/aggregations.rb +1 -1
  6. data/lib/active_record/association_relation.rb +0 -10
  7. data/lib/active_record/associations/association.rb +33 -17
  8. data/lib/active_record/associations/association_scope.rb +1 -3
  9. data/lib/active_record/associations/belongs_to_association.rb +15 -4
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  11. data/lib/active_record/associations/builder/association.rb +8 -2
  12. data/lib/active_record/associations/builder/belongs_to.rb +19 -6
  13. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  14. data/lib/active_record/associations/builder/has_many.rb +3 -2
  15. data/lib/active_record/associations/builder/has_one.rb +2 -1
  16. data/lib/active_record/associations/builder/singular_association.rb +2 -2
  17. data/lib/active_record/associations/collection_association.rb +34 -27
  18. data/lib/active_record/associations/collection_proxy.rb +8 -3
  19. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  20. data/lib/active_record/associations/has_many_association.rb +1 -1
  21. data/lib/active_record/associations/has_many_through_association.rb +2 -1
  22. data/lib/active_record/associations/has_one_association.rb +10 -7
  23. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  24. data/lib/active_record/associations/preloader/association.rb +187 -55
  25. data/lib/active_record/associations/preloader/batch.rb +48 -0
  26. data/lib/active_record/associations/preloader/branch.rb +147 -0
  27. data/lib/active_record/associations/preloader/through_association.rb +49 -13
  28. data/lib/active_record/associations/preloader.rb +39 -113
  29. data/lib/active_record/associations/singular_association.rb +8 -2
  30. data/lib/active_record/associations/through_association.rb +3 -3
  31. data/lib/active_record/associations.rb +90 -82
  32. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  33. data/lib/active_record/attribute_assignment.rb +1 -1
  34. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  35. data/lib/active_record/attribute_methods/dirty.rb +49 -16
  36. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  37. data/lib/active_record/attribute_methods/query.rb +2 -2
  38. data/lib/active_record/attribute_methods/read.rb +7 -5
  39. data/lib/active_record/attribute_methods/serialization.rb +66 -12
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
  41. data/lib/active_record/attribute_methods/write.rb +7 -10
  42. data/lib/active_record/attribute_methods.rb +13 -14
  43. data/lib/active_record/attributes.rb +24 -35
  44. data/lib/active_record/autosave_association.rb +6 -21
  45. data/lib/active_record/base.rb +19 -1
  46. data/lib/active_record/callbacks.rb +2 -2
  47. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
  48. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
  49. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
  50. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +47 -561
  51. data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -17
  52. data/lib/active_record/connection_adapters/abstract/database_statements.rb +46 -22
  53. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
  54. data/lib/active_record/connection_adapters/abstract/quoting.rb +42 -72
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +34 -9
  57. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +69 -18
  58. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -22
  59. data/lib/active_record/connection_adapters/abstract_adapter.rb +149 -74
  60. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +97 -81
  61. data/lib/active_record/connection_adapters/column.rb +4 -0
  62. data/lib/active_record/connection_adapters/mysql/database_statements.rb +35 -23
  63. data/lib/active_record/connection_adapters/mysql/quoting.rb +35 -21
  64. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +4 -1
  65. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
  66. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  67. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -1
  68. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -12
  69. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  70. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  72. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  73. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +28 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  76. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  77. data/lib/active_record/connection_adapters/postgresql/quoting.rb +50 -50
  78. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -0
  79. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
  80. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
  81. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
  82. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +27 -16
  83. data/lib/active_record/connection_adapters/postgresql_adapter.rb +205 -105
  84. data/lib/active_record/connection_adapters/schema_cache.rb +29 -4
  85. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +25 -19
  86. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +15 -16
  87. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -2
  88. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -30
  89. data/lib/active_record/connection_adapters.rb +6 -5
  90. data/lib/active_record/connection_handling.rb +47 -53
  91. data/lib/active_record/core.rb +122 -132
  92. data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
  93. data/lib/active_record/database_configurations/database_config.rb +12 -9
  94. data/lib/active_record/database_configurations/hash_config.rb +63 -5
  95. data/lib/active_record/database_configurations/url_config.rb +2 -2
  96. data/lib/active_record/database_configurations.rb +16 -32
  97. data/lib/active_record/delegated_type.rb +52 -11
  98. data/lib/active_record/destroy_association_async_job.rb +1 -1
  99. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  100. data/lib/active_record/dynamic_matchers.rb +1 -1
  101. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  102. data/lib/active_record/encryption/cipher.rb +53 -0
  103. data/lib/active_record/encryption/config.rb +44 -0
  104. data/lib/active_record/encryption/configurable.rb +61 -0
  105. data/lib/active_record/encryption/context.rb +35 -0
  106. data/lib/active_record/encryption/contexts.rb +72 -0
  107. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  108. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  109. data/lib/active_record/encryption/encryptable_record.rb +208 -0
  110. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  111. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  112. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  113. data/lib/active_record/encryption/encryptor.rb +155 -0
  114. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  115. data/lib/active_record/encryption/errors.rb +15 -0
  116. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  117. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  118. data/lib/active_record/encryption/key.rb +28 -0
  119. data/lib/active_record/encryption/key_generator.rb +42 -0
  120. data/lib/active_record/encryption/key_provider.rb +46 -0
  121. data/lib/active_record/encryption/message.rb +33 -0
  122. data/lib/active_record/encryption/message_serializer.rb +90 -0
  123. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  124. data/lib/active_record/encryption/properties.rb +76 -0
  125. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  126. data/lib/active_record/encryption/scheme.rb +99 -0
  127. data/lib/active_record/encryption.rb +55 -0
  128. data/lib/active_record/enum.rb +49 -42
  129. data/lib/active_record/errors.rb +67 -4
  130. data/lib/active_record/explain_registry.rb +11 -6
  131. data/lib/active_record/fixture_set/file.rb +15 -1
  132. data/lib/active_record/fixture_set/table_row.rb +41 -6
  133. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  134. data/lib/active_record/fixtures.rb +17 -20
  135. data/lib/active_record/future_result.rb +139 -0
  136. data/lib/active_record/gem_version.rb +4 -4
  137. data/lib/active_record/inheritance.rb +55 -17
  138. data/lib/active_record/insert_all.rb +80 -14
  139. data/lib/active_record/integration.rb +4 -3
  140. data/lib/active_record/internal_metadata.rb +3 -5
  141. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  142. data/lib/active_record/locking/optimistic.rb +10 -9
  143. data/lib/active_record/locking/pessimistic.rb +9 -3
  144. data/lib/active_record/log_subscriber.rb +14 -3
  145. data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
  146. data/lib/active_record/middleware/database_selector.rb +8 -3
  147. data/lib/active_record/middleware/shard_selector.rb +60 -0
  148. data/lib/active_record/migration/command_recorder.rb +4 -4
  149. data/lib/active_record/migration/compatibility.rb +83 -1
  150. data/lib/active_record/migration/join_table.rb +1 -1
  151. data/lib/active_record/migration.rb +109 -79
  152. data/lib/active_record/model_schema.rb +45 -58
  153. data/lib/active_record/nested_attributes.rb +13 -12
  154. data/lib/active_record/no_touching.rb +3 -3
  155. data/lib/active_record/null_relation.rb +2 -6
  156. data/lib/active_record/persistence.rb +219 -52
  157. data/lib/active_record/query_cache.rb +2 -2
  158. data/lib/active_record/query_logs.rb +138 -0
  159. data/lib/active_record/querying.rb +15 -5
  160. data/lib/active_record/railtie.rb +127 -17
  161. data/lib/active_record/railties/controller_runtime.rb +1 -1
  162. data/lib/active_record/railties/databases.rake +66 -129
  163. data/lib/active_record/readonly_attributes.rb +11 -0
  164. data/lib/active_record/reflection.rb +67 -50
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  166. data/lib/active_record/relation/batches.rb +3 -3
  167. data/lib/active_record/relation/calculations.rb +40 -36
  168. data/lib/active_record/relation/delegation.rb +6 -6
  169. data/lib/active_record/relation/finder_methods.rb +31 -35
  170. data/lib/active_record/relation/merger.rb +20 -13
  171. data/lib/active_record/relation/predicate_builder.rb +1 -6
  172. data/lib/active_record/relation/query_attribute.rb +5 -11
  173. data/lib/active_record/relation/query_methods.rb +235 -61
  174. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  175. data/lib/active_record/relation/spawn_methods.rb +2 -2
  176. data/lib/active_record/relation/where_clause.rb +10 -19
  177. data/lib/active_record/relation.rb +171 -84
  178. data/lib/active_record/result.rb +17 -7
  179. data/lib/active_record/runtime_registry.rb +9 -13
  180. data/lib/active_record/sanitization.rb +11 -7
  181. data/lib/active_record/schema_dumper.rb +10 -3
  182. data/lib/active_record/schema_migration.rb +0 -4
  183. data/lib/active_record/scoping/default.rb +61 -12
  184. data/lib/active_record/scoping/named.rb +3 -11
  185. data/lib/active_record/scoping.rb +64 -34
  186. data/lib/active_record/serialization.rb +1 -1
  187. data/lib/active_record/signed_id.rb +1 -1
  188. data/lib/active_record/suppressor.rb +11 -15
  189. data/lib/active_record/tasks/database_tasks.rb +116 -58
  190. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  191. data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -12
  192. data/lib/active_record/test_databases.rb +1 -1
  193. data/lib/active_record/test_fixtures.rb +4 -4
  194. data/lib/active_record/timestamp.rb +3 -4
  195. data/lib/active_record/transactions.rb +9 -14
  196. data/lib/active_record/translation.rb +2 -2
  197. data/lib/active_record/type/adapter_specific_registry.rb +32 -7
  198. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  199. data/lib/active_record/type/internal/timezone.rb +2 -2
  200. data/lib/active_record/type/serialized.rb +1 -1
  201. data/lib/active_record/type/type_map.rb +17 -20
  202. data/lib/active_record/type.rb +1 -2
  203. data/lib/active_record/validations/associated.rb +1 -1
  204. data/lib/active_record/validations/uniqueness.rb +1 -1
  205. data/lib/active_record.rb +204 -28
  206. data/lib/arel/attributes/attribute.rb +0 -8
  207. data/lib/arel/crud.rb +28 -22
  208. data/lib/arel/delete_manager.rb +18 -4
  209. data/lib/arel/filter_predications.rb +9 -0
  210. data/lib/arel/insert_manager.rb +2 -3
  211. data/lib/arel/nodes/casted.rb +1 -1
  212. data/lib/arel/nodes/delete_statement.rb +12 -13
  213. data/lib/arel/nodes/filter.rb +10 -0
  214. data/lib/arel/nodes/function.rb +1 -0
  215. data/lib/arel/nodes/insert_statement.rb +2 -2
  216. data/lib/arel/nodes/select_core.rb +2 -2
  217. data/lib/arel/nodes/select_statement.rb +2 -2
  218. data/lib/arel/nodes/update_statement.rb +8 -3
  219. data/lib/arel/nodes.rb +1 -0
  220. data/lib/arel/predications.rb +11 -3
  221. data/lib/arel/select_manager.rb +10 -4
  222. data/lib/arel/table.rb +0 -1
  223. data/lib/arel/tree_manager.rb +0 -12
  224. data/lib/arel/update_manager.rb +18 -4
  225. data/lib/arel/visitors/dot.rb +80 -90
  226. data/lib/arel/visitors/mysql.rb +8 -2
  227. data/lib/arel/visitors/postgresql.rb +0 -10
  228. data/lib/arel/visitors/to_sql.rb +58 -2
  229. data/lib/arel.rb +2 -1
  230. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  231. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  232. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  233. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  234. metadata +55 -12
@@ -63,8 +63,8 @@ module ActiveRecord
63
63
  # go through Active Record's type casting and serialization.
64
64
  #
65
65
  # See <tt>ActiveRecord::Persistence#insert_all</tt> for documentation.
66
- def insert(attributes, returning: nil, unique_by: nil)
67
- insert_all([ attributes ], returning: returning, unique_by: unique_by)
66
+ def insert(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
67
+ insert_all([ attributes ], returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
68
68
  end
69
69
 
70
70
  # Inserts multiple records into the database in a single SQL INSERT
@@ -91,6 +91,9 @@ module ActiveRecord
91
91
  # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
92
92
  # clause entirely.
93
93
  #
94
+ # 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>).
96
+ #
94
97
  # [:unique_by]
95
98
  # (PostgreSQL and SQLite only) By default rows are considered to be unique
96
99
  # by every unique index on the table. Any duplicate rows are skipped.
@@ -107,6 +110,17 @@ module ActiveRecord
107
110
  # unique_by: %i[ author_id name ]
108
111
  # unique_by: :index_books_on_isbn
109
112
  #
113
+ # [:record_timestamps]
114
+ # By default, automatic setting of timestamp columns is controlled by
115
+ # the model's <tt>record_timestamps</tt> config, matching typical
116
+ # behavior.
117
+ #
118
+ # To override this and force automatic setting of timestamp columns one
119
+ # way or the other, pass <tt>:record_timestamps</tt>:
120
+ #
121
+ # record_timestamps: true # Always set timestamps automatically
122
+ # record_timestamps: false # Never set timestamps automatically
123
+ #
110
124
  # Because it relies on the index information from the database
111
125
  # <tt>:unique_by</tt> is recommended to be paired with
112
126
  # Active Record's schema_cache.
@@ -120,8 +134,16 @@ module ActiveRecord
120
134
  # { id: 1, title: "Rework", author: "David" },
121
135
  # { id: 1, title: "Eloquent Ruby", author: "Russ" }
122
136
  # ])
123
- def insert_all(attributes, returning: nil, unique_by: nil)
124
- InsertAll.new(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by).execute
137
+ #
138
+ # # insert_all works on chained scopes, and you can use create_with
139
+ # # to set default attributes for all inserted records.
140
+ #
141
+ # author.books.create_with(created_at: Time.now).insert_all([
142
+ # { id: 1, title: "Rework" },
143
+ # { id: 2, title: "Eloquent Ruby" }
144
+ # ])
145
+ def insert_all(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
146
+ InsertAll.new(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps).execute
125
147
  end
126
148
 
127
149
  # Inserts a single record into the database in a single SQL INSERT
@@ -130,8 +152,8 @@ module ActiveRecord
130
152
  # go through Active Record's type casting and serialization.
131
153
  #
132
154
  # See <tt>ActiveRecord::Persistence#insert_all!</tt> for more.
133
- def insert!(attributes, returning: nil)
134
- insert_all!([ attributes ], returning: returning)
155
+ def insert!(attributes, returning: nil, record_timestamps: nil)
156
+ insert_all!([ attributes ], returning: returning, record_timestamps: record_timestamps)
135
157
  end
136
158
 
137
159
  # Inserts multiple records into the database in a single SQL INSERT
@@ -160,6 +182,20 @@ module ActiveRecord
160
182
  # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
161
183
  # clause entirely.
162
184
  #
185
+ # 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>).
187
+ #
188
+ # [:record_timestamps]
189
+ # By default, automatic setting of timestamp columns is controlled by
190
+ # the model's <tt>record_timestamps</tt> config, matching typical
191
+ # behavior.
192
+ #
193
+ # To override this and force automatic setting of timestamp columns one
194
+ # way or the other, pass <tt>:record_timestamps</tt>:
195
+ #
196
+ # record_timestamps: true # Always set timestamps automatically
197
+ # record_timestamps: false # Never set timestamps automatically
198
+ #
163
199
  # ==== Examples
164
200
  #
165
201
  # # Insert multiple records
@@ -174,8 +210,8 @@ module ActiveRecord
174
210
  # { id: 1, title: "Rework", author: "David" },
175
211
  # { id: 1, title: "Eloquent Ruby", author: "Russ" }
176
212
  # ])
177
- def insert_all!(attributes, returning: nil)
178
- InsertAll.new(self, attributes, on_duplicate: :raise, returning: returning).execute
213
+ def insert_all!(attributes, returning: nil, record_timestamps: nil)
214
+ InsertAll.new(self, attributes, on_duplicate: :raise, returning: returning, record_timestamps: record_timestamps).execute
179
215
  end
180
216
 
181
217
  # Updates or inserts (upserts) a single record into the database in a
@@ -184,8 +220,8 @@ module ActiveRecord
184
220
  # go through Active Record's type casting and serialization.
185
221
  #
186
222
  # See <tt>ActiveRecord::Persistence#upsert_all</tt> for documentation.
187
- def upsert(attributes, returning: nil, unique_by: nil)
188
- upsert_all([ attributes ], returning: returning, unique_by: unique_by)
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)
189
225
  end
190
226
 
191
227
  # Updates or inserts (upserts) multiple records into the database in a
@@ -199,6 +235,10 @@ module ActiveRecord
199
235
  # Returns an <tt>ActiveRecord::Result</tt> with its contents based on
200
236
  # <tt>:returning</tt> (see below).
201
237
  #
238
+ # By default, +upsert_all+ will update all the columns that can be updated when
239
+ # there is a conflict. These are all the columns except primary keys, read-only
240
+ # columns, and columns covered by the optional +unique_by+.
241
+ #
202
242
  # ==== Options
203
243
  #
204
244
  # [:returning]
@@ -208,6 +248,9 @@ module ActiveRecord
208
248
  # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
209
249
  # clause entirely.
210
250
  #
251
+ # 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>).
253
+ #
211
254
  # [:unique_by]
212
255
  # (PostgreSQL and SQLite only) By default rows are considered to be unique
213
256
  # by every unique index on the table. Any duplicate rows are skipped.
@@ -228,6 +271,54 @@ module ActiveRecord
228
271
  # <tt>:unique_by</tt> is recommended to be paired with
229
272
  # Active Record's schema_cache.
230
273
  #
274
+ # [:on_duplicate]
275
+ # Configure the SQL update sentence that will be used in case of conflict.
276
+ #
277
+ # NOTE: If you use this option you must provide all the columns you want to update
278
+ # by yourself.
279
+ #
280
+ # Example:
281
+ #
282
+ # Commodity.upsert_all(
283
+ # [
284
+ # { id: 2, name: "Copper", price: 4.84 },
285
+ # { id: 4, name: "Gold", price: 1380.87 },
286
+ # { id: 6, name: "Aluminium", price: 0.35 }
287
+ # ],
288
+ # on_duplicate: Arel.sql("price = GREATEST(commodities.price, EXCLUDED.price)")
289
+ # )
290
+ #
291
+ # See the related +:update_only+ option. Both options can't be used at the same time.
292
+ #
293
+ # [:update_only]
294
+ # Provide a list of column names that will be updated in case of conflict. If not provided,
295
+ # +upsert_all+ will update all the columns that can be updated. These are all the columns
296
+ # except primary keys, read-only columns, and columns covered by the optional +unique_by+
297
+ #
298
+ # Example:
299
+ #
300
+ # Commodity.upsert_all(
301
+ # [
302
+ # { id: 2, name: "Copper", price: 4.84 },
303
+ # { id: 4, name: "Gold", price: 1380.87 },
304
+ # { id: 6, name: "Aluminium", price: 0.35 }
305
+ # ],
306
+ # update_only: [:price] # Only prices will be updated
307
+ # )
308
+ #
309
+ # See the related +:on_duplicate+ option. Both options can't be used at the same time.
310
+ #
311
+ # [:record_timestamps]
312
+ # By default, automatic setting of timestamp columns is controlled by
313
+ # the model's <tt>record_timestamps</tt> config, matching typical
314
+ # behavior.
315
+ #
316
+ # To override this and force automatic setting of timestamp columns one
317
+ # way or the other, pass <tt>:record_timestamps</tt>:
318
+ #
319
+ # record_timestamps: true # Always set timestamps automatically
320
+ # record_timestamps: false # Never set timestamps automatically
321
+ #
231
322
  # ==== Examples
232
323
  #
233
324
  # # Inserts multiple records, performing an upsert when records have duplicate ISBNs.
@@ -239,8 +330,8 @@ module ActiveRecord
239
330
  # ], unique_by: :isbn)
240
331
  #
241
332
  # Book.find_by(isbn: "1").title # => "Eloquent Ruby"
242
- def upsert_all(attributes, returning: nil, unique_by: nil)
243
- InsertAll.new(self, attributes, on_duplicate: :update, returning: returning, unique_by: unique_by).execute
333
+ def upsert_all(attributes, on_duplicate: :update, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
334
+ InsertAll.new(self, attributes, on_duplicate: on_duplicate, update_only: update_only, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps).execute
244
335
  end
245
336
 
246
337
  # Given an attributes hash, +instantiate+ returns a new instance of
@@ -264,6 +355,7 @@ module ActiveRecord
264
355
  # ==== Parameters
265
356
  #
266
357
  # * +id+ - This should be the id or an array of ids to be updated.
358
+ # Optional argument, defaults to all records in the relation.
267
359
  # * +attributes+ - This should be a hash of attributes or an array of hashes.
268
360
  #
269
361
  # ==== Examples
@@ -286,6 +378,11 @@ module ActiveRecord
286
378
  # for updating all records in a single query.
287
379
  def update(id = :all, attributes)
288
380
  if id.is_a?(Array)
381
+ if id.any?(ActiveRecord::Base)
382
+ raise ArgumentError,
383
+ "You are passing an array of ActiveRecord::Base instances to `update`. " \
384
+ "Please pass the ids of the objects by calling `pluck(:id)` or `map(&:id)`."
385
+ end
289
386
  id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
290
387
  object.update(attributes[idx])
291
388
  }
@@ -303,6 +400,32 @@ module ActiveRecord
303
400
  end
304
401
  end
305
402
 
403
+ # Updates the object (or multiple objects) just like #update but calls #update! instead
404
+ # of +update+, so an exception is raised if the record is invalid and saving will fail.
405
+ def update!(id = :all, attributes)
406
+ if id.is_a?(Array)
407
+ if id.any?(ActiveRecord::Base)
408
+ raise ArgumentError,
409
+ "You are passing an array of ActiveRecord::Base instances to `update!`. " \
410
+ "Please pass the ids of the objects by calling `pluck(:id)` or `map(&:id)`."
411
+ end
412
+ id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
413
+ object.update!(attributes[idx])
414
+ }
415
+ elsif id == :all
416
+ all.each { |record| record.update!(attributes) }
417
+ else
418
+ if ActiveRecord::Base === id
419
+ raise ArgumentError,
420
+ "You are passing an instance of ActiveRecord::Base to `update!`. " \
421
+ "Please pass the id of the object by calling `.id`."
422
+ end
423
+ object = find(id)
424
+ object.update!(attributes)
425
+ object
426
+ end
427
+ end
428
+
306
429
  # Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
307
430
  # therefore all callbacks and filters are fired off before the object is deleted. This method is
308
431
  # less efficient than #delete but allows cleanup methods and other actions to be run.
@@ -356,40 +479,52 @@ module ActiveRecord
356
479
  primary_key = self.primary_key
357
480
  primary_key_value = nil
358
481
 
359
- if primary_key && Hash === values
360
- primary_key_value = values[primary_key]
361
-
362
- if !primary_key_value && prefetch_primary_key?
482
+ if prefetch_primary_key? && primary_key
483
+ values[primary_key] ||= begin
363
484
  primary_key_value = next_sequence_value
364
- values[primary_key] = primary_key_value
485
+ _default_attributes[primary_key].with_cast_value(primary_key_value)
365
486
  end
366
487
  end
367
488
 
489
+ im = Arel::InsertManager.new(arel_table)
490
+
368
491
  if values.empty?
369
- im = arel_table.compile_insert(connection.empty_insert_statement_value(primary_key))
370
- im.into arel_table
492
+ im.insert(connection.empty_insert_statement_value(primary_key))
371
493
  else
372
- im = arel_table.compile_insert(_substitute_values(values))
494
+ im.insert(values.transform_keys { |name| arel_table[name] })
373
495
  end
374
496
 
375
497
  connection.insert(im, "#{self} Create", primary_key || false, primary_key_value)
376
498
  end
377
499
 
378
500
  def _update_record(values, constraints) # :nodoc:
379
- constraints = _substitute_values(constraints).map { |attr, bind| attr.eq(bind) }
501
+ constraints = constraints.map { |name, value| predicate_builder[name, value] }
502
+
503
+ default_constraint = build_default_constraint
504
+ constraints << default_constraint if default_constraint
505
+
506
+ if current_scope = self.global_current_scope
507
+ constraints << current_scope.where_clause.ast
508
+ end
380
509
 
381
- um = arel_table.where(
382
- constraints.reduce(&:and)
383
- ).compile_update(_substitute_values(values), primary_key)
510
+ um = Arel::UpdateManager.new(arel_table)
511
+ um.set(values.transform_keys { |name| arel_table[name] })
512
+ um.wheres = constraints
384
513
 
385
514
  connection.update(um, "#{self} Update")
386
515
  end
387
516
 
388
517
  def _delete_record(constraints) # :nodoc:
389
- constraints = _substitute_values(constraints).map { |attr, bind| attr.eq(bind) }
518
+ constraints = constraints.map { |name, value| predicate_builder[name, value] }
390
519
 
391
- dm = Arel::DeleteManager.new
392
- dm.from(arel_table)
520
+ default_constraint = build_default_constraint
521
+ constraints << default_constraint if default_constraint
522
+
523
+ if current_scope = self.global_current_scope
524
+ constraints << current_scope.where_clause.ast
525
+ end
526
+
527
+ dm = Arel::DeleteManager.new(arel_table)
393
528
  dm.wheres = constraints
394
529
 
395
530
  connection.delete(dm, "#{self} Destroy")
@@ -412,12 +547,14 @@ module ActiveRecord
412
547
  self
413
548
  end
414
549
 
415
- def _substitute_values(values)
416
- values.map do |name, value|
417
- attr = arel_table[name]
418
- bind = predicate_builder.build_bind_attribute(attr.name, value)
419
- [attr, bind]
420
- end
550
+ # Called by +_update_record+ and +_delete_record+
551
+ # to build `where` clause from default scopes.
552
+ # Skips empty scopes.
553
+ def build_default_constraint
554
+ return unless default_scopes?(all_queries: true)
555
+
556
+ default_where_clause = default_scoped(all_queries: true).where_clause
557
+ default_where_clause.ast unless default_where_clause.empty?
421
558
  end
422
559
  end
423
560
 
@@ -434,6 +571,11 @@ module ActiveRecord
434
571
  @previously_new_record
435
572
  end
436
573
 
574
+ # Returns true if this object was previously persisted but now it has been deleted.
575
+ def previously_persisted?
576
+ !new_record? && destroyed?
577
+ end
578
+
437
579
  # Returns true if this object has been destroyed, otherwise returns false.
438
580
  def destroyed?
439
581
  @destroyed
@@ -556,17 +698,17 @@ module ActiveRecord
556
698
  end
557
699
 
558
700
  # Returns an instance of the specified +klass+ with the attributes of the
559
- # current record. This is mostly useful in relation to single-table
560
- # inheritance structures where you want a subclass to appear as the
701
+ # current record. This is mostly useful in relation to single table
702
+ # inheritance (STI) structures where you want a subclass to appear as the
561
703
  # superclass. This can be used along with record identification in
562
704
  # Action Pack to allow, say, <tt>Client < Company</tt> to do something
563
705
  # like render <tt>partial: @client.becomes(Company)</tt> to render that
564
706
  # instance using the companies/company partial instead of clients/client.
565
707
  #
566
708
  # Note: The new instance will share a link to the same attributes as the original class.
567
- # Therefore the sti column value will still be the same.
709
+ # Therefore the STI column value will still be the same.
568
710
  # Any change to the attributes on either instance will affect both instances.
569
- # If you want to change the sti column as well, use #becomes! instead.
711
+ # If you want to change the STI column as well, use #becomes! instead.
570
712
  def becomes(klass)
571
713
  became = klass.allocate
572
714
 
@@ -581,11 +723,11 @@ module ActiveRecord
581
723
  became
582
724
  end
583
725
 
584
- # Wrapper around #becomes that also changes the instance's sti column value.
726
+ # Wrapper around #becomes that also changes the instance's STI column value.
585
727
  # This is especially useful if you want to persist the changed class in your
586
728
  # database.
587
729
  #
588
- # Note: The old instance's sti column value will be changed too, as both objects
730
+ # Note: The old instance's STI column value will be changed too, as both objects
589
731
  # share the same set of attributes.
590
732
  def becomes!(klass)
591
733
  became = becomes(klass)
@@ -671,14 +813,15 @@ module ActiveRecord
671
813
  verify_readonly_attribute(name) || name
672
814
  end
673
815
 
674
- id_in_database = self.id_in_database
675
- attributes.each do |k, v|
676
- write_attribute_without_type_cast(k, v)
816
+ update_constraints = _primary_key_constraints_hash
817
+ attributes = attributes.each_with_object({}) do |(k, v), h|
818
+ h[k] = @attributes.write_cast_value(k, v)
819
+ clear_attribute_change(k)
677
820
  end
678
821
 
679
822
  affected_rows = self.class._update_record(
680
823
  attributes,
681
- @primary_key => id_in_database
824
+ update_constraints
682
825
  )
683
826
 
684
827
  affected_rows == 1
@@ -800,13 +943,13 @@ module ActiveRecord
800
943
  def reload(options = nil)
801
944
  self.class.connection.clear_query_cache
802
945
 
803
- fresh_object =
804
- if options && options[:lock]
805
- self.class.unscoped { self.class.lock(options[:lock]).find(id) }
806
- else
807
- self.class.unscoped { self.class.find(id) }
808
- end
946
+ fresh_object = if apply_scoping?(options)
947
+ _find_record(options)
948
+ else
949
+ self.class.unscoped { _find_record(options) }
950
+ end
809
951
 
952
+ @association_cache = fresh_object.instance_variable_get(:@association_cache)
810
953
  @attributes = fresh_object.instance_variable_get(:@attributes)
811
954
  @new_record = false
812
955
  @previously_new_record = false
@@ -865,6 +1008,29 @@ module ActiveRecord
865
1008
  end
866
1009
 
867
1010
  private
1011
+ def strict_loaded_associations
1012
+ @association_cache.find_all do |_, assoc|
1013
+ assoc.owner.strict_loading? && !assoc.owner.strict_loading_n_plus_one_only?
1014
+ end.map(&:first)
1015
+ end
1016
+
1017
+ def _find_record(options)
1018
+ if options && options[:lock]
1019
+ self.class.preload(strict_loaded_associations).lock(options[:lock]).find(id)
1020
+ else
1021
+ self.class.preload(strict_loaded_associations).find(id)
1022
+ end
1023
+ end
1024
+
1025
+ def apply_scoping?(options)
1026
+ !(options && options[:unscoped]) &&
1027
+ (self.class.default_scopes?(all_queries: true) || self.class.global_current_scope)
1028
+ end
1029
+
1030
+ def _primary_key_constraints_hash
1031
+ { @primary_key => id_in_database }
1032
+ end
1033
+
868
1034
  # A hook to be overridden by association modules.
869
1035
  def destroy_associations
870
1036
  end
@@ -874,7 +1040,7 @@ module ActiveRecord
874
1040
  end
875
1041
 
876
1042
  def _delete_row
877
- self.class._delete_record(@primary_key => id_in_database)
1043
+ self.class._delete_record(_primary_key_constraints_hash)
878
1044
  end
879
1045
 
880
1046
  def _touch_row(attribute_names, time)
@@ -890,7 +1056,7 @@ module ActiveRecord
890
1056
  def _update_row(attribute_names, attempted_action = "update")
891
1057
  self.class._update_record(
892
1058
  attributes_with_values(attribute_names),
893
- @primary_key => id_in_database
1059
+ _primary_key_constraints_hash
894
1060
  )
895
1061
  end
896
1062
 
@@ -946,7 +1112,8 @@ module ActiveRecord
946
1112
 
947
1113
  def _raise_record_not_destroyed
948
1114
  @_association_destroy_exception ||= nil
949
- raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy the record", self)
1115
+ key = self.class.primary_key
1116
+ raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy #{self.class} with #{key}=#{send(key)}", self)
950
1117
  ensure
951
1118
  @_association_destroy_exception = nil
952
1119
  end
@@ -28,7 +28,7 @@ module ActiveRecord
28
28
  def self.run
29
29
  pools = []
30
30
 
31
- if ActiveRecord::Base.legacy_connection_handling
31
+ if ActiveRecord.legacy_connection_handling
32
32
  ActiveRecord::Base.connection_handlers.each do |key, handler|
33
33
  pools.concat(handler.connection_pool_list.reject { |p| p.query_cache_enabled }.each { |p| p.enable_query_cache! })
34
34
  end
@@ -42,7 +42,7 @@ module ActiveRecord
42
42
  def self.complete(pools)
43
43
  pools.each { |pool| pool.disable_query_cache! }
44
44
 
45
- if ActiveRecord::Base.legacy_connection_handling
45
+ if ActiveRecord.legacy_connection_handling
46
46
  ActiveRecord::Base.connection_handlers.each do |_, handler|
47
47
  handler.connection_pool_list.each do |pool|
48
48
  pool.release_connection if pool.active_connection? && !pool.connection.transaction_open?
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/module/attribute_accessors_per_thread"
4
+
5
+ module ActiveRecord
6
+ # = Active Record Query Logs
7
+ #
8
+ # Automatically tag SQL queries with runtime information.
9
+ #
10
+ # Default tags available for use:
11
+ #
12
+ # * +application+
13
+ # * +pid+
14
+ # * +socket+
15
+ # * +db_host+
16
+ # * +database+
17
+ #
18
+ # _Action Controller and Active Job tags are also defined when used in Rails:_
19
+ #
20
+ # * +controller+
21
+ # * +action+
22
+ # * +job+
23
+ #
24
+ # The tags used in a query can be configured directly:
25
+ #
26
+ # ActiveRecord::QueryLogs.tags = [ :application, :controller, :action, :job ]
27
+ #
28
+ # or via Rails configuration:
29
+ #
30
+ # config.active_record.query_log_tags = [ :application, :controller, :action, :job ]
31
+ #
32
+ # To add new comment tags, add a hash to the tags array containing the keys and values you
33
+ # want to add to the comment. Dynamic content can be created by setting a proc or lambda value in a hash,
34
+ # and can reference any value stored in the +context+ object.
35
+ #
36
+ # Example:
37
+ #
38
+ # tags = [
39
+ # :application,
40
+ # {
41
+ # custom_tag: ->(context) { context[:controller]&.controller_name },
42
+ # custom_value: -> { Custom.value },
43
+ # }
44
+ # ]
45
+ # ActiveRecord::QueryLogs.tags = tags
46
+ #
47
+ # The QueryLogs +context+ can be manipulated via the +ActiveSupport::ExecutionContext.set+ method.
48
+ #
49
+ # Temporary updates limited to the execution of a block:
50
+ #
51
+ # ActiveSupport::ExecutionContext.set(foo: Bar.new) do
52
+ # posts = Post.all
53
+ # end
54
+ #
55
+ # Direct updates to a context value:
56
+ #
57
+ # ActiveSupport::ExecutionContext[:foo] = Bar.new
58
+ #
59
+ # Tag comments can be prepended to the query:
60
+ #
61
+ # ActiveRecord::QueryLogs.prepend_comment = true
62
+ #
63
+ # For applications where the content will not change during the lifetime of
64
+ # the request or job execution, the tags can be cached for reuse in every query:
65
+ #
66
+ # ActiveRecord::QueryLogs.cache_query_log_tags = true
67
+ #
68
+ # This option can be set during application configuration or in a Rails initializer:
69
+ #
70
+ # config.active_record.cache_query_log_tags = true
71
+ module QueryLogs
72
+ mattr_accessor :taggings, instance_accessor: false, default: {}
73
+ mattr_accessor :tags, instance_accessor: false, default: [ :application ]
74
+ mattr_accessor :prepend_comment, instance_accessor: false, default: false
75
+ mattr_accessor :cache_query_log_tags, instance_accessor: false, default: false
76
+ thread_mattr_accessor :cached_comment, instance_accessor: false
77
+
78
+ class << self
79
+ def call(sql) # :nodoc:
80
+ if prepend_comment
81
+ "#{self.comment} #{sql}"
82
+ else
83
+ "#{sql} #{self.comment}"
84
+ end.strip
85
+ end
86
+
87
+ def clear_cache # :nodoc:
88
+ self.cached_comment = nil
89
+ end
90
+
91
+ ActiveSupport::ExecutionContext.after_change { ActiveRecord::QueryLogs.clear_cache }
92
+
93
+ private
94
+ # Returns an SQL comment +String+ containing the query log tags.
95
+ # Sets and returns a cached comment if <tt>cache_query_log_tags</tt> is +true+.
96
+ def comment
97
+ if cache_query_log_tags
98
+ self.cached_comment ||= uncached_comment
99
+ else
100
+ uncached_comment
101
+ end
102
+ end
103
+
104
+ def uncached_comment
105
+ content = tag_content
106
+ if content.present?
107
+ "/*#{escape_sql_comment(content)}*/"
108
+ end
109
+ end
110
+
111
+ def escape_sql_comment(content)
112
+ content.to_s.gsub(%r{ (/ (?: | \g<1>) \*) \+? \s* | \s* (\* (?: | \g<2>) /) }x, "")
113
+ end
114
+
115
+ def tag_content
116
+ context = ActiveSupport::ExecutionContext.to_h
117
+
118
+ tags.flat_map { |i| [*i] }.filter_map do |tag|
119
+ key, handler = tag
120
+ handler ||= taggings[key]
121
+
122
+ val = if handler.nil?
123
+ context[key]
124
+ elsif handler.respond_to?(:call)
125
+ if handler.arity == 0
126
+ handler.call
127
+ else
128
+ handler.call(context)
129
+ end
130
+ else
131
+ handler
132
+ end
133
+ "#{key}:#{val}" unless val.nil?
134
+ end.join(",")
135
+ end
136
+ end
137
+ end
138
+ end
@@ -3,7 +3,7 @@
3
3
  module ActiveRecord
4
4
  module Querying
5
5
  QUERYING_METHODS = [
6
- :find, :find_by, :find_by!, :take, :take!, :first, :first!, :last, :last!,
6
+ :find, :find_by, :find_by!, :take, :take!, :sole, :find_sole_by, :first, :first!, :last, :last!,
7
7
  :second, :second!, :third, :third!, :fourth, :fourth!, :fifth, :fifth!,
8
8
  :forty_two, :forty_two!, :third_to_last, :third_to_last!, :second_to_last, :second_to_last!,
9
9
  :exists?, :any?, :many?, :none?, :one?,
@@ -12,12 +12,12 @@ module ActiveRecord
12
12
  :create_or_find_by, :create_or_find_by!,
13
13
  :destroy_all, :delete_all, :update_all, :touch_all, :destroy_by, :delete_by,
14
14
  :find_each, :find_in_batches, :in_batches,
15
- :select, :reselect, :order, :reorder, :group, :limit, :offset, :joins, :left_joins, :left_outer_joins,
16
- :where, :rewhere, :preload, :extract_associated, :eager_load, :includes, :from, :lock, :readonly,
15
+ :select, :reselect, :order, :in_order_of, :reorder, :group, :limit, :offset, :joins, :left_joins, :left_outer_joins,
16
+ :where, :rewhere, :invert_where, :preload, :extract_associated, :eager_load, :includes, :from, :lock, :readonly,
17
17
  :and, :or, :annotate, :optimizer_hints, :extending,
18
18
  :having, :create_with, :distinct, :references, :none, :unscope, :merge, :except, :only,
19
19
  :count, :average, :minimum, :maximum, :sum, :calculate,
20
- :pluck, :pick, :ids, :strict_loading
20
+ :pluck, :pick, :ids, :strict_loading, :excluding, :without
21
21
  ].freeze # :nodoc:
22
22
  delegate(*QUERYING_METHODS, to: :all)
23
23
 
@@ -43,8 +43,18 @@ module ActiveRecord
43
43
  #
44
44
  # Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date]
45
45
  # Post.find_by_sql ["SELECT body FROM comments WHERE author = :user_id OR approved_by = :user_id", { :user_id => user_id }]
46
+ #
47
+ # Note that building your own SQL query string from user input may expose your application to
48
+ # injection attacks (https://guides.rubyonrails.org/security.html#sql-injection).
46
49
  def find_by_sql(sql, binds = [], preparable: nil, &block)
47
- result_set = connection.select_all(sanitize_sql(sql), "#{name} Load", binds, preparable: preparable)
50
+ _load_from_sql(_query_by_sql(sql, binds, preparable: preparable), &block)
51
+ end
52
+
53
+ def _query_by_sql(sql, binds = [], preparable: nil, async: false) # :nodoc:
54
+ connection.select_all(sanitize_sql(sql), "#{name} Load", binds, preparable: preparable, async: async)
55
+ end
56
+
57
+ def _load_from_sql(result_set, &block) # :nodoc:
48
58
  column_types = result_set.column_types
49
59
 
50
60
  unless column_types.empty?