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
@@ -3,6 +3,54 @@
|
|
3
3
|
module ActiveRecord
|
4
4
|
# = Active Record \Relation
|
5
5
|
class Relation
|
6
|
+
class ExplainProxy # :nodoc:
|
7
|
+
def initialize(relation, options)
|
8
|
+
@relation = relation
|
9
|
+
@options = options
|
10
|
+
end
|
11
|
+
|
12
|
+
def inspect
|
13
|
+
exec_explain { @relation.send(:exec_queries) }
|
14
|
+
end
|
15
|
+
|
16
|
+
def average(column_name)
|
17
|
+
exec_explain { @relation.average(column_name) }
|
18
|
+
end
|
19
|
+
|
20
|
+
def count(column_name = nil)
|
21
|
+
exec_explain { @relation.count(column_name) }
|
22
|
+
end
|
23
|
+
|
24
|
+
def first(limit = nil)
|
25
|
+
exec_explain { @relation.first(limit) }
|
26
|
+
end
|
27
|
+
|
28
|
+
def last(limit = nil)
|
29
|
+
exec_explain { @relation.last(limit) }
|
30
|
+
end
|
31
|
+
|
32
|
+
def maximum(column_name)
|
33
|
+
exec_explain { @relation.maximum(column_name) }
|
34
|
+
end
|
35
|
+
|
36
|
+
def minimum(column_name)
|
37
|
+
exec_explain { @relation.minimum(column_name) }
|
38
|
+
end
|
39
|
+
|
40
|
+
def pluck(*column_names)
|
41
|
+
exec_explain { @relation.pluck(*column_names) }
|
42
|
+
end
|
43
|
+
|
44
|
+
def sum(identity_or_column = nil)
|
45
|
+
exec_explain { @relation.sum(identity_or_column) }
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
def exec_explain(&block)
|
50
|
+
@relation.exec_explain(@relation.collecting_queries_for_explain { block.call }, @options)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
6
54
|
MULTI_VALUE_METHODS = [:includes, :eager_load, :preload, :select, :group,
|
7
55
|
:order, :joins, :left_outer_joins, :references,
|
8
56
|
:extending, :unscope, :optimizer_hints, :annotate,
|
@@ -12,26 +60,34 @@ module ActiveRecord
|
|
12
60
|
:reverse_order, :distinct, :create_with, :skip_query_cache]
|
13
61
|
|
14
62
|
CLAUSE_METHODS = [:where, :having, :from]
|
15
|
-
INVALID_METHODS_FOR_DELETE_ALL = [:distinct, :with]
|
63
|
+
INVALID_METHODS_FOR_DELETE_ALL = [:distinct, :with, :with_recursive]
|
16
64
|
|
17
65
|
VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS + CLAUSE_METHODS
|
18
66
|
|
19
67
|
include Enumerable
|
20
68
|
include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain, Delegation
|
69
|
+
include SignedId::RelationMethods, TokenFor::RelationMethods
|
21
70
|
|
22
|
-
attr_reader :table, :
|
71
|
+
attr_reader :table, :model, :loaded, :predicate_builder
|
23
72
|
attr_accessor :skip_preloading_value
|
24
|
-
alias :
|
73
|
+
alias :klass :model
|
25
74
|
alias :loaded? :loaded
|
26
75
|
alias :locked? :lock_value
|
27
76
|
|
28
|
-
def initialize(
|
29
|
-
|
77
|
+
def initialize(model, table: nil, predicate_builder: nil, values: {})
|
78
|
+
if table
|
79
|
+
predicate_builder ||= model.predicate_builder.with(TableMetadata.new(model, table))
|
80
|
+
else
|
81
|
+
table = model.arel_table
|
82
|
+
predicate_builder ||= model.predicate_builder
|
83
|
+
end
|
84
|
+
|
85
|
+
@model = model
|
30
86
|
@table = table
|
31
87
|
@values = values
|
32
88
|
@loaded = false
|
33
89
|
@predicate_builder = predicate_builder
|
34
|
-
@
|
90
|
+
@delegate_to_model = false
|
35
91
|
@future_result = nil
|
36
92
|
@records = nil
|
37
93
|
@async = false
|
@@ -44,7 +100,7 @@ module ActiveRecord
|
|
44
100
|
end
|
45
101
|
|
46
102
|
def bind_attribute(name, value) # :nodoc:
|
47
|
-
if reflection =
|
103
|
+
if reflection = model._reflect_on_association(name)
|
48
104
|
name = reflection.foreign_key
|
49
105
|
value = value.read_attribute(reflection.association_primary_key) unless value.nil?
|
50
106
|
end
|
@@ -207,18 +263,22 @@ module ActiveRecord
|
|
207
263
|
# the problem of running out of integers, if the underlying table is still stuck on a primary
|
208
264
|
# key of type int (note: All \Rails apps since 5.1+ have defaulted to bigint, which is not liable
|
209
265
|
# to this problem).
|
266
|
+
# * Columns with unique database constraints should not have uniqueness validations defined,
|
267
|
+
# otherwise #create will fail due to validation errors and #find_by will never be called.
|
210
268
|
#
|
211
269
|
# This method will return a record if all given attributes are covered by unique constraints
|
212
270
|
# (unless the INSERT -> DELETE -> SELECT race condition is triggered), but if creation was attempted
|
213
271
|
# and failed due to validation errors it won't be persisted, you get what #create returns in
|
214
272
|
# such situation.
|
215
273
|
def create_or_find_by(attributes, &block)
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
274
|
+
with_connection do |connection|
|
275
|
+
transaction(requires_new: true) { create(attributes, &block) }
|
276
|
+
rescue ActiveRecord::RecordNotUnique
|
277
|
+
if connection.transaction_open?
|
278
|
+
where(attributes).lock.find_by!(attributes)
|
279
|
+
else
|
280
|
+
find_by!(attributes)
|
281
|
+
end
|
222
282
|
end
|
223
283
|
end
|
224
284
|
|
@@ -226,12 +286,14 @@ module ActiveRecord
|
|
226
286
|
# {create!}[rdoc-ref:Persistence::ClassMethods#create!] so an exception
|
227
287
|
# is raised if the created record is invalid.
|
228
288
|
def create_or_find_by!(attributes, &block)
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
289
|
+
with_connection do |connection|
|
290
|
+
transaction(requires_new: true) { create!(attributes, &block) }
|
291
|
+
rescue ActiveRecord::RecordNotUnique
|
292
|
+
if connection.transaction_open?
|
293
|
+
where(attributes).lock.find_by!(attributes)
|
294
|
+
else
|
295
|
+
find_by!(attributes)
|
296
|
+
end
|
235
297
|
end
|
236
298
|
end
|
237
299
|
|
@@ -245,13 +307,30 @@ module ActiveRecord
|
|
245
307
|
# returns the result as a string. The string is formatted imitating the
|
246
308
|
# ones printed by the database shell.
|
247
309
|
#
|
310
|
+
# User.all.explain
|
311
|
+
# # EXPLAIN SELECT `users`.* FROM `users`
|
312
|
+
# # ...
|
313
|
+
#
|
248
314
|
# Note that this method actually runs the queries, since the results of some
|
249
315
|
# are needed by the next ones when eager loading is going on.
|
250
316
|
#
|
317
|
+
# To run EXPLAIN on queries created by +first+, +pluck+ and +count+, call
|
318
|
+
# these methods on +explain+:
|
319
|
+
#
|
320
|
+
# User.all.explain.count
|
321
|
+
# # EXPLAIN SELECT COUNT(*) FROM `users`
|
322
|
+
# # ...
|
323
|
+
#
|
324
|
+
# The column name can be passed if required:
|
325
|
+
#
|
326
|
+
# User.all.explain.maximum(:id)
|
327
|
+
# # EXPLAIN SELECT MAX(`users`.`id`) FROM `users`
|
328
|
+
# # ...
|
329
|
+
#
|
251
330
|
# Please see further details in the
|
252
331
|
# {Active Record Query Interface guide}[https://guides.rubyonrails.org/active_record_querying.html#running-explain].
|
253
332
|
def explain(*options)
|
254
|
-
|
333
|
+
ExplainProxy.new(self, options)
|
255
334
|
end
|
256
335
|
|
257
336
|
# Converts relation objects to Array.
|
@@ -358,14 +437,14 @@ module ActiveRecord
|
|
358
437
|
# Product.where("name like ?", "%Game%").cache_key(:last_reviewed_at)
|
359
438
|
def cache_key(timestamp_column = "updated_at")
|
360
439
|
@cache_keys ||= {}
|
361
|
-
@cache_keys[timestamp_column] ||=
|
440
|
+
@cache_keys[timestamp_column] ||= model.collection_cache_key(self, timestamp_column)
|
362
441
|
end
|
363
442
|
|
364
443
|
def compute_cache_key(timestamp_column = :updated_at) # :nodoc:
|
365
444
|
query_signature = ActiveSupport::Digest.hexdigest(to_sql)
|
366
|
-
key = "#{
|
445
|
+
key = "#{model.model_name.cache_key}/query-#{query_signature}"
|
367
446
|
|
368
|
-
if collection_cache_versioning
|
447
|
+
if model.collection_cache_versioning
|
369
448
|
key
|
370
449
|
else
|
371
450
|
"#{key}-#{compute_cache_version(timestamp_column)}"
|
@@ -384,7 +463,7 @@ module ActiveRecord
|
|
384
463
|
#
|
385
464
|
# SELECT COUNT(*), MAX("products"."updated_at") FROM "products" WHERE (name like '%Cosmic Encounter%')
|
386
465
|
def cache_version(timestamp_column = :updated_at)
|
387
|
-
if collection_cache_versioning
|
466
|
+
if model.collection_cache_versioning
|
388
467
|
@cache_versions ||= {}
|
389
468
|
@cache_versions[timestamp_column] ||= compute_cache_version(timestamp_column)
|
390
469
|
end
|
@@ -401,33 +480,35 @@ module ActiveRecord
|
|
401
480
|
else
|
402
481
|
collection = eager_loading? ? apply_join_dependency : self
|
403
482
|
|
404
|
-
|
405
|
-
|
483
|
+
with_connection do |c|
|
484
|
+
column = c.visitor.compile(table[timestamp_column])
|
485
|
+
select_values = "COUNT(*) AS #{model.adapter_class.quote_column_name("size")}, MAX(%s) AS timestamp"
|
406
486
|
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
487
|
+
if collection.has_limit_or_offset?
|
488
|
+
query = collection.select("#{column} AS collection_cache_key_timestamp")
|
489
|
+
query._select!(table[Arel.star]) if distinct_value && collection.select_values.empty?
|
490
|
+
subquery_alias = "subquery_for_cache_key"
|
491
|
+
subquery_column = "#{subquery_alias}.collection_cache_key_timestamp"
|
492
|
+
arel = query.build_subquery(subquery_alias, select_values % subquery_column)
|
493
|
+
else
|
494
|
+
query = collection.unscope(:order)
|
495
|
+
query.select_values = [select_values % column]
|
496
|
+
arel = query.arel
|
497
|
+
end
|
418
498
|
|
419
|
-
|
499
|
+
size, timestamp = c.select_rows(arel, nil).first
|
420
500
|
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
501
|
+
if size
|
502
|
+
column_type = model.type_for_attribute(timestamp_column)
|
503
|
+
timestamp = column_type.deserialize(timestamp)
|
504
|
+
else
|
505
|
+
size = 0
|
506
|
+
end
|
426
507
|
end
|
427
508
|
end
|
428
509
|
|
429
510
|
if timestamp
|
430
|
-
"#{size}-#{timestamp.utc.to_fs(cache_timestamp_format)}"
|
511
|
+
"#{size}-#{timestamp.utc.to_fs(model.cache_timestamp_format)}"
|
431
512
|
else
|
432
513
|
"#{size}"
|
433
514
|
end
|
@@ -458,7 +539,7 @@ module ActiveRecord
|
|
458
539
|
# Please check unscoped if you want to remove all previous scopes (including
|
459
540
|
# the default_scope) during the execution of a block.
|
460
541
|
def scoping(all_queries: nil, &block)
|
461
|
-
registry =
|
542
|
+
registry = model.scope_registry
|
462
543
|
if global_scope?(registry) && all_queries == false
|
463
544
|
raise ArgumentError, "Scoping is set to apply to all queries and cannot be unset in a nested block."
|
464
545
|
elsif already_in_scope?(registry)
|
@@ -469,11 +550,11 @@ module ActiveRecord
|
|
469
550
|
end
|
470
551
|
|
471
552
|
def _exec_scope(...) # :nodoc:
|
472
|
-
@
|
473
|
-
registry =
|
553
|
+
@delegate_to_model = true
|
554
|
+
registry = model.scope_registry
|
474
555
|
_scoping(nil, registry) { instance_exec(...) || self }
|
475
556
|
ensure
|
476
|
-
@
|
557
|
+
@delegate_to_model = false
|
477
558
|
end
|
478
559
|
|
479
560
|
# Updates all records in the current relation with details given. This method constructs a single SQL UPDATE
|
@@ -510,36 +591,38 @@ module ActiveRecord
|
|
510
591
|
return 0 if @none
|
511
592
|
|
512
593
|
if updates.is_a?(Hash)
|
513
|
-
if
|
514
|
-
!updates.key?(
|
515
|
-
!updates.key?(
|
516
|
-
attr = table[
|
594
|
+
if model.locking_enabled? &&
|
595
|
+
!updates.key?(model.locking_column) &&
|
596
|
+
!updates.key?(model.locking_column.to_sym)
|
597
|
+
attr = table[model.locking_column]
|
517
598
|
updates[attr.name] = _increment_attribute(attr)
|
518
599
|
end
|
519
600
|
values = _substitute_values(updates)
|
520
601
|
else
|
521
|
-
values = Arel.sql(
|
602
|
+
values = Arel.sql(model.sanitize_sql_for_assignment(updates, table.name))
|
522
603
|
end
|
523
604
|
|
524
|
-
|
525
|
-
|
605
|
+
model.with_connection do |c|
|
606
|
+
arel = eager_loading? ? apply_join_dependency.arel : build_arel(c)
|
607
|
+
arel.source.left = table
|
526
608
|
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
609
|
+
group_values_arel_columns = arel_columns(group_values.uniq)
|
610
|
+
having_clause_ast = having_clause.ast unless having_clause.empty?
|
611
|
+
key = if model.composite_primary_key?
|
612
|
+
primary_key.map { |pk| table[pk] }
|
613
|
+
else
|
614
|
+
table[primary_key]
|
615
|
+
end
|
616
|
+
stmt = arel.compile_update(values, key, having_clause_ast, group_values_arel_columns)
|
617
|
+
c.update(stmt, "#{model} Update All").tap { reset }
|
533
618
|
end
|
534
|
-
stmt = arel.compile_update(values, key, having_clause_ast, group_values_arel_columns)
|
535
|
-
klass.connection.update(stmt, "#{klass} Update All").tap { reset }
|
536
619
|
end
|
537
620
|
|
538
621
|
def update(id = :all, attributes) # :nodoc:
|
539
622
|
if id == :all
|
540
623
|
each { |record| record.update(attributes) }
|
541
624
|
else
|
542
|
-
|
625
|
+
model.update(id, attributes)
|
543
626
|
end
|
544
627
|
end
|
545
628
|
|
@@ -547,10 +630,287 @@ module ActiveRecord
|
|
547
630
|
if id == :all
|
548
631
|
each { |record| record.update!(attributes) }
|
549
632
|
else
|
550
|
-
|
633
|
+
model.update!(id, attributes)
|
551
634
|
end
|
552
635
|
end
|
553
636
|
|
637
|
+
|
638
|
+
# Inserts a single record into the database in a single SQL INSERT
|
639
|
+
# statement. It does not instantiate any models nor does it trigger
|
640
|
+
# Active Record callbacks or validations. Though passed values
|
641
|
+
# go through Active Record's type casting and serialization.
|
642
|
+
#
|
643
|
+
# See #insert_all for documentation.
|
644
|
+
def insert(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
|
645
|
+
insert_all([ attributes ], returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
|
646
|
+
end
|
647
|
+
|
648
|
+
# Inserts multiple records into the database in a single SQL INSERT
|
649
|
+
# statement. It does not instantiate any models nor does it trigger
|
650
|
+
# Active Record callbacks or validations. Though passed values
|
651
|
+
# go through Active Record's type casting and serialization.
|
652
|
+
#
|
653
|
+
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
654
|
+
# the attributes for a single row and must have the same keys.
|
655
|
+
#
|
656
|
+
# Rows are considered to be unique by every unique index on the table. Any
|
657
|
+
# duplicate rows are skipped.
|
658
|
+
# Override with <tt>:unique_by</tt> (see below).
|
659
|
+
#
|
660
|
+
# Returns an ActiveRecord::Result with its contents based on
|
661
|
+
# <tt>:returning</tt> (see below).
|
662
|
+
#
|
663
|
+
# ==== Options
|
664
|
+
#
|
665
|
+
# [:returning]
|
666
|
+
# (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
|
667
|
+
# inserted records, which by default is the primary key.
|
668
|
+
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
669
|
+
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
670
|
+
# clause entirely.
|
671
|
+
#
|
672
|
+
# You can also pass an SQL string if you need more control on the return values
|
673
|
+
# (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
|
674
|
+
#
|
675
|
+
# [:unique_by]
|
676
|
+
# (PostgreSQL and SQLite only) By default rows are considered to be unique
|
677
|
+
# by every unique index on the table. Any duplicate rows are skipped.
|
678
|
+
#
|
679
|
+
# To skip rows according to just one unique index pass <tt>:unique_by</tt>.
|
680
|
+
#
|
681
|
+
# Consider a Book model where no duplicate ISBNs make sense, but if any
|
682
|
+
# row has an existing id, or is not unique by another unique index,
|
683
|
+
# ActiveRecord::RecordNotUnique is raised.
|
684
|
+
#
|
685
|
+
# Unique indexes can be identified by columns or name:
|
686
|
+
#
|
687
|
+
# unique_by: :isbn
|
688
|
+
# unique_by: %i[ author_id name ]
|
689
|
+
# unique_by: :index_books_on_isbn
|
690
|
+
#
|
691
|
+
# [:record_timestamps]
|
692
|
+
# By default, automatic setting of timestamp columns is controlled by
|
693
|
+
# the model's <tt>record_timestamps</tt> config, matching typical
|
694
|
+
# behavior.
|
695
|
+
#
|
696
|
+
# To override this and force automatic setting of timestamp columns one
|
697
|
+
# way or the other, pass <tt>:record_timestamps</tt>:
|
698
|
+
#
|
699
|
+
# record_timestamps: true # Always set timestamps automatically
|
700
|
+
# record_timestamps: false # Never set timestamps automatically
|
701
|
+
#
|
702
|
+
# Because it relies on the index information from the database
|
703
|
+
# <tt>:unique_by</tt> is recommended to be paired with
|
704
|
+
# Active Record's schema_cache.
|
705
|
+
#
|
706
|
+
# ==== Example
|
707
|
+
#
|
708
|
+
# # Insert records and skip inserting any duplicates.
|
709
|
+
# # Here "Eloquent Ruby" is skipped because its id is not unique.
|
710
|
+
#
|
711
|
+
# Book.insert_all([
|
712
|
+
# { id: 1, title: "Rework", author: "David" },
|
713
|
+
# { id: 1, title: "Eloquent Ruby", author: "Russ" }
|
714
|
+
# ])
|
715
|
+
#
|
716
|
+
# # insert_all works on chained scopes, and you can use create_with
|
717
|
+
# # to set default attributes for all inserted records.
|
718
|
+
#
|
719
|
+
# author.books.create_with(created_at: Time.now).insert_all([
|
720
|
+
# { id: 1, title: "Rework" },
|
721
|
+
# { id: 2, title: "Eloquent Ruby" }
|
722
|
+
# ])
|
723
|
+
def insert_all(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
|
724
|
+
InsertAll.execute(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
|
725
|
+
end
|
726
|
+
|
727
|
+
# Inserts a single record into the database in a single SQL INSERT
|
728
|
+
# statement. It does not instantiate any models nor does it trigger
|
729
|
+
# Active Record callbacks or validations. Though passed values
|
730
|
+
# go through Active Record's type casting and serialization.
|
731
|
+
#
|
732
|
+
# See #insert_all! for more.
|
733
|
+
def insert!(attributes, returning: nil, record_timestamps: nil)
|
734
|
+
insert_all!([ attributes ], returning: returning, record_timestamps: record_timestamps)
|
735
|
+
end
|
736
|
+
|
737
|
+
# Inserts multiple records into the database in a single SQL INSERT
|
738
|
+
# statement. It does not instantiate any models nor does it trigger
|
739
|
+
# Active Record callbacks or validations. Though passed values
|
740
|
+
# go through Active Record's type casting and serialization.
|
741
|
+
#
|
742
|
+
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
743
|
+
# the attributes for a single row and must have the same keys.
|
744
|
+
#
|
745
|
+
# Raises ActiveRecord::RecordNotUnique if any rows violate a
|
746
|
+
# unique index on the table. In that case, no rows are inserted.
|
747
|
+
#
|
748
|
+
# To skip duplicate rows, see #insert_all. To replace them, see #upsert_all.
|
749
|
+
#
|
750
|
+
# Returns an ActiveRecord::Result with its contents based on
|
751
|
+
# <tt>:returning</tt> (see below).
|
752
|
+
#
|
753
|
+
# ==== Options
|
754
|
+
#
|
755
|
+
# [:returning]
|
756
|
+
# (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
|
757
|
+
# inserted records, which by default is the primary key.
|
758
|
+
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
759
|
+
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
760
|
+
# clause entirely.
|
761
|
+
#
|
762
|
+
# You can also pass an SQL string if you need more control on the return values
|
763
|
+
# (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
|
764
|
+
#
|
765
|
+
# [:record_timestamps]
|
766
|
+
# By default, automatic setting of timestamp columns is controlled by
|
767
|
+
# the model's <tt>record_timestamps</tt> config, matching typical
|
768
|
+
# behavior.
|
769
|
+
#
|
770
|
+
# To override this and force automatic setting of timestamp columns one
|
771
|
+
# way or the other, pass <tt>:record_timestamps</tt>:
|
772
|
+
#
|
773
|
+
# record_timestamps: true # Always set timestamps automatically
|
774
|
+
# record_timestamps: false # Never set timestamps automatically
|
775
|
+
#
|
776
|
+
# ==== Examples
|
777
|
+
#
|
778
|
+
# # Insert multiple records
|
779
|
+
# Book.insert_all!([
|
780
|
+
# { title: "Rework", author: "David" },
|
781
|
+
# { title: "Eloquent Ruby", author: "Russ" }
|
782
|
+
# ])
|
783
|
+
#
|
784
|
+
# # Raises ActiveRecord::RecordNotUnique because "Eloquent Ruby"
|
785
|
+
# # does not have a unique id.
|
786
|
+
# Book.insert_all!([
|
787
|
+
# { id: 1, title: "Rework", author: "David" },
|
788
|
+
# { id: 1, title: "Eloquent Ruby", author: "Russ" }
|
789
|
+
# ])
|
790
|
+
def insert_all!(attributes, returning: nil, record_timestamps: nil)
|
791
|
+
InsertAll.execute(self, attributes, on_duplicate: :raise, returning: returning, record_timestamps: record_timestamps)
|
792
|
+
end
|
793
|
+
|
794
|
+
# Updates or inserts (upserts) a single record into the database in a
|
795
|
+
# single SQL INSERT statement. It does not instantiate any models nor does
|
796
|
+
# it trigger Active Record callbacks or validations. Though passed values
|
797
|
+
# go through Active Record's type casting and serialization.
|
798
|
+
#
|
799
|
+
# See #upsert_all for documentation.
|
800
|
+
def upsert(attributes, **kwargs)
|
801
|
+
upsert_all([ attributes ], **kwargs)
|
802
|
+
end
|
803
|
+
|
804
|
+
# Updates or inserts (upserts) multiple records into the database in a
|
805
|
+
# single SQL INSERT statement. It does not instantiate any models nor does
|
806
|
+
# it trigger Active Record callbacks or validations. Though passed values
|
807
|
+
# go through Active Record's type casting and serialization.
|
808
|
+
#
|
809
|
+
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
810
|
+
# the attributes for a single row and must have the same keys.
|
811
|
+
#
|
812
|
+
# Returns an ActiveRecord::Result with its contents based on
|
813
|
+
# <tt>:returning</tt> (see below).
|
814
|
+
#
|
815
|
+
# By default, +upsert_all+ will update all the columns that can be updated when
|
816
|
+
# there is a conflict. These are all the columns except primary keys, read-only
|
817
|
+
# columns, and columns covered by the optional +unique_by+.
|
818
|
+
#
|
819
|
+
# ==== Options
|
820
|
+
#
|
821
|
+
# [:returning]
|
822
|
+
# (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
|
823
|
+
# upserted records, which by default is the primary key.
|
824
|
+
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
825
|
+
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
826
|
+
# clause entirely.
|
827
|
+
#
|
828
|
+
# You can also pass an SQL string if you need more control on the return values
|
829
|
+
# (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
|
830
|
+
#
|
831
|
+
# [:unique_by]
|
832
|
+
# (PostgreSQL and SQLite only) By default rows are considered to be unique
|
833
|
+
# by every unique index on the table. Any duplicate rows are skipped.
|
834
|
+
#
|
835
|
+
# To skip rows according to just one unique index pass <tt>:unique_by</tt>.
|
836
|
+
#
|
837
|
+
# Consider a Book model where no duplicate ISBNs make sense, but if any
|
838
|
+
# row has an existing id, or is not unique by another unique index,
|
839
|
+
# ActiveRecord::RecordNotUnique is raised.
|
840
|
+
#
|
841
|
+
# Unique indexes can be identified by columns or name:
|
842
|
+
#
|
843
|
+
# unique_by: :isbn
|
844
|
+
# unique_by: %i[ author_id name ]
|
845
|
+
# unique_by: :index_books_on_isbn
|
846
|
+
#
|
847
|
+
# Because it relies on the index information from the database
|
848
|
+
# <tt>:unique_by</tt> is recommended to be paired with
|
849
|
+
# Active Record's schema_cache.
|
850
|
+
#
|
851
|
+
# [:on_duplicate]
|
852
|
+
# Configure the SQL update sentence that will be used in case of conflict.
|
853
|
+
#
|
854
|
+
# NOTE: If you use this option you must provide all the columns you want to update
|
855
|
+
# by yourself.
|
856
|
+
#
|
857
|
+
# Example:
|
858
|
+
#
|
859
|
+
# Commodity.upsert_all(
|
860
|
+
# [
|
861
|
+
# { id: 2, name: "Copper", price: 4.84 },
|
862
|
+
# { id: 4, name: "Gold", price: 1380.87 },
|
863
|
+
# { id: 6, name: "Aluminium", price: 0.35 }
|
864
|
+
# ],
|
865
|
+
# on_duplicate: Arel.sql("price = GREATEST(commodities.price, EXCLUDED.price)")
|
866
|
+
# )
|
867
|
+
#
|
868
|
+
# See the related +:update_only+ option. Both options can't be used at the same time.
|
869
|
+
#
|
870
|
+
# [:update_only]
|
871
|
+
# Provide a list of column names that will be updated in case of conflict. If not provided,
|
872
|
+
# +upsert_all+ will update all the columns that can be updated. These are all the columns
|
873
|
+
# except primary keys, read-only columns, and columns covered by the optional +unique_by+
|
874
|
+
#
|
875
|
+
# Example:
|
876
|
+
#
|
877
|
+
# Commodity.upsert_all(
|
878
|
+
# [
|
879
|
+
# { id: 2, name: "Copper", price: 4.84 },
|
880
|
+
# { id: 4, name: "Gold", price: 1380.87 },
|
881
|
+
# { id: 6, name: "Aluminium", price: 0.35 }
|
882
|
+
# ],
|
883
|
+
# update_only: [:price] # Only prices will be updated
|
884
|
+
# )
|
885
|
+
#
|
886
|
+
# See the related +:on_duplicate+ option. Both options can't be used at the same time.
|
887
|
+
#
|
888
|
+
# [:record_timestamps]
|
889
|
+
# By default, automatic setting of timestamp columns is controlled by
|
890
|
+
# the model's <tt>record_timestamps</tt> config, matching typical
|
891
|
+
# behavior.
|
892
|
+
#
|
893
|
+
# To override this and force automatic setting of timestamp columns one
|
894
|
+
# way or the other, pass <tt>:record_timestamps</tt>:
|
895
|
+
#
|
896
|
+
# record_timestamps: true # Always set timestamps automatically
|
897
|
+
# record_timestamps: false # Never set timestamps automatically
|
898
|
+
#
|
899
|
+
# ==== Examples
|
900
|
+
#
|
901
|
+
# # Inserts multiple records, performing an upsert when records have duplicate ISBNs.
|
902
|
+
# # Here "Eloquent Ruby" overwrites "Rework" because its ISBN is duplicate.
|
903
|
+
#
|
904
|
+
# Book.upsert_all([
|
905
|
+
# { title: "Rework", author: "David", isbn: "1" },
|
906
|
+
# { title: "Eloquent Ruby", author: "Russ", isbn: "1" }
|
907
|
+
# ], unique_by: :isbn)
|
908
|
+
#
|
909
|
+
# Book.find_by(isbn: "1").title # => "Eloquent Ruby"
|
910
|
+
def upsert_all(attributes, on_duplicate: :update, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
|
911
|
+
InsertAll.execute(self, attributes, on_duplicate: on_duplicate, update_only: update_only, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
|
912
|
+
end
|
913
|
+
|
554
914
|
# Updates the counters of the records in the current relation.
|
555
915
|
#
|
556
916
|
# ==== Parameters
|
@@ -576,7 +936,7 @@ module ActiveRecord
|
|
576
936
|
names = touch if touch != true
|
577
937
|
names = Array.wrap(names)
|
578
938
|
options = names.extract_options!
|
579
|
-
touch_updates =
|
939
|
+
touch_updates = model.touch_attributes_with_time(*names, **options)
|
580
940
|
updates.merge!(touch_updates) unless touch_updates.empty?
|
581
941
|
end
|
582
942
|
|
@@ -607,7 +967,7 @@ module ActiveRecord
|
|
607
967
|
# Person.where(name: 'David').touch_all
|
608
968
|
# # => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670' WHERE \"people\".\"name\" = 'David'"
|
609
969
|
def touch_all(*names, time: nil)
|
610
|
-
update_all
|
970
|
+
update_all model.touch_attributes_with_time(*names, time: time)
|
611
971
|
end
|
612
972
|
|
613
973
|
# Destroys the records by instantiating each
|
@@ -659,19 +1019,79 @@ module ActiveRecord
|
|
659
1019
|
raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
|
660
1020
|
end
|
661
1021
|
|
662
|
-
|
663
|
-
|
1022
|
+
model.with_connection do |c|
|
1023
|
+
arel = eager_loading? ? apply_join_dependency.arel : build_arel(c)
|
1024
|
+
arel.source.left = table
|
664
1025
|
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
1026
|
+
group_values_arel_columns = arel_columns(group_values.uniq)
|
1027
|
+
having_clause_ast = having_clause.ast unless having_clause.empty?
|
1028
|
+
key = if model.composite_primary_key?
|
1029
|
+
primary_key.map { |pk| table[pk] }
|
1030
|
+
else
|
1031
|
+
table[primary_key]
|
1032
|
+
end
|
1033
|
+
stmt = arel.compile_delete(key, having_clause_ast, group_values_arel_columns)
|
1034
|
+
|
1035
|
+
c.delete(stmt, "#{model} Delete All").tap { reset }
|
1036
|
+
end
|
1037
|
+
end
|
1038
|
+
|
1039
|
+
# Deletes the row with a primary key matching the +id+ argument, using an
|
1040
|
+
# SQL +DELETE+ statement, and returns the number of rows deleted. Active
|
1041
|
+
# Record objects are not instantiated, so the object's callbacks are not
|
1042
|
+
# executed, including any <tt>:dependent</tt> association options.
|
1043
|
+
#
|
1044
|
+
# You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
|
1045
|
+
#
|
1046
|
+
# Note: Although it is often much faster than the alternative, #destroy,
|
1047
|
+
# skipping callbacks might bypass business logic in your application
|
1048
|
+
# that ensures referential integrity or performs other essential jobs.
|
1049
|
+
#
|
1050
|
+
# ==== Examples
|
1051
|
+
#
|
1052
|
+
# # Delete a single row
|
1053
|
+
# Todo.delete(1)
|
1054
|
+
#
|
1055
|
+
# # Delete multiple rows
|
1056
|
+
# Todo.delete([2,3,4])
|
1057
|
+
def delete(id_or_array)
|
1058
|
+
return 0 if id_or_array.nil? || (id_or_array.is_a?(Array) && id_or_array.empty?)
|
1059
|
+
|
1060
|
+
where(model.primary_key => id_or_array).delete_all
|
1061
|
+
end
|
1062
|
+
|
1063
|
+
|
1064
|
+
# Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
|
1065
|
+
# therefore all callbacks and filters are fired off before the object is deleted. This method is
|
1066
|
+
# less efficient than #delete but allows cleanup methods and other actions to be run.
|
1067
|
+
#
|
1068
|
+
# This essentially finds the object (or multiple objects) with the given id, creates a new object
|
1069
|
+
# from the attributes, and then calls destroy on it.
|
1070
|
+
#
|
1071
|
+
# ==== Parameters
|
1072
|
+
#
|
1073
|
+
# * +id+ - This should be the id or an array of ids to be destroyed.
|
1074
|
+
#
|
1075
|
+
# ==== Examples
|
1076
|
+
#
|
1077
|
+
# # Destroy a single object
|
1078
|
+
# Todo.destroy(1)
|
1079
|
+
#
|
1080
|
+
# # Destroy multiple objects
|
1081
|
+
# todos = [1,2,3]
|
1082
|
+
# Todo.destroy(todos)
|
1083
|
+
def destroy(id)
|
1084
|
+
multiple_ids = if model.composite_primary_key?
|
1085
|
+
id.first.is_a?(Array)
|
669
1086
|
else
|
670
|
-
|
1087
|
+
id.is_a?(Array)
|
671
1088
|
end
|
672
|
-
stmt = arel.compile_delete(key, having_clause_ast, group_values_arel_columns)
|
673
1089
|
|
674
|
-
|
1090
|
+
if multiple_ids
|
1091
|
+
find(id).each(&:destroy)
|
1092
|
+
else
|
1093
|
+
find(id).destroy
|
1094
|
+
end
|
675
1095
|
end
|
676
1096
|
|
677
1097
|
# Finds and destroys all records matching the specified conditions.
|
@@ -711,30 +1131,39 @@ module ActiveRecord
|
|
711
1131
|
# for queries to actually be executed concurrently. Otherwise it defaults to
|
712
1132
|
# executing them in the foreground.
|
713
1133
|
#
|
714
|
-
# +load_async+ will also fall back to executing in the foreground in the test environment when transactional
|
715
|
-
# fixtures are enabled.
|
716
|
-
#
|
717
1134
|
# If the query was actually executed in the background, the Active Record logs will show
|
718
1135
|
# it by prefixing the log line with <tt>ASYNC</tt>:
|
719
1136
|
#
|
720
1137
|
# ASYNC Post Load (0.0ms) (db time 2ms) SELECT "posts".* FROM "posts" LIMIT 100
|
721
1138
|
def load_async
|
722
|
-
|
1139
|
+
with_connection do |c|
|
1140
|
+
return load if !c.async_enabled?
|
723
1141
|
|
724
|
-
|
725
|
-
|
1142
|
+
unless loaded?
|
1143
|
+
result = exec_main_query(async: !c.current_transaction.joinable?)
|
726
1144
|
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
1145
|
+
if result.is_a?(Array)
|
1146
|
+
@records = result
|
1147
|
+
else
|
1148
|
+
@future_result = result
|
1149
|
+
end
|
1150
|
+
@loaded = true
|
731
1151
|
end
|
732
|
-
@loaded = true
|
733
1152
|
end
|
734
1153
|
|
735
1154
|
self
|
736
1155
|
end
|
737
1156
|
|
1157
|
+
def then(&block) # :nodoc:
|
1158
|
+
if @future_result
|
1159
|
+
@future_result.then do
|
1160
|
+
yield self
|
1161
|
+
end
|
1162
|
+
else
|
1163
|
+
super
|
1164
|
+
end
|
1165
|
+
end
|
1166
|
+
|
738
1167
|
# Returns <tt>true</tt> if the relation was scheduled on the background
|
739
1168
|
# thread pool.
|
740
1169
|
def scheduled?
|
@@ -765,7 +1194,7 @@ module ActiveRecord
|
|
765
1194
|
def reset
|
766
1195
|
@future_result&.cancel
|
767
1196
|
@future_result = nil
|
768
|
-
@
|
1197
|
+
@delegate_to_model = false
|
769
1198
|
@to_sql = @arel = @loaded = @should_eager_load = nil
|
770
1199
|
@offsets = @take = nil
|
771
1200
|
@cache_keys = nil
|
@@ -785,8 +1214,9 @@ module ActiveRecord
|
|
785
1214
|
relation.to_sql
|
786
1215
|
end
|
787
1216
|
else
|
788
|
-
|
789
|
-
|
1217
|
+
model.with_connection do |conn|
|
1218
|
+
conn.unprepared_statement { conn.to_sql(arel) }
|
1219
|
+
end
|
790
1220
|
end
|
791
1221
|
end
|
792
1222
|
|
@@ -794,12 +1224,12 @@ module ActiveRecord
|
|
794
1224
|
#
|
795
1225
|
# User.where(name: 'Oscar').where_values_hash
|
796
1226
|
# # => {name: "Oscar"}
|
797
|
-
def where_values_hash(relation_table_name =
|
1227
|
+
def where_values_hash(relation_table_name = model.table_name) # :nodoc:
|
798
1228
|
where_clause.to_h(relation_table_name)
|
799
1229
|
end
|
800
1230
|
|
801
1231
|
def scope_for_create
|
802
|
-
hash = where_clause.to_h(
|
1232
|
+
hash = where_clause.to_h(model.table_name, equality_only: true)
|
803
1233
|
create_with_value.each { |k, v| hash[k.to_s] = v } unless create_with_value.empty?
|
804
1234
|
hash
|
805
1235
|
end
|
@@ -845,6 +1275,10 @@ module ActiveRecord
|
|
845
1275
|
records.blank?
|
846
1276
|
end
|
847
1277
|
|
1278
|
+
def readonly?
|
1279
|
+
readonly_value
|
1280
|
+
end
|
1281
|
+
|
848
1282
|
def values
|
849
1283
|
@values.dup
|
850
1284
|
end
|
@@ -863,7 +1297,7 @@ module ActiveRecord
|
|
863
1297
|
end
|
864
1298
|
|
865
1299
|
def empty_scope? # :nodoc:
|
866
|
-
@values ==
|
1300
|
+
@values == model.unscoped.values
|
867
1301
|
end
|
868
1302
|
|
869
1303
|
def has_limit_or_offset? # :nodoc:
|
@@ -871,7 +1305,7 @@ module ActiveRecord
|
|
871
1305
|
end
|
872
1306
|
|
873
1307
|
def alias_tracker(joins = [], aliases = nil) # :nodoc:
|
874
|
-
ActiveRecord::Associations::AliasTracker.create(
|
1308
|
+
ActiveRecord::Associations::AliasTracker.create(model.connection_pool, table.name, joins, aliases)
|
875
1309
|
end
|
876
1310
|
|
877
1311
|
class StrictLoadingScope # :nodoc:
|
@@ -901,54 +1335,58 @@ module ActiveRecord
|
|
901
1335
|
|
902
1336
|
private
|
903
1337
|
def already_in_scope?(registry)
|
904
|
-
@
|
1338
|
+
@delegate_to_model && registry.current_scope(model, true)
|
905
1339
|
end
|
906
1340
|
|
907
1341
|
def global_scope?(registry)
|
908
|
-
registry.global_current_scope(
|
1342
|
+
registry.global_current_scope(model, true)
|
909
1343
|
end
|
910
1344
|
|
911
1345
|
def current_scope_restoring_block(&block)
|
912
|
-
current_scope =
|
1346
|
+
current_scope = model.current_scope(true)
|
913
1347
|
-> record do
|
914
|
-
|
1348
|
+
model.current_scope = current_scope
|
915
1349
|
yield record if block_given?
|
916
1350
|
end
|
917
1351
|
end
|
918
1352
|
|
919
1353
|
def _new(attributes, &block)
|
920
|
-
|
1354
|
+
model.new(attributes, &block)
|
921
1355
|
end
|
922
1356
|
|
923
1357
|
def _create(attributes, &block)
|
924
|
-
|
1358
|
+
model.create(attributes, &block)
|
925
1359
|
end
|
926
1360
|
|
927
1361
|
def _create!(attributes, &block)
|
928
|
-
|
1362
|
+
model.create!(attributes, &block)
|
929
1363
|
end
|
930
1364
|
|
931
1365
|
def _scoping(scope, registry, all_queries = false)
|
932
|
-
previous = registry.current_scope(
|
933
|
-
registry.set_current_scope(
|
1366
|
+
previous = registry.current_scope(model, true)
|
1367
|
+
registry.set_current_scope(model, scope)
|
934
1368
|
|
935
1369
|
if all_queries
|
936
|
-
previous_global = registry.global_current_scope(
|
937
|
-
registry.set_global_current_scope(
|
1370
|
+
previous_global = registry.global_current_scope(model, true)
|
1371
|
+
registry.set_global_current_scope(model, scope)
|
938
1372
|
end
|
939
1373
|
yield
|
940
1374
|
ensure
|
941
|
-
registry.set_current_scope(
|
1375
|
+
registry.set_current_scope(model, previous)
|
942
1376
|
if all_queries
|
943
|
-
registry.set_global_current_scope(
|
1377
|
+
registry.set_global_current_scope(model, previous_global)
|
944
1378
|
end
|
945
1379
|
end
|
946
1380
|
|
947
1381
|
def _substitute_values(values)
|
948
1382
|
values.map do |name, value|
|
949
1383
|
attr = table[name]
|
950
|
-
|
951
|
-
|
1384
|
+
if Arel.arel_node?(value)
|
1385
|
+
if value.is_a?(Arel::Nodes::SqlLiteral)
|
1386
|
+
value = Arel::Nodes::Grouping.new(value)
|
1387
|
+
end
|
1388
|
+
else
|
1389
|
+
type = model.type_for_attribute(attr.name)
|
952
1390
|
value = predicate_builder.build_bind_attribute(attr.name, type.cast(value))
|
953
1391
|
end
|
954
1392
|
[attr, value]
|
@@ -995,17 +1433,21 @@ module ActiveRecord
|
|
995
1433
|
if where_clause.contradiction?
|
996
1434
|
[].freeze
|
997
1435
|
elsif eager_loading?
|
998
|
-
|
999
|
-
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1436
|
+
model.with_connection do |c|
|
1437
|
+
apply_join_dependency do |relation, join_dependency|
|
1438
|
+
if relation.null_relation?
|
1439
|
+
[].freeze
|
1440
|
+
else
|
1441
|
+
relation = join_dependency.apply_column_aliases(relation)
|
1442
|
+
@_join_dependency = join_dependency
|
1443
|
+
c.select_all(relation.arel, "SQL", async: async)
|
1444
|
+
end
|
1005
1445
|
end
|
1006
1446
|
end
|
1007
1447
|
else
|
1008
|
-
|
1448
|
+
model.with_connection do |c|
|
1449
|
+
model._query_by_sql(c, arel, async: async)
|
1450
|
+
end
|
1009
1451
|
end
|
1010
1452
|
end
|
1011
1453
|
end
|
@@ -1017,13 +1459,13 @@ module ActiveRecord
|
|
1017
1459
|
@_join_dependency = nil
|
1018
1460
|
records
|
1019
1461
|
else
|
1020
|
-
|
1462
|
+
model._load_from_sql(rows, &block).freeze
|
1021
1463
|
end
|
1022
1464
|
end
|
1023
1465
|
|
1024
1466
|
def skip_query_cache_if_necessary(&block)
|
1025
1467
|
if skip_query_cache_value
|
1026
|
-
uncached(&block)
|
1468
|
+
model.uncached(&block)
|
1027
1469
|
else
|
1028
1470
|
yield
|
1029
1471
|
end
|