activerecord 7.2.3 → 8.0.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.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +192 -1261
- data/README.rdoc +2 -2
- data/lib/active_record/associations/alias_tracker.rb +4 -6
- data/lib/active_record/associations/association.rb +25 -5
- data/lib/active_record/associations/belongs_to_association.rb +2 -18
- data/lib/active_record/associations/builder/association.rb +7 -6
- data/lib/active_record/associations/collection_association.rb +4 -4
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +4 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
- data/lib/active_record/associations/preloader/association.rb +2 -2
- data/lib/active_record/associations/singular_association.rb +8 -3
- data/lib/active_record/associations.rb +50 -32
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- data/lib/active_record/attribute_methods/serialization.rb +1 -1
- data/lib/active_record/attribute_methods.rb +19 -24
- data/lib/active_record/attributes.rb +26 -37
- data/lib/active_record/autosave_association.rb +81 -49
- data/lib/active_record/base.rb +2 -2
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +16 -10
- 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 +31 -75
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +90 -43
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +14 -19
- data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +2 -6
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +27 -9
- data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -5
- data/lib/active_record/connection_adapters/abstract_adapter.rb +27 -57
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +28 -58
- data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -15
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +43 -45
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +42 -98
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +2 -16
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +64 -42
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +12 -14
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +51 -9
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +44 -101
- data/lib/active_record/connection_adapters/schema_cache.rb +1 -3
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +76 -100
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +0 -13
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +8 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +60 -22
- 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_handling.rb +29 -11
- data/lib/active_record/core.rb +15 -60
- data/lib/active_record/counter_cache.rb +1 -1
- data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -3
- 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 +35 -29
- 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 +12 -13
- data/lib/active_record/errors.rb +16 -8
- data/lib/active_record/fixture_set/table_row.rb +2 -19
- data/lib/active_record/fixtures.rb +0 -1
- data/lib/active_record/future_result.rb +14 -10
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/insert_all.rb +1 -1
- data/lib/active_record/marshalling.rb +1 -4
- data/lib/active_record/migration/command_recorder.rb +22 -5
- data/lib/active_record/migration/compatibility.rb +5 -2
- data/lib/active_record/migration.rb +36 -35
- data/lib/active_record/model_schema.rb +1 -1
- data/lib/active_record/nested_attributes.rb +4 -6
- data/lib/active_record/persistence.rb +128 -130
- data/lib/active_record/query_cache.rb +5 -4
- data/lib/active_record/query_logs.rb +98 -44
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +10 -10
- data/lib/active_record/railtie.rb +5 -6
- data/lib/active_record/railties/databases.rake +1 -2
- data/lib/active_record/reflection.rb +9 -7
- 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 +55 -55
- data/lib/active_record/relation/delegation.rb +25 -14
- data/lib/active_record/relation/finder_methods.rb +31 -32
- data/lib/active_record/relation/merger.rb +8 -8
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +0 -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 +5 -0
- data/lib/active_record/relation/query_attribute.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +90 -91
- data/lib/active_record/relation/record_fetch_warning.rb +2 -2
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/relation/where_clause.rb +2 -8
- data/lib/active_record/relation.rb +77 -76
- data/lib/active_record/result.rb +68 -7
- data/lib/active_record/sanitization.rb +7 -6
- data/lib/active_record/schema_dumper.rb +16 -29
- 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 +6 -7
- data/lib/active_record/statement_cache.rb +12 -12
- data/lib/active_record/store.rb +7 -3
- data/lib/active_record/tasks/database_tasks.rb +24 -15
- data/lib/active_record/tasks/mysql_database_tasks.rb +0 -2
- data/lib/active_record/tasks/postgresql_database_tasks.rb +0 -7
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -2
- data/lib/active_record/test_fixtures.rb +12 -0
- data/lib/active_record/testing/query_assertions.rb +2 -2
- data/lib/active_record/token_for.rb +1 -1
- data/lib/active_record/transactions.rb +1 -3
- data/lib/active_record/validations/uniqueness.rb +8 -8
- data/lib/active_record.rb +16 -1
- data/lib/arel/collectors/bind.rb +1 -1
- data/lib/arel/crud.rb +0 -2
- data/lib/arel/delete_manager.rb +0 -5
- data/lib/arel/nodes/delete_statement.rb +2 -4
- data/lib/arel/nodes/update_statement.rb +2 -4
- data/lib/arel/select_manager.rb +2 -6
- data/lib/arel/update_manager.rb +0 -5
- data/lib/arel/visitors/dot.rb +0 -2
- data/lib/arel/visitors/sqlite.rb +0 -25
- data/lib/arel/visitors/to_sql.rb +1 -3
- metadata +14 -11
|
@@ -68,19 +68,19 @@ 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: model.arel_table, predicate_builder: model.predicate_builder, values: {})
|
|
78
|
+
@model = model
|
|
79
79
|
@table = table
|
|
80
80
|
@values = values
|
|
81
81
|
@loaded = false
|
|
82
82
|
@predicate_builder = predicate_builder
|
|
83
|
-
@
|
|
83
|
+
@delegate_to_model = false
|
|
84
84
|
@future_result = nil
|
|
85
85
|
@records = nil
|
|
86
86
|
@async = false
|
|
@@ -93,7 +93,7 @@ module ActiveRecord
|
|
|
93
93
|
end
|
|
94
94
|
|
|
95
95
|
def bind_attribute(name, value) # :nodoc:
|
|
96
|
-
if reflection =
|
|
96
|
+
if reflection = model._reflect_on_association(name)
|
|
97
97
|
name = reflection.foreign_key
|
|
98
98
|
value = value.read_attribute(reflection.association_primary_key) unless value.nil?
|
|
99
99
|
end
|
|
@@ -265,12 +265,7 @@ module ActiveRecord
|
|
|
265
265
|
# such situation.
|
|
266
266
|
def create_or_find_by(attributes, &block)
|
|
267
267
|
with_connection do |connection|
|
|
268
|
-
|
|
269
|
-
transaction(requires_new: true) do
|
|
270
|
-
record = create(attributes, &block)
|
|
271
|
-
record._last_transaction_return_status || raise(ActiveRecord::Rollback)
|
|
272
|
-
end
|
|
273
|
-
record
|
|
268
|
+
transaction(requires_new: true) { create(attributes, &block) }
|
|
274
269
|
rescue ActiveRecord::RecordNotUnique
|
|
275
270
|
if connection.transaction_open?
|
|
276
271
|
where(attributes).lock.find_by!(attributes)
|
|
@@ -285,12 +280,7 @@ module ActiveRecord
|
|
|
285
280
|
# is raised if the created record is invalid.
|
|
286
281
|
def create_or_find_by!(attributes, &block)
|
|
287
282
|
with_connection do |connection|
|
|
288
|
-
|
|
289
|
-
transaction(requires_new: true) do
|
|
290
|
-
record = create!(attributes, &block)
|
|
291
|
-
record._last_transaction_return_status || raise(ActiveRecord::Rollback)
|
|
292
|
-
end
|
|
293
|
-
record
|
|
283
|
+
transaction(requires_new: true) { create!(attributes, &block) }
|
|
294
284
|
rescue ActiveRecord::RecordNotUnique
|
|
295
285
|
if connection.transaction_open?
|
|
296
286
|
where(attributes).lock.find_by!(attributes)
|
|
@@ -300,7 +290,7 @@ module ActiveRecord
|
|
|
300
290
|
end
|
|
301
291
|
end
|
|
302
292
|
|
|
303
|
-
# Like #find_or_create_by, but calls {new}[rdoc-ref:Core
|
|
293
|
+
# Like #find_or_create_by, but calls {new}[rdoc-ref:Core#new]
|
|
304
294
|
# instead of {create}[rdoc-ref:Persistence::ClassMethods#create].
|
|
305
295
|
def find_or_initialize_by(attributes, &block)
|
|
306
296
|
find_by(attributes) || new(attributes, &block)
|
|
@@ -440,14 +430,14 @@ module ActiveRecord
|
|
|
440
430
|
# Product.where("name like ?", "%Game%").cache_key(:last_reviewed_at)
|
|
441
431
|
def cache_key(timestamp_column = "updated_at")
|
|
442
432
|
@cache_keys ||= {}
|
|
443
|
-
@cache_keys[timestamp_column] ||=
|
|
433
|
+
@cache_keys[timestamp_column] ||= model.collection_cache_key(self, timestamp_column)
|
|
444
434
|
end
|
|
445
435
|
|
|
446
436
|
def compute_cache_key(timestamp_column = :updated_at) # :nodoc:
|
|
447
437
|
query_signature = ActiveSupport::Digest.hexdigest(to_sql)
|
|
448
|
-
key = "#{
|
|
438
|
+
key = "#{model.model_name.cache_key}/query-#{query_signature}"
|
|
449
439
|
|
|
450
|
-
if collection_cache_versioning
|
|
440
|
+
if model.collection_cache_versioning
|
|
451
441
|
key
|
|
452
442
|
else
|
|
453
443
|
"#{key}-#{compute_cache_version(timestamp_column)}"
|
|
@@ -466,7 +456,7 @@ module ActiveRecord
|
|
|
466
456
|
#
|
|
467
457
|
# SELECT COUNT(*), MAX("products"."updated_at") FROM "products" WHERE (name like '%Cosmic Encounter%')
|
|
468
458
|
def cache_version(timestamp_column = :updated_at)
|
|
469
|
-
if collection_cache_versioning
|
|
459
|
+
if model.collection_cache_versioning
|
|
470
460
|
@cache_versions ||= {}
|
|
471
461
|
@cache_versions[timestamp_column] ||= compute_cache_version(timestamp_column)
|
|
472
462
|
end
|
|
@@ -485,7 +475,7 @@ module ActiveRecord
|
|
|
485
475
|
|
|
486
476
|
with_connection do |c|
|
|
487
477
|
column = c.visitor.compile(table[timestamp_column])
|
|
488
|
-
select_values = "COUNT(*) AS #{adapter_class.quote_column_name("size")}, MAX(%s) AS timestamp"
|
|
478
|
+
select_values = "COUNT(*) AS #{model.adapter_class.quote_column_name("size")}, MAX(%s) AS timestamp"
|
|
489
479
|
|
|
490
480
|
if collection.has_limit_or_offset?
|
|
491
481
|
query = collection.select("#{column} AS collection_cache_key_timestamp")
|
|
@@ -502,7 +492,7 @@ module ActiveRecord
|
|
|
502
492
|
size, timestamp = c.select_rows(arel, nil).first
|
|
503
493
|
|
|
504
494
|
if size
|
|
505
|
-
column_type =
|
|
495
|
+
column_type = model.type_for_attribute(timestamp_column)
|
|
506
496
|
timestamp = column_type.deserialize(timestamp)
|
|
507
497
|
else
|
|
508
498
|
size = 0
|
|
@@ -511,7 +501,7 @@ module ActiveRecord
|
|
|
511
501
|
end
|
|
512
502
|
|
|
513
503
|
if timestamp
|
|
514
|
-
"#{size}-#{timestamp.utc.to_fs(cache_timestamp_format)}"
|
|
504
|
+
"#{size}-#{timestamp.utc.to_fs(model.cache_timestamp_format)}"
|
|
515
505
|
else
|
|
516
506
|
"#{size}"
|
|
517
507
|
end
|
|
@@ -542,7 +532,7 @@ module ActiveRecord
|
|
|
542
532
|
# Please check unscoped if you want to remove all previous scopes (including
|
|
543
533
|
# the default_scope) during the execution of a block.
|
|
544
534
|
def scoping(all_queries: nil, &block)
|
|
545
|
-
registry =
|
|
535
|
+
registry = model.scope_registry
|
|
546
536
|
if global_scope?(registry) && all_queries == false
|
|
547
537
|
raise ArgumentError, "Scoping is set to apply to all queries and cannot be unset in a nested block."
|
|
548
538
|
elsif already_in_scope?(registry)
|
|
@@ -553,11 +543,11 @@ module ActiveRecord
|
|
|
553
543
|
end
|
|
554
544
|
|
|
555
545
|
def _exec_scope(...) # :nodoc:
|
|
556
|
-
@
|
|
557
|
-
registry =
|
|
546
|
+
@delegate_to_model = true
|
|
547
|
+
registry = model.scope_registry
|
|
558
548
|
_scoping(nil, registry) { instance_exec(...) || self }
|
|
559
549
|
ensure
|
|
560
|
-
@
|
|
550
|
+
@delegate_to_model = false
|
|
561
551
|
end
|
|
562
552
|
|
|
563
553
|
# Updates all records in the current relation with details given. This method constructs a single SQL UPDATE
|
|
@@ -594,30 +584,30 @@ module ActiveRecord
|
|
|
594
584
|
return 0 if @none
|
|
595
585
|
|
|
596
586
|
if updates.is_a?(Hash)
|
|
597
|
-
if
|
|
598
|
-
!updates.key?(
|
|
599
|
-
!updates.key?(
|
|
600
|
-
attr = table[
|
|
587
|
+
if model.locking_enabled? &&
|
|
588
|
+
!updates.key?(model.locking_column) &&
|
|
589
|
+
!updates.key?(model.locking_column.to_sym)
|
|
590
|
+
attr = table[model.locking_column]
|
|
601
591
|
updates[attr.name] = _increment_attribute(attr)
|
|
602
592
|
end
|
|
603
593
|
values = _substitute_values(updates)
|
|
604
594
|
else
|
|
605
|
-
values = Arel.sql(
|
|
595
|
+
values = Arel.sql(model.sanitize_sql_for_assignment(updates, table.name))
|
|
606
596
|
end
|
|
607
597
|
|
|
608
|
-
|
|
598
|
+
model.with_connection do |c|
|
|
609
599
|
arel = eager_loading? ? apply_join_dependency.arel : build_arel(c)
|
|
610
600
|
arel.source.left = table
|
|
611
601
|
|
|
612
602
|
group_values_arel_columns = arel_columns(group_values.uniq)
|
|
613
603
|
having_clause_ast = having_clause.ast unless having_clause.empty?
|
|
614
|
-
key = if
|
|
604
|
+
key = if model.composite_primary_key?
|
|
615
605
|
primary_key.map { |pk| table[pk] }
|
|
616
606
|
else
|
|
617
607
|
table[primary_key]
|
|
618
608
|
end
|
|
619
609
|
stmt = arel.compile_update(values, key, having_clause_ast, group_values_arel_columns)
|
|
620
|
-
c.update(stmt, "#{
|
|
610
|
+
c.update(stmt, "#{model} Update All").tap { reset }
|
|
621
611
|
end
|
|
622
612
|
end
|
|
623
613
|
|
|
@@ -625,7 +615,7 @@ module ActiveRecord
|
|
|
625
615
|
if id == :all
|
|
626
616
|
each { |record| record.update(attributes) }
|
|
627
617
|
else
|
|
628
|
-
|
|
618
|
+
model.update(id, attributes)
|
|
629
619
|
end
|
|
630
620
|
end
|
|
631
621
|
|
|
@@ -633,7 +623,7 @@ module ActiveRecord
|
|
|
633
623
|
if id == :all
|
|
634
624
|
each { |record| record.update!(attributes) }
|
|
635
625
|
else
|
|
636
|
-
|
|
626
|
+
model.update!(id, attributes)
|
|
637
627
|
end
|
|
638
628
|
end
|
|
639
629
|
|
|
@@ -823,7 +813,7 @@ module ActiveRecord
|
|
|
823
813
|
#
|
|
824
814
|
# [:returning]
|
|
825
815
|
# (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
|
|
826
|
-
#
|
|
816
|
+
# inserted records, which by default is the primary key.
|
|
827
817
|
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
|
828
818
|
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
|
829
819
|
# clause entirely.
|
|
@@ -939,7 +929,7 @@ module ActiveRecord
|
|
|
939
929
|
names = touch if touch != true
|
|
940
930
|
names = Array.wrap(names)
|
|
941
931
|
options = names.extract_options!
|
|
942
|
-
touch_updates =
|
|
932
|
+
touch_updates = model.touch_attributes_with_time(*names, **options)
|
|
943
933
|
updates.merge!(touch_updates) unless touch_updates.empty?
|
|
944
934
|
end
|
|
945
935
|
|
|
@@ -952,7 +942,7 @@ module ActiveRecord
|
|
|
952
942
|
# If attribute names are passed, they are updated along with +updated_at+/+updated_on+ attributes.
|
|
953
943
|
# If no time argument is passed, the current time is used as default.
|
|
954
944
|
#
|
|
955
|
-
#
|
|
945
|
+
# === Examples
|
|
956
946
|
#
|
|
957
947
|
# # Touch all records
|
|
958
948
|
# Person.all.touch_all
|
|
@@ -970,7 +960,7 @@ module ActiveRecord
|
|
|
970
960
|
# Person.where(name: 'David').touch_all
|
|
971
961
|
# # => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670' WHERE \"people\".\"name\" = 'David'"
|
|
972
962
|
def touch_all(*names, time: nil)
|
|
973
|
-
update_all
|
|
963
|
+
update_all model.touch_attributes_with_time(*names, time: time)
|
|
974
964
|
end
|
|
975
965
|
|
|
976
966
|
# Destroys the records by instantiating each
|
|
@@ -1022,20 +1012,20 @@ module ActiveRecord
|
|
|
1022
1012
|
raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
|
|
1023
1013
|
end
|
|
1024
1014
|
|
|
1025
|
-
|
|
1015
|
+
model.with_connection do |c|
|
|
1026
1016
|
arel = eager_loading? ? apply_join_dependency.arel : build_arel(c)
|
|
1027
1017
|
arel.source.left = table
|
|
1028
1018
|
|
|
1029
1019
|
group_values_arel_columns = arel_columns(group_values.uniq)
|
|
1030
1020
|
having_clause_ast = having_clause.ast unless having_clause.empty?
|
|
1031
|
-
key = if
|
|
1021
|
+
key = if model.composite_primary_key?
|
|
1032
1022
|
primary_key.map { |pk| table[pk] }
|
|
1033
1023
|
else
|
|
1034
1024
|
table[primary_key]
|
|
1035
1025
|
end
|
|
1036
1026
|
stmt = arel.compile_delete(key, having_clause_ast, group_values_arel_columns)
|
|
1037
1027
|
|
|
1038
|
-
c.delete(stmt, "#{
|
|
1028
|
+
c.delete(stmt, "#{model} Delete All").tap { reset }
|
|
1039
1029
|
end
|
|
1040
1030
|
end
|
|
1041
1031
|
|
|
@@ -1134,9 +1124,6 @@ module ActiveRecord
|
|
|
1134
1124
|
# for queries to actually be executed concurrently. Otherwise it defaults to
|
|
1135
1125
|
# executing them in the foreground.
|
|
1136
1126
|
#
|
|
1137
|
-
# +load_async+ will also fall back to executing in the foreground in the test environment when transactional
|
|
1138
|
-
# fixtures are enabled.
|
|
1139
|
-
#
|
|
1140
1127
|
# If the query was actually executed in the background, the Active Record logs will show
|
|
1141
1128
|
# it by prefixing the log line with <tt>ASYNC</tt>:
|
|
1142
1129
|
#
|
|
@@ -1146,7 +1133,7 @@ module ActiveRecord
|
|
|
1146
1133
|
return load if !c.async_enabled?
|
|
1147
1134
|
|
|
1148
1135
|
unless loaded?
|
|
1149
|
-
result = exec_main_query(async: c.current_transaction.
|
|
1136
|
+
result = exec_main_query(async: !c.current_transaction.joinable?)
|
|
1150
1137
|
|
|
1151
1138
|
if result.is_a?(Array)
|
|
1152
1139
|
@records = result
|
|
@@ -1160,6 +1147,16 @@ module ActiveRecord
|
|
|
1160
1147
|
self
|
|
1161
1148
|
end
|
|
1162
1149
|
|
|
1150
|
+
def then(&block) # :nodoc:
|
|
1151
|
+
if @future_result
|
|
1152
|
+
@future_result.then do
|
|
1153
|
+
yield self
|
|
1154
|
+
end
|
|
1155
|
+
else
|
|
1156
|
+
super
|
|
1157
|
+
end
|
|
1158
|
+
end
|
|
1159
|
+
|
|
1163
1160
|
# Returns <tt>true</tt> if the relation was scheduled on the background
|
|
1164
1161
|
# thread pool.
|
|
1165
1162
|
def scheduled?
|
|
@@ -1190,7 +1187,7 @@ module ActiveRecord
|
|
|
1190
1187
|
def reset
|
|
1191
1188
|
@future_result&.cancel
|
|
1192
1189
|
@future_result = nil
|
|
1193
|
-
@
|
|
1190
|
+
@delegate_to_model = false
|
|
1194
1191
|
@to_sql = @arel = @loaded = @should_eager_load = nil
|
|
1195
1192
|
@offsets = @take = nil
|
|
1196
1193
|
@cache_keys = nil
|
|
@@ -1210,7 +1207,7 @@ module ActiveRecord
|
|
|
1210
1207
|
relation.to_sql
|
|
1211
1208
|
end
|
|
1212
1209
|
else
|
|
1213
|
-
|
|
1210
|
+
model.with_connection do |conn|
|
|
1214
1211
|
conn.unprepared_statement { conn.to_sql(arel) }
|
|
1215
1212
|
end
|
|
1216
1213
|
end
|
|
@@ -1220,12 +1217,12 @@ module ActiveRecord
|
|
|
1220
1217
|
#
|
|
1221
1218
|
# User.where(name: 'Oscar').where_values_hash
|
|
1222
1219
|
# # => {name: "Oscar"}
|
|
1223
|
-
def where_values_hash(relation_table_name =
|
|
1220
|
+
def where_values_hash(relation_table_name = model.table_name) # :nodoc:
|
|
1224
1221
|
where_clause.to_h(relation_table_name)
|
|
1225
1222
|
end
|
|
1226
1223
|
|
|
1227
1224
|
def scope_for_create
|
|
1228
|
-
hash = where_clause.to_h(
|
|
1225
|
+
hash = where_clause.to_h(model.table_name, equality_only: true)
|
|
1229
1226
|
create_with_value.each { |k, v| hash[k.to_s] = v } unless create_with_value.empty?
|
|
1230
1227
|
hash
|
|
1231
1228
|
end
|
|
@@ -1271,6 +1268,10 @@ module ActiveRecord
|
|
|
1271
1268
|
records.blank?
|
|
1272
1269
|
end
|
|
1273
1270
|
|
|
1271
|
+
def readonly?
|
|
1272
|
+
readonly_value
|
|
1273
|
+
end
|
|
1274
|
+
|
|
1274
1275
|
def values
|
|
1275
1276
|
@values.dup
|
|
1276
1277
|
end
|
|
@@ -1289,7 +1290,7 @@ module ActiveRecord
|
|
|
1289
1290
|
end
|
|
1290
1291
|
|
|
1291
1292
|
def empty_scope? # :nodoc:
|
|
1292
|
-
@values ==
|
|
1293
|
+
@values == model.unscoped.values
|
|
1293
1294
|
end
|
|
1294
1295
|
|
|
1295
1296
|
def has_limit_or_offset? # :nodoc:
|
|
@@ -1297,7 +1298,7 @@ module ActiveRecord
|
|
|
1297
1298
|
end
|
|
1298
1299
|
|
|
1299
1300
|
def alias_tracker(joins = [], aliases = nil) # :nodoc:
|
|
1300
|
-
ActiveRecord::Associations::AliasTracker.create(connection_pool, table.name, joins, aliases)
|
|
1301
|
+
ActiveRecord::Associations::AliasTracker.create(model.connection_pool, table.name, joins, aliases)
|
|
1301
1302
|
end
|
|
1302
1303
|
|
|
1303
1304
|
class StrictLoadingScope # :nodoc:
|
|
@@ -1327,46 +1328,46 @@ module ActiveRecord
|
|
|
1327
1328
|
|
|
1328
1329
|
private
|
|
1329
1330
|
def already_in_scope?(registry)
|
|
1330
|
-
@
|
|
1331
|
+
@delegate_to_model && registry.current_scope(model, true)
|
|
1331
1332
|
end
|
|
1332
1333
|
|
|
1333
1334
|
def global_scope?(registry)
|
|
1334
|
-
registry.global_current_scope(
|
|
1335
|
+
registry.global_current_scope(model, true)
|
|
1335
1336
|
end
|
|
1336
1337
|
|
|
1337
1338
|
def current_scope_restoring_block(&block)
|
|
1338
|
-
current_scope =
|
|
1339
|
+
current_scope = model.current_scope(true)
|
|
1339
1340
|
-> record do
|
|
1340
|
-
|
|
1341
|
+
model.current_scope = current_scope
|
|
1341
1342
|
yield record if block_given?
|
|
1342
1343
|
end
|
|
1343
1344
|
end
|
|
1344
1345
|
|
|
1345
1346
|
def _new(attributes, &block)
|
|
1346
|
-
|
|
1347
|
+
model.new(attributes, &block)
|
|
1347
1348
|
end
|
|
1348
1349
|
|
|
1349
1350
|
def _create(attributes, &block)
|
|
1350
|
-
|
|
1351
|
+
model.create(attributes, &block)
|
|
1351
1352
|
end
|
|
1352
1353
|
|
|
1353
1354
|
def _create!(attributes, &block)
|
|
1354
|
-
|
|
1355
|
+
model.create!(attributes, &block)
|
|
1355
1356
|
end
|
|
1356
1357
|
|
|
1357
1358
|
def _scoping(scope, registry, all_queries = false)
|
|
1358
|
-
previous = registry.current_scope(
|
|
1359
|
-
registry.set_current_scope(
|
|
1359
|
+
previous = registry.current_scope(model, true)
|
|
1360
|
+
registry.set_current_scope(model, scope)
|
|
1360
1361
|
|
|
1361
1362
|
if all_queries
|
|
1362
|
-
previous_global = registry.global_current_scope(
|
|
1363
|
-
registry.set_global_current_scope(
|
|
1363
|
+
previous_global = registry.global_current_scope(model, true)
|
|
1364
|
+
registry.set_global_current_scope(model, scope)
|
|
1364
1365
|
end
|
|
1365
1366
|
yield
|
|
1366
1367
|
ensure
|
|
1367
|
-
registry.set_current_scope(
|
|
1368
|
+
registry.set_current_scope(model, previous)
|
|
1368
1369
|
if all_queries
|
|
1369
|
-
registry.set_global_current_scope(
|
|
1370
|
+
registry.set_global_current_scope(model, previous_global)
|
|
1370
1371
|
end
|
|
1371
1372
|
end
|
|
1372
1373
|
|
|
@@ -1378,7 +1379,7 @@ module ActiveRecord
|
|
|
1378
1379
|
value = Arel::Nodes::Grouping.new(value)
|
|
1379
1380
|
end
|
|
1380
1381
|
else
|
|
1381
|
-
type =
|
|
1382
|
+
type = model.type_for_attribute(attr.name)
|
|
1382
1383
|
value = predicate_builder.build_bind_attribute(attr.name, type.cast(value))
|
|
1383
1384
|
end
|
|
1384
1385
|
[attr, value]
|
|
@@ -1425,7 +1426,7 @@ module ActiveRecord
|
|
|
1425
1426
|
if where_clause.contradiction?
|
|
1426
1427
|
[].freeze
|
|
1427
1428
|
elsif eager_loading?
|
|
1428
|
-
|
|
1429
|
+
model.with_connection do |c|
|
|
1429
1430
|
apply_join_dependency do |relation, join_dependency|
|
|
1430
1431
|
if relation.null_relation?
|
|
1431
1432
|
[].freeze
|
|
@@ -1437,8 +1438,8 @@ module ActiveRecord
|
|
|
1437
1438
|
end
|
|
1438
1439
|
end
|
|
1439
1440
|
else
|
|
1440
|
-
|
|
1441
|
-
|
|
1441
|
+
model.with_connection do |c|
|
|
1442
|
+
model._query_by_sql(c, arel, async: async)
|
|
1442
1443
|
end
|
|
1443
1444
|
end
|
|
1444
1445
|
end
|
|
@@ -1451,13 +1452,13 @@ module ActiveRecord
|
|
|
1451
1452
|
@_join_dependency = nil
|
|
1452
1453
|
records
|
|
1453
1454
|
else
|
|
1454
|
-
|
|
1455
|
+
model._load_from_sql(rows, &block).freeze
|
|
1455
1456
|
end
|
|
1456
1457
|
end
|
|
1457
1458
|
|
|
1458
1459
|
def skip_query_cache_if_necessary(&block)
|
|
1459
1460
|
if skip_query_cache_value
|
|
1460
|
-
uncached(&block)
|
|
1461
|
+
model.uncached(&block)
|
|
1461
1462
|
else
|
|
1462
1463
|
yield
|
|
1463
1464
|
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,14 +120,16 @@ 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)
|
|
74
129
|
if block_given?
|
|
75
|
-
|
|
130
|
+
indexed_rows.each(&block)
|
|
76
131
|
else
|
|
77
|
-
|
|
132
|
+
indexed_rows.to_enum { @rows.size }
|
|
78
133
|
end
|
|
79
134
|
end
|
|
80
135
|
|
|
@@ -134,14 +189,13 @@ 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
|
-
@hash_rows = nil
|
|
141
194
|
end
|
|
142
195
|
|
|
143
196
|
def freeze # :nodoc:
|
|
144
197
|
hash_rows.freeze
|
|
198
|
+
indexed_rows.freeze
|
|
145
199
|
super
|
|
146
200
|
end
|
|
147
201
|
|
|
@@ -154,7 +208,7 @@ module ActiveRecord
|
|
|
154
208
|
hash[columns[index]] = index
|
|
155
209
|
index += 1
|
|
156
210
|
end
|
|
157
|
-
hash
|
|
211
|
+
hash.freeze
|
|
158
212
|
end
|
|
159
213
|
end
|
|
160
214
|
|
|
@@ -167,6 +221,13 @@ module ActiveRecord
|
|
|
167
221
|
end
|
|
168
222
|
end
|
|
169
223
|
|
|
224
|
+
def indexed_rows
|
|
225
|
+
@indexed_rows ||= begin
|
|
226
|
+
columns = column_indexes
|
|
227
|
+
@rows.map { |row| IndexedRow.new(columns, row) }.freeze
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
170
231
|
def hash_rows
|
|
171
232
|
# We use transform_values to rows.
|
|
172
233
|
# This is faster because we avoid any reallocs and avoid hashing entirely.
|
|
@@ -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,17 +207,12 @@ module ActiveRecord
|
|
|
202
207
|
end
|
|
203
208
|
|
|
204
209
|
indexes_in_create(table, tbl)
|
|
205
|
-
|
|
210
|
+
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
|
|
|
211
|
-
if remaining
|
|
212
|
-
tbl.puts
|
|
213
|
-
tbl.print remaining.string
|
|
214
|
-
end
|
|
215
|
-
|
|
216
216
|
stream.print tbl.string
|
|
217
217
|
rescue => e
|
|
218
218
|
stream.puts "# Could not dump table #{table.inspect} because of following #{e.class}"
|
|
@@ -277,35 +277,22 @@ module ActiveRecord
|
|
|
277
277
|
|
|
278
278
|
def check_constraints_in_create(table, stream)
|
|
279
279
|
if (check_constraints = @connection.check_constraints(table)).any?
|
|
280
|
-
|
|
280
|
+
add_check_constraint_statements = check_constraints.map do |check_constraint|
|
|
281
|
+
parts = [
|
|
282
|
+
"t.check_constraint #{check_constraint.expression.inspect}"
|
|
283
|
+
]
|
|
281
284
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
" t.check_constraint #{check_parts(check).join(', ')}"
|
|
285
|
+
if check_constraint.export_name_on_schema_dump?
|
|
286
|
+
parts << "name: #{check_constraint.name.inspect}"
|
|
285
287
|
end
|
|
286
288
|
|
|
287
|
-
|
|
288
|
-
end
|
|
289
|
-
|
|
290
|
-
unless check_invalid.empty?
|
|
291
|
-
remaining = StringIO.new
|
|
292
|
-
table_name = remove_prefix_and_suffix(table).inspect
|
|
293
|
-
|
|
294
|
-
add_check_constraint_statements = check_invalid.map do |check|
|
|
295
|
-
" add_check_constraint #{([table_name] + check_parts(check)).join(', ')}"
|
|
296
|
-
end
|
|
289
|
+
parts << "validate: #{check_constraint.validate?.inspect}" unless check_constraint.validate?
|
|
297
290
|
|
|
298
|
-
|
|
299
|
-
remaining
|
|
291
|
+
" #{parts.join(', ')}"
|
|
300
292
|
end
|
|
301
|
-
end
|
|
302
|
-
end
|
|
303
293
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
check_parts << "name: #{check.name.inspect}" if check.export_name_on_schema_dump?
|
|
307
|
-
check_parts << "validate: #{check.validate?.inspect}" unless check.validate?
|
|
308
|
-
check_parts
|
|
294
|
+
stream.puts add_check_constraint_statements.sort.join("\n")
|
|
295
|
+
end
|
|
309
296
|
end
|
|
310
297
|
|
|
311
298
|
def foreign_keys(table, stream)
|
|
@@ -34,7 +34,8 @@ module ActiveRecord
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
def delete_all_versions
|
|
37
|
-
|
|
37
|
+
# Eagerly check in connection to avoid checking in/out many times in the called method.
|
|
38
|
+
@pool.with_connection do
|
|
38
39
|
versions.each do |version|
|
|
39
40
|
delete_version(version)
|
|
40
41
|
end
|