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