activerecord 7.1.5.1 → 7.2.0.beta1
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 +515 -2445
- 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 +9 -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 +6 -4
- data/lib/active_record/associations/collection_proxy.rb +14 -1
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +29 -28
- data/lib/active_record/associations/join_dependency.rb +5 -5
- 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 +33 -16
- data/lib/active_record/attribute_assignment.rb +1 -11
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +1 -1
- data/lib/active_record/attribute_methods/primary_key.rb +23 -55
- data/lib/active_record/attribute_methods/read.rb +4 -16
- data/lib/active_record/attribute_methods/serialization.rb +4 -24
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -10
- data/lib/active_record/attribute_methods/write.rb +3 -3
- data/lib/active_record/attribute_methods.rb +60 -71
- data/lib/active_record/attributes.rb +55 -42
- data/lib/active_record/autosave_association.rb +13 -32
- data/lib/active_record/base.rb +2 -3
- 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 +248 -65
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +34 -17
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +159 -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 +14 -5
- data/lib/active_record/connection_adapters/abstract/transaction.rb +60 -57
- data/lib/active_record/connection_adapters/abstract_adapter.rb +18 -46
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +32 -6
- 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 +7 -1
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +11 -5
- 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/cidr.rb +1 -1
- 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 -13
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +26 -21
- 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 +107 -75
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +12 -6
- 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 +53 -37
- data/lib/active_record/counter_cache.rb +18 -9
- data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -3
- data/lib/active_record/database_configurations/database_config.rb +15 -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 +2 -2
- data/lib/active_record/encryption/encrypted_attribute_type.rb +22 -2
- data/lib/active_record/encryption/encryptor.rb +17 -2
- 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.rb +0 -2
- data/lib/active_record/enum.rb +10 -1
- data/lib/active_record/errors.rb +16 -11
- 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 +3 -3
- 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/marshalling.rb +1 -4
- 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 +28 -68
- data/lib/active_record/nested_attributes.rb +13 -16
- data/lib/active_record/normalization.rb +3 -7
- data/lib/active_record/persistence.rb +30 -352
- data/lib/active_record/query_cache.rb +18 -6
- data/lib/active_record/query_logs.rb +15 -0
- data/lib/active_record/querying.rb +21 -9
- data/lib/active_record/railtie.rb +50 -62
- data/lib/active_record/railties/controller_runtime.rb +13 -4
- data/lib/active_record/railties/databases.rake +41 -44
- data/lib/active_record/reflection.rb +90 -35
- data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
- data/lib/active_record/relation/batches.rb +3 -3
- data/lib/active_record/relation/calculations.rb +94 -61
- 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.rb +3 -3
- data/lib/active_record/relation/query_methods.rb +196 -57
- 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/signed_id.rb +11 -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 +76 -70
- 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 +81 -91
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +1 -1
- data/lib/active_record/token_for.rb +22 -12
- data/lib/active_record/touch_later.rb +1 -1
- data/lib/active_record/transaction.rb +68 -0
- data/lib/active_record/transactions.rb +43 -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 +14 -10
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +149 -40
- 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/to_sql.rb +29 -16
- data/lib/arel.rb +7 -3
- metadata +20 -15
@@ -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,12 +60,13 @@ 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
71
|
attr_reader :table, :klass, :loaded, :predicate_builder
|
23
72
|
attr_accessor :skip_preloading_value
|
@@ -207,18 +256,22 @@ module ActiveRecord
|
|
207
256
|
# the problem of running out of integers, if the underlying table is still stuck on a primary
|
208
257
|
# key of type int (note: All \Rails apps since 5.1+ have defaulted to bigint, which is not liable
|
209
258
|
# to this problem).
|
259
|
+
# * Columns with unique database constraints should not have uniqueness validations defined,
|
260
|
+
# otherwise #create will fail due to validation errors and #find_by will never be called.
|
210
261
|
#
|
211
262
|
# This method will return a record if all given attributes are covered by unique constraints
|
212
263
|
# (unless the INSERT -> DELETE -> SELECT race condition is triggered), but if creation was attempted
|
213
264
|
# and failed due to validation errors it won't be persisted, you get what #create returns in
|
214
265
|
# such situation.
|
215
266
|
def create_or_find_by(attributes, &block)
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
267
|
+
with_connection do |connection|
|
268
|
+
transaction(requires_new: true) { create(attributes, &block) }
|
269
|
+
rescue ActiveRecord::RecordNotUnique
|
270
|
+
if connection.transaction_open?
|
271
|
+
where(attributes).lock.find_by!(attributes)
|
272
|
+
else
|
273
|
+
find_by!(attributes)
|
274
|
+
end
|
222
275
|
end
|
223
276
|
end
|
224
277
|
|
@@ -226,12 +279,14 @@ module ActiveRecord
|
|
226
279
|
# {create!}[rdoc-ref:Persistence::ClassMethods#create!] so an exception
|
227
280
|
# is raised if the created record is invalid.
|
228
281
|
def create_or_find_by!(attributes, &block)
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
282
|
+
with_connection do |connection|
|
283
|
+
transaction(requires_new: true) { create!(attributes, &block) }
|
284
|
+
rescue ActiveRecord::RecordNotUnique
|
285
|
+
if connection.transaction_open?
|
286
|
+
where(attributes).lock.find_by!(attributes)
|
287
|
+
else
|
288
|
+
find_by!(attributes)
|
289
|
+
end
|
235
290
|
end
|
236
291
|
end
|
237
292
|
|
@@ -245,13 +300,30 @@ module ActiveRecord
|
|
245
300
|
# returns the result as a string. The string is formatted imitating the
|
246
301
|
# ones printed by the database shell.
|
247
302
|
#
|
303
|
+
# User.all.explain
|
304
|
+
# # EXPLAIN SELECT `users`.* FROM `users`
|
305
|
+
# # ...
|
306
|
+
#
|
248
307
|
# Note that this method actually runs the queries, since the results of some
|
249
308
|
# are needed by the next ones when eager loading is going on.
|
250
309
|
#
|
310
|
+
# To run EXPLAIN on queries created by +first+, +pluck+ and +count+, call
|
311
|
+
# these methods on +explain+:
|
312
|
+
#
|
313
|
+
# User.all.explain.count
|
314
|
+
# # EXPLAIN SELECT COUNT(*) FROM `users`
|
315
|
+
# # ...
|
316
|
+
#
|
317
|
+
# The column name can be passed if required:
|
318
|
+
#
|
319
|
+
# User.all.explain.maximum(:id)
|
320
|
+
# # EXPLAIN SELECT MAX(`users`.`id`) FROM `users`
|
321
|
+
# # ...
|
322
|
+
#
|
251
323
|
# Please see further details in the
|
252
324
|
# {Active Record Query Interface guide}[https://guides.rubyonrails.org/active_record_querying.html#running-explain].
|
253
325
|
def explain(*options)
|
254
|
-
|
326
|
+
ExplainProxy.new(self, options)
|
255
327
|
end
|
256
328
|
|
257
329
|
# Converts relation objects to Array.
|
@@ -401,28 +473,30 @@ module ActiveRecord
|
|
401
473
|
else
|
402
474
|
collection = eager_loading? ? apply_join_dependency : self
|
403
475
|
|
404
|
-
|
405
|
-
|
476
|
+
with_connection do |c|
|
477
|
+
column = c.visitor.compile(table[timestamp_column])
|
478
|
+
select_values = "COUNT(*) AS #{adapter_class.quote_column_name("size")}, MAX(%s) AS timestamp"
|
406
479
|
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
480
|
+
if collection.has_limit_or_offset?
|
481
|
+
query = collection.select("#{column} AS collection_cache_key_timestamp")
|
482
|
+
query._select!(table[Arel.star]) if distinct_value && collection.select_values.empty?
|
483
|
+
subquery_alias = "subquery_for_cache_key"
|
484
|
+
subquery_column = "#{subquery_alias}.collection_cache_key_timestamp"
|
485
|
+
arel = query.build_subquery(subquery_alias, select_values % subquery_column)
|
486
|
+
else
|
487
|
+
query = collection.unscope(:order)
|
488
|
+
query.select_values = [select_values % column]
|
489
|
+
arel = query.arel
|
490
|
+
end
|
418
491
|
|
419
|
-
|
492
|
+
size, timestamp = c.select_rows(arel, nil).first
|
420
493
|
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
494
|
+
if size
|
495
|
+
column_type = klass.type_for_attribute(timestamp_column)
|
496
|
+
timestamp = column_type.deserialize(timestamp)
|
497
|
+
else
|
498
|
+
size = 0
|
499
|
+
end
|
426
500
|
end
|
427
501
|
end
|
428
502
|
|
@@ -521,18 +595,20 @@ module ActiveRecord
|
|
521
595
|
values = Arel.sql(klass.sanitize_sql_for_assignment(updates, table.name))
|
522
596
|
end
|
523
597
|
|
524
|
-
|
525
|
-
|
598
|
+
klass.with_connection do |c|
|
599
|
+
arel = eager_loading? ? apply_join_dependency.arel : build_arel(c)
|
600
|
+
arel.source.left = table
|
526
601
|
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
602
|
+
group_values_arel_columns = arel_columns(group_values.uniq)
|
603
|
+
having_clause_ast = having_clause.ast unless having_clause.empty?
|
604
|
+
key = if klass.composite_primary_key?
|
605
|
+
primary_key.map { |pk| table[pk] }
|
606
|
+
else
|
607
|
+
table[primary_key]
|
608
|
+
end
|
609
|
+
stmt = arel.compile_update(values, key, having_clause_ast, group_values_arel_columns)
|
610
|
+
c.update(stmt, "#{klass} Update All").tap { reset }
|
533
611
|
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
612
|
end
|
537
613
|
|
538
614
|
def update(id = :all, attributes) # :nodoc:
|
@@ -551,6 +627,283 @@ module ActiveRecord
|
|
551
627
|
end
|
552
628
|
end
|
553
629
|
|
630
|
+
|
631
|
+
# Inserts a single record into the database in a single SQL INSERT
|
632
|
+
# statement. It does not instantiate any models nor does it trigger
|
633
|
+
# Active Record callbacks or validations. Though passed values
|
634
|
+
# go through Active Record's type casting and serialization.
|
635
|
+
#
|
636
|
+
# See #insert_all for documentation.
|
637
|
+
def insert(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
|
638
|
+
insert_all([ attributes ], returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
|
639
|
+
end
|
640
|
+
|
641
|
+
# Inserts multiple records into the database in a single SQL INSERT
|
642
|
+
# statement. It does not instantiate any models nor does it trigger
|
643
|
+
# Active Record callbacks or validations. Though passed values
|
644
|
+
# go through Active Record's type casting and serialization.
|
645
|
+
#
|
646
|
+
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
647
|
+
# the attributes for a single row and must have the same keys.
|
648
|
+
#
|
649
|
+
# Rows are considered to be unique by every unique index on the table. Any
|
650
|
+
# duplicate rows are skipped.
|
651
|
+
# Override with <tt>:unique_by</tt> (see below).
|
652
|
+
#
|
653
|
+
# Returns an ActiveRecord::Result with its contents based on
|
654
|
+
# <tt>:returning</tt> (see below).
|
655
|
+
#
|
656
|
+
# ==== Options
|
657
|
+
#
|
658
|
+
# [:returning]
|
659
|
+
# (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
|
660
|
+
# inserted records, which by default is the primary key.
|
661
|
+
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
662
|
+
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
663
|
+
# clause entirely.
|
664
|
+
#
|
665
|
+
# You can also pass an SQL string if you need more control on the return values
|
666
|
+
# (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
|
667
|
+
#
|
668
|
+
# [:unique_by]
|
669
|
+
# (PostgreSQL and SQLite only) By default rows are considered to be unique
|
670
|
+
# by every unique index on the table. Any duplicate rows are skipped.
|
671
|
+
#
|
672
|
+
# To skip rows according to just one unique index pass <tt>:unique_by</tt>.
|
673
|
+
#
|
674
|
+
# Consider a Book model where no duplicate ISBNs make sense, but if any
|
675
|
+
# row has an existing id, or is not unique by another unique index,
|
676
|
+
# ActiveRecord::RecordNotUnique is raised.
|
677
|
+
#
|
678
|
+
# Unique indexes can be identified by columns or name:
|
679
|
+
#
|
680
|
+
# unique_by: :isbn
|
681
|
+
# unique_by: %i[ author_id name ]
|
682
|
+
# unique_by: :index_books_on_isbn
|
683
|
+
#
|
684
|
+
# [:record_timestamps]
|
685
|
+
# By default, automatic setting of timestamp columns is controlled by
|
686
|
+
# the model's <tt>record_timestamps</tt> config, matching typical
|
687
|
+
# behavior.
|
688
|
+
#
|
689
|
+
# To override this and force automatic setting of timestamp columns one
|
690
|
+
# way or the other, pass <tt>:record_timestamps</tt>:
|
691
|
+
#
|
692
|
+
# record_timestamps: true # Always set timestamps automatically
|
693
|
+
# record_timestamps: false # Never set timestamps automatically
|
694
|
+
#
|
695
|
+
# Because it relies on the index information from the database
|
696
|
+
# <tt>:unique_by</tt> is recommended to be paired with
|
697
|
+
# Active Record's schema_cache.
|
698
|
+
#
|
699
|
+
# ==== Example
|
700
|
+
#
|
701
|
+
# # Insert records and skip inserting any duplicates.
|
702
|
+
# # Here "Eloquent Ruby" is skipped because its id is not unique.
|
703
|
+
#
|
704
|
+
# Book.insert_all([
|
705
|
+
# { id: 1, title: "Rework", author: "David" },
|
706
|
+
# { id: 1, title: "Eloquent Ruby", author: "Russ" }
|
707
|
+
# ])
|
708
|
+
#
|
709
|
+
# # insert_all works on chained scopes, and you can use create_with
|
710
|
+
# # to set default attributes for all inserted records.
|
711
|
+
#
|
712
|
+
# author.books.create_with(created_at: Time.now).insert_all([
|
713
|
+
# { id: 1, title: "Rework" },
|
714
|
+
# { id: 2, title: "Eloquent Ruby" }
|
715
|
+
# ])
|
716
|
+
def insert_all(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
|
717
|
+
InsertAll.execute(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
|
718
|
+
end
|
719
|
+
|
720
|
+
# Inserts a single record into the database in a single SQL INSERT
|
721
|
+
# statement. It does not instantiate any models nor does it trigger
|
722
|
+
# Active Record callbacks or validations. Though passed values
|
723
|
+
# go through Active Record's type casting and serialization.
|
724
|
+
#
|
725
|
+
# See #insert_all! for more.
|
726
|
+
def insert!(attributes, returning: nil, record_timestamps: nil)
|
727
|
+
insert_all!([ attributes ], returning: returning, record_timestamps: record_timestamps)
|
728
|
+
end
|
729
|
+
|
730
|
+
# Inserts multiple records into the database in a single SQL INSERT
|
731
|
+
# statement. It does not instantiate any models nor does it trigger
|
732
|
+
# Active Record callbacks or validations. Though passed values
|
733
|
+
# go through Active Record's type casting and serialization.
|
734
|
+
#
|
735
|
+
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
736
|
+
# the attributes for a single row and must have the same keys.
|
737
|
+
#
|
738
|
+
# Raises ActiveRecord::RecordNotUnique if any rows violate a
|
739
|
+
# unique index on the table. In that case, no rows are inserted.
|
740
|
+
#
|
741
|
+
# To skip duplicate rows, see #insert_all. To replace them, see #upsert_all.
|
742
|
+
#
|
743
|
+
# Returns an ActiveRecord::Result with its contents based on
|
744
|
+
# <tt>:returning</tt> (see below).
|
745
|
+
#
|
746
|
+
# ==== Options
|
747
|
+
#
|
748
|
+
# [:returning]
|
749
|
+
# (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
|
750
|
+
# inserted records, which by default is the primary key.
|
751
|
+
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
752
|
+
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
753
|
+
# clause entirely.
|
754
|
+
#
|
755
|
+
# You can also pass an SQL string if you need more control on the return values
|
756
|
+
# (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
|
757
|
+
#
|
758
|
+
# [:record_timestamps]
|
759
|
+
# By default, automatic setting of timestamp columns is controlled by
|
760
|
+
# the model's <tt>record_timestamps</tt> config, matching typical
|
761
|
+
# behavior.
|
762
|
+
#
|
763
|
+
# To override this and force automatic setting of timestamp columns one
|
764
|
+
# way or the other, pass <tt>:record_timestamps</tt>:
|
765
|
+
#
|
766
|
+
# record_timestamps: true # Always set timestamps automatically
|
767
|
+
# record_timestamps: false # Never set timestamps automatically
|
768
|
+
#
|
769
|
+
# ==== Examples
|
770
|
+
#
|
771
|
+
# # Insert multiple records
|
772
|
+
# Book.insert_all!([
|
773
|
+
# { title: "Rework", author: "David" },
|
774
|
+
# { title: "Eloquent Ruby", author: "Russ" }
|
775
|
+
# ])
|
776
|
+
#
|
777
|
+
# # Raises ActiveRecord::RecordNotUnique because "Eloquent Ruby"
|
778
|
+
# # does not have a unique id.
|
779
|
+
# Book.insert_all!([
|
780
|
+
# { id: 1, title: "Rework", author: "David" },
|
781
|
+
# { id: 1, title: "Eloquent Ruby", author: "Russ" }
|
782
|
+
# ])
|
783
|
+
def insert_all!(attributes, returning: nil, record_timestamps: nil)
|
784
|
+
InsertAll.execute(self, attributes, on_duplicate: :raise, returning: returning, record_timestamps: record_timestamps)
|
785
|
+
end
|
786
|
+
|
787
|
+
# Updates or inserts (upserts) a single record into the database in a
|
788
|
+
# single SQL INSERT statement. It does not instantiate any models nor does
|
789
|
+
# it trigger Active Record callbacks or validations. Though passed values
|
790
|
+
# go through Active Record's type casting and serialization.
|
791
|
+
#
|
792
|
+
# See #upsert_all for documentation.
|
793
|
+
def upsert(attributes, **kwargs)
|
794
|
+
upsert_all([ attributes ], **kwargs)
|
795
|
+
end
|
796
|
+
|
797
|
+
# Updates or inserts (upserts) multiple records into the database in a
|
798
|
+
# single SQL INSERT statement. It does not instantiate any models nor does
|
799
|
+
# it trigger Active Record callbacks or validations. Though passed values
|
800
|
+
# go through Active Record's type casting and serialization.
|
801
|
+
#
|
802
|
+
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
803
|
+
# the attributes for a single row and must have the same keys.
|
804
|
+
#
|
805
|
+
# Returns an ActiveRecord::Result with its contents based on
|
806
|
+
# <tt>:returning</tt> (see below).
|
807
|
+
#
|
808
|
+
# By default, +upsert_all+ will update all the columns that can be updated when
|
809
|
+
# there is a conflict. These are all the columns except primary keys, read-only
|
810
|
+
# columns, and columns covered by the optional +unique_by+.
|
811
|
+
#
|
812
|
+
# ==== Options
|
813
|
+
#
|
814
|
+
# [:returning]
|
815
|
+
# (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
|
816
|
+
# inserted records, which by default is the primary key.
|
817
|
+
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
818
|
+
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
819
|
+
# clause entirely.
|
820
|
+
#
|
821
|
+
# You can also pass an SQL string if you need more control on the return values
|
822
|
+
# (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
|
823
|
+
#
|
824
|
+
# [:unique_by]
|
825
|
+
# (PostgreSQL and SQLite only) By default rows are considered to be unique
|
826
|
+
# by every unique index on the table. Any duplicate rows are skipped.
|
827
|
+
#
|
828
|
+
# To skip rows according to just one unique index pass <tt>:unique_by</tt>.
|
829
|
+
#
|
830
|
+
# Consider a Book model where no duplicate ISBNs make sense, but if any
|
831
|
+
# row has an existing id, or is not unique by another unique index,
|
832
|
+
# ActiveRecord::RecordNotUnique is raised.
|
833
|
+
#
|
834
|
+
# Unique indexes can be identified by columns or name:
|
835
|
+
#
|
836
|
+
# unique_by: :isbn
|
837
|
+
# unique_by: %i[ author_id name ]
|
838
|
+
# unique_by: :index_books_on_isbn
|
839
|
+
#
|
840
|
+
# Because it relies on the index information from the database
|
841
|
+
# <tt>:unique_by</tt> is recommended to be paired with
|
842
|
+
# Active Record's schema_cache.
|
843
|
+
#
|
844
|
+
# [:on_duplicate]
|
845
|
+
# Configure the SQL update sentence that will be used in case of conflict.
|
846
|
+
#
|
847
|
+
# NOTE: If you use this option you must provide all the columns you want to update
|
848
|
+
# by yourself.
|
849
|
+
#
|
850
|
+
# Example:
|
851
|
+
#
|
852
|
+
# Commodity.upsert_all(
|
853
|
+
# [
|
854
|
+
# { id: 2, name: "Copper", price: 4.84 },
|
855
|
+
# { id: 4, name: "Gold", price: 1380.87 },
|
856
|
+
# { id: 6, name: "Aluminium", price: 0.35 }
|
857
|
+
# ],
|
858
|
+
# on_duplicate: Arel.sql("price = GREATEST(commodities.price, EXCLUDED.price)")
|
859
|
+
# )
|
860
|
+
#
|
861
|
+
# See the related +:update_only+ option. Both options can't be used at the same time.
|
862
|
+
#
|
863
|
+
# [:update_only]
|
864
|
+
# Provide a list of column names that will be updated in case of conflict. If not provided,
|
865
|
+
# +upsert_all+ will update all the columns that can be updated. These are all the columns
|
866
|
+
# except primary keys, read-only columns, and columns covered by the optional +unique_by+
|
867
|
+
#
|
868
|
+
# Example:
|
869
|
+
#
|
870
|
+
# Commodity.upsert_all(
|
871
|
+
# [
|
872
|
+
# { id: 2, name: "Copper", price: 4.84 },
|
873
|
+
# { id: 4, name: "Gold", price: 1380.87 },
|
874
|
+
# { id: 6, name: "Aluminium", price: 0.35 }
|
875
|
+
# ],
|
876
|
+
# update_only: [:price] # Only prices will be updated
|
877
|
+
# )
|
878
|
+
#
|
879
|
+
# See the related +:on_duplicate+ option. Both options can't be used at the same time.
|
880
|
+
#
|
881
|
+
# [:record_timestamps]
|
882
|
+
# By default, automatic setting of timestamp columns is controlled by
|
883
|
+
# the model's <tt>record_timestamps</tt> config, matching typical
|
884
|
+
# behavior.
|
885
|
+
#
|
886
|
+
# To override this and force automatic setting of timestamp columns one
|
887
|
+
# way or the other, pass <tt>:record_timestamps</tt>:
|
888
|
+
#
|
889
|
+
# record_timestamps: true # Always set timestamps automatically
|
890
|
+
# record_timestamps: false # Never set timestamps automatically
|
891
|
+
#
|
892
|
+
# ==== Examples
|
893
|
+
#
|
894
|
+
# # Inserts multiple records, performing an upsert when records have duplicate ISBNs.
|
895
|
+
# # Here "Eloquent Ruby" overwrites "Rework" because its ISBN is duplicate.
|
896
|
+
#
|
897
|
+
# Book.upsert_all([
|
898
|
+
# { title: "Rework", author: "David", isbn: "1" },
|
899
|
+
# { title: "Eloquent Ruby", author: "Russ", isbn: "1" }
|
900
|
+
# ], unique_by: :isbn)
|
901
|
+
#
|
902
|
+
# Book.find_by(isbn: "1").title # => "Eloquent Ruby"
|
903
|
+
def upsert_all(attributes, on_duplicate: :update, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
|
904
|
+
InsertAll.execute(self, attributes, on_duplicate: on_duplicate, update_only: update_only, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
|
905
|
+
end
|
906
|
+
|
554
907
|
# Updates the counters of the records in the current relation.
|
555
908
|
#
|
556
909
|
# ==== Parameters
|
@@ -659,19 +1012,79 @@ module ActiveRecord
|
|
659
1012
|
raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
|
660
1013
|
end
|
661
1014
|
|
662
|
-
|
663
|
-
|
1015
|
+
klass.with_connection do |c|
|
1016
|
+
arel = eager_loading? ? apply_join_dependency.arel : build_arel(c)
|
1017
|
+
arel.source.left = table
|
1018
|
+
|
1019
|
+
group_values_arel_columns = arel_columns(group_values.uniq)
|
1020
|
+
having_clause_ast = having_clause.ast unless having_clause.empty?
|
1021
|
+
key = if klass.composite_primary_key?
|
1022
|
+
primary_key.map { |pk| table[pk] }
|
1023
|
+
else
|
1024
|
+
table[primary_key]
|
1025
|
+
end
|
1026
|
+
stmt = arel.compile_delete(key, having_clause_ast, group_values_arel_columns)
|
1027
|
+
|
1028
|
+
c.delete(stmt, "#{klass} Delete All").tap { reset }
|
1029
|
+
end
|
1030
|
+
end
|
1031
|
+
|
1032
|
+
# Deletes the row with a primary key matching the +id+ argument, using an
|
1033
|
+
# SQL +DELETE+ statement, and returns the number of rows deleted. Active
|
1034
|
+
# Record objects are not instantiated, so the object's callbacks are not
|
1035
|
+
# executed, including any <tt>:dependent</tt> association options.
|
1036
|
+
#
|
1037
|
+
# You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
|
1038
|
+
#
|
1039
|
+
# Note: Although it is often much faster than the alternative, #destroy,
|
1040
|
+
# skipping callbacks might bypass business logic in your application
|
1041
|
+
# that ensures referential integrity or performs other essential jobs.
|
1042
|
+
#
|
1043
|
+
# ==== Examples
|
1044
|
+
#
|
1045
|
+
# # Delete a single row
|
1046
|
+
# Todo.delete(1)
|
1047
|
+
#
|
1048
|
+
# # Delete multiple rows
|
1049
|
+
# Todo.delete([2,3,4])
|
1050
|
+
def delete(id_or_array)
|
1051
|
+
return 0 if id_or_array.nil? || (id_or_array.is_a?(Array) && id_or_array.empty?)
|
1052
|
+
|
1053
|
+
where(model.primary_key => id_or_array).delete_all
|
1054
|
+
end
|
664
1055
|
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
1056
|
+
|
1057
|
+
# Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
|
1058
|
+
# therefore all callbacks and filters are fired off before the object is deleted. This method is
|
1059
|
+
# less efficient than #delete but allows cleanup methods and other actions to be run.
|
1060
|
+
#
|
1061
|
+
# This essentially finds the object (or multiple objects) with the given id, creates a new object
|
1062
|
+
# from the attributes, and then calls destroy on it.
|
1063
|
+
#
|
1064
|
+
# ==== Parameters
|
1065
|
+
#
|
1066
|
+
# * +id+ - This should be the id or an array of ids to be destroyed.
|
1067
|
+
#
|
1068
|
+
# ==== Examples
|
1069
|
+
#
|
1070
|
+
# # Destroy a single object
|
1071
|
+
# Todo.destroy(1)
|
1072
|
+
#
|
1073
|
+
# # Destroy multiple objects
|
1074
|
+
# todos = [1,2,3]
|
1075
|
+
# Todo.destroy(todos)
|
1076
|
+
def destroy(id)
|
1077
|
+
multiple_ids = if model.composite_primary_key?
|
1078
|
+
id.first.is_a?(Array)
|
669
1079
|
else
|
670
|
-
|
1080
|
+
id.is_a?(Array)
|
671
1081
|
end
|
672
|
-
stmt = arel.compile_delete(key, having_clause_ast, group_values_arel_columns)
|
673
1082
|
|
674
|
-
|
1083
|
+
if multiple_ids
|
1084
|
+
find(id).each(&:destroy)
|
1085
|
+
else
|
1086
|
+
find(id).destroy
|
1087
|
+
end
|
675
1088
|
end
|
676
1089
|
|
677
1090
|
# Finds and destroys all records matching the specified conditions.
|
@@ -719,17 +1132,19 @@ module ActiveRecord
|
|
719
1132
|
#
|
720
1133
|
# ASYNC Post Load (0.0ms) (db time 2ms) SELECT "posts".* FROM "posts" LIMIT 100
|
721
1134
|
def load_async
|
722
|
-
|
1135
|
+
with_connection do |c|
|
1136
|
+
return load if !c.async_enabled?
|
723
1137
|
|
724
|
-
|
725
|
-
|
1138
|
+
unless loaded?
|
1139
|
+
result = exec_main_query(async: c.current_transaction.closed?)
|
726
1140
|
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
1141
|
+
if result.is_a?(Array)
|
1142
|
+
@records = result
|
1143
|
+
else
|
1144
|
+
@future_result = result
|
1145
|
+
end
|
1146
|
+
@loaded = true
|
731
1147
|
end
|
732
|
-
@loaded = true
|
733
1148
|
end
|
734
1149
|
|
735
1150
|
self
|
@@ -785,8 +1200,9 @@ module ActiveRecord
|
|
785
1200
|
relation.to_sql
|
786
1201
|
end
|
787
1202
|
else
|
788
|
-
|
789
|
-
|
1203
|
+
klass.with_connection do |conn|
|
1204
|
+
conn.unprepared_statement { conn.to_sql(arel) }
|
1205
|
+
end
|
790
1206
|
end
|
791
1207
|
end
|
792
1208
|
|
@@ -871,7 +1287,7 @@ module ActiveRecord
|
|
871
1287
|
end
|
872
1288
|
|
873
1289
|
def alias_tracker(joins = [], aliases = nil) # :nodoc:
|
874
|
-
ActiveRecord::Associations::AliasTracker.create(
|
1290
|
+
ActiveRecord::Associations::AliasTracker.create(connection_pool, table.name, joins, aliases)
|
875
1291
|
end
|
876
1292
|
|
877
1293
|
class StrictLoadingScope # :nodoc:
|
@@ -947,7 +1363,11 @@ module ActiveRecord
|
|
947
1363
|
def _substitute_values(values)
|
948
1364
|
values.map do |name, value|
|
949
1365
|
attr = table[name]
|
950
|
-
|
1366
|
+
if Arel.arel_node?(value)
|
1367
|
+
if value.is_a?(Arel::Nodes::SqlLiteral)
|
1368
|
+
value = Arel::Nodes::Grouping.new(value)
|
1369
|
+
end
|
1370
|
+
else
|
951
1371
|
type = klass.type_for_attribute(attr.name)
|
952
1372
|
value = predicate_builder.build_bind_attribute(attr.name, type.cast(value))
|
953
1373
|
end
|
@@ -995,17 +1415,21 @@ module ActiveRecord
|
|
995
1415
|
if where_clause.contradiction?
|
996
1416
|
[].freeze
|
997
1417
|
elsif eager_loading?
|
998
|
-
|
999
|
-
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1418
|
+
klass.with_connection do |c|
|
1419
|
+
apply_join_dependency do |relation, join_dependency|
|
1420
|
+
if relation.null_relation?
|
1421
|
+
[].freeze
|
1422
|
+
else
|
1423
|
+
relation = join_dependency.apply_column_aliases(relation)
|
1424
|
+
@_join_dependency = join_dependency
|
1425
|
+
c.select_all(relation.arel, "SQL", async: async)
|
1426
|
+
end
|
1005
1427
|
end
|
1006
1428
|
end
|
1007
1429
|
else
|
1008
|
-
klass.
|
1430
|
+
klass.with_connection do |c|
|
1431
|
+
klass._query_by_sql(c, arel, async: async)
|
1432
|
+
end
|
1009
1433
|
end
|
1010
1434
|
end
|
1011
1435
|
end
|