activerecord 7.2.2.1 → 8.0.5
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 +523 -677
- data/README.rdoc +2 -2
- data/lib/active_record/association_relation.rb +1 -0
- data/lib/active_record/associations/alias_tracker.rb +6 -4
- data/lib/active_record/associations/association.rb +34 -10
- data/lib/active_record/associations/belongs_to_association.rb +18 -2
- data/lib/active_record/associations/builder/association.rb +7 -6
- data/lib/active_record/associations/collection_association.rb +10 -8
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +3 -2
- data/lib/active_record/associations/join_dependency/join_association.rb +25 -27
- data/lib/active_record/associations/join_dependency.rb +2 -2
- data/lib/active_record/associations/preloader/association.rb +2 -2
- data/lib/active_record/associations/preloader/batch.rb +7 -1
- data/lib/active_record/associations/singular_association.rb +8 -3
- data/lib/active_record/associations.rb +34 -4
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- data/lib/active_record/attribute_methods/primary_key.rb +4 -8
- data/lib/active_record/attribute_methods/query.rb +34 -0
- data/lib/active_record/attribute_methods/serialization.rb +1 -1
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +2 -12
- data/lib/active_record/attribute_methods.rb +24 -19
- data/lib/active_record/attributes.rb +37 -26
- data/lib/active_record/autosave_association.rb +91 -39
- data/lib/active_record/base.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +34 -25
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +55 -47
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +91 -44
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +25 -8
- data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
- 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 +40 -10
- data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -5
- data/lib/active_record/connection_adapters/abstract_adapter.rb +92 -70
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +35 -46
- data/lib/active_record/connection_adapters/mysql/quoting.rb +7 -9
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +50 -45
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +84 -94
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +9 -9
- data/lib/active_record/connection_adapters/pool_config.rb +7 -7
- data/lib/active_record/connection_adapters/postgresql/column.rb +4 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +72 -43
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- 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 +20 -24
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +60 -17
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +61 -99
- data/lib/active_record/connection_adapters/schema_cache.rb +1 -3
- data/lib/active_record/connection_adapters/sqlite3/column.rb +8 -2
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +80 -100
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +27 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +10 -1
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +63 -17
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +37 -67
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +1 -18
- data/lib/active_record/connection_adapters.rb +0 -56
- data/lib/active_record/connection_handling.rb +35 -9
- data/lib/active_record/core.rb +57 -22
- data/lib/active_record/counter_cache.rb +1 -1
- data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -1
- data/lib/active_record/database_configurations/database_config.rb +4 -0
- data/lib/active_record/database_configurations/hash_config.rb +16 -2
- data/lib/active_record/delegated_type.rb +18 -18
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +5 -5
- data/lib/active_record/encryption/encrypted_attribute_type.rb +11 -2
- data/lib/active_record/encryption/encryptor.rb +37 -28
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- data/lib/active_record/encryption/scheme.rb +8 -1
- data/lib/active_record/enum.rb +22 -34
- data/lib/active_record/errors.rb +16 -8
- data/lib/active_record/fixture_set/table_row.rb +19 -2
- data/lib/active_record/fixtures.rb +0 -2
- data/lib/active_record/future_result.rb +15 -9
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/insert_all.rb +3 -3
- data/lib/active_record/locking/optimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +5 -11
- data/lib/active_record/migration/command_recorder.rb +32 -12
- data/lib/active_record/migration/compatibility.rb +5 -2
- data/lib/active_record/migration.rb +40 -43
- data/lib/active_record/model_schema.rb +3 -4
- data/lib/active_record/nested_attributes.rb +4 -6
- data/lib/active_record/persistence.rb +128 -130
- data/lib/active_record/query_logs.rb +106 -50
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +12 -12
- data/lib/active_record/railtie.rb +4 -28
- data/lib/active_record/railties/databases.rake +11 -34
- data/lib/active_record/reflection.rb +18 -21
- data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
- data/lib/active_record/relation/batches.rb +132 -72
- data/lib/active_record/relation/calculations.rb +71 -65
- data/lib/active_record/relation/delegation.rb +25 -14
- data/lib/active_record/relation/finder_methods.rb +32 -31
- data/lib/active_record/relation/merger.rb +8 -8
- data/lib/active_record/relation/predicate_builder/array_handler.rb +3 -1
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +2 -0
- 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 +13 -0
- data/lib/active_record/relation/query_attribute.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +121 -70
- data/lib/active_record/relation/spawn_methods.rb +7 -7
- data/lib/active_record/relation/where_clause.rb +9 -3
- data/lib/active_record/relation.rb +95 -67
- data/lib/active_record/result.rb +66 -4
- data/lib/active_record/sanitization.rb +7 -6
- data/lib/active_record/schema_dumper.rb +34 -11
- data/lib/active_record/schema_migration.rb +2 -1
- data/lib/active_record/scoping/named.rb +5 -2
- data/lib/active_record/secure_token.rb +3 -3
- data/lib/active_record/signed_id.rb +7 -6
- data/lib/active_record/statement_cache.rb +14 -14
- data/lib/active_record/store.rb +7 -3
- data/lib/active_record/table_metadata.rb +1 -3
- data/lib/active_record/tasks/database_tasks.rb +69 -60
- data/lib/active_record/tasks/mysql_database_tasks.rb +0 -2
- data/lib/active_record/tasks/postgresql_database_tasks.rb +9 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -2
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +12 -0
- data/lib/active_record/token_for.rb +1 -1
- data/lib/active_record/transactions.rb +8 -7
- data/lib/active_record/type/serialized.rb +5 -0
- data/lib/active_record/validations/uniqueness.rb +8 -8
- data/lib/active_record.rb +22 -49
- data/lib/arel/collectors/bind.rb +2 -2
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +2 -2
- data/lib/arel/crud.rb +2 -0
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/nodes/binary.rb +1 -1
- data/lib/arel/nodes/delete_statement.rb +4 -2
- data/lib/arel/nodes/node.rb +1 -1
- data/lib/arel/nodes/sql_literal.rb +1 -1
- data/lib/arel/nodes/update_statement.rb +4 -2
- data/lib/arel/predications.rb +1 -3
- data/lib/arel/select_manager.rb +6 -2
- data/lib/arel/table.rb +3 -7
- data/lib/arel/update_manager.rb +5 -0
- data/lib/arel/visitors/dot.rb +2 -0
- data/lib/arel/visitors/to_sql.rb +3 -1
- metadata +11 -15
- data/lib/active_record/relation/record_fetch_warning.rb +0 -52
|
@@ -68,19 +68,26 @@ module ActiveRecord
|
|
|
68
68
|
include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain, Delegation
|
|
69
69
|
include SignedId::RelationMethods, TokenFor::RelationMethods
|
|
70
70
|
|
|
71
|
-
attr_reader :table, :
|
|
71
|
+
attr_reader :table, :model, :loaded, :predicate_builder
|
|
72
72
|
attr_accessor :skip_preloading_value
|
|
73
|
-
alias :
|
|
73
|
+
alias :klass :model
|
|
74
74
|
alias :loaded? :loaded
|
|
75
75
|
alias :locked? :lock_value
|
|
76
76
|
|
|
77
|
-
def initialize(
|
|
78
|
-
|
|
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
|
|
79
86
|
@table = table
|
|
80
87
|
@values = values
|
|
81
88
|
@loaded = false
|
|
82
89
|
@predicate_builder = predicate_builder
|
|
83
|
-
@
|
|
90
|
+
@delegate_to_model = false
|
|
84
91
|
@future_result = nil
|
|
85
92
|
@records = nil
|
|
86
93
|
@async = false
|
|
@@ -93,7 +100,7 @@ module ActiveRecord
|
|
|
93
100
|
end
|
|
94
101
|
|
|
95
102
|
def bind_attribute(name, value) # :nodoc:
|
|
96
|
-
if reflection =
|
|
103
|
+
if reflection = model._reflect_on_association(name)
|
|
97
104
|
name = reflection.foreign_key
|
|
98
105
|
value = value.read_attribute(reflection.association_primary_key) unless value.nil?
|
|
99
106
|
end
|
|
@@ -265,7 +272,12 @@ module ActiveRecord
|
|
|
265
272
|
# such situation.
|
|
266
273
|
def create_or_find_by(attributes, &block)
|
|
267
274
|
with_connection do |connection|
|
|
268
|
-
|
|
275
|
+
record = nil
|
|
276
|
+
transaction(requires_new: true) do
|
|
277
|
+
record = create(attributes, &block)
|
|
278
|
+
record._last_transaction_return_status || raise(ActiveRecord::Rollback)
|
|
279
|
+
end
|
|
280
|
+
record
|
|
269
281
|
rescue ActiveRecord::RecordNotUnique
|
|
270
282
|
if connection.transaction_open?
|
|
271
283
|
where(attributes).lock.find_by!(attributes)
|
|
@@ -280,7 +292,12 @@ module ActiveRecord
|
|
|
280
292
|
# is raised if the created record is invalid.
|
|
281
293
|
def create_or_find_by!(attributes, &block)
|
|
282
294
|
with_connection do |connection|
|
|
283
|
-
|
|
295
|
+
record = nil
|
|
296
|
+
transaction(requires_new: true) do
|
|
297
|
+
record = create!(attributes, &block)
|
|
298
|
+
record._last_transaction_return_status || raise(ActiveRecord::Rollback)
|
|
299
|
+
end
|
|
300
|
+
record
|
|
284
301
|
rescue ActiveRecord::RecordNotUnique
|
|
285
302
|
if connection.transaction_open?
|
|
286
303
|
where(attributes).lock.find_by!(attributes)
|
|
@@ -290,7 +307,7 @@ module ActiveRecord
|
|
|
290
307
|
end
|
|
291
308
|
end
|
|
292
309
|
|
|
293
|
-
# Like #find_or_create_by, but calls {new}[rdoc-ref:Core
|
|
310
|
+
# Like #find_or_create_by, but calls {new}[rdoc-ref:Core.new]
|
|
294
311
|
# instead of {create}[rdoc-ref:Persistence::ClassMethods#create].
|
|
295
312
|
def find_or_initialize_by(attributes, &block)
|
|
296
313
|
find_by(attributes) || new(attributes, &block)
|
|
@@ -430,14 +447,14 @@ module ActiveRecord
|
|
|
430
447
|
# Product.where("name like ?", "%Game%").cache_key(:last_reviewed_at)
|
|
431
448
|
def cache_key(timestamp_column = "updated_at")
|
|
432
449
|
@cache_keys ||= {}
|
|
433
|
-
@cache_keys[timestamp_column] ||=
|
|
450
|
+
@cache_keys[timestamp_column] ||= model.collection_cache_key(self, timestamp_column)
|
|
434
451
|
end
|
|
435
452
|
|
|
436
453
|
def compute_cache_key(timestamp_column = :updated_at) # :nodoc:
|
|
437
454
|
query_signature = ActiveSupport::Digest.hexdigest(to_sql)
|
|
438
|
-
key = "#{
|
|
455
|
+
key = "#{model.model_name.cache_key}/query-#{query_signature}"
|
|
439
456
|
|
|
440
|
-
if collection_cache_versioning
|
|
457
|
+
if model.collection_cache_versioning
|
|
441
458
|
key
|
|
442
459
|
else
|
|
443
460
|
"#{key}-#{compute_cache_version(timestamp_column)}"
|
|
@@ -456,7 +473,7 @@ module ActiveRecord
|
|
|
456
473
|
#
|
|
457
474
|
# SELECT COUNT(*), MAX("products"."updated_at") FROM "products" WHERE (name like '%Cosmic Encounter%')
|
|
458
475
|
def cache_version(timestamp_column = :updated_at)
|
|
459
|
-
if collection_cache_versioning
|
|
476
|
+
if model.collection_cache_versioning
|
|
460
477
|
@cache_versions ||= {}
|
|
461
478
|
@cache_versions[timestamp_column] ||= compute_cache_version(timestamp_column)
|
|
462
479
|
end
|
|
@@ -475,7 +492,7 @@ module ActiveRecord
|
|
|
475
492
|
|
|
476
493
|
with_connection do |c|
|
|
477
494
|
column = c.visitor.compile(table[timestamp_column])
|
|
478
|
-
select_values = "COUNT(*) AS #{adapter_class.quote_column_name("size")}, MAX(%s) AS timestamp"
|
|
495
|
+
select_values = "COUNT(*) AS #{model.adapter_class.quote_column_name("size")}, MAX(%s) AS timestamp"
|
|
479
496
|
|
|
480
497
|
if collection.has_limit_or_offset?
|
|
481
498
|
query = collection.select("#{column} AS collection_cache_key_timestamp")
|
|
@@ -492,7 +509,7 @@ module ActiveRecord
|
|
|
492
509
|
size, timestamp = c.select_rows(arel, nil).first
|
|
493
510
|
|
|
494
511
|
if size
|
|
495
|
-
column_type =
|
|
512
|
+
column_type = model.type_for_attribute(timestamp_column)
|
|
496
513
|
timestamp = column_type.deserialize(timestamp)
|
|
497
514
|
else
|
|
498
515
|
size = 0
|
|
@@ -501,7 +518,7 @@ module ActiveRecord
|
|
|
501
518
|
end
|
|
502
519
|
|
|
503
520
|
if timestamp
|
|
504
|
-
"#{size}-#{timestamp.utc.to_fs(cache_timestamp_format)}"
|
|
521
|
+
"#{size}-#{timestamp.utc.to_fs(model.cache_timestamp_format)}"
|
|
505
522
|
else
|
|
506
523
|
"#{size}"
|
|
507
524
|
end
|
|
@@ -532,7 +549,7 @@ module ActiveRecord
|
|
|
532
549
|
# Please check unscoped if you want to remove all previous scopes (including
|
|
533
550
|
# the default_scope) during the execution of a block.
|
|
534
551
|
def scoping(all_queries: nil, &block)
|
|
535
|
-
registry =
|
|
552
|
+
registry = model.scope_registry
|
|
536
553
|
if global_scope?(registry) && all_queries == false
|
|
537
554
|
raise ArgumentError, "Scoping is set to apply to all queries and cannot be unset in a nested block."
|
|
538
555
|
elsif already_in_scope?(registry)
|
|
@@ -543,11 +560,11 @@ module ActiveRecord
|
|
|
543
560
|
end
|
|
544
561
|
|
|
545
562
|
def _exec_scope(...) # :nodoc:
|
|
546
|
-
@
|
|
547
|
-
registry =
|
|
563
|
+
@delegate_to_model = true
|
|
564
|
+
registry = model.scope_registry
|
|
548
565
|
_scoping(nil, registry) { instance_exec(...) || self }
|
|
549
566
|
ensure
|
|
550
|
-
@
|
|
567
|
+
@delegate_to_model = false
|
|
551
568
|
end
|
|
552
569
|
|
|
553
570
|
# Updates all records in the current relation with details given. This method constructs a single SQL UPDATE
|
|
@@ -584,30 +601,30 @@ module ActiveRecord
|
|
|
584
601
|
return 0 if @none
|
|
585
602
|
|
|
586
603
|
if updates.is_a?(Hash)
|
|
587
|
-
if
|
|
588
|
-
!updates.key?(
|
|
589
|
-
!updates.key?(
|
|
590
|
-
attr = table[
|
|
604
|
+
if model.locking_enabled? &&
|
|
605
|
+
!updates.key?(model.locking_column) &&
|
|
606
|
+
!updates.key?(model.locking_column.to_sym)
|
|
607
|
+
attr = table[model.locking_column]
|
|
591
608
|
updates[attr.name] = _increment_attribute(attr)
|
|
592
609
|
end
|
|
593
610
|
values = _substitute_values(updates)
|
|
594
611
|
else
|
|
595
|
-
values = Arel.sql(
|
|
612
|
+
values = Arel.sql(model.sanitize_sql_for_assignment(updates, table.name))
|
|
596
613
|
end
|
|
597
614
|
|
|
598
|
-
|
|
615
|
+
model.with_connection do |c|
|
|
599
616
|
arel = eager_loading? ? apply_join_dependency.arel : build_arel(c)
|
|
600
617
|
arel.source.left = table
|
|
601
618
|
|
|
602
619
|
group_values_arel_columns = arel_columns(group_values.uniq)
|
|
603
620
|
having_clause_ast = having_clause.ast unless having_clause.empty?
|
|
604
|
-
key = if
|
|
621
|
+
key = if model.composite_primary_key?
|
|
605
622
|
primary_key.map { |pk| table[pk] }
|
|
606
623
|
else
|
|
607
624
|
table[primary_key]
|
|
608
625
|
end
|
|
609
626
|
stmt = arel.compile_update(values, key, having_clause_ast, group_values_arel_columns)
|
|
610
|
-
c.update(stmt, "#{
|
|
627
|
+
c.update(stmt, "#{model} Update All").tap { reset }
|
|
611
628
|
end
|
|
612
629
|
end
|
|
613
630
|
|
|
@@ -615,7 +632,7 @@ module ActiveRecord
|
|
|
615
632
|
if id == :all
|
|
616
633
|
each { |record| record.update(attributes) }
|
|
617
634
|
else
|
|
618
|
-
|
|
635
|
+
model.update(id, attributes)
|
|
619
636
|
end
|
|
620
637
|
end
|
|
621
638
|
|
|
@@ -623,7 +640,7 @@ module ActiveRecord
|
|
|
623
640
|
if id == :all
|
|
624
641
|
each { |record| record.update!(attributes) }
|
|
625
642
|
else
|
|
626
|
-
|
|
643
|
+
model.update!(id, attributes)
|
|
627
644
|
end
|
|
628
645
|
end
|
|
629
646
|
|
|
@@ -813,7 +830,7 @@ module ActiveRecord
|
|
|
813
830
|
#
|
|
814
831
|
# [:returning]
|
|
815
832
|
# (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
|
|
816
|
-
#
|
|
833
|
+
# upserted records, which by default is the primary key.
|
|
817
834
|
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
|
818
835
|
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
|
819
836
|
# clause entirely.
|
|
@@ -929,7 +946,7 @@ module ActiveRecord
|
|
|
929
946
|
names = touch if touch != true
|
|
930
947
|
names = Array.wrap(names)
|
|
931
948
|
options = names.extract_options!
|
|
932
|
-
touch_updates =
|
|
949
|
+
touch_updates = model.touch_attributes_with_time(*names, **options)
|
|
933
950
|
updates.merge!(touch_updates) unless touch_updates.empty?
|
|
934
951
|
end
|
|
935
952
|
|
|
@@ -942,7 +959,7 @@ module ActiveRecord
|
|
|
942
959
|
# If attribute names are passed, they are updated along with +updated_at+/+updated_on+ attributes.
|
|
943
960
|
# If no time argument is passed, the current time is used as default.
|
|
944
961
|
#
|
|
945
|
-
#
|
|
962
|
+
# ==== Examples
|
|
946
963
|
#
|
|
947
964
|
# # Touch all records
|
|
948
965
|
# Person.all.touch_all
|
|
@@ -960,7 +977,7 @@ module ActiveRecord
|
|
|
960
977
|
# Person.where(name: 'David').touch_all
|
|
961
978
|
# # => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670' WHERE \"people\".\"name\" = 'David'"
|
|
962
979
|
def touch_all(*names, time: nil)
|
|
963
|
-
update_all
|
|
980
|
+
update_all model.touch_attributes_with_time(*names, time: time)
|
|
964
981
|
end
|
|
965
982
|
|
|
966
983
|
# Destroys the records by instantiating each
|
|
@@ -993,7 +1010,7 @@ module ActiveRecord
|
|
|
993
1010
|
#
|
|
994
1011
|
# Post.where(person_id: 5).where(category: ['Something', 'Else']).delete_all
|
|
995
1012
|
#
|
|
996
|
-
#
|
|
1013
|
+
# This call deletes the affected posts all at once with a single DELETE statement.
|
|
997
1014
|
# If you need to destroy dependent associations or call your <tt>before_*</tt> or
|
|
998
1015
|
# +after_destroy+ callbacks, use the #destroy_all method instead.
|
|
999
1016
|
#
|
|
@@ -1012,20 +1029,20 @@ module ActiveRecord
|
|
|
1012
1029
|
raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
|
|
1013
1030
|
end
|
|
1014
1031
|
|
|
1015
|
-
|
|
1032
|
+
model.with_connection do |c|
|
|
1016
1033
|
arel = eager_loading? ? apply_join_dependency.arel : build_arel(c)
|
|
1017
1034
|
arel.source.left = table
|
|
1018
1035
|
|
|
1019
1036
|
group_values_arel_columns = arel_columns(group_values.uniq)
|
|
1020
1037
|
having_clause_ast = having_clause.ast unless having_clause.empty?
|
|
1021
|
-
key = if
|
|
1038
|
+
key = if model.composite_primary_key?
|
|
1022
1039
|
primary_key.map { |pk| table[pk] }
|
|
1023
1040
|
else
|
|
1024
1041
|
table[primary_key]
|
|
1025
1042
|
end
|
|
1026
1043
|
stmt = arel.compile_delete(key, having_clause_ast, group_values_arel_columns)
|
|
1027
1044
|
|
|
1028
|
-
c.delete(stmt, "#{
|
|
1045
|
+
c.delete(stmt, "#{model} Delete All").tap { reset }
|
|
1029
1046
|
end
|
|
1030
1047
|
end
|
|
1031
1048
|
|
|
@@ -1124,9 +1141,6 @@ module ActiveRecord
|
|
|
1124
1141
|
# for queries to actually be executed concurrently. Otherwise it defaults to
|
|
1125
1142
|
# executing them in the foreground.
|
|
1126
1143
|
#
|
|
1127
|
-
# +load_async+ will also fall back to executing in the foreground in the test environment when transactional
|
|
1128
|
-
# fixtures are enabled.
|
|
1129
|
-
#
|
|
1130
1144
|
# If the query was actually executed in the background, the Active Record logs will show
|
|
1131
1145
|
# it by prefixing the log line with <tt>ASYNC</tt>:
|
|
1132
1146
|
#
|
|
@@ -1136,7 +1150,7 @@ module ActiveRecord
|
|
|
1136
1150
|
return load if !c.async_enabled?
|
|
1137
1151
|
|
|
1138
1152
|
unless loaded?
|
|
1139
|
-
result = exec_main_query(async: c.current_transaction.
|
|
1153
|
+
result = exec_main_query(async: !c.current_transaction.joinable?)
|
|
1140
1154
|
|
|
1141
1155
|
if result.is_a?(Array)
|
|
1142
1156
|
@records = result
|
|
@@ -1150,6 +1164,16 @@ module ActiveRecord
|
|
|
1150
1164
|
self
|
|
1151
1165
|
end
|
|
1152
1166
|
|
|
1167
|
+
def then(&block) # :nodoc:
|
|
1168
|
+
if @future_result
|
|
1169
|
+
@future_result.then do
|
|
1170
|
+
yield self
|
|
1171
|
+
end
|
|
1172
|
+
else
|
|
1173
|
+
super
|
|
1174
|
+
end
|
|
1175
|
+
end
|
|
1176
|
+
|
|
1153
1177
|
# Returns <tt>true</tt> if the relation was scheduled on the background
|
|
1154
1178
|
# thread pool.
|
|
1155
1179
|
def scheduled?
|
|
@@ -1180,7 +1204,7 @@ module ActiveRecord
|
|
|
1180
1204
|
def reset
|
|
1181
1205
|
@future_result&.cancel
|
|
1182
1206
|
@future_result = nil
|
|
1183
|
-
@
|
|
1207
|
+
@delegate_to_model = false
|
|
1184
1208
|
@to_sql = @arel = @loaded = @should_eager_load = nil
|
|
1185
1209
|
@offsets = @take = nil
|
|
1186
1210
|
@cache_keys = nil
|
|
@@ -1200,7 +1224,7 @@ module ActiveRecord
|
|
|
1200
1224
|
relation.to_sql
|
|
1201
1225
|
end
|
|
1202
1226
|
else
|
|
1203
|
-
|
|
1227
|
+
model.with_connection do |conn|
|
|
1204
1228
|
conn.unprepared_statement { conn.to_sql(arel) }
|
|
1205
1229
|
end
|
|
1206
1230
|
end
|
|
@@ -1210,12 +1234,12 @@ module ActiveRecord
|
|
|
1210
1234
|
#
|
|
1211
1235
|
# User.where(name: 'Oscar').where_values_hash
|
|
1212
1236
|
# # => {name: "Oscar"}
|
|
1213
|
-
def where_values_hash(relation_table_name =
|
|
1237
|
+
def where_values_hash(relation_table_name = model.table_name) # :nodoc:
|
|
1214
1238
|
where_clause.to_h(relation_table_name)
|
|
1215
1239
|
end
|
|
1216
1240
|
|
|
1217
1241
|
def scope_for_create
|
|
1218
|
-
hash = where_clause.to_h(
|
|
1242
|
+
hash = where_clause.to_h(model.table_name, equality_only: true)
|
|
1219
1243
|
create_with_value.each { |k, v| hash[k.to_s] = v } unless create_with_value.empty?
|
|
1220
1244
|
hash
|
|
1221
1245
|
end
|
|
@@ -1261,6 +1285,10 @@ module ActiveRecord
|
|
|
1261
1285
|
records.blank?
|
|
1262
1286
|
end
|
|
1263
1287
|
|
|
1288
|
+
def readonly?
|
|
1289
|
+
readonly_value
|
|
1290
|
+
end
|
|
1291
|
+
|
|
1264
1292
|
def values
|
|
1265
1293
|
@values.dup
|
|
1266
1294
|
end
|
|
@@ -1279,7 +1307,7 @@ module ActiveRecord
|
|
|
1279
1307
|
end
|
|
1280
1308
|
|
|
1281
1309
|
def empty_scope? # :nodoc:
|
|
1282
|
-
@values ==
|
|
1310
|
+
@values == model.unscoped.values
|
|
1283
1311
|
end
|
|
1284
1312
|
|
|
1285
1313
|
def has_limit_or_offset? # :nodoc:
|
|
@@ -1287,7 +1315,7 @@ module ActiveRecord
|
|
|
1287
1315
|
end
|
|
1288
1316
|
|
|
1289
1317
|
def alias_tracker(joins = [], aliases = nil) # :nodoc:
|
|
1290
|
-
ActiveRecord::Associations::AliasTracker.create(connection_pool, table.name, joins, aliases)
|
|
1318
|
+
ActiveRecord::Associations::AliasTracker.create(model.connection_pool, table.name, joins, aliases)
|
|
1291
1319
|
end
|
|
1292
1320
|
|
|
1293
1321
|
class StrictLoadingScope # :nodoc:
|
|
@@ -1317,46 +1345,46 @@ module ActiveRecord
|
|
|
1317
1345
|
|
|
1318
1346
|
private
|
|
1319
1347
|
def already_in_scope?(registry)
|
|
1320
|
-
@
|
|
1348
|
+
@delegate_to_model && registry.current_scope(model, true)
|
|
1321
1349
|
end
|
|
1322
1350
|
|
|
1323
1351
|
def global_scope?(registry)
|
|
1324
|
-
registry.global_current_scope(
|
|
1352
|
+
registry.global_current_scope(model, true)
|
|
1325
1353
|
end
|
|
1326
1354
|
|
|
1327
1355
|
def current_scope_restoring_block(&block)
|
|
1328
|
-
current_scope =
|
|
1356
|
+
current_scope = model.current_scope(true)
|
|
1329
1357
|
-> record do
|
|
1330
|
-
|
|
1358
|
+
model.current_scope = current_scope
|
|
1331
1359
|
yield record if block_given?
|
|
1332
1360
|
end
|
|
1333
1361
|
end
|
|
1334
1362
|
|
|
1335
1363
|
def _new(attributes, &block)
|
|
1336
|
-
|
|
1364
|
+
model.new(attributes, &block)
|
|
1337
1365
|
end
|
|
1338
1366
|
|
|
1339
1367
|
def _create(attributes, &block)
|
|
1340
|
-
|
|
1368
|
+
model.create(attributes, &block)
|
|
1341
1369
|
end
|
|
1342
1370
|
|
|
1343
1371
|
def _create!(attributes, &block)
|
|
1344
|
-
|
|
1372
|
+
model.create!(attributes, &block)
|
|
1345
1373
|
end
|
|
1346
1374
|
|
|
1347
1375
|
def _scoping(scope, registry, all_queries = false)
|
|
1348
|
-
previous = registry.current_scope(
|
|
1349
|
-
registry.set_current_scope(
|
|
1376
|
+
previous = registry.current_scope(model, true)
|
|
1377
|
+
registry.set_current_scope(model, scope)
|
|
1350
1378
|
|
|
1351
1379
|
if all_queries
|
|
1352
|
-
previous_global = registry.global_current_scope(
|
|
1353
|
-
registry.set_global_current_scope(
|
|
1380
|
+
previous_global = registry.global_current_scope(model, true)
|
|
1381
|
+
registry.set_global_current_scope(model, scope)
|
|
1354
1382
|
end
|
|
1355
1383
|
yield
|
|
1356
1384
|
ensure
|
|
1357
|
-
registry.set_current_scope(
|
|
1385
|
+
registry.set_current_scope(model, previous)
|
|
1358
1386
|
if all_queries
|
|
1359
|
-
registry.set_global_current_scope(
|
|
1387
|
+
registry.set_global_current_scope(model, previous_global)
|
|
1360
1388
|
end
|
|
1361
1389
|
end
|
|
1362
1390
|
|
|
@@ -1368,7 +1396,7 @@ module ActiveRecord
|
|
|
1368
1396
|
value = Arel::Nodes::Grouping.new(value)
|
|
1369
1397
|
end
|
|
1370
1398
|
else
|
|
1371
|
-
type =
|
|
1399
|
+
type = model.type_for_attribute(attr.name)
|
|
1372
1400
|
value = predicate_builder.build_bind_attribute(attr.name, type.cast(value))
|
|
1373
1401
|
end
|
|
1374
1402
|
[attr, value]
|
|
@@ -1415,7 +1443,7 @@ module ActiveRecord
|
|
|
1415
1443
|
if where_clause.contradiction?
|
|
1416
1444
|
[].freeze
|
|
1417
1445
|
elsif eager_loading?
|
|
1418
|
-
|
|
1446
|
+
model.with_connection do |c|
|
|
1419
1447
|
apply_join_dependency do |relation, join_dependency|
|
|
1420
1448
|
if relation.null_relation?
|
|
1421
1449
|
[].freeze
|
|
@@ -1427,8 +1455,8 @@ module ActiveRecord
|
|
|
1427
1455
|
end
|
|
1428
1456
|
end
|
|
1429
1457
|
else
|
|
1430
|
-
|
|
1431
|
-
|
|
1458
|
+
model.with_connection do |c|
|
|
1459
|
+
model._query_by_sql(c, arel, async: async)
|
|
1432
1460
|
end
|
|
1433
1461
|
end
|
|
1434
1462
|
end
|
|
@@ -1441,13 +1469,13 @@ module ActiveRecord
|
|
|
1441
1469
|
@_join_dependency = nil
|
|
1442
1470
|
records
|
|
1443
1471
|
else
|
|
1444
|
-
|
|
1472
|
+
model._load_from_sql(rows, &block).freeze
|
|
1445
1473
|
end
|
|
1446
1474
|
end
|
|
1447
1475
|
|
|
1448
1476
|
def skip_query_cache_if_necessary(&block)
|
|
1449
1477
|
if skip_query_cache_value
|
|
1450
|
-
uncached(&block)
|
|
1478
|
+
model.uncached(&block)
|
|
1451
1479
|
else
|
|
1452
1480
|
yield
|
|
1453
1481
|
end
|
data/lib/active_record/result.rb
CHANGED
|
@@ -36,6 +36,59 @@ module ActiveRecord
|
|
|
36
36
|
class Result
|
|
37
37
|
include Enumerable
|
|
38
38
|
|
|
39
|
+
class IndexedRow
|
|
40
|
+
def initialize(column_indexes, row)
|
|
41
|
+
@column_indexes = column_indexes
|
|
42
|
+
@row = row
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def size
|
|
46
|
+
@column_indexes.size
|
|
47
|
+
end
|
|
48
|
+
alias_method :length, :size
|
|
49
|
+
|
|
50
|
+
def each_key(&block)
|
|
51
|
+
@column_indexes.each_key(&block)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def keys
|
|
55
|
+
@column_indexes.keys
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def ==(other)
|
|
59
|
+
if other.is_a?(Hash)
|
|
60
|
+
to_hash == other
|
|
61
|
+
else
|
|
62
|
+
super
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def key?(column)
|
|
67
|
+
@column_indexes.key?(column)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def fetch(column)
|
|
71
|
+
if index = @column_indexes[column]
|
|
72
|
+
@row[index]
|
|
73
|
+
elsif block_given?
|
|
74
|
+
yield
|
|
75
|
+
else
|
|
76
|
+
raise KeyError, "key not found: #{column.inspect}"
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def [](column)
|
|
81
|
+
if index = @column_indexes[column]
|
|
82
|
+
@row[index]
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def to_h
|
|
87
|
+
@column_indexes.transform_values { |index| @row[index] }
|
|
88
|
+
end
|
|
89
|
+
alias_method :to_hash, :to_h
|
|
90
|
+
end
|
|
91
|
+
|
|
39
92
|
attr_reader :columns, :rows, :column_types
|
|
40
93
|
|
|
41
94
|
def self.empty(async: false) # :nodoc:
|
|
@@ -67,7 +120,9 @@ module ActiveRecord
|
|
|
67
120
|
end
|
|
68
121
|
|
|
69
122
|
# Calls the given block once for each element in row collection, passing
|
|
70
|
-
# row as parameter.
|
|
123
|
+
# row as parameter. Each row is a Hash-like, read only object.
|
|
124
|
+
#
|
|
125
|
+
# To get real hashes, use +.to_a.each+.
|
|
71
126
|
#
|
|
72
127
|
# Returns an +Enumerator+ if no block is given.
|
|
73
128
|
def each(&block)
|
|
@@ -134,14 +189,14 @@ module ActiveRecord
|
|
|
134
189
|
end
|
|
135
190
|
|
|
136
191
|
def initialize_copy(other)
|
|
137
|
-
@
|
|
138
|
-
@rows = rows.dup
|
|
192
|
+
@rows = rows.dup
|
|
139
193
|
@column_types = column_types.dup
|
|
140
194
|
@hash_rows = nil
|
|
141
195
|
end
|
|
142
196
|
|
|
143
197
|
def freeze # :nodoc:
|
|
144
198
|
hash_rows.freeze
|
|
199
|
+
indexed_rows.freeze
|
|
145
200
|
super
|
|
146
201
|
end
|
|
147
202
|
|
|
@@ -154,7 +209,14 @@ module ActiveRecord
|
|
|
154
209
|
hash[columns[index]] = index
|
|
155
210
|
index += 1
|
|
156
211
|
end
|
|
157
|
-
hash
|
|
212
|
+
hash.freeze
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def indexed_rows # :nodoc:
|
|
217
|
+
@indexed_rows ||= begin
|
|
218
|
+
columns = column_indexes
|
|
219
|
+
@rows.map { |row| IndexedRow.new(columns, row) }.freeze
|
|
158
220
|
end
|
|
159
221
|
end
|
|
160
222
|
|
|
@@ -105,12 +105,13 @@ module ActiveRecord
|
|
|
105
105
|
# sanitize_sql_hash_for_assignment({ status: nil, group_id: 1 }, "posts")
|
|
106
106
|
# # => "`posts`.`status` = NULL, `posts`.`group_id` = 1"
|
|
107
107
|
def sanitize_sql_hash_for_assignment(attrs, table)
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
108
|
+
with_connection do |c|
|
|
109
|
+
attrs.map do |attr, value|
|
|
110
|
+
type = type_for_attribute(attr)
|
|
111
|
+
value = type.serialize(type.cast(value))
|
|
112
|
+
"#{c.quote_table_name_for_assignment(table, attr)} = #{c.quote(value)}"
|
|
113
|
+
end.join(", ")
|
|
114
|
+
end
|
|
114
115
|
end
|
|
115
116
|
|
|
116
117
|
# Sanitizes a +string+ so that it is safe to use within an SQL
|
|
@@ -63,6 +63,7 @@ module ActiveRecord
|
|
|
63
63
|
extensions(stream)
|
|
64
64
|
types(stream)
|
|
65
65
|
tables(stream)
|
|
66
|
+
virtual_tables(stream)
|
|
66
67
|
trailer(stream)
|
|
67
68
|
stream
|
|
68
69
|
end
|
|
@@ -126,6 +127,10 @@ module ActiveRecord
|
|
|
126
127
|
def schemas(stream)
|
|
127
128
|
end
|
|
128
129
|
|
|
130
|
+
# virtual tables are only supported by SQLite
|
|
131
|
+
def virtual_tables(stream)
|
|
132
|
+
end
|
|
133
|
+
|
|
129
134
|
def tables(stream)
|
|
130
135
|
sorted_tables = @connection.tables.sort
|
|
131
136
|
|
|
@@ -202,12 +207,17 @@ module ActiveRecord
|
|
|
202
207
|
end
|
|
203
208
|
|
|
204
209
|
indexes_in_create(table, tbl)
|
|
205
|
-
check_constraints_in_create(table, tbl) if @connection.supports_check_constraints?
|
|
210
|
+
remaining = check_constraints_in_create(table, tbl) if @connection.supports_check_constraints?
|
|
206
211
|
exclusion_constraints_in_create(table, tbl) if @connection.supports_exclusion_constraints?
|
|
207
212
|
unique_constraints_in_create(table, tbl) if @connection.supports_unique_constraints?
|
|
208
213
|
|
|
209
214
|
tbl.puts " end"
|
|
210
215
|
|
|
216
|
+
if remaining
|
|
217
|
+
tbl.puts
|
|
218
|
+
tbl.print remaining.string
|
|
219
|
+
end
|
|
220
|
+
|
|
211
221
|
stream.print tbl.string
|
|
212
222
|
rescue => e
|
|
213
223
|
stream.puts "# Could not dump table #{table.inspect} because of following #{e.class}"
|
|
@@ -272,24 +282,37 @@ module ActiveRecord
|
|
|
272
282
|
|
|
273
283
|
def check_constraints_in_create(table, stream)
|
|
274
284
|
if (check_constraints = @connection.check_constraints(table)).any?
|
|
275
|
-
|
|
276
|
-
parts = [
|
|
277
|
-
"t.check_constraint #{check_constraint.expression.inspect}"
|
|
278
|
-
]
|
|
285
|
+
check_valid, check_invalid = check_constraints.partition { |chk| chk.validate? }
|
|
279
286
|
|
|
280
|
-
|
|
281
|
-
|
|
287
|
+
unless check_valid.empty?
|
|
288
|
+
check_constraint_statements = check_valid.map do |check|
|
|
289
|
+
" t.check_constraint #{check_parts(check).join(', ')}"
|
|
282
290
|
end
|
|
283
291
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
" #{parts.join(', ')}"
|
|
292
|
+
stream.puts check_constraint_statements.sort.join("\n")
|
|
287
293
|
end
|
|
288
294
|
|
|
289
|
-
|
|
295
|
+
unless check_invalid.empty?
|
|
296
|
+
remaining = StringIO.new
|
|
297
|
+
table_name = remove_prefix_and_suffix(table).inspect
|
|
298
|
+
|
|
299
|
+
add_check_constraint_statements = check_invalid.map do |check|
|
|
300
|
+
" add_check_constraint #{([table_name] + check_parts(check)).join(', ')}"
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
remaining.puts add_check_constraint_statements.sort.join("\n")
|
|
304
|
+
remaining
|
|
305
|
+
end
|
|
290
306
|
end
|
|
291
307
|
end
|
|
292
308
|
|
|
309
|
+
def check_parts(check)
|
|
310
|
+
check_parts = [ check.expression.inspect ]
|
|
311
|
+
check_parts << "name: #{check.name.inspect}" if check.export_name_on_schema_dump?
|
|
312
|
+
check_parts << "validate: #{check.validate?.inspect}" unless check.validate?
|
|
313
|
+
check_parts
|
|
314
|
+
end
|
|
315
|
+
|
|
293
316
|
def foreign_keys(table, stream)
|
|
294
317
|
if (foreign_keys = @connection.foreign_keys(table)).any?
|
|
295
318
|
add_foreign_key_statements = foreign_keys.map do |foreign_key|
|