activerecord 6.1.6 → 7.0.4

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 (243) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1314 -975
  3. data/README.rdoc +1 -1
  4. data/lib/active_record/aggregations.rb +1 -1
  5. data/lib/active_record/association_relation.rb +0 -10
  6. data/lib/active_record/associations/association.rb +33 -17
  7. data/lib/active_record/associations/association_scope.rb +1 -3
  8. data/lib/active_record/associations/belongs_to_association.rb +15 -4
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  10. data/lib/active_record/associations/builder/association.rb +8 -2
  11. data/lib/active_record/associations/builder/belongs_to.rb +19 -6
  12. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  13. data/lib/active_record/associations/builder/has_many.rb +3 -2
  14. data/lib/active_record/associations/builder/has_one.rb +2 -1
  15. data/lib/active_record/associations/builder/singular_association.rb +2 -2
  16. data/lib/active_record/associations/collection_association.rb +19 -21
  17. data/lib/active_record/associations/collection_proxy.rb +10 -5
  18. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  19. data/lib/active_record/associations/has_many_association.rb +8 -5
  20. data/lib/active_record/associations/has_many_through_association.rb +2 -1
  21. data/lib/active_record/associations/has_one_association.rb +10 -7
  22. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  23. data/lib/active_record/associations/join_dependency.rb +23 -15
  24. data/lib/active_record/associations/preloader/association.rb +186 -52
  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 +124 -95
  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 +57 -19
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +8 -3
  41. data/lib/active_record/attribute_methods/write.rb +7 -10
  42. data/lib/active_record/attribute_methods.rb +14 -15
  43. data/lib/active_record/attributes.rb +24 -35
  44. data/lib/active_record/autosave_association.rb +8 -23
  45. data/lib/active_record/base.rb +19 -1
  46. data/lib/active_record/callbacks.rb +2 -2
  47. data/lib/active_record/coders/yaml_column.rb +10 -2
  48. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
  49. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
  50. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +47 -561
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -17
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +46 -22
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +42 -72
  56. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
  57. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +38 -13
  58. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +80 -24
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -22
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +149 -74
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +105 -81
  63. data/lib/active_record/connection_adapters/column.rb +4 -0
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +36 -24
  65. data/lib/active_record/connection_adapters/mysql/quoting.rb +37 -21
  66. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -1
  67. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +20 -1
  68. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
  69. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  70. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -1
  71. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -12
  72. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  75. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  76. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  79. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  80. data/lib/active_record/connection_adapters/postgresql/quoting.rb +51 -51
  81. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +34 -0
  82. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
  83. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
  84. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
  85. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +37 -19
  86. data/lib/active_record/connection_adapters/postgresql_adapter.rb +208 -107
  87. data/lib/active_record/connection_adapters/schema_cache.rb +39 -38
  88. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +25 -19
  89. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +28 -16
  90. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +17 -15
  91. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +96 -32
  92. data/lib/active_record/connection_adapters.rb +6 -5
  93. data/lib/active_record/connection_handling.rb +49 -55
  94. data/lib/active_record/core.rb +124 -134
  95. data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
  96. data/lib/active_record/database_configurations/database_config.rb +12 -9
  97. data/lib/active_record/database_configurations/hash_config.rb +63 -5
  98. data/lib/active_record/database_configurations/url_config.rb +2 -2
  99. data/lib/active_record/database_configurations.rb +15 -32
  100. data/lib/active_record/delegated_type.rb +53 -12
  101. data/lib/active_record/destroy_association_async_job.rb +1 -1
  102. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  103. data/lib/active_record/dynamic_matchers.rb +1 -1
  104. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  105. data/lib/active_record/encryption/cipher.rb +53 -0
  106. data/lib/active_record/encryption/config.rb +44 -0
  107. data/lib/active_record/encryption/configurable.rb +67 -0
  108. data/lib/active_record/encryption/context.rb +35 -0
  109. data/lib/active_record/encryption/contexts.rb +72 -0
  110. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  111. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  112. data/lib/active_record/encryption/encryptable_record.rb +206 -0
  113. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  114. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  115. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  116. data/lib/active_record/encryption/encryptor.rb +155 -0
  117. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  118. data/lib/active_record/encryption/errors.rb +15 -0
  119. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  120. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  121. data/lib/active_record/encryption/key.rb +28 -0
  122. data/lib/active_record/encryption/key_generator.rb +42 -0
  123. data/lib/active_record/encryption/key_provider.rb +46 -0
  124. data/lib/active_record/encryption/message.rb +33 -0
  125. data/lib/active_record/encryption/message_serializer.rb +90 -0
  126. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  127. data/lib/active_record/encryption/properties.rb +76 -0
  128. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  129. data/lib/active_record/encryption/scheme.rb +99 -0
  130. data/lib/active_record/encryption.rb +55 -0
  131. data/lib/active_record/enum.rb +50 -43
  132. data/lib/active_record/errors.rb +67 -4
  133. data/lib/active_record/explain_registry.rb +11 -6
  134. data/lib/active_record/fixture_set/file.rb +15 -1
  135. data/lib/active_record/fixture_set/table_row.rb +41 -6
  136. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  137. data/lib/active_record/fixtures.rb +20 -23
  138. data/lib/active_record/future_result.rb +139 -0
  139. data/lib/active_record/gem_version.rb +4 -4
  140. data/lib/active_record/inheritance.rb +55 -17
  141. data/lib/active_record/insert_all.rb +80 -14
  142. data/lib/active_record/integration.rb +4 -3
  143. data/lib/active_record/internal_metadata.rb +1 -5
  144. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  145. data/lib/active_record/locking/optimistic.rb +10 -9
  146. data/lib/active_record/locking/pessimistic.rb +10 -4
  147. data/lib/active_record/log_subscriber.rb +23 -7
  148. data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
  149. data/lib/active_record/middleware/database_selector.rb +18 -6
  150. data/lib/active_record/middleware/shard_selector.rb +60 -0
  151. data/lib/active_record/migration/command_recorder.rb +7 -7
  152. data/lib/active_record/migration/compatibility.rb +84 -2
  153. data/lib/active_record/migration/join_table.rb +1 -1
  154. data/lib/active_record/migration.rb +114 -83
  155. data/lib/active_record/model_schema.rb +58 -59
  156. data/lib/active_record/nested_attributes.rb +13 -12
  157. data/lib/active_record/no_touching.rb +3 -3
  158. data/lib/active_record/null_relation.rb +2 -6
  159. data/lib/active_record/persistence.rb +228 -60
  160. data/lib/active_record/query_cache.rb +2 -2
  161. data/lib/active_record/query_logs.rb +138 -0
  162. data/lib/active_record/querying.rb +16 -6
  163. data/lib/active_record/railtie.rb +136 -22
  164. data/lib/active_record/railties/controller_runtime.rb +1 -1
  165. data/lib/active_record/railties/databases.rake +78 -136
  166. data/lib/active_record/readonly_attributes.rb +11 -0
  167. data/lib/active_record/reflection.rb +73 -50
  168. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  169. data/lib/active_record/relation/batches.rb +6 -6
  170. data/lib/active_record/relation/calculations.rb +43 -38
  171. data/lib/active_record/relation/delegation.rb +7 -7
  172. data/lib/active_record/relation/finder_methods.rb +31 -35
  173. data/lib/active_record/relation/merger.rb +20 -13
  174. data/lib/active_record/relation/predicate_builder.rb +1 -6
  175. data/lib/active_record/relation/query_attribute.rb +5 -11
  176. data/lib/active_record/relation/query_methods.rb +276 -67
  177. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  178. data/lib/active_record/relation/spawn_methods.rb +2 -2
  179. data/lib/active_record/relation/where_clause.rb +10 -19
  180. data/lib/active_record/relation.rb +189 -88
  181. data/lib/active_record/result.rb +17 -7
  182. data/lib/active_record/runtime_registry.rb +9 -13
  183. data/lib/active_record/sanitization.rb +17 -12
  184. data/lib/active_record/schema.rb +38 -23
  185. data/lib/active_record/schema_dumper.rb +25 -19
  186. data/lib/active_record/schema_migration.rb +4 -4
  187. data/lib/active_record/scoping/default.rb +60 -13
  188. data/lib/active_record/scoping/named.rb +3 -11
  189. data/lib/active_record/scoping.rb +64 -34
  190. data/lib/active_record/serialization.rb +6 -1
  191. data/lib/active_record/signed_id.rb +3 -3
  192. data/lib/active_record/store.rb +7 -2
  193. data/lib/active_record/suppressor.rb +11 -15
  194. data/lib/active_record/tasks/database_tasks.rb +127 -60
  195. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  196. data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -13
  197. data/lib/active_record/test_databases.rb +1 -1
  198. data/lib/active_record/test_fixtures.rb +16 -9
  199. data/lib/active_record/timestamp.rb +3 -4
  200. data/lib/active_record/transactions.rb +9 -14
  201. data/lib/active_record/translation.rb +3 -3
  202. data/lib/active_record/type/adapter_specific_registry.rb +32 -7
  203. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  204. data/lib/active_record/type/internal/timezone.rb +2 -2
  205. data/lib/active_record/type/serialized.rb +1 -1
  206. data/lib/active_record/type/type_map.rb +17 -20
  207. data/lib/active_record/type.rb +1 -2
  208. data/lib/active_record/validations/associated.rb +4 -4
  209. data/lib/active_record/validations/presence.rb +2 -2
  210. data/lib/active_record/validations/uniqueness.rb +4 -4
  211. data/lib/active_record/version.rb +1 -1
  212. data/lib/active_record.rb +217 -27
  213. data/lib/arel/attributes/attribute.rb +0 -8
  214. data/lib/arel/crud.rb +28 -22
  215. data/lib/arel/delete_manager.rb +18 -4
  216. data/lib/arel/filter_predications.rb +9 -0
  217. data/lib/arel/insert_manager.rb +2 -3
  218. data/lib/arel/nodes/casted.rb +1 -1
  219. data/lib/arel/nodes/delete_statement.rb +12 -13
  220. data/lib/arel/nodes/filter.rb +10 -0
  221. data/lib/arel/nodes/function.rb +1 -0
  222. data/lib/arel/nodes/insert_statement.rb +2 -2
  223. data/lib/arel/nodes/select_core.rb +2 -2
  224. data/lib/arel/nodes/select_statement.rb +2 -2
  225. data/lib/arel/nodes/update_statement.rb +8 -3
  226. data/lib/arel/nodes.rb +1 -0
  227. data/lib/arel/predications.rb +11 -3
  228. data/lib/arel/select_manager.rb +10 -4
  229. data/lib/arel/table.rb +0 -1
  230. data/lib/arel/tree_manager.rb +0 -12
  231. data/lib/arel/update_manager.rb +18 -4
  232. data/lib/arel/visitors/dot.rb +80 -90
  233. data/lib/arel/visitors/mysql.rb +8 -2
  234. data/lib/arel/visitors/postgresql.rb +0 -10
  235. data/lib/arel/visitors/to_sql.rb +58 -2
  236. data/lib/arel.rb +2 -1
  237. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  238. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  239. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  240. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  241. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  242. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  243. metadata +55 -11
@@ -62,9 +62,9 @@ module ActiveRecord
62
62
  # Active Record callbacks or validations. Though passed values
63
63
  # go through Active Record's type casting and serialization.
64
64
  #
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)
65
+ # See #insert_all for documentation.
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
@@ -79,7 +79,7 @@ module ActiveRecord
79
79
  # duplicate rows are skipped.
80
80
  # Override with <tt>:unique_by</tt> (see below).
81
81
  #
82
- # Returns an <tt>ActiveRecord::Result</tt> with its contents based on
82
+ # Returns an ActiveRecord::Result with its contents based on
83
83
  # <tt>:returning</tt> (see below).
84
84
  #
85
85
  # ==== Options
@@ -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
@@ -129,9 +151,9 @@ module ActiveRecord
129
151
  # Active Record callbacks or validations. Though passed values
130
152
  # go through Active Record's type casting and serialization.
131
153
  #
132
- # See <tt>ActiveRecord::Persistence#insert_all!</tt> for more.
133
- def insert!(attributes, returning: nil)
134
- insert_all!([ attributes ], returning: returning)
154
+ # See #insert_all! for more.
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
@@ -145,10 +167,9 @@ module ActiveRecord
145
167
  # Raises <tt>ActiveRecord::RecordNotUnique</tt> if any rows violate a
146
168
  # unique index on the table. In that case, no rows are inserted.
147
169
  #
148
- # To skip duplicate rows, see <tt>ActiveRecord::Persistence#insert_all</tt>.
149
- # To replace them, see <tt>ActiveRecord::Persistence#upsert_all</tt>.
170
+ # To skip duplicate rows, see #insert_all. To replace them, see #upsert_all.
150
171
  #
151
- # Returns an <tt>ActiveRecord::Result</tt> with its contents based on
172
+ # Returns an ActiveRecord::Result with its contents based on
152
173
  # <tt>:returning</tt> (see below).
153
174
  #
154
175
  # ==== Options
@@ -160,6 +181,20 @@ module ActiveRecord
160
181
  # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
161
182
  # clause entirely.
162
183
  #
184
+ # You can also pass an SQL string if you need more control on the return values
185
+ # (for example, <tt>returning: "id, name as new_name"</tt>).
186
+ #
187
+ # [:record_timestamps]
188
+ # By default, automatic setting of timestamp columns is controlled by
189
+ # the model's <tt>record_timestamps</tt> config, matching typical
190
+ # behavior.
191
+ #
192
+ # To override this and force automatic setting of timestamp columns one
193
+ # way or the other, pass <tt>:record_timestamps</tt>:
194
+ #
195
+ # record_timestamps: true # Always set timestamps automatically
196
+ # record_timestamps: false # Never set timestamps automatically
197
+ #
163
198
  # ==== Examples
164
199
  #
165
200
  # # Insert multiple records
@@ -174,8 +209,8 @@ module ActiveRecord
174
209
  # { id: 1, title: "Rework", author: "David" },
175
210
  # { id: 1, title: "Eloquent Ruby", author: "Russ" }
176
211
  # ])
177
- def insert_all!(attributes, returning: nil)
178
- InsertAll.new(self, attributes, on_duplicate: :raise, returning: returning).execute
212
+ def insert_all!(attributes, returning: nil, record_timestamps: nil)
213
+ InsertAll.new(self, attributes, on_duplicate: :raise, returning: returning, record_timestamps: record_timestamps).execute
179
214
  end
180
215
 
181
216
  # Updates or inserts (upserts) a single record into the database in a
@@ -183,9 +218,9 @@ module ActiveRecord
183
218
  # it trigger Active Record callbacks or validations. Though passed values
184
219
  # go through Active Record's type casting and serialization.
185
220
  #
186
- # 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)
221
+ # See #upsert_all for documentation.
222
+ def upsert(attributes, on_duplicate: :update, returning: nil, unique_by: nil, record_timestamps: nil)
223
+ upsert_all([ attributes ], on_duplicate: on_duplicate, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
189
224
  end
190
225
 
191
226
  # Updates or inserts (upserts) multiple records into the database in a
@@ -196,9 +231,13 @@ module ActiveRecord
196
231
  # The +attributes+ parameter is an Array of Hashes. Every Hash determines
197
232
  # the attributes for a single row and must have the same keys.
198
233
  #
199
- # Returns an <tt>ActiveRecord::Result</tt> with its contents based on
234
+ # Returns an ActiveRecord::Result with its contents based on
200
235
  # <tt>:returning</tt> (see below).
201
236
  #
237
+ # By default, +upsert_all+ will update all the columns that can be updated when
238
+ # there is a conflict. These are all the columns except primary keys, read-only
239
+ # columns, and columns covered by the optional +unique_by+.
240
+ #
202
241
  # ==== Options
203
242
  #
204
243
  # [:returning]
@@ -208,6 +247,9 @@ module ActiveRecord
208
247
  # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
209
248
  # clause entirely.
210
249
  #
250
+ # You can also pass an SQL string if you need more control on the return values
251
+ # (for example, <tt>returning: "id, name as new_name"</tt>).
252
+ #
211
253
  # [:unique_by]
212
254
  # (PostgreSQL and SQLite only) By default rows are considered to be unique
213
255
  # by every unique index on the table. Any duplicate rows are skipped.
@@ -228,6 +270,54 @@ module ActiveRecord
228
270
  # <tt>:unique_by</tt> is recommended to be paired with
229
271
  # Active Record's schema_cache.
230
272
  #
273
+ # [:on_duplicate]
274
+ # Configure the SQL update sentence that will be used in case of conflict.
275
+ #
276
+ # NOTE: If you use this option you must provide all the columns you want to update
277
+ # by yourself.
278
+ #
279
+ # Example:
280
+ #
281
+ # Commodity.upsert_all(
282
+ # [
283
+ # { id: 2, name: "Copper", price: 4.84 },
284
+ # { id: 4, name: "Gold", price: 1380.87 },
285
+ # { id: 6, name: "Aluminium", price: 0.35 }
286
+ # ],
287
+ # on_duplicate: Arel.sql("price = GREATEST(commodities.price, EXCLUDED.price)")
288
+ # )
289
+ #
290
+ # See the related +:update_only+ option. Both options can't be used at the same time.
291
+ #
292
+ # [:update_only]
293
+ # Provide a list of column names that will be updated in case of conflict. If not provided,
294
+ # +upsert_all+ will update all the columns that can be updated. These are all the columns
295
+ # except primary keys, read-only columns, and columns covered by the optional +unique_by+
296
+ #
297
+ # Example:
298
+ #
299
+ # Commodity.upsert_all(
300
+ # [
301
+ # { id: 2, name: "Copper", price: 4.84 },
302
+ # { id: 4, name: "Gold", price: 1380.87 },
303
+ # { id: 6, name: "Aluminium", price: 0.35 }
304
+ # ],
305
+ # update_only: [:price] # Only prices will be updated
306
+ # )
307
+ #
308
+ # See the related +:on_duplicate+ option. Both options can't be used at the same time.
309
+ #
310
+ # [:record_timestamps]
311
+ # By default, automatic setting of timestamp columns is controlled by
312
+ # the model's <tt>record_timestamps</tt> config, matching typical
313
+ # behavior.
314
+ #
315
+ # To override this and force automatic setting of timestamp columns one
316
+ # way or the other, pass <tt>:record_timestamps</tt>:
317
+ #
318
+ # record_timestamps: true # Always set timestamps automatically
319
+ # record_timestamps: false # Never set timestamps automatically
320
+ #
231
321
  # ==== Examples
232
322
  #
233
323
  # # Inserts multiple records, performing an upsert when records have duplicate ISBNs.
@@ -239,8 +329,8 @@ module ActiveRecord
239
329
  # ], unique_by: :isbn)
240
330
  #
241
331
  # 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
332
+ def upsert_all(attributes, on_duplicate: :update, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
333
+ InsertAll.new(self, attributes, on_duplicate: on_duplicate, update_only: update_only, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps).execute
244
334
  end
245
335
 
246
336
  # Given an attributes hash, +instantiate+ returns a new instance of
@@ -264,6 +354,7 @@ module ActiveRecord
264
354
  # ==== Parameters
265
355
  #
266
356
  # * +id+ - This should be the id or an array of ids to be updated.
357
+ # Optional argument, defaults to all records in the relation.
267
358
  # * +attributes+ - This should be a hash of attributes or an array of hashes.
268
359
  #
269
360
  # ==== Examples
@@ -286,6 +377,11 @@ module ActiveRecord
286
377
  # for updating all records in a single query.
287
378
  def update(id = :all, attributes)
288
379
  if id.is_a?(Array)
380
+ if id.any?(ActiveRecord::Base)
381
+ raise ArgumentError,
382
+ "You are passing an array of ActiveRecord::Base instances to `update`. " \
383
+ "Please pass the ids of the objects by calling `pluck(:id)` or `map(&:id)`."
384
+ end
289
385
  id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
290
386
  object.update(attributes[idx])
291
387
  }
@@ -303,6 +399,32 @@ module ActiveRecord
303
399
  end
304
400
  end
305
401
 
402
+ # Updates the object (or multiple objects) just like #update but calls #update! instead
403
+ # of +update+, so an exception is raised if the record is invalid and saving will fail.
404
+ def update!(id = :all, attributes)
405
+ if id.is_a?(Array)
406
+ if id.any?(ActiveRecord::Base)
407
+ raise ArgumentError,
408
+ "You are passing an array of ActiveRecord::Base instances to `update!`. " \
409
+ "Please pass the ids of the objects by calling `pluck(:id)` or `map(&:id)`."
410
+ end
411
+ id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
412
+ object.update!(attributes[idx])
413
+ }
414
+ elsif id == :all
415
+ all.each { |record| record.update!(attributes) }
416
+ else
417
+ if ActiveRecord::Base === id
418
+ raise ArgumentError,
419
+ "You are passing an instance of ActiveRecord::Base to `update!`. " \
420
+ "Please pass the id of the object by calling `.id`."
421
+ end
422
+ object = find(id)
423
+ object.update!(attributes)
424
+ object
425
+ end
426
+ end
427
+
306
428
  # Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
307
429
  # therefore all callbacks and filters are fired off before the object is deleted. This method is
308
430
  # less efficient than #delete but allows cleanup methods and other actions to be run.
@@ -356,40 +478,52 @@ module ActiveRecord
356
478
  primary_key = self.primary_key
357
479
  primary_key_value = nil
358
480
 
359
- if primary_key && Hash === values
360
- primary_key_value = values[primary_key]
361
-
362
- if !primary_key_value && prefetch_primary_key?
481
+ if prefetch_primary_key? && primary_key
482
+ values[primary_key] ||= begin
363
483
  primary_key_value = next_sequence_value
364
- values[primary_key] = primary_key_value
484
+ _default_attributes[primary_key].with_cast_value(primary_key_value)
365
485
  end
366
486
  end
367
487
 
488
+ im = Arel::InsertManager.new(arel_table)
489
+
368
490
  if values.empty?
369
- im = arel_table.compile_insert(connection.empty_insert_statement_value(primary_key))
370
- im.into arel_table
491
+ im.insert(connection.empty_insert_statement_value(primary_key))
371
492
  else
372
- im = arel_table.compile_insert(_substitute_values(values))
493
+ im.insert(values.transform_keys { |name| arel_table[name] })
373
494
  end
374
495
 
375
496
  connection.insert(im, "#{self} Create", primary_key || false, primary_key_value)
376
497
  end
377
498
 
378
499
  def _update_record(values, constraints) # :nodoc:
379
- constraints = _substitute_values(constraints).map { |attr, bind| attr.eq(bind) }
500
+ constraints = constraints.map { |name, value| predicate_builder[name, value] }
380
501
 
381
- um = arel_table.where(
382
- constraints.reduce(&:and)
383
- ).compile_update(_substitute_values(values), primary_key)
502
+ default_constraint = build_default_constraint
503
+ constraints << default_constraint if default_constraint
504
+
505
+ if current_scope = self.global_current_scope
506
+ constraints << current_scope.where_clause.ast
507
+ end
508
+
509
+ um = Arel::UpdateManager.new(arel_table)
510
+ um.set(values.transform_keys { |name| arel_table[name] })
511
+ um.wheres = constraints
384
512
 
385
513
  connection.update(um, "#{self} Update")
386
514
  end
387
515
 
388
516
  def _delete_record(constraints) # :nodoc:
389
- constraints = _substitute_values(constraints).map { |attr, bind| attr.eq(bind) }
517
+ constraints = constraints.map { |name, value| predicate_builder[name, value] }
390
518
 
391
- dm = Arel::DeleteManager.new
392
- dm.from(arel_table)
519
+ default_constraint = build_default_constraint
520
+ constraints << default_constraint if default_constraint
521
+
522
+ if current_scope = self.global_current_scope
523
+ constraints << current_scope.where_clause.ast
524
+ end
525
+
526
+ dm = Arel::DeleteManager.new(arel_table)
393
527
  dm.wheres = constraints
394
528
 
395
529
  connection.delete(dm, "#{self} Destroy")
@@ -412,12 +546,14 @@ module ActiveRecord
412
546
  self
413
547
  end
414
548
 
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
549
+ # Called by +_update_record+ and +_delete_record+
550
+ # to build `where` clause from default scopes.
551
+ # Skips empty scopes.
552
+ def build_default_constraint
553
+ return unless default_scopes?(all_queries: true)
554
+
555
+ default_where_clause = default_scoped(all_queries: true).where_clause
556
+ default_where_clause.ast unless default_where_clause.empty?
421
557
  end
422
558
  end
423
559
 
@@ -434,6 +570,11 @@ module ActiveRecord
434
570
  @previously_new_record
435
571
  end
436
572
 
573
+ # Returns true if this object was previously persisted but now it has been deleted.
574
+ def previously_persisted?
575
+ !new_record? && destroyed?
576
+ end
577
+
437
578
  # Returns true if this object has been destroyed, otherwise returns false.
438
579
  def destroyed?
439
580
  @destroyed
@@ -556,17 +697,17 @@ module ActiveRecord
556
697
  end
557
698
 
558
699
  # 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
700
+ # current record. This is mostly useful in relation to single table
701
+ # inheritance (STI) structures where you want a subclass to appear as the
561
702
  # superclass. This can be used along with record identification in
562
703
  # Action Pack to allow, say, <tt>Client < Company</tt> to do something
563
704
  # like render <tt>partial: @client.becomes(Company)</tt> to render that
564
705
  # instance using the companies/company partial instead of clients/client.
565
706
  #
566
707
  # 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.
708
+ # Therefore the STI column value will still be the same.
568
709
  # 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.
710
+ # If you want to change the STI column as well, use #becomes! instead.
570
711
  def becomes(klass)
571
712
  became = klass.allocate
572
713
 
@@ -581,11 +722,11 @@ module ActiveRecord
581
722
  became
582
723
  end
583
724
 
584
- # Wrapper around #becomes that also changes the instance's sti column value.
725
+ # Wrapper around #becomes that also changes the instance's STI column value.
585
726
  # This is especially useful if you want to persist the changed class in your
586
727
  # database.
587
728
  #
588
- # Note: The old instance's sti column value will be changed too, as both objects
729
+ # Note: The old instance's STI column value will be changed too, as both objects
589
730
  # share the same set of attributes.
590
731
  def becomes!(klass)
591
732
  became = becomes(klass)
@@ -664,6 +805,7 @@ module ActiveRecord
664
805
  def update_columns(attributes)
665
806
  raise ActiveRecordError, "cannot update a new record" if new_record?
666
807
  raise ActiveRecordError, "cannot update a destroyed record" if destroyed?
808
+ _raise_readonly_record_error if readonly?
667
809
 
668
810
  attributes = attributes.transform_keys do |key|
669
811
  name = key.to_s
@@ -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
@@ -849,6 +992,7 @@ module ActiveRecord
849
992
  #
850
993
  def touch(*names, time: nil)
851
994
  _raise_record_not_touched_error unless persisted?
995
+ _raise_readonly_record_error if readonly?
852
996
 
853
997
  attribute_names = timestamp_attributes_for_update_in_model
854
998
  attribute_names |= names.map! do |name|
@@ -865,6 +1009,29 @@ module ActiveRecord
865
1009
  end
866
1010
 
867
1011
  private
1012
+ def strict_loaded_associations
1013
+ @association_cache.find_all do |_, assoc|
1014
+ assoc.owner.strict_loading? && !assoc.owner.strict_loading_n_plus_one_only?
1015
+ end.map(&:first)
1016
+ end
1017
+
1018
+ def _find_record(options)
1019
+ if options && options[:lock]
1020
+ self.class.preload(strict_loaded_associations).lock(options[:lock]).find(id)
1021
+ else
1022
+ self.class.preload(strict_loaded_associations).find(id)
1023
+ end
1024
+ end
1025
+
1026
+ def apply_scoping?(options)
1027
+ !(options && options[:unscoped]) &&
1028
+ (self.class.default_scopes?(all_queries: true) || self.class.global_current_scope)
1029
+ end
1030
+
1031
+ def _primary_key_constraints_hash
1032
+ { @primary_key => id_in_database }
1033
+ end
1034
+
868
1035
  # A hook to be overridden by association modules.
869
1036
  def destroy_associations
870
1037
  end
@@ -874,7 +1041,7 @@ module ActiveRecord
874
1041
  end
875
1042
 
876
1043
  def _delete_row
877
- self.class._delete_record(@primary_key => id_in_database)
1044
+ self.class._delete_record(_primary_key_constraints_hash)
878
1045
  end
879
1046
 
880
1047
  def _touch_row(attribute_names, time)
@@ -890,7 +1057,7 @@ module ActiveRecord
890
1057
  def _update_row(attribute_names, attempted_action = "update")
891
1058
  self.class._update_record(
892
1059
  attributes_with_values(attribute_names),
893
- @primary_key => id_in_database
1060
+ _primary_key_constraints_hash
894
1061
  )
895
1062
  end
896
1063
 
@@ -946,7 +1113,8 @@ module ActiveRecord
946
1113
 
947
1114
  def _raise_record_not_destroyed
948
1115
  @_association_destroy_exception ||= nil
949
- raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy the record", self)
1116
+ key = self.class.primary_key
1117
+ raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy #{self.class} with #{key}=#{send(key)}", self)
950
1118
  ensure
951
1119
  @_association_destroy_exception = nil
952
1120
  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