activerecord 7.1.5.1 → 8.0.2
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 +369 -2484
- data/README.rdoc +15 -15
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +2 -1
- data/lib/active_record/associations/alias_tracker.rb +31 -23
- data/lib/active_record/associations/association.rb +43 -12
- data/lib/active_record/associations/belongs_to_association.rb +21 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +7 -6
- data/lib/active_record/associations/builder/belongs_to.rb +1 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/collection_association.rb +17 -9
- data/lib/active_record/associations/collection_proxy.rb +14 -1
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +4 -3
- data/lib/active_record/associations/preloader/branch.rb +7 -1
- data/lib/active_record/associations/preloader/through_association.rb +1 -3
- data/lib/active_record/associations/singular_association.rb +14 -3
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +92 -295
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/primary_key.rb +25 -61
- data/lib/active_record/attribute_methods/read.rb +1 -13
- data/lib/active_record/attribute_methods/serialization.rb +4 -24
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +9 -18
- data/lib/active_record/attribute_methods.rb +71 -75
- data/lib/active_record/attributes.rb +63 -49
- data/lib/active_record/autosave_association.rb +92 -57
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +48 -122
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +286 -77
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +119 -55
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +197 -76
- data/lib/active_record/connection_adapters/abstract/quoting.rb +66 -92
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +12 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +48 -12
- data/lib/active_record/connection_adapters/abstract/transaction.rb +140 -67
- data/lib/active_record/connection_adapters/abstract_adapter.rb +85 -90
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +71 -52
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -57
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +56 -45
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +92 -101
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +13 -31
- data/lib/active_record/connection_adapters/pool_config.rb +14 -13
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +86 -41
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -11
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +36 -20
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +75 -28
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +73 -113
- data/lib/active_record/connection_adapters/schema_cache.rb +124 -131
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +81 -97
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +29 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +35 -3
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +183 -87
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +39 -69
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -65
- data/lib/active_record/connection_adapters.rb +65 -0
- data/lib/active_record/connection_handling.rb +74 -37
- data/lib/active_record/core.rb +132 -51
- data/lib/active_record/counter_cache.rb +19 -10
- data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
- data/lib/active_record/database_configurations/database_config.rb +23 -4
- data/lib/active_record/database_configurations/hash_config.rb +46 -34
- data/lib/active_record/database_configurations/url_config.rb +20 -1
- data/lib/active_record/database_configurations.rb +1 -1
- data/lib/active_record/delegated_type.rb +41 -17
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +7 -7
- data/lib/active_record/encryption/encrypted_attribute_type.rb +33 -4
- data/lib/active_record/encryption/encryptor.rb +28 -6
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- data/lib/active_record/encryption/key_provider.rb +1 -1
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +4 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +8 -1
- data/lib/active_record/enum.rb +20 -16
- data/lib/active_record/errors.rb +54 -20
- data/lib/active_record/explain.rb +13 -24
- data/lib/active_record/fixtures.rb +37 -33
- data/lib/active_record/future_result.rb +21 -13
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +4 -2
- data/lib/active_record/insert_all.rb +19 -16
- data/lib/active_record/integration.rb +4 -1
- data/lib/active_record/internal_metadata.rb +48 -34
- data/lib/active_record/locking/optimistic.rb +8 -7
- data/lib/active_record/log_subscriber.rb +5 -32
- data/lib/active_record/message_pack.rb +1 -1
- data/lib/active_record/migration/command_recorder.rb +33 -14
- data/lib/active_record/migration/compatibility.rb +8 -3
- data/lib/active_record/migration/default_strategy.rb +4 -5
- data/lib/active_record/migration/pending_migration_connection.rb +2 -2
- data/lib/active_record/migration.rb +104 -98
- data/lib/active_record/model_schema.rb +32 -70
- data/lib/active_record/nested_attributes.rb +15 -9
- data/lib/active_record/normalization.rb +3 -7
- data/lib/active_record/persistence.rb +127 -451
- data/lib/active_record/query_cache.rb +19 -8
- data/lib/active_record/query_logs.rb +104 -37
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +24 -12
- data/lib/active_record/railtie.rb +26 -68
- data/lib/active_record/railties/controller_runtime.rb +13 -4
- data/lib/active_record/railties/databases.rake +43 -61
- data/lib/active_record/reflection.rb +112 -53
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +138 -72
- data/lib/active_record/relation/calculations.rb +122 -82
- data/lib/active_record/relation/delegation.rb +30 -22
- data/lib/active_record/relation/finder_methods.rb +32 -18
- data/lib/active_record/relation/merger.rb +12 -14
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +10 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +16 -3
- data/lib/active_record/relation/query_attribute.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +317 -101
- data/lib/active_record/relation/spawn_methods.rb +3 -19
- data/lib/active_record/relation/where_clause.rb +7 -19
- data/lib/active_record/relation.rb +561 -119
- data/lib/active_record/result.rb +95 -46
- data/lib/active_record/runtime_registry.rb +39 -0
- data/lib/active_record/sanitization.rb +31 -25
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +53 -20
- data/lib/active_record/schema_migration.rb +31 -14
- data/lib/active_record/scoping/named.rb +6 -2
- data/lib/active_record/signed_id.rb +24 -4
- data/lib/active_record/statement_cache.rb +19 -19
- data/lib/active_record/store.rb +7 -3
- data/lib/active_record/table_metadata.rb +2 -13
- data/lib/active_record/tasks/database_tasks.rb +87 -58
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -3
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +4 -3
- data/lib/active_record/test_fixtures.rb +98 -89
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +2 -2
- data/lib/active_record/token_for.rb +22 -12
- data/lib/active_record/touch_later.rb +1 -1
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +72 -17
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/serialized.rb +1 -3
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/associated.rb +9 -3
- data/lib/active_record/validations/uniqueness.rb +23 -18
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +138 -57
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +4 -2
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +2 -2
- data/lib/arel/collectors/substitute_binds.rb +3 -3
- data/lib/arel/nodes/binary.rb +1 -7
- data/lib/arel/nodes/bound_sql_literal.rb +9 -5
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +5 -4
- data/lib/arel/nodes/sql_literal.rb +8 -1
- data/lib/arel/nodes.rb +2 -2
- data/lib/arel/predications.rb +1 -1
- data/lib/arel/select_manager.rb +1 -1
- data/lib/arel/table.rb +3 -7
- data/lib/arel/tree_manager.rb +3 -2
- data/lib/arel/update_manager.rb +2 -1
- data/lib/arel/visitors/dot.rb +1 -0
- data/lib/arel/visitors/mysql.rb +9 -4
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/sqlite.rb +25 -0
- data/lib/arel/visitors/to_sql.rb +29 -16
- data/lib/arel.rb +7 -3
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- metadata +18 -16
- data/lib/active_record/relation/record_fetch_warning.rb +0 -49
@@ -87,282 +87,6 @@ module ActiveRecord
|
|
87
87
|
end
|
88
88
|
end
|
89
89
|
|
90
|
-
# Inserts a single record into the database in a single SQL INSERT
|
91
|
-
# statement. It does not instantiate any models nor does it trigger
|
92
|
-
# Active Record callbacks or validations. Though passed values
|
93
|
-
# go through Active Record's type casting and serialization.
|
94
|
-
#
|
95
|
-
# See #insert_all for documentation.
|
96
|
-
def insert(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
|
97
|
-
insert_all([ attributes ], returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
|
98
|
-
end
|
99
|
-
|
100
|
-
# Inserts multiple records into the database in a single SQL INSERT
|
101
|
-
# statement. It does not instantiate any models nor does it trigger
|
102
|
-
# Active Record callbacks or validations. Though passed values
|
103
|
-
# go through Active Record's type casting and serialization.
|
104
|
-
#
|
105
|
-
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
106
|
-
# the attributes for a single row and must have the same keys.
|
107
|
-
#
|
108
|
-
# Rows are considered to be unique by every unique index on the table. Any
|
109
|
-
# duplicate rows are skipped.
|
110
|
-
# Override with <tt>:unique_by</tt> (see below).
|
111
|
-
#
|
112
|
-
# Returns an ActiveRecord::Result with its contents based on
|
113
|
-
# <tt>:returning</tt> (see below).
|
114
|
-
#
|
115
|
-
# ==== Options
|
116
|
-
#
|
117
|
-
# [:returning]
|
118
|
-
# (PostgreSQL and SQLite3 only) An array of attributes to return for all successfully
|
119
|
-
# inserted records, which by default is the primary key.
|
120
|
-
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
121
|
-
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
122
|
-
# clause entirely.
|
123
|
-
#
|
124
|
-
# You can also pass an SQL string if you need more control on the return values
|
125
|
-
# (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
|
126
|
-
#
|
127
|
-
# [:unique_by]
|
128
|
-
# (PostgreSQL and SQLite only) By default rows are considered to be unique
|
129
|
-
# by every unique index on the table. Any duplicate rows are skipped.
|
130
|
-
#
|
131
|
-
# To skip rows according to just one unique index pass <tt>:unique_by</tt>.
|
132
|
-
#
|
133
|
-
# Consider a Book model where no duplicate ISBNs make sense, but if any
|
134
|
-
# row has an existing id, or is not unique by another unique index,
|
135
|
-
# ActiveRecord::RecordNotUnique is raised.
|
136
|
-
#
|
137
|
-
# Unique indexes can be identified by columns or name:
|
138
|
-
#
|
139
|
-
# unique_by: :isbn
|
140
|
-
# unique_by: %i[ author_id name ]
|
141
|
-
# unique_by: :index_books_on_isbn
|
142
|
-
#
|
143
|
-
# [:record_timestamps]
|
144
|
-
# By default, automatic setting of timestamp columns is controlled by
|
145
|
-
# the model's <tt>record_timestamps</tt> config, matching typical
|
146
|
-
# behavior.
|
147
|
-
#
|
148
|
-
# To override this and force automatic setting of timestamp columns one
|
149
|
-
# way or the other, pass <tt>:record_timestamps</tt>:
|
150
|
-
#
|
151
|
-
# record_timestamps: true # Always set timestamps automatically
|
152
|
-
# record_timestamps: false # Never set timestamps automatically
|
153
|
-
#
|
154
|
-
# Because it relies on the index information from the database
|
155
|
-
# <tt>:unique_by</tt> is recommended to be paired with
|
156
|
-
# Active Record's schema_cache.
|
157
|
-
#
|
158
|
-
# ==== Example
|
159
|
-
#
|
160
|
-
# # Insert records and skip inserting any duplicates.
|
161
|
-
# # Here "Eloquent Ruby" is skipped because its id is not unique.
|
162
|
-
#
|
163
|
-
# Book.insert_all([
|
164
|
-
# { id: 1, title: "Rework", author: "David" },
|
165
|
-
# { id: 1, title: "Eloquent Ruby", author: "Russ" }
|
166
|
-
# ])
|
167
|
-
#
|
168
|
-
# # insert_all works on chained scopes, and you can use create_with
|
169
|
-
# # to set default attributes for all inserted records.
|
170
|
-
#
|
171
|
-
# author.books.create_with(created_at: Time.now).insert_all([
|
172
|
-
# { id: 1, title: "Rework" },
|
173
|
-
# { id: 2, title: "Eloquent Ruby" }
|
174
|
-
# ])
|
175
|
-
def insert_all(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
|
176
|
-
InsertAll.new(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps).execute
|
177
|
-
end
|
178
|
-
|
179
|
-
# Inserts a single record into the database in a single SQL INSERT
|
180
|
-
# statement. It does not instantiate any models nor does it trigger
|
181
|
-
# Active Record callbacks or validations. Though passed values
|
182
|
-
# go through Active Record's type casting and serialization.
|
183
|
-
#
|
184
|
-
# See #insert_all! for more.
|
185
|
-
def insert!(attributes, returning: nil, record_timestamps: nil)
|
186
|
-
insert_all!([ attributes ], returning: returning, record_timestamps: record_timestamps)
|
187
|
-
end
|
188
|
-
|
189
|
-
# Inserts multiple records into the database in a single SQL INSERT
|
190
|
-
# statement. It does not instantiate any models nor does it trigger
|
191
|
-
# Active Record callbacks or validations. Though passed values
|
192
|
-
# go through Active Record's type casting and serialization.
|
193
|
-
#
|
194
|
-
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
195
|
-
# the attributes for a single row and must have the same keys.
|
196
|
-
#
|
197
|
-
# Raises ActiveRecord::RecordNotUnique if any rows violate a
|
198
|
-
# unique index on the table. In that case, no rows are inserted.
|
199
|
-
#
|
200
|
-
# To skip duplicate rows, see #insert_all. To replace them, see #upsert_all.
|
201
|
-
#
|
202
|
-
# Returns an ActiveRecord::Result with its contents based on
|
203
|
-
# <tt>:returning</tt> (see below).
|
204
|
-
#
|
205
|
-
# ==== Options
|
206
|
-
#
|
207
|
-
# [:returning]
|
208
|
-
# (PostgreSQL and SQLite3 only) An array of attributes to return for all successfully
|
209
|
-
# inserted records, which by default is the primary key.
|
210
|
-
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
211
|
-
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
212
|
-
# clause entirely.
|
213
|
-
#
|
214
|
-
# You can also pass an SQL string if you need more control on the return values
|
215
|
-
# (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
|
216
|
-
#
|
217
|
-
# [:record_timestamps]
|
218
|
-
# By default, automatic setting of timestamp columns is controlled by
|
219
|
-
# the model's <tt>record_timestamps</tt> config, matching typical
|
220
|
-
# behavior.
|
221
|
-
#
|
222
|
-
# To override this and force automatic setting of timestamp columns one
|
223
|
-
# way or the other, pass <tt>:record_timestamps</tt>:
|
224
|
-
#
|
225
|
-
# record_timestamps: true # Always set timestamps automatically
|
226
|
-
# record_timestamps: false # Never set timestamps automatically
|
227
|
-
#
|
228
|
-
# ==== Examples
|
229
|
-
#
|
230
|
-
# # Insert multiple records
|
231
|
-
# Book.insert_all!([
|
232
|
-
# { title: "Rework", author: "David" },
|
233
|
-
# { title: "Eloquent Ruby", author: "Russ" }
|
234
|
-
# ])
|
235
|
-
#
|
236
|
-
# # Raises ActiveRecord::RecordNotUnique because "Eloquent Ruby"
|
237
|
-
# # does not have a unique id.
|
238
|
-
# Book.insert_all!([
|
239
|
-
# { id: 1, title: "Rework", author: "David" },
|
240
|
-
# { id: 1, title: "Eloquent Ruby", author: "Russ" }
|
241
|
-
# ])
|
242
|
-
def insert_all!(attributes, returning: nil, record_timestamps: nil)
|
243
|
-
InsertAll.new(self, attributes, on_duplicate: :raise, returning: returning, record_timestamps: record_timestamps).execute
|
244
|
-
end
|
245
|
-
|
246
|
-
# Updates or inserts (upserts) a single record into the database in a
|
247
|
-
# single SQL INSERT statement. It does not instantiate any models nor does
|
248
|
-
# it trigger Active Record callbacks or validations. Though passed values
|
249
|
-
# go through Active Record's type casting and serialization.
|
250
|
-
#
|
251
|
-
# See #upsert_all for documentation.
|
252
|
-
def upsert(attributes, **kwargs)
|
253
|
-
upsert_all([ attributes ], **kwargs)
|
254
|
-
end
|
255
|
-
|
256
|
-
# Updates or inserts (upserts) multiple records into the database in a
|
257
|
-
# single SQL INSERT statement. It does not instantiate any models nor does
|
258
|
-
# it trigger Active Record callbacks or validations. Though passed values
|
259
|
-
# go through Active Record's type casting and serialization.
|
260
|
-
#
|
261
|
-
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
262
|
-
# the attributes for a single row and must have the same keys.
|
263
|
-
#
|
264
|
-
# Returns an ActiveRecord::Result with its contents based on
|
265
|
-
# <tt>:returning</tt> (see below).
|
266
|
-
#
|
267
|
-
# By default, +upsert_all+ will update all the columns that can be updated when
|
268
|
-
# there is a conflict. These are all the columns except primary keys, read-only
|
269
|
-
# columns, and columns covered by the optional +unique_by+.
|
270
|
-
#
|
271
|
-
# ==== Options
|
272
|
-
#
|
273
|
-
# [:returning]
|
274
|
-
# (PostgreSQL and SQLite3 only) An array of attributes to return for all successfully
|
275
|
-
# inserted records, which by default is the primary key.
|
276
|
-
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
277
|
-
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
278
|
-
# clause entirely.
|
279
|
-
#
|
280
|
-
# You can also pass an SQL string if you need more control on the return values
|
281
|
-
# (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
|
282
|
-
#
|
283
|
-
# [:unique_by]
|
284
|
-
# (PostgreSQL and SQLite only) By default rows are considered to be unique
|
285
|
-
# by every unique index on the table. Any duplicate rows are skipped.
|
286
|
-
#
|
287
|
-
# To skip rows according to just one unique index pass <tt>:unique_by</tt>.
|
288
|
-
#
|
289
|
-
# Consider a Book model where no duplicate ISBNs make sense, but if any
|
290
|
-
# row has an existing id, or is not unique by another unique index,
|
291
|
-
# ActiveRecord::RecordNotUnique is raised.
|
292
|
-
#
|
293
|
-
# Unique indexes can be identified by columns or name:
|
294
|
-
#
|
295
|
-
# unique_by: :isbn
|
296
|
-
# unique_by: %i[ author_id name ]
|
297
|
-
# unique_by: :index_books_on_isbn
|
298
|
-
#
|
299
|
-
# Because it relies on the index information from the database
|
300
|
-
# <tt>:unique_by</tt> is recommended to be paired with
|
301
|
-
# Active Record's schema_cache.
|
302
|
-
#
|
303
|
-
# [:on_duplicate]
|
304
|
-
# Configure the SQL update sentence that will be used in case of conflict.
|
305
|
-
#
|
306
|
-
# NOTE: If you use this option you must provide all the columns you want to update
|
307
|
-
# by yourself.
|
308
|
-
#
|
309
|
-
# Example:
|
310
|
-
#
|
311
|
-
# Commodity.upsert_all(
|
312
|
-
# [
|
313
|
-
# { id: 2, name: "Copper", price: 4.84 },
|
314
|
-
# { id: 4, name: "Gold", price: 1380.87 },
|
315
|
-
# { id: 6, name: "Aluminium", price: 0.35 }
|
316
|
-
# ],
|
317
|
-
# on_duplicate: Arel.sql("price = GREATEST(commodities.price, EXCLUDED.price)")
|
318
|
-
# )
|
319
|
-
#
|
320
|
-
# See the related +:update_only+ option. Both options can't be used at the same time.
|
321
|
-
#
|
322
|
-
# [:update_only]
|
323
|
-
# Provide a list of column names that will be updated in case of conflict. If not provided,
|
324
|
-
# +upsert_all+ will update all the columns that can be updated. These are all the columns
|
325
|
-
# except primary keys, read-only columns, and columns covered by the optional +unique_by+
|
326
|
-
#
|
327
|
-
# Example:
|
328
|
-
#
|
329
|
-
# Commodity.upsert_all(
|
330
|
-
# [
|
331
|
-
# { id: 2, name: "Copper", price: 4.84 },
|
332
|
-
# { id: 4, name: "Gold", price: 1380.87 },
|
333
|
-
# { id: 6, name: "Aluminium", price: 0.35 }
|
334
|
-
# ],
|
335
|
-
# update_only: [:price] # Only prices will be updated
|
336
|
-
# )
|
337
|
-
#
|
338
|
-
# See the related +:on_duplicate+ option. Both options can't be used at the same time.
|
339
|
-
#
|
340
|
-
# [:record_timestamps]
|
341
|
-
# By default, automatic setting of timestamp columns is controlled by
|
342
|
-
# the model's <tt>record_timestamps</tt> config, matching typical
|
343
|
-
# behavior.
|
344
|
-
#
|
345
|
-
# To override this and force automatic setting of timestamp columns one
|
346
|
-
# way or the other, pass <tt>:record_timestamps</tt>:
|
347
|
-
#
|
348
|
-
# record_timestamps: true # Always set timestamps automatically
|
349
|
-
# record_timestamps: false # Never set timestamps automatically
|
350
|
-
#
|
351
|
-
# ==== Examples
|
352
|
-
#
|
353
|
-
# # Inserts multiple records, performing an upsert when records have duplicate ISBNs.
|
354
|
-
# # Here "Eloquent Ruby" overwrites "Rework" because its ISBN is duplicate.
|
355
|
-
#
|
356
|
-
# Book.upsert_all([
|
357
|
-
# { title: "Rework", author: "David", isbn: "1" },
|
358
|
-
# { title: "Eloquent Ruby", author: "Russ", isbn: "1" }
|
359
|
-
# ], unique_by: :isbn)
|
360
|
-
#
|
361
|
-
# Book.find_by(isbn: "1").title # => "Eloquent Ruby"
|
362
|
-
def upsert_all(attributes, on_duplicate: :update, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
|
363
|
-
InsertAll.new(self, attributes, on_duplicate: on_duplicate, update_only: update_only, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps).execute
|
364
|
-
end
|
365
|
-
|
366
90
|
# Given an attributes hash, +instantiate+ returns a new instance of
|
367
91
|
# the appropriate class. Accepts only keys as strings.
|
368
92
|
#
|
@@ -511,62 +235,7 @@ module ActiveRecord
|
|
511
235
|
@composite_query_constraints_list ||= query_constraints_list || Array(primary_key)
|
512
236
|
end
|
513
237
|
|
514
|
-
|
515
|
-
# therefore all callbacks and filters are fired off before the object is deleted. This method is
|
516
|
-
# less efficient than #delete but allows cleanup methods and other actions to be run.
|
517
|
-
#
|
518
|
-
# This essentially finds the object (or multiple objects) with the given id, creates a new object
|
519
|
-
# from the attributes, and then calls destroy on it.
|
520
|
-
#
|
521
|
-
# ==== Parameters
|
522
|
-
#
|
523
|
-
# * +id+ - This should be the id or an array of ids to be destroyed.
|
524
|
-
#
|
525
|
-
# ==== Examples
|
526
|
-
#
|
527
|
-
# # Destroy a single object
|
528
|
-
# Todo.destroy(1)
|
529
|
-
#
|
530
|
-
# # Destroy multiple objects
|
531
|
-
# todos = [1,2,3]
|
532
|
-
# Todo.destroy(todos)
|
533
|
-
def destroy(id)
|
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
|
541
|
-
find(id).each(&:destroy)
|
542
|
-
else
|
543
|
-
find(id).destroy
|
544
|
-
end
|
545
|
-
end
|
546
|
-
|
547
|
-
# Deletes the row with a primary key matching the +id+ argument, using an
|
548
|
-
# SQL +DELETE+ statement, and returns the number of rows deleted. Active
|
549
|
-
# Record objects are not instantiated, so the object's callbacks are not
|
550
|
-
# executed, including any <tt>:dependent</tt> association options.
|
551
|
-
#
|
552
|
-
# You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
|
553
|
-
#
|
554
|
-
# Note: Although it is often much faster than the alternative, #destroy,
|
555
|
-
# skipping callbacks might bypass business logic in your application
|
556
|
-
# that ensures referential integrity or performs other essential jobs.
|
557
|
-
#
|
558
|
-
# ==== Examples
|
559
|
-
#
|
560
|
-
# # Delete a single row
|
561
|
-
# Todo.delete(1)
|
562
|
-
#
|
563
|
-
# # Delete multiple rows
|
564
|
-
# Todo.delete([2,3,4])
|
565
|
-
def delete(id_or_array)
|
566
|
-
delete_by(primary_key => id_or_array)
|
567
|
-
end
|
568
|
-
|
569
|
-
def _insert_record(values, returning) # :nodoc:
|
238
|
+
def _insert_record(connection, values, returning) # :nodoc:
|
570
239
|
primary_key = self.primary_key
|
571
240
|
primary_key_value = nil
|
572
241
|
|
@@ -605,7 +274,9 @@ module ActiveRecord
|
|
605
274
|
um.set(values.transform_keys { |name| arel_table[name] })
|
606
275
|
um.wheres = constraints
|
607
276
|
|
608
|
-
|
277
|
+
with_connection do |c|
|
278
|
+
c.update(um, "#{self} Update")
|
279
|
+
end
|
609
280
|
end
|
610
281
|
|
611
282
|
def _delete_record(constraints) # :nodoc:
|
@@ -621,7 +292,9 @@ module ActiveRecord
|
|
621
292
|
dm = Arel::DeleteManager.new(arel_table)
|
622
293
|
dm.wheres = constraints
|
623
294
|
|
624
|
-
|
295
|
+
with_connection do |c|
|
296
|
+
c.delete(dm, "#{self} Destroy")
|
297
|
+
end
|
625
298
|
end
|
626
299
|
|
627
300
|
private
|
@@ -1067,7 +740,7 @@ module ActiveRecord
|
|
1067
740
|
# end
|
1068
741
|
#
|
1069
742
|
def reload(options = nil)
|
1070
|
-
self.class.
|
743
|
+
self.class.connection_pool.clear_query_cache
|
1071
744
|
|
1072
745
|
fresh_object = if apply_scoping?(options)
|
1073
746
|
_find_record((options || {}).merge(all_queries: true))
|
@@ -1137,156 +810,159 @@ module ActiveRecord
|
|
1137
810
|
end
|
1138
811
|
end
|
1139
812
|
|
1140
|
-
|
1141
|
-
|
1142
|
-
|
1143
|
-
|
1144
|
-
|
1145
|
-
|
813
|
+
private
|
814
|
+
def init_internals
|
815
|
+
super
|
816
|
+
@_trigger_destroy_callback = @_trigger_update_callback = nil
|
817
|
+
@previously_new_record = false
|
818
|
+
end
|
1146
819
|
|
1147
|
-
|
1148
|
-
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
820
|
+
def strict_loaded_associations
|
821
|
+
@association_cache.find_all do |_, assoc|
|
822
|
+
assoc.owner.strict_loading? && !assoc.owner.strict_loading_n_plus_one_only?
|
823
|
+
end.map(&:first)
|
824
|
+
end
|
1152
825
|
|
1153
|
-
|
1154
|
-
|
1155
|
-
|
826
|
+
def _find_record(options)
|
827
|
+
all_queries = options ? options[:all_queries] : nil
|
828
|
+
base = self.class.all(all_queries: all_queries).preload(strict_loaded_associations)
|
1156
829
|
|
1157
|
-
|
1158
|
-
|
1159
|
-
|
1160
|
-
|
830
|
+
if options && options[:lock]
|
831
|
+
base.lock(options[:lock]).find_by!(_in_memory_query_constraints_hash)
|
832
|
+
else
|
833
|
+
base.find_by!(_in_memory_query_constraints_hash)
|
834
|
+
end
|
1161
835
|
end
|
1162
|
-
end
|
1163
836
|
|
1164
|
-
|
1165
|
-
|
1166
|
-
|
1167
|
-
|
1168
|
-
|
1169
|
-
|
837
|
+
def _in_memory_query_constraints_hash
|
838
|
+
if self.class.query_constraints_list.nil?
|
839
|
+
{ @primary_key => id }
|
840
|
+
else
|
841
|
+
self.class.query_constraints_list.index_with do |column_name|
|
842
|
+
attribute(column_name)
|
843
|
+
end
|
1170
844
|
end
|
1171
845
|
end
|
1172
|
-
end
|
1173
846
|
|
1174
|
-
|
1175
|
-
|
1176
|
-
|
1177
|
-
|
847
|
+
def apply_scoping?(options)
|
848
|
+
!(options && options[:unscoped]) &&
|
849
|
+
(self.class.default_scopes?(all_queries: true) || self.class.global_current_scope)
|
850
|
+
end
|
1178
851
|
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
1184
|
-
|
852
|
+
def _query_constraints_hash
|
853
|
+
if self.class.query_constraints_list.nil?
|
854
|
+
{ @primary_key => id_in_database }
|
855
|
+
else
|
856
|
+
self.class.query_constraints_list.index_with do |column_name|
|
857
|
+
attribute_in_database(column_name)
|
858
|
+
end
|
1185
859
|
end
|
1186
860
|
end
|
1187
|
-
end
|
1188
|
-
|
1189
|
-
# A hook to be overridden by association modules.
|
1190
|
-
def destroy_associations
|
1191
|
-
end
|
1192
|
-
|
1193
|
-
def destroy_row
|
1194
|
-
_delete_row
|
1195
|
-
end
|
1196
861
|
|
1197
|
-
|
1198
|
-
|
1199
|
-
|
862
|
+
# A hook to be overridden by association modules.
|
863
|
+
def destroy_associations
|
864
|
+
end
|
1200
865
|
|
1201
|
-
|
1202
|
-
|
866
|
+
def destroy_row
|
867
|
+
_delete_row
|
868
|
+
end
|
1203
869
|
|
1204
|
-
|
1205
|
-
|
870
|
+
def _delete_row
|
871
|
+
self.class._delete_record(_query_constraints_hash)
|
1206
872
|
end
|
1207
873
|
|
1208
|
-
|
1209
|
-
|
874
|
+
def _touch_row(attribute_names, time)
|
875
|
+
time ||= current_time_from_proper_timezone
|
1210
876
|
|
1211
|
-
|
1212
|
-
|
1213
|
-
|
1214
|
-
_query_constraints_hash
|
1215
|
-
)
|
1216
|
-
end
|
877
|
+
attribute_names.each do |attr_name|
|
878
|
+
_write_attribute(attr_name, time)
|
879
|
+
end
|
1217
880
|
|
1218
|
-
|
1219
|
-
|
1220
|
-
return false if destroyed?
|
1221
|
-
result = new_record? ? _create_record(&block) : _update_record(&block)
|
1222
|
-
result != false
|
1223
|
-
end
|
881
|
+
_update_row(attribute_names, "touch")
|
882
|
+
end
|
1224
883
|
|
1225
|
-
|
1226
|
-
|
1227
|
-
|
1228
|
-
|
884
|
+
def _update_row(attribute_names, attempted_action = "update")
|
885
|
+
self.class._update_record(
|
886
|
+
attributes_with_values(attribute_names),
|
887
|
+
_query_constraints_hash
|
888
|
+
)
|
889
|
+
end
|
1229
890
|
|
1230
|
-
|
1231
|
-
|
1232
|
-
|
1233
|
-
|
1234
|
-
|
1235
|
-
@_trigger_update_callback = affected_rows == 1
|
891
|
+
def create_or_update(**, &block)
|
892
|
+
_raise_readonly_record_error if readonly?
|
893
|
+
return false if destroyed?
|
894
|
+
result = new_record? ? _create_record(&block) : _update_record(&block)
|
895
|
+
result != false
|
1236
896
|
end
|
1237
897
|
|
1238
|
-
|
898
|
+
# Updates the associated record with values matching those of the instance attributes.
|
899
|
+
# Returns the number of affected rows.
|
900
|
+
def _update_record(attribute_names = self.attribute_names)
|
901
|
+
attribute_names = attributes_for_update(attribute_names)
|
1239
902
|
|
1240
|
-
|
903
|
+
if attribute_names.empty?
|
904
|
+
affected_rows = 0
|
905
|
+
@_trigger_update_callback = true
|
906
|
+
else
|
907
|
+
affected_rows = _update_row(attribute_names)
|
908
|
+
@_trigger_update_callback = affected_rows == 1
|
909
|
+
end
|
1241
910
|
|
1242
|
-
|
1243
|
-
end
|
911
|
+
@previously_new_record = false
|
1244
912
|
|
1245
|
-
|
1246
|
-
# and returns its id.
|
1247
|
-
def _create_record(attribute_names = self.attribute_names)
|
1248
|
-
attribute_names = attributes_for_create(attribute_names)
|
913
|
+
yield(self) if block_given?
|
1249
914
|
|
1250
|
-
|
915
|
+
affected_rows
|
916
|
+
end
|
1251
917
|
|
1252
|
-
|
1253
|
-
|
1254
|
-
|
1255
|
-
|
918
|
+
# Creates a record with values matching those of the instance attributes
|
919
|
+
# and returns its id.
|
920
|
+
def _create_record(attribute_names = self.attribute_names)
|
921
|
+
attribute_names = attributes_for_create(attribute_names)
|
1256
922
|
|
1257
|
-
|
1258
|
-
|
1259
|
-
end if returning_values
|
923
|
+
self.class.with_connection do |connection|
|
924
|
+
returning_columns = self.class._returning_columns_for_insert(connection)
|
1260
925
|
|
1261
|
-
|
1262
|
-
|
926
|
+
returning_values = self.class._insert_record(
|
927
|
+
connection,
|
928
|
+
attributes_with_values(attribute_names),
|
929
|
+
returning_columns
|
930
|
+
)
|
1263
931
|
|
1264
|
-
|
932
|
+
returning_columns.zip(returning_values).each do |column, value|
|
933
|
+
_write_attribute(column, type_for_attribute(column).deserialize(value)) if !_read_attribute(column)
|
934
|
+
end if returning_values
|
935
|
+
end
|
1265
936
|
|
1266
|
-
|
1267
|
-
|
937
|
+
@new_record = false
|
938
|
+
@previously_new_record = true
|
1268
939
|
|
1269
|
-
|
1270
|
-
raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attribute?(name)
|
1271
|
-
end
|
940
|
+
yield(self) if block_given?
|
1272
941
|
|
1273
|
-
|
1274
|
-
|
1275
|
-
key = self.class.primary_key
|
1276
|
-
raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy #{self.class} with #{key}=#{id}", self)
|
1277
|
-
ensure
|
1278
|
-
@_association_destroy_exception = nil
|
1279
|
-
end
|
942
|
+
id
|
943
|
+
end
|
1280
944
|
|
1281
|
-
|
1282
|
-
|
1283
|
-
|
945
|
+
def verify_readonly_attribute(name)
|
946
|
+
raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attribute?(name)
|
947
|
+
end
|
1284
948
|
|
1285
|
-
|
1286
|
-
|
1287
|
-
|
1288
|
-
|
1289
|
-
|
1290
|
-
|
949
|
+
def _raise_record_not_destroyed
|
950
|
+
@_association_destroy_exception ||= nil
|
951
|
+
key = self.class.primary_key
|
952
|
+
raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy #{self.class} with #{key}=#{id}", self)
|
953
|
+
ensure
|
954
|
+
@_association_destroy_exception = nil
|
955
|
+
end
|
956
|
+
|
957
|
+
def _raise_readonly_record_error
|
958
|
+
raise ReadOnlyRecord, "#{self.class} is marked as readonly"
|
959
|
+
end
|
960
|
+
|
961
|
+
def _raise_record_not_touched_error
|
962
|
+
raise ActiveRecordError, <<~MSG.squish
|
963
|
+
Cannot touch on a new or destroyed record object. Consider using
|
964
|
+
persisted?, new_record?, or destroyed? before touching.
|
965
|
+
MSG
|
966
|
+
end
|
1291
967
|
end
|
1292
968
|
end
|