activerecord 7.0.6 → 7.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (231) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1424 -1390
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +15 -16
  5. data/lib/active_record/aggregations.rb +16 -13
  6. data/lib/active_record/association_relation.rb +1 -1
  7. data/lib/active_record/associations/association.rb +18 -3
  8. data/lib/active_record/associations/association_scope.rb +16 -9
  9. data/lib/active_record/associations/belongs_to_association.rb +14 -6
  10. data/lib/active_record/associations/builder/association.rb +3 -3
  11. data/lib/active_record/associations/builder/belongs_to.rb +21 -8
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
  13. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  14. data/lib/active_record/associations/collection_association.rb +16 -10
  15. data/lib/active_record/associations/collection_proxy.rb +20 -10
  16. data/lib/active_record/associations/foreign_association.rb +10 -3
  17. data/lib/active_record/associations/has_many_association.rb +20 -13
  18. data/lib/active_record/associations/has_many_through_association.rb +10 -6
  19. data/lib/active_record/associations/has_one_association.rb +10 -7
  20. data/lib/active_record/associations/join_dependency.rb +10 -8
  21. data/lib/active_record/associations/preloader/association.rb +27 -6
  22. data/lib/active_record/associations/preloader.rb +13 -10
  23. data/lib/active_record/associations/singular_association.rb +6 -8
  24. data/lib/active_record/associations/through_association.rb +22 -11
  25. data/lib/active_record/associations.rb +295 -199
  26. data/lib/active_record/attribute_assignment.rb +0 -2
  27. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  28. data/lib/active_record/attribute_methods/dirty.rb +40 -26
  29. data/lib/active_record/attribute_methods/primary_key.rb +76 -24
  30. data/lib/active_record/attribute_methods/query.rb +28 -16
  31. data/lib/active_record/attribute_methods/read.rb +18 -5
  32. data/lib/active_record/attribute_methods/serialization.rb +150 -31
  33. data/lib/active_record/attribute_methods/write.rb +3 -3
  34. data/lib/active_record/attribute_methods.rb +105 -21
  35. data/lib/active_record/attributes.rb +3 -3
  36. data/lib/active_record/autosave_association.rb +60 -18
  37. data/lib/active_record/base.rb +7 -2
  38. data/lib/active_record/callbacks.rb +10 -24
  39. data/lib/active_record/coders/column_serializer.rb +61 -0
  40. data/lib/active_record/coders/json.rb +1 -1
  41. data/lib/active_record/coders/yaml_column.rb +70 -42
  42. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +163 -88
  43. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  44. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
  45. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +63 -43
  46. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  47. data/lib/active_record/connection_adapters/abstract/database_statements.rb +128 -32
  48. data/lib/active_record/connection_adapters/abstract/query_cache.rb +60 -22
  49. data/lib/active_record/connection_adapters/abstract/quoting.rb +41 -6
  50. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  51. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  52. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
  53. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +289 -124
  54. data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
  55. data/lib/active_record/connection_adapters/abstract_adapter.rb +496 -102
  56. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +214 -113
  57. data/lib/active_record/connection_adapters/column.rb +9 -0
  58. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  59. data/lib/active_record/connection_adapters/mysql/database_statements.rb +23 -144
  60. data/lib/active_record/connection_adapters/mysql/quoting.rb +21 -14
  61. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  62. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
  63. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  64. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +18 -13
  65. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -0
  66. data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
  67. data/lib/active_record/connection_adapters/pool_config.rb +14 -5
  68. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  69. data/lib/active_record/connection_adapters/postgresql/column.rb +14 -3
  70. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +71 -40
  71. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  72. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
  73. data/lib/active_record/connection_adapters/postgresql/quoting.rb +15 -8
  74. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
  75. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  76. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
  77. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
  78. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +349 -55
  79. data/lib/active_record/connection_adapters/postgresql_adapter.rb +338 -176
  80. data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
  81. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  82. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +45 -39
  83. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +9 -5
  84. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
  85. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +28 -9
  86. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +210 -83
  87. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  88. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
  89. data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
  90. data/lib/active_record/connection_adapters.rb +3 -1
  91. data/lib/active_record/connection_handling.rb +71 -94
  92. data/lib/active_record/core.rb +136 -148
  93. data/lib/active_record/counter_cache.rb +46 -25
  94. data/lib/active_record/database_configurations/database_config.rb +9 -3
  95. data/lib/active_record/database_configurations/hash_config.rb +22 -12
  96. data/lib/active_record/database_configurations/url_config.rb +17 -11
  97. data/lib/active_record/database_configurations.rb +86 -33
  98. data/lib/active_record/delegated_type.rb +8 -3
  99. data/lib/active_record/deprecator.rb +7 -0
  100. data/lib/active_record/destroy_association_async_job.rb +2 -0
  101. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  102. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  103. data/lib/active_record/encryption/config.rb +25 -1
  104. data/lib/active_record/encryption/configurable.rb +12 -19
  105. data/lib/active_record/encryption/context.rb +10 -3
  106. data/lib/active_record/encryption/contexts.rb +5 -1
  107. data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
  108. data/lib/active_record/encryption/encryptable_record.rb +36 -18
  109. data/lib/active_record/encryption/encrypted_attribute_type.rb +17 -6
  110. data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -54
  111. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  112. data/lib/active_record/encryption/key_generator.rb +12 -1
  113. data/lib/active_record/encryption/message_serializer.rb +2 -0
  114. data/lib/active_record/encryption/properties.rb +3 -3
  115. data/lib/active_record/encryption/scheme.rb +19 -22
  116. data/lib/active_record/encryption.rb +1 -0
  117. data/lib/active_record/enum.rb +113 -26
  118. data/lib/active_record/errors.rb +108 -15
  119. data/lib/active_record/explain.rb +23 -3
  120. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  121. data/lib/active_record/fixture_set/render_context.rb +2 -0
  122. data/lib/active_record/fixture_set/table_row.rb +29 -8
  123. data/lib/active_record/fixtures.rb +119 -71
  124. data/lib/active_record/future_result.rb +30 -5
  125. data/lib/active_record/gem_version.rb +3 -3
  126. data/lib/active_record/inheritance.rb +30 -16
  127. data/lib/active_record/insert_all.rb +55 -8
  128. data/lib/active_record/integration.rb +8 -8
  129. data/lib/active_record/internal_metadata.rb +120 -30
  130. data/lib/active_record/locking/pessimistic.rb +5 -2
  131. data/lib/active_record/log_subscriber.rb +29 -12
  132. data/lib/active_record/marshalling.rb +56 -0
  133. data/lib/active_record/message_pack.rb +124 -0
  134. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  135. data/lib/active_record/middleware/database_selector.rb +5 -7
  136. data/lib/active_record/middleware/shard_selector.rb +3 -1
  137. data/lib/active_record/migration/command_recorder.rb +104 -5
  138. data/lib/active_record/migration/compatibility.rb +142 -58
  139. data/lib/active_record/migration/default_strategy.rb +23 -0
  140. data/lib/active_record/migration/execution_strategy.rb +19 -0
  141. data/lib/active_record/migration.rb +265 -112
  142. data/lib/active_record/model_schema.rb +60 -40
  143. data/lib/active_record/nested_attributes.rb +21 -3
  144. data/lib/active_record/normalization.rb +159 -0
  145. data/lib/active_record/persistence.rb +187 -35
  146. data/lib/active_record/promise.rb +84 -0
  147. data/lib/active_record/query_cache.rb +3 -21
  148. data/lib/active_record/query_logs.rb +77 -52
  149. data/lib/active_record/query_logs_formatter.rb +41 -0
  150. data/lib/active_record/querying.rb +15 -2
  151. data/lib/active_record/railtie.rb +109 -47
  152. data/lib/active_record/railties/controller_runtime.rb +12 -8
  153. data/lib/active_record/railties/databases.rake +139 -145
  154. data/lib/active_record/railties/job_runtime.rb +23 -0
  155. data/lib/active_record/readonly_attributes.rb +32 -5
  156. data/lib/active_record/reflection.rb +162 -44
  157. data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
  158. data/lib/active_record/relation/batches.rb +190 -61
  159. data/lib/active_record/relation/calculations.rb +160 -63
  160. data/lib/active_record/relation/delegation.rb +22 -8
  161. data/lib/active_record/relation/finder_methods.rb +77 -16
  162. data/lib/active_record/relation/merger.rb +2 -0
  163. data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -2
  164. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
  165. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  166. data/lib/active_record/relation/predicate_builder.rb +27 -16
  167. data/lib/active_record/relation/query_attribute.rb +25 -1
  168. data/lib/active_record/relation/query_methods.rb +378 -70
  169. data/lib/active_record/relation/spawn_methods.rb +18 -1
  170. data/lib/active_record/relation.rb +76 -35
  171. data/lib/active_record/result.rb +19 -5
  172. data/lib/active_record/runtime_registry.rb +10 -1
  173. data/lib/active_record/sanitization.rb +51 -11
  174. data/lib/active_record/schema.rb +2 -3
  175. data/lib/active_record/schema_dumper.rb +46 -7
  176. data/lib/active_record/schema_migration.rb +68 -33
  177. data/lib/active_record/scoping/default.rb +15 -5
  178. data/lib/active_record/scoping/named.rb +2 -2
  179. data/lib/active_record/scoping.rb +2 -1
  180. data/lib/active_record/secure_password.rb +60 -0
  181. data/lib/active_record/secure_token.rb +21 -3
  182. data/lib/active_record/signed_id.rb +7 -5
  183. data/lib/active_record/store.rb +8 -8
  184. data/lib/active_record/suppressor.rb +3 -1
  185. data/lib/active_record/table_metadata.rb +11 -2
  186. data/lib/active_record/tasks/database_tasks.rb +127 -105
  187. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  188. data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
  189. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  190. data/lib/active_record/test_fixtures.rb +113 -96
  191. data/lib/active_record/timestamp.rb +27 -15
  192. data/lib/active_record/token_for.rb +113 -0
  193. data/lib/active_record/touch_later.rb +11 -6
  194. data/lib/active_record/transactions.rb +39 -13
  195. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  196. data/lib/active_record/type/internal/timezone.rb +7 -2
  197. data/lib/active_record/type/serialized.rb +4 -0
  198. data/lib/active_record/type/time.rb +4 -0
  199. data/lib/active_record/validations/absence.rb +1 -1
  200. data/lib/active_record/validations/numericality.rb +5 -4
  201. data/lib/active_record/validations/presence.rb +5 -28
  202. data/lib/active_record/validations/uniqueness.rb +47 -2
  203. data/lib/active_record/validations.rb +8 -4
  204. data/lib/active_record/version.rb +1 -1
  205. data/lib/active_record.rb +121 -16
  206. data/lib/arel/errors.rb +10 -0
  207. data/lib/arel/factory_methods.rb +4 -0
  208. data/lib/arel/nodes/and.rb +4 -0
  209. data/lib/arel/nodes/binary.rb +6 -1
  210. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  211. data/lib/arel/nodes/cte.rb +36 -0
  212. data/lib/arel/nodes/fragments.rb +35 -0
  213. data/lib/arel/nodes/homogeneous_in.rb +0 -8
  214. data/lib/arel/nodes/leading_join.rb +8 -0
  215. data/lib/arel/nodes/node.rb +111 -2
  216. data/lib/arel/nodes/sql_literal.rb +6 -0
  217. data/lib/arel/nodes/table_alias.rb +4 -0
  218. data/lib/arel/nodes.rb +4 -0
  219. data/lib/arel/predications.rb +2 -0
  220. data/lib/arel/table.rb +9 -5
  221. data/lib/arel/visitors/mysql.rb +8 -1
  222. data/lib/arel/visitors/to_sql.rb +81 -17
  223. data/lib/arel/visitors/visitor.rb +2 -2
  224. data/lib/arel.rb +16 -2
  225. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  226. data/lib/rails/generators/active_record/migration.rb +3 -1
  227. data/lib/rails/generators/active_record/model/USAGE +113 -0
  228. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  229. metadata +50 -15
  230. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  231. data/lib/active_record/null_relation.rb +0 -63
@@ -57,6 +57,36 @@ 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
@@ -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
  #
@@ -164,7 +194,7 @@ 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
200
  # To skip duplicate rows, see #insert_all. To replace them, see #upsert_all.
@@ -182,7 +212,7 @@ module ActiveRecord
182
212
  # clause entirely.
183
213
  #
184
214
  # 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>).
215
+ # (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
186
216
  #
187
217
  # [:record_timestamps]
188
218
  # By default, automatic setting of timestamp columns is controlled by
@@ -219,8 +249,8 @@ module ActiveRecord
219
249
  # go through Active Record's type casting and serialization.
220
250
  #
221
251
  # 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)
252
+ def upsert(attributes, **kwargs)
253
+ upsert_all([ attributes ], **kwargs)
224
254
  end
225
255
 
226
256
  # Updates or inserts (upserts) multiple records into the database in a
@@ -248,7 +278,7 @@ module ActiveRecord
248
278
  # clause entirely.
249
279
  #
250
280
  # 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>).
281
+ # (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
252
282
  #
253
283
  # [:unique_by]
254
284
  # (PostgreSQL and SQLite only) By default rows are considered to be unique
@@ -258,7 +288,7 @@ module ActiveRecord
258
288
  #
259
289
  # Consider a Book model where no duplicate ISBNs make sense, but if any
260
290
  # row has an existing id, or is not unique by another unique index,
261
- # <tt>ActiveRecord::RecordNotUnique</tt> is raised.
291
+ # ActiveRecord::RecordNotUnique is raised.
262
292
  #
263
293
  # Unique indexes can be identified by columns or name:
264
294
  #
@@ -425,6 +455,62 @@ module ActiveRecord
425
455
  end
426
456
  end
427
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
+
428
514
  # Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
429
515
  # therefore all callbacks and filters are fired off before the object is deleted. This method is
430
516
  # less efficient than #delete but allows cleanup methods and other actions to be run.
@@ -445,7 +531,13 @@ module ActiveRecord
445
531
  # todos = [1,2,3]
446
532
  # Todo.destroy(todos)
447
533
  def destroy(id)
448
- 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
449
541
  find(id).each(&:destroy)
450
542
  else
451
543
  find(id).destroy
@@ -474,7 +566,7 @@ module ActiveRecord
474
566
  delete_by(primary_key => id_or_array)
475
567
  end
476
568
 
477
- def _insert_record(values) # :nodoc:
569
+ def _insert_record(values, returning) # :nodoc:
478
570
  primary_key = self.primary_key
479
571
  primary_key_value = nil
480
572
 
@@ -493,7 +585,10 @@ module ActiveRecord
493
585
  im.insert(values.transform_keys { |name| arel_table[name] })
494
586
  end
495
587
 
496
- 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
+ )
497
592
  end
498
593
 
499
594
  def _update_record(values, constraints) # :nodoc:
@@ -530,6 +625,14 @@ module ActiveRecord
530
625
  end
531
626
 
532
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
+
533
636
  # Given a class, an attributes hash, +instantiate_instance_of+ returns a
534
637
  # new instance of the class. Accepts only keys as strings.
535
638
  def instantiate_instance_of(klass, attributes, column_types = {}, &block)
@@ -564,7 +667,7 @@ module ActiveRecord
564
667
  end
565
668
 
566
669
  # Returns true if this object was just created -- that is, prior to the last
567
- # 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
568
671
  # returned true.
569
672
  def previously_new_record?
570
673
  @previously_new_record
@@ -663,6 +766,7 @@ module ActiveRecord
663
766
  def delete
664
767
  _delete_row if persisted?
665
768
  @destroyed = true
769
+ @previously_new_record = false
666
770
  freeze
667
771
  end
668
772
 
@@ -676,12 +780,9 @@ module ActiveRecord
676
780
  def destroy
677
781
  _raise_readonly_record_error if readonly?
678
782
  destroy_associations
679
- @_trigger_destroy_callback = if persisted?
680
- destroy_row > 0
681
- else
682
- true
683
- end
783
+ @_trigger_destroy_callback ||= persisted? && destroy_row > 0
684
784
  @destroyed = true
785
+ @previously_new_record = false
685
786
  freeze
686
787
  end
687
788
 
@@ -707,11 +808,14 @@ module ActiveRecord
707
808
  # Note: The new instance will share a link to the same attributes as the original class.
708
809
  # Therefore the STI column value will still be the same.
709
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
+ #
710
813
  # If you want to change the STI column as well, use #becomes! instead.
711
814
  def becomes(klass)
712
815
  became = klass.allocate
713
816
 
714
817
  became.send(:initialize) do |becoming|
818
+ @attributes.reverse_merge!(becoming.instance_variable_get(:@attributes))
715
819
  becoming.instance_variable_set(:@attributes, @attributes)
716
820
  becoming.instance_variable_set(:@mutations_from_database, @mutations_from_database ||= nil)
717
821
  becoming.instance_variable_set(:@new_record, new_record?)
@@ -746,7 +850,7 @@ module ActiveRecord
746
850
  # * updated_at/updated_on column is updated if that column is available.
747
851
  # * Updates all the attributes that are dirty in this object.
748
852
  #
749
- # This method raises an ActiveRecord::ActiveRecordError if the
853
+ # This method raises an ActiveRecord::ActiveRecordError if the
750
854
  # attribute is marked as readonly.
751
855
  #
752
856
  # Also see #update_column.
@@ -758,6 +862,28 @@ module ActiveRecord
758
862
  save(validate: false)
759
863
  end
760
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
+
761
887
  # Updates the attributes of the model from the passed-in hash and saves the
762
888
  # record, all wrapped in a transaction. If the object is invalid, the saving
763
889
  # will fail and false will be returned.
@@ -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
@@ -995,10 +1121,12 @@ module ActiveRecord
995
1121
  _raise_readonly_record_error if readonly?
996
1122
 
997
1123
  attribute_names = timestamp_attributes_for_update_in_model
998
- attribute_names |= names.map! do |name|
1124
+ attribute_names = (attribute_names | names).map! do |name|
999
1125
  name = name.to_s
1000
- self.class.attribute_aliases[name] || name
1001
- end unless names.empty?
1126
+ name = self.class.attribute_aliases[name] || name
1127
+ verify_readonly_attribute(name)
1128
+ name
1129
+ end
1002
1130
 
1003
1131
  unless attribute_names.empty?
1004
1132
  affected_rows = _touch_row(attribute_names, time)
@@ -1009,6 +1137,12 @@ module ActiveRecord
1009
1137
  end
1010
1138
 
1011
1139
  private
1140
+ def init_internals
1141
+ super
1142
+ @_trigger_destroy_callback = @_trigger_update_callback = nil
1143
+ @previously_new_record = false
1144
+ end
1145
+
1012
1146
  def strict_loaded_associations
1013
1147
  @association_cache.find_all do |_, assoc|
1014
1148
  assoc.owner.strict_loading? && !assoc.owner.strict_loading_n_plus_one_only?
@@ -1016,10 +1150,23 @@ module ActiveRecord
1016
1150
  end
1017
1151
 
1018
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
+
1019
1156
  if options && options[:lock]
1020
- self.class.preload(strict_loaded_associations).lock(options[:lock]).find(id)
1157
+ base.lock(options[:lock]).find_by!(_in_memory_query_constraints_hash)
1021
1158
  else
1022
- 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
1023
1170
  end
1024
1171
  end
1025
1172
 
@@ -1029,7 +1176,13 @@ module ActiveRecord
1029
1176
  end
1030
1177
 
1031
1178
  def _query_constraints_hash
1032
- { @primary_key => id_in_database }
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
1033
1186
  end
1034
1187
 
1035
1188
  # A hook to be overridden by association modules.
@@ -1093,11 +1246,16 @@ module ActiveRecord
1093
1246
  def _create_record(attribute_names = self.attribute_names)
1094
1247
  attribute_names = attributes_for_create(attribute_names)
1095
1248
 
1096
- new_id = self.class._insert_record(
1097
- 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
1098
1254
  )
1099
1255
 
1100
- 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
1101
1259
 
1102
1260
  @new_record = false
1103
1261
  @previously_new_record = true
@@ -1114,7 +1272,7 @@ module ActiveRecord
1114
1272
  def _raise_record_not_destroyed
1115
1273
  @_association_destroy_exception ||= nil
1116
1274
  key = self.class.primary_key
1117
- 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)
1118
1276
  ensure
1119
1277
  @_association_destroy_exception = nil
1120
1278
  end
@@ -1129,11 +1287,5 @@ module ActiveRecord
1129
1287
  persisted?, new_record?, or destroyed? before touching.
1130
1288
  MSG
1131
1289
  end
1132
-
1133
- # The name of the method used to touch a +belongs_to+ association when the
1134
- # +:touch+ option is used.
1135
- def belongs_to_touch_method
1136
- :touch
1137
- end
1138
1290
  end
1139
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