activerecord 7.0.0 → 7.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1607 -1040
- data/MIT-LICENSE +1 -1
- data/README.rdoc +17 -18
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/association.rb +18 -3
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +14 -6
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +21 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +17 -12
- data/lib/active_record/associations/collection_proxy.rb +22 -12
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +27 -17
- data/lib/active_record/associations/has_many_through_association.rb +10 -6
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency.rb +20 -14
- data/lib/active_record/associations/preloader/association.rb +27 -6
- data/lib/active_record/associations/preloader/through_association.rb +1 -1
- data/lib/active_record/associations/preloader.rb +13 -10
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +345 -219
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/dirty.rb +40 -26
- data/lib/active_record/attribute_methods/primary_key.rb +76 -24
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +18 -5
- data/lib/active_record/attribute_methods/serialization.rb +172 -69
- data/lib/active_record/attribute_methods/write.rb +3 -3
- data/lib/active_record/attribute_methods.rb +110 -28
- data/lib/active_record/attributes.rb +3 -3
- data/lib/active_record/autosave_association.rb +56 -10
- data/lib/active_record/base.rb +10 -5
- data/lib/active_record/callbacks.rb +16 -32
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -34
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +164 -89
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +63 -43
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +128 -32
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +60 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +52 -8
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +163 -29
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +302 -129
- data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
- data/lib/active_record/connection_adapters/abstract_adapter.rb +504 -106
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +217 -104
- data/lib/active_record/connection_adapters/column.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +23 -144
- data/lib/active_record/connection_adapters/mysql/quoting.rb +29 -12
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +38 -14
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
- data/lib/active_record/connection_adapters/pool_config.rb +14 -5
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +72 -45
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +41 -8
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +6 -10
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +358 -57
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +343 -181
- data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
- data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +45 -39
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +22 -5
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +41 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +242 -81
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
- data/lib/active_record/connection_adapters.rb +3 -1
- data/lib/active_record/connection_handling.rb +73 -96
- data/lib/active_record/core.rb +136 -148
- data/lib/active_record/counter_cache.rb +46 -25
- data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -0
- data/lib/active_record/database_configurations/database_config.rb +9 -3
- data/lib/active_record/database_configurations/hash_config.rb +22 -12
- data/lib/active_record/database_configurations/url_config.rb +17 -11
- data/lib/active_record/database_configurations.rb +87 -34
- data/lib/active_record/delegated_type.rb +9 -4
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +2 -0
- data/lib/active_record/disable_joins_association_relation.rb +1 -1
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
- data/lib/active_record/encryption/config.rb +25 -1
- data/lib/active_record/encryption/configurable.rb +13 -14
- data/lib/active_record/encryption/context.rb +10 -3
- data/lib/active_record/encryption/contexts.rb +8 -4
- data/lib/active_record/encryption/derived_secret_key_provider.rb +9 -3
- data/lib/active_record/encryption/deterministic_key_provider.rb +1 -1
- data/lib/active_record/encryption/encryptable_record.rb +38 -22
- data/lib/active_record/encryption/encrypted_attribute_type.rb +19 -8
- data/lib/active_record/encryption/encryptor.rb +7 -7
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +3 -3
- data/lib/active_record/encryption/extended_deterministic_queries.rb +83 -71
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
- data/lib/active_record/encryption/key_generator.rb +12 -1
- data/lib/active_record/encryption/message.rb +1 -1
- data/lib/active_record/encryption/message_serializer.rb +2 -0
- data/lib/active_record/encryption/properties.rb +4 -4
- data/lib/active_record/encryption/scheme.rb +20 -23
- data/lib/active_record/encryption.rb +1 -0
- data/lib/active_record/enum.rb +114 -27
- data/lib/active_record/errors.rb +108 -15
- data/lib/active_record/explain.rb +23 -3
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/model_metadata.rb +14 -4
- data/lib/active_record/fixture_set/render_context.rb +2 -0
- data/lib/active_record/fixture_set/table_row.rb +29 -8
- data/lib/active_record/fixtures.rb +121 -73
- data/lib/active_record/future_result.rb +30 -5
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +30 -16
- data/lib/active_record/insert_all.rb +55 -8
- data/lib/active_record/integration.rb +10 -10
- data/lib/active_record/internal_metadata.rb +118 -30
- data/lib/active_record/locking/optimistic.rb +32 -18
- data/lib/active_record/locking/pessimistic.rb +8 -5
- data/lib/active_record/log_subscriber.rb +39 -17
- data/lib/active_record/marshalling.rb +56 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
- data/lib/active_record/middleware/database_selector.rb +18 -13
- data/lib/active_record/middleware/shard_selector.rb +7 -5
- data/lib/active_record/migration/command_recorder.rb +104 -9
- data/lib/active_record/migration/compatibility.rb +158 -64
- data/lib/active_record/migration/default_strategy.rb +23 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration.rb +271 -117
- data/lib/active_record/model_schema.rb +82 -50
- data/lib/active_record/nested_attributes.rb +23 -3
- data/lib/active_record/normalization.rb +159 -0
- data/lib/active_record/persistence.rb +200 -47
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +3 -21
- data/lib/active_record/query_logs.rb +87 -51
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +16 -3
- data/lib/active_record/railtie.rb +127 -61
- data/lib/active_record/railties/controller_runtime.rb +12 -8
- data/lib/active_record/railties/databases.rake +142 -143
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +32 -5
- data/lib/active_record/reflection.rb +177 -45
- data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
- data/lib/active_record/relation/batches.rb +190 -61
- data/lib/active_record/relation/calculations.rb +200 -83
- data/lib/active_record/relation/delegation.rb +23 -9
- data/lib/active_record/relation/finder_methods.rb +77 -16
- data/lib/active_record/relation/merger.rb +2 -0
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +26 -14
- data/lib/active_record/relation/query_attribute.rb +25 -1
- data/lib/active_record/relation/query_methods.rb +429 -76
- data/lib/active_record/relation/spawn_methods.rb +18 -1
- data/lib/active_record/relation.rb +98 -41
- data/lib/active_record/result.rb +25 -9
- data/lib/active_record/runtime_registry.rb +10 -1
- data/lib/active_record/sanitization.rb +57 -16
- data/lib/active_record/schema.rb +36 -22
- data/lib/active_record/schema_dumper.rb +65 -23
- data/lib/active_record/schema_migration.rb +68 -33
- data/lib/active_record/scoping/default.rb +20 -12
- data/lib/active_record/scoping/named.rb +2 -2
- data/lib/active_record/scoping.rb +2 -1
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/serialization.rb +5 -0
- data/lib/active_record/signed_id.rb +9 -7
- data/lib/active_record/store.rb +16 -11
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +16 -3
- data/lib/active_record/tasks/database_tasks.rb +138 -107
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +17 -15
- data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
- data/lib/active_record/test_fixtures.rb +123 -99
- data/lib/active_record/timestamp.rb +26 -14
- data/lib/active_record/token_for.rb +113 -0
- data/lib/active_record/touch_later.rb +11 -6
- data/lib/active_record/transactions.rb +39 -13
- data/lib/active_record/translation.rb +1 -1
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +8 -4
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +3 -3
- data/lib/active_record/validations/numericality.rb +5 -4
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +50 -5
- data/lib/active_record/validations.rb +8 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +143 -16
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +1 -1
- data/lib/arel/nodes/and.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -1
- data/lib/arel/nodes/bound_sql_literal.rb +61 -0
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/filter.rb +1 -1
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/homogeneous_in.rb +0 -8
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/node.rb +111 -2
- data/lib/arel/nodes/sql_literal.rb +6 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes.rb +4 -0
- data/lib/arel/predications.rb +2 -0
- data/lib/arel/table.rb +9 -5
- data/lib/arel/visitors/mysql.rb +8 -1
- data/lib/arel/visitors/to_sql.rb +81 -17
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +16 -2
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/migration.rb +3 -1
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
- metadata +50 -15
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- 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
|
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
|
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
|
-
#
|
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
|
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
|
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
|
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
|
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
|
223
|
-
def upsert(attributes,
|
224
|
-
upsert_all([ attributes ],
|
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
|
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
|
-
#
|
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
|
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(
|
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
|
-
#
|
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
|
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
|
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 =
|
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
|
1124
|
+
attribute_names = (attribute_names | names).map! do |name|
|
998
1125
|
name = name.to_s
|
999
|
-
self.class.attribute_aliases[name] || name
|
1000
|
-
|
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
|
-
|
1157
|
+
base.lock(options[:lock]).find_by!(_in_memory_query_constraints_hash)
|
1020
1158
|
else
|
1021
|
-
|
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
|
1031
|
-
|
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(
|
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
|
-
|
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
|
-
|
1096
|
-
|
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
|
-
|
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}=#{
|
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
|
-
|
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
|
-
|
46
|
-
|
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
|
|