activerecord 7.1.5.1 → 7.2.2.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 +645 -2329
- data/README.rdoc +15 -15
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +15 -8
- data/lib/active_record/associations/belongs_to_association.rb +14 -7
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- 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 +7 -1
- data/lib/active_record/associations/collection_proxy.rb +14 -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 +7 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +2 -1
- 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 +6 -0
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +59 -292
- 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 +23 -55
- 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 +7 -6
- data/lib/active_record/attribute_methods.rb +54 -63
- data/lib/active_record/attributes.rb +61 -47
- data/lib/active_record/autosave_association.rb +12 -29
- 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 +24 -107
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +270 -65
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +34 -17
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +189 -74
- data/lib/active_record/connection_adapters/abstract/quoting.rb +65 -91
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +15 -6
- data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -62
- data/lib/active_record/connection_adapters/abstract_adapter.rb +24 -44
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +40 -10
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +43 -48
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +6 -0
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +16 -15
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +5 -23
- data/lib/active_record/connection_adapters/pool_config.rb +7 -6
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +27 -4
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- 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/schema_definitions.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +15 -11
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +29 -24
- data/lib/active_record/connection_adapters/schema_cache.rb +123 -128
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -6
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +44 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +25 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +125 -75
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +15 -15
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -48
- data/lib/active_record/connection_adapters.rb +121 -0
- data/lib/active_record/connection_handling.rb +56 -41
- data/lib/active_record/core.rb +85 -37
- data/lib/active_record/counter_cache.rb +18 -9
- data/lib/active_record/database_configurations/connection_url_resolver.rb +7 -2
- data/lib/active_record/database_configurations/database_config.rb +19 -4
- data/lib/active_record/database_configurations/hash_config.rb +38 -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 +24 -0
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/encryptable_record.rb +3 -3
- data/lib/active_record/encryption/encrypted_attribute_type.rb +24 -4
- data/lib/active_record/encryption/encryptor.rb +18 -3
- 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/enum.rb +18 -1
- data/lib/active_record/errors.rb +46 -20
- data/lib/active_record/explain.rb +13 -24
- data/lib/active_record/fixtures.rb +37 -31
- data/lib/active_record/future_result.rb +8 -4
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +4 -2
- data/lib/active_record/insert_all.rb +18 -15
- data/lib/active_record/integration.rb +4 -1
- data/lib/active_record/internal_metadata.rb +48 -34
- data/lib/active_record/locking/optimistic.rb +7 -6
- data/lib/active_record/log_subscriber.rb +0 -21
- data/lib/active_record/message_pack.rb +1 -1
- data/lib/active_record/migration/command_recorder.rb +2 -3
- data/lib/active_record/migration/compatibility.rb +5 -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 +85 -76
- data/lib/active_record/model_schema.rb +31 -68
- data/lib/active_record/nested_attributes.rb +11 -3
- data/lib/active_record/normalization.rb +3 -7
- data/lib/active_record/persistence.rb +30 -352
- data/lib/active_record/query_cache.rb +19 -8
- data/lib/active_record/query_logs.rb +15 -0
- data/lib/active_record/querying.rb +21 -9
- data/lib/active_record/railtie.rb +37 -55
- data/lib/active_record/railties/controller_runtime.rb +13 -4
- data/lib/active_record/railties/databases.rake +40 -43
- data/lib/active_record/reflection.rb +98 -36
- data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
- data/lib/active_record/relation/batches.rb +14 -8
- data/lib/active_record/relation/calculations.rb +96 -63
- data/lib/active_record/relation/delegation.rb +8 -11
- data/lib/active_record/relation/finder_methods.rb +16 -2
- data/lib/active_record/relation/merger.rb +4 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +9 -3
- data/lib/active_record/relation/predicate_builder.rb +3 -3
- data/lib/active_record/relation/query_methods.rb +224 -58
- data/lib/active_record/relation/record_fetch_warning.rb +3 -0
- data/lib/active_record/relation/spawn_methods.rb +2 -18
- data/lib/active_record/relation/where_clause.rb +7 -19
- data/lib/active_record/relation.rb +496 -72
- data/lib/active_record/result.rb +31 -44
- data/lib/active_record/runtime_registry.rb +39 -0
- data/lib/active_record/sanitization.rb +24 -19
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +19 -9
- data/lib/active_record/schema_migration.rb +30 -14
- data/lib/active_record/scoping/named.rb +1 -0
- data/lib/active_record/signed_id.rb +20 -1
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/table_metadata.rb +1 -10
- data/lib/active_record/tasks/database_tasks.rb +69 -41
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -1
- data/lib/active_record/test_fixtures.rb +86 -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 +70 -14
- 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 +15 -10
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +148 -39
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +2 -0
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/nodes/binary.rb +0 -6
- 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 +4 -3
- data/lib/arel/nodes/sql_literal.rb +7 -0
- 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/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 +16 -10
@@ -86,10 +86,8 @@ module ActiveRecord # :nodoc:
|
|
86
86
|
#
|
87
87
|
# User.normalize_value_for(:phone, "+1 (555) 867-5309") # => "5558675309"
|
88
88
|
def normalizes(*names, with:, apply_to_nil: false)
|
89
|
-
names
|
90
|
-
|
91
|
-
NormalizedValueType.new(cast_type: cast_type, normalizer: with, normalize_nil: apply_to_nil)
|
92
|
-
end
|
89
|
+
decorate_attributes(names) do |name, cast_type|
|
90
|
+
NormalizedValueType.new(cast_type: cast_type, normalizer: with, normalize_nil: apply_to_nil)
|
93
91
|
end
|
94
92
|
|
95
93
|
self.normalized_attributes += names.map(&:to_sym)
|
@@ -154,9 +152,7 @@ module ActiveRecord # :nodoc:
|
|
154
152
|
[self.class, cast_type, normalizer, normalize_nil?].hash
|
155
153
|
end
|
156
154
|
|
157
|
-
|
158
|
-
Kernel.instance_method(:inspect).bind_call(self)
|
159
|
-
end
|
155
|
+
define_method(:inspect, Kernel.instance_method(:inspect))
|
160
156
|
|
161
157
|
private
|
162
158
|
def normalize(value)
|
@@ -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
|
|
@@ -579,16 +248,18 @@ module ActiveRecord
|
|
579
248
|
|
580
249
|
im = Arel::InsertManager.new(arel_table)
|
581
250
|
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
251
|
+
with_connection do |c|
|
252
|
+
if values.empty?
|
253
|
+
im.insert(connection.empty_insert_statement_value(primary_key))
|
254
|
+
else
|
255
|
+
im.insert(values.transform_keys { |name| arel_table[name] })
|
256
|
+
end
|
587
257
|
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
258
|
+
connection.insert(
|
259
|
+
im, "#{self} Create", primary_key || false, primary_key_value,
|
260
|
+
returning: returning
|
261
|
+
)
|
262
|
+
end
|
592
263
|
end
|
593
264
|
|
594
265
|
def _update_record(values, constraints) # :nodoc:
|
@@ -605,7 +276,9 @@ module ActiveRecord
|
|
605
276
|
um.set(values.transform_keys { |name| arel_table[name] })
|
606
277
|
um.wheres = constraints
|
607
278
|
|
608
|
-
|
279
|
+
with_connection do |c|
|
280
|
+
c.update(um, "#{self} Update")
|
281
|
+
end
|
609
282
|
end
|
610
283
|
|
611
284
|
def _delete_record(constraints) # :nodoc:
|
@@ -621,7 +294,9 @@ module ActiveRecord
|
|
621
294
|
dm = Arel::DeleteManager.new(arel_table)
|
622
295
|
dm.wheres = constraints
|
623
296
|
|
624
|
-
|
297
|
+
with_connection do |c|
|
298
|
+
c.delete(dm, "#{self} Destroy")
|
299
|
+
end
|
625
300
|
end
|
626
301
|
|
627
302
|
private
|
@@ -1067,7 +742,7 @@ module ActiveRecord
|
|
1067
742
|
# end
|
1068
743
|
#
|
1069
744
|
def reload(options = nil)
|
1070
|
-
self.class.
|
745
|
+
self.class.connection_pool.clear_query_cache
|
1071
746
|
|
1072
747
|
fresh_object = if apply_scoping?(options)
|
1073
748
|
_find_record((options || {}).merge(all_queries: true))
|
@@ -1247,16 +922,19 @@ module ActiveRecord
|
|
1247
922
|
def _create_record(attribute_names = self.attribute_names)
|
1248
923
|
attribute_names = attributes_for_create(attribute_names)
|
1249
924
|
|
1250
|
-
|
925
|
+
self.class.with_connection do |connection|
|
926
|
+
returning_columns = self.class._returning_columns_for_insert(connection)
|
1251
927
|
|
1252
|
-
|
1253
|
-
|
1254
|
-
|
1255
|
-
|
928
|
+
returning_values = self.class._insert_record(
|
929
|
+
connection,
|
930
|
+
attributes_with_values(attribute_names),
|
931
|
+
returning_columns
|
932
|
+
)
|
1256
933
|
|
1257
|
-
|
1258
|
-
|
1259
|
-
|
934
|
+
returning_columns.zip(returning_values).each do |column, value|
|
935
|
+
_write_attribute(column, value) if !_read_attribute(column)
|
936
|
+
end if returning_values
|
937
|
+
end
|
1260
938
|
|
1261
939
|
@new_record = false
|
1262
940
|
@previously_new_record = true
|
@@ -8,7 +8,13 @@ module ActiveRecord
|
|
8
8
|
# If it's not, it will execute the given block.
|
9
9
|
def cache(&block)
|
10
10
|
if connected? || !configurations.empty?
|
11
|
-
|
11
|
+
pool = connection_pool
|
12
|
+
was_enabled = pool.query_cache_enabled
|
13
|
+
begin
|
14
|
+
pool.enable_query_cache(&block)
|
15
|
+
ensure
|
16
|
+
pool.clear_query_cache unless was_enabled
|
17
|
+
end
|
12
18
|
else
|
13
19
|
yield
|
14
20
|
end
|
@@ -16,9 +22,12 @@ module ActiveRecord
|
|
16
22
|
|
17
23
|
# Disable the query cache within the block if Active Record is configured.
|
18
24
|
# If it's not, it will execute the given block.
|
19
|
-
|
25
|
+
#
|
26
|
+
# Set <tt>dirties: false</tt> to prevent query caches on all connections from being cleared by write operations.
|
27
|
+
# (By default, write operations dirty all connections' query caches in case they are replicas whose cache would now be outdated.)
|
28
|
+
def uncached(dirties: true, &block)
|
20
29
|
if connected? || !configurations.empty?
|
21
|
-
|
30
|
+
connection_pool.disable_query_cache(dirties: dirties, &block)
|
22
31
|
else
|
23
32
|
yield
|
24
33
|
end
|
@@ -26,14 +35,16 @@ module ActiveRecord
|
|
26
35
|
end
|
27
36
|
|
28
37
|
def self.run
|
29
|
-
ActiveRecord::Base.connection_handler.each_connection_pool.reject
|
38
|
+
ActiveRecord::Base.connection_handler.each_connection_pool.reject(&:query_cache_enabled).each do |pool|
|
39
|
+
next if pool.db_config&.query_cache == false
|
40
|
+
pool.enable_query_cache!
|
41
|
+
end
|
30
42
|
end
|
31
43
|
|
32
44
|
def self.complete(pools)
|
33
|
-
pools.each
|
34
|
-
|
35
|
-
|
36
|
-
pool.release_connection if pool.active_connection? && !pool.connection.transaction_open?
|
45
|
+
pools.each do |pool|
|
46
|
+
pool.disable_query_cache!
|
47
|
+
pool.clear_query_cache
|
37
48
|
end
|
38
49
|
end
|
39
50
|
|
@@ -26,6 +26,7 @@ module ActiveRecord
|
|
26
26
|
# * +socket+
|
27
27
|
# * +db_host+
|
28
28
|
# * +database+
|
29
|
+
# * +source_location+
|
29
30
|
#
|
30
31
|
# Action Controller adds default tags when loaded:
|
31
32
|
#
|
@@ -108,6 +109,20 @@ module ActiveRecord
|
|
108
109
|
end
|
109
110
|
end
|
110
111
|
|
112
|
+
if Thread.respond_to?(:each_caller_location)
|
113
|
+
def query_source_location # :nodoc:
|
114
|
+
Thread.each_caller_location do |location|
|
115
|
+
frame = LogSubscriber.backtrace_cleaner.clean_frame(location)
|
116
|
+
return frame if frame
|
117
|
+
end
|
118
|
+
nil
|
119
|
+
end
|
120
|
+
else
|
121
|
+
def query_source_location # :nodoc:
|
122
|
+
LogSubscriber.backtrace_cleaner.clean(caller_locations(1).each).first
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
111
126
|
ActiveSupport::ExecutionContext.after_change { ActiveRecord::QueryLogs.clear_cache }
|
112
127
|
|
113
128
|
private
|
@@ -10,15 +10,16 @@ module ActiveRecord
|
|
10
10
|
:first_or_create, :first_or_create!, :first_or_initialize,
|
11
11
|
:find_or_create_by, :find_or_create_by!, :find_or_initialize_by,
|
12
12
|
:create_or_find_by, :create_or_find_by!,
|
13
|
-
:destroy_all, :delete_all, :update_all, :touch_all, :destroy_by, :delete_by,
|
13
|
+
:destroy, :destroy_all, :delete, :delete_all, :update_all, :touch_all, :destroy_by, :delete_by,
|
14
14
|
:find_each, :find_in_batches, :in_batches,
|
15
15
|
:select, :reselect, :order, :regroup, :in_order_of, :reorder, :group, :limit, :offset, :joins, :left_joins, :left_outer_joins,
|
16
16
|
:where, :rewhere, :invert_where, :preload, :extract_associated, :eager_load, :includes, :from, :lock, :readonly,
|
17
17
|
:and, :or, :annotate, :optimizer_hints, :extending,
|
18
18
|
:having, :create_with, :distinct, :references, :none, :unscope, :merge, :except, :only,
|
19
19
|
:count, :average, :minimum, :maximum, :sum, :calculate,
|
20
|
-
:pluck, :pick, :ids, :async_ids, :strict_loading, :excluding, :without, :with,
|
20
|
+
:pluck, :pick, :ids, :async_ids, :strict_loading, :excluding, :without, :with, :with_recursive,
|
21
21
|
:async_count, :async_average, :async_minimum, :async_maximum, :async_sum, :async_pluck, :async_pick,
|
22
|
+
:insert, :insert_all, :insert!, :insert_all!, :upsert, :upsert_all
|
22
23
|
].freeze # :nodoc:
|
23
24
|
delegate(*QUERYING_METHODS, to: :all)
|
24
25
|
|
@@ -47,19 +48,26 @@ module ActiveRecord
|
|
47
48
|
#
|
48
49
|
# Note that building your own SQL query string from user input may expose your application to
|
49
50
|
# injection attacks (https://guides.rubyonrails.org/security.html#sql-injection).
|
50
|
-
def find_by_sql(sql, binds = [], preparable: nil, &block)
|
51
|
-
|
51
|
+
def find_by_sql(sql, binds = [], preparable: nil, allow_retry: false, &block)
|
52
|
+
result = with_connection do |c|
|
53
|
+
_query_by_sql(c, sql, binds, preparable: preparable, allow_retry: allow_retry)
|
54
|
+
end
|
55
|
+
_load_from_sql(result, &block)
|
52
56
|
end
|
53
57
|
|
54
58
|
# Same as <tt>#find_by_sql</tt> but perform the query asynchronously and returns an ActiveRecord::Promise.
|
55
59
|
def async_find_by_sql(sql, binds = [], preparable: nil, &block)
|
56
|
-
|
60
|
+
result = with_connection do |c|
|
61
|
+
_query_by_sql(c, sql, binds, preparable: preparable, async: true)
|
62
|
+
end
|
63
|
+
|
64
|
+
result.then do |result|
|
57
65
|
_load_from_sql(result, &block)
|
58
66
|
end
|
59
67
|
end
|
60
68
|
|
61
|
-
def _query_by_sql(sql, binds = [], preparable: nil, async: false) # :nodoc:
|
62
|
-
connection.select_all(sanitize_sql(sql), "#{name} Load", binds, preparable: preparable, async: async)
|
69
|
+
def _query_by_sql(connection, sql, binds = [], preparable: nil, async: false, allow_retry: false) # :nodoc:
|
70
|
+
connection.select_all(sanitize_sql(sql), "#{name} Load", binds, preparable: preparable, async: async, allow_retry: allow_retry)
|
63
71
|
end
|
64
72
|
|
65
73
|
def _load_from_sql(result_set, &block) # :nodoc:
|
@@ -99,12 +107,16 @@ module ActiveRecord
|
|
99
107
|
#
|
100
108
|
# * +sql+ - An SQL statement which should return a count query from the database, see the example above.
|
101
109
|
def count_by_sql(sql)
|
102
|
-
|
110
|
+
with_connection do |c|
|
111
|
+
c.select_value(sanitize_sql(sql), "#{name} Count").to_i
|
112
|
+
end
|
103
113
|
end
|
104
114
|
|
105
115
|
# Same as <tt>#count_by_sql</tt> but perform the query asynchronously and returns an ActiveRecord::Promise.
|
106
116
|
def async_count_by_sql(sql)
|
107
|
-
|
117
|
+
with_connection do |c|
|
118
|
+
c.select_value(sanitize_sql(sql), "#{name} Count", async: true).then(&:to_i)
|
119
|
+
end
|
108
120
|
end
|
109
121
|
end
|
110
122
|
end
|