activerecord 7.0.0 → 7.1.0

Sign up to get free protection for your applications and to get access to all the features.
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