activerecord 7.2.3 → 8.0.4
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 +391 -958
- data/README.rdoc +1 -1
- data/lib/active_record/association_relation.rb +1 -0
- data/lib/active_record/associations/association.rb +34 -10
- data/lib/active_record/associations/builder/association.rb +7 -6
- data/lib/active_record/associations/collection_association.rb +1 -1
- 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/preloader/association.rb +2 -2
- 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/time_zone_conversion.rb +2 -12
- data/lib/active_record/autosave_association.rb +69 -27
- 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 +6 -15
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +90 -43
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +8 -2
- 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 +7 -2
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +34 -7
- data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -5
- data/lib/active_record/connection_adapters/abstract_adapter.rb +31 -43
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +21 -40
- data/lib/active_record/connection_adapters/mysql/quoting.rb +0 -8
- 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 +1 -8
- data/lib/active_record/connection_adapters/pool_config.rb +7 -7
- 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 +6 -12
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +59 -16
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +46 -96
- data/lib/active_record/connection_adapters/schema_cache.rb +1 -3
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +80 -100
- 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 +9 -1
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +53 -12
- 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 +0 -17
- data/lib/active_record/connection_adapters.rb +0 -56
- data/lib/active_record/connection_handling.rb +23 -1
- data/lib/active_record/core.rb +29 -14
- 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/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +4 -4
- data/lib/active_record/encryption/encrypted_attribute_type.rb +10 -1
- data/lib/active_record/encryption/encryptor.rb +16 -8
- 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 +9 -22
- data/lib/active_record/errors.rb +13 -5
- data/lib/active_record/fixtures.rb +0 -2
- data/lib/active_record/future_result.rb +13 -9
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/insert_all.rb +1 -1
- 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 +31 -11
- data/lib/active_record/migration/compatibility.rb +5 -2
- data/lib/active_record/migration.rb +38 -42
- 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 +102 -50
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +8 -8
- data/lib/active_record/railtie.rb +2 -26
- data/lib/active_record/railties/databases.rake +11 -35
- 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 +40 -39
- data/lib/active_record/relation/delegation.rb +25 -14
- data/lib/active_record/relation/finder_methods.rb +18 -18
- data/lib/active_record/relation/merger.rb +8 -8
- 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_methods.rb +105 -61
- data/lib/active_record/relation/spawn_methods.rb +7 -7
- data/lib/active_record/relation.rb +79 -61
- data/lib/active_record/result.rb +66 -4
- data/lib/active_record/sanitization.rb +7 -6
- data/lib/active_record/schema_dumper.rb +5 -0
- data/lib/active_record/schema_migration.rb +2 -1
- data/lib/active_record/scoping/named.rb +5 -2
- 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 +2 -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 +5 -6
- data/lib/active_record/validations/uniqueness.rb +8 -8
- data/lib/active_record.rb +21 -48
- 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/nodes/binary.rb +1 -1
- data/lib/arel/nodes/node.rb +1 -1
- data/lib/arel/nodes/sql_literal.rb +1 -1
- data/lib/arel/table.rb +3 -7
- metadata +9 -10
- data/lib/active_record/relation/record_fetch_warning.rb +0 -52
|
@@ -7,7 +7,7 @@ require "active_record/relation/merger"
|
|
|
7
7
|
module ActiveRecord
|
|
8
8
|
module SpawnMethods
|
|
9
9
|
def spawn # :nodoc:
|
|
10
|
-
already_in_scope?(
|
|
10
|
+
already_in_scope?(model.scope_registry) ? model.all : clone
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
# Merges in the conditions from <tt>other</tt>, if <tt>other</tt> is an ActiveRecord::Relation.
|
|
@@ -52,18 +52,18 @@ module ActiveRecord
|
|
|
52
52
|
end
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
-
# Removes
|
|
55
|
+
# Removes the condition(s) specified in +skips+ from the query.
|
|
56
56
|
#
|
|
57
|
-
# Post.order('id asc').except(:order) #
|
|
58
|
-
# Post.where('id > 10').order('id asc').except(:where) #
|
|
57
|
+
# Post.order('id asc').except(:order) # removes the order condition
|
|
58
|
+
# Post.where('id > 10').order('id asc').except(:where) # removes the where condition but keeps the order
|
|
59
59
|
def except(*skips)
|
|
60
60
|
relation_with values.except(*skips)
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
-
#
|
|
63
|
+
# Keeps only the condition(s) specified in +onlies+ in the query, removing all others.
|
|
64
64
|
#
|
|
65
|
-
# Post.order('id asc').only(:where) #
|
|
66
|
-
# Post.order('id asc').only(:where, :order) #
|
|
65
|
+
# Post.order('id asc').only(:where) # keeps only the where condition, removes the order
|
|
66
|
+
# Post.order('id asc').only(:where, :order) # keeps only the where and order conditions
|
|
67
67
|
def only(*onlies)
|
|
68
68
|
relation_with values.slice(*onlies)
|
|
69
69
|
end
|
|
@@ -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
|
|
@@ -440,14 +447,14 @@ module ActiveRecord
|
|
|
440
447
|
# Product.where("name like ?", "%Game%").cache_key(:last_reviewed_at)
|
|
441
448
|
def cache_key(timestamp_column = "updated_at")
|
|
442
449
|
@cache_keys ||= {}
|
|
443
|
-
@cache_keys[timestamp_column] ||=
|
|
450
|
+
@cache_keys[timestamp_column] ||= model.collection_cache_key(self, timestamp_column)
|
|
444
451
|
end
|
|
445
452
|
|
|
446
453
|
def compute_cache_key(timestamp_column = :updated_at) # :nodoc:
|
|
447
454
|
query_signature = ActiveSupport::Digest.hexdigest(to_sql)
|
|
448
|
-
key = "#{
|
|
455
|
+
key = "#{model.model_name.cache_key}/query-#{query_signature}"
|
|
449
456
|
|
|
450
|
-
if collection_cache_versioning
|
|
457
|
+
if model.collection_cache_versioning
|
|
451
458
|
key
|
|
452
459
|
else
|
|
453
460
|
"#{key}-#{compute_cache_version(timestamp_column)}"
|
|
@@ -466,7 +473,7 @@ module ActiveRecord
|
|
|
466
473
|
#
|
|
467
474
|
# SELECT COUNT(*), MAX("products"."updated_at") FROM "products" WHERE (name like '%Cosmic Encounter%')
|
|
468
475
|
def cache_version(timestamp_column = :updated_at)
|
|
469
|
-
if collection_cache_versioning
|
|
476
|
+
if model.collection_cache_versioning
|
|
470
477
|
@cache_versions ||= {}
|
|
471
478
|
@cache_versions[timestamp_column] ||= compute_cache_version(timestamp_column)
|
|
472
479
|
end
|
|
@@ -485,7 +492,7 @@ module ActiveRecord
|
|
|
485
492
|
|
|
486
493
|
with_connection do |c|
|
|
487
494
|
column = c.visitor.compile(table[timestamp_column])
|
|
488
|
-
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"
|
|
489
496
|
|
|
490
497
|
if collection.has_limit_or_offset?
|
|
491
498
|
query = collection.select("#{column} AS collection_cache_key_timestamp")
|
|
@@ -502,7 +509,7 @@ module ActiveRecord
|
|
|
502
509
|
size, timestamp = c.select_rows(arel, nil).first
|
|
503
510
|
|
|
504
511
|
if size
|
|
505
|
-
column_type =
|
|
512
|
+
column_type = model.type_for_attribute(timestamp_column)
|
|
506
513
|
timestamp = column_type.deserialize(timestamp)
|
|
507
514
|
else
|
|
508
515
|
size = 0
|
|
@@ -511,7 +518,7 @@ module ActiveRecord
|
|
|
511
518
|
end
|
|
512
519
|
|
|
513
520
|
if timestamp
|
|
514
|
-
"#{size}-#{timestamp.utc.to_fs(cache_timestamp_format)}"
|
|
521
|
+
"#{size}-#{timestamp.utc.to_fs(model.cache_timestamp_format)}"
|
|
515
522
|
else
|
|
516
523
|
"#{size}"
|
|
517
524
|
end
|
|
@@ -542,7 +549,7 @@ module ActiveRecord
|
|
|
542
549
|
# Please check unscoped if you want to remove all previous scopes (including
|
|
543
550
|
# the default_scope) during the execution of a block.
|
|
544
551
|
def scoping(all_queries: nil, &block)
|
|
545
|
-
registry =
|
|
552
|
+
registry = model.scope_registry
|
|
546
553
|
if global_scope?(registry) && all_queries == false
|
|
547
554
|
raise ArgumentError, "Scoping is set to apply to all queries and cannot be unset in a nested block."
|
|
548
555
|
elsif already_in_scope?(registry)
|
|
@@ -553,11 +560,11 @@ module ActiveRecord
|
|
|
553
560
|
end
|
|
554
561
|
|
|
555
562
|
def _exec_scope(...) # :nodoc:
|
|
556
|
-
@
|
|
557
|
-
registry =
|
|
563
|
+
@delegate_to_model = true
|
|
564
|
+
registry = model.scope_registry
|
|
558
565
|
_scoping(nil, registry) { instance_exec(...) || self }
|
|
559
566
|
ensure
|
|
560
|
-
@
|
|
567
|
+
@delegate_to_model = false
|
|
561
568
|
end
|
|
562
569
|
|
|
563
570
|
# Updates all records in the current relation with details given. This method constructs a single SQL UPDATE
|
|
@@ -594,30 +601,30 @@ module ActiveRecord
|
|
|
594
601
|
return 0 if @none
|
|
595
602
|
|
|
596
603
|
if updates.is_a?(Hash)
|
|
597
|
-
if
|
|
598
|
-
!updates.key?(
|
|
599
|
-
!updates.key?(
|
|
600
|
-
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]
|
|
601
608
|
updates[attr.name] = _increment_attribute(attr)
|
|
602
609
|
end
|
|
603
610
|
values = _substitute_values(updates)
|
|
604
611
|
else
|
|
605
|
-
values = Arel.sql(
|
|
612
|
+
values = Arel.sql(model.sanitize_sql_for_assignment(updates, table.name))
|
|
606
613
|
end
|
|
607
614
|
|
|
608
|
-
|
|
615
|
+
model.with_connection do |c|
|
|
609
616
|
arel = eager_loading? ? apply_join_dependency.arel : build_arel(c)
|
|
610
617
|
arel.source.left = table
|
|
611
618
|
|
|
612
619
|
group_values_arel_columns = arel_columns(group_values.uniq)
|
|
613
620
|
having_clause_ast = having_clause.ast unless having_clause.empty?
|
|
614
|
-
key = if
|
|
621
|
+
key = if model.composite_primary_key?
|
|
615
622
|
primary_key.map { |pk| table[pk] }
|
|
616
623
|
else
|
|
617
624
|
table[primary_key]
|
|
618
625
|
end
|
|
619
626
|
stmt = arel.compile_update(values, key, having_clause_ast, group_values_arel_columns)
|
|
620
|
-
c.update(stmt, "#{
|
|
627
|
+
c.update(stmt, "#{model} Update All").tap { reset }
|
|
621
628
|
end
|
|
622
629
|
end
|
|
623
630
|
|
|
@@ -625,7 +632,7 @@ module ActiveRecord
|
|
|
625
632
|
if id == :all
|
|
626
633
|
each { |record| record.update(attributes) }
|
|
627
634
|
else
|
|
628
|
-
|
|
635
|
+
model.update(id, attributes)
|
|
629
636
|
end
|
|
630
637
|
end
|
|
631
638
|
|
|
@@ -633,7 +640,7 @@ module ActiveRecord
|
|
|
633
640
|
if id == :all
|
|
634
641
|
each { |record| record.update!(attributes) }
|
|
635
642
|
else
|
|
636
|
-
|
|
643
|
+
model.update!(id, attributes)
|
|
637
644
|
end
|
|
638
645
|
end
|
|
639
646
|
|
|
@@ -939,7 +946,7 @@ module ActiveRecord
|
|
|
939
946
|
names = touch if touch != true
|
|
940
947
|
names = Array.wrap(names)
|
|
941
948
|
options = names.extract_options!
|
|
942
|
-
touch_updates =
|
|
949
|
+
touch_updates = model.touch_attributes_with_time(*names, **options)
|
|
943
950
|
updates.merge!(touch_updates) unless touch_updates.empty?
|
|
944
951
|
end
|
|
945
952
|
|
|
@@ -970,7 +977,7 @@ module ActiveRecord
|
|
|
970
977
|
# Person.where(name: 'David').touch_all
|
|
971
978
|
# # => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670' WHERE \"people\".\"name\" = 'David'"
|
|
972
979
|
def touch_all(*names, time: nil)
|
|
973
|
-
update_all
|
|
980
|
+
update_all model.touch_attributes_with_time(*names, time: time)
|
|
974
981
|
end
|
|
975
982
|
|
|
976
983
|
# Destroys the records by instantiating each
|
|
@@ -1022,20 +1029,20 @@ module ActiveRecord
|
|
|
1022
1029
|
raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
|
|
1023
1030
|
end
|
|
1024
1031
|
|
|
1025
|
-
|
|
1032
|
+
model.with_connection do |c|
|
|
1026
1033
|
arel = eager_loading? ? apply_join_dependency.arel : build_arel(c)
|
|
1027
1034
|
arel.source.left = table
|
|
1028
1035
|
|
|
1029
1036
|
group_values_arel_columns = arel_columns(group_values.uniq)
|
|
1030
1037
|
having_clause_ast = having_clause.ast unless having_clause.empty?
|
|
1031
|
-
key = if
|
|
1038
|
+
key = if model.composite_primary_key?
|
|
1032
1039
|
primary_key.map { |pk| table[pk] }
|
|
1033
1040
|
else
|
|
1034
1041
|
table[primary_key]
|
|
1035
1042
|
end
|
|
1036
1043
|
stmt = arel.compile_delete(key, having_clause_ast, group_values_arel_columns)
|
|
1037
1044
|
|
|
1038
|
-
c.delete(stmt, "#{
|
|
1045
|
+
c.delete(stmt, "#{model} Delete All").tap { reset }
|
|
1039
1046
|
end
|
|
1040
1047
|
end
|
|
1041
1048
|
|
|
@@ -1134,9 +1141,6 @@ module ActiveRecord
|
|
|
1134
1141
|
# for queries to actually be executed concurrently. Otherwise it defaults to
|
|
1135
1142
|
# executing them in the foreground.
|
|
1136
1143
|
#
|
|
1137
|
-
# +load_async+ will also fall back to executing in the foreground in the test environment when transactional
|
|
1138
|
-
# fixtures are enabled.
|
|
1139
|
-
#
|
|
1140
1144
|
# If the query was actually executed in the background, the Active Record logs will show
|
|
1141
1145
|
# it by prefixing the log line with <tt>ASYNC</tt>:
|
|
1142
1146
|
#
|
|
@@ -1146,7 +1150,7 @@ module ActiveRecord
|
|
|
1146
1150
|
return load if !c.async_enabled?
|
|
1147
1151
|
|
|
1148
1152
|
unless loaded?
|
|
1149
|
-
result = exec_main_query(async: c.current_transaction.
|
|
1153
|
+
result = exec_main_query(async: !c.current_transaction.joinable?)
|
|
1150
1154
|
|
|
1151
1155
|
if result.is_a?(Array)
|
|
1152
1156
|
@records = result
|
|
@@ -1160,6 +1164,16 @@ module ActiveRecord
|
|
|
1160
1164
|
self
|
|
1161
1165
|
end
|
|
1162
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
|
+
|
|
1163
1177
|
# Returns <tt>true</tt> if the relation was scheduled on the background
|
|
1164
1178
|
# thread pool.
|
|
1165
1179
|
def scheduled?
|
|
@@ -1190,7 +1204,7 @@ module ActiveRecord
|
|
|
1190
1204
|
def reset
|
|
1191
1205
|
@future_result&.cancel
|
|
1192
1206
|
@future_result = nil
|
|
1193
|
-
@
|
|
1207
|
+
@delegate_to_model = false
|
|
1194
1208
|
@to_sql = @arel = @loaded = @should_eager_load = nil
|
|
1195
1209
|
@offsets = @take = nil
|
|
1196
1210
|
@cache_keys = nil
|
|
@@ -1210,7 +1224,7 @@ module ActiveRecord
|
|
|
1210
1224
|
relation.to_sql
|
|
1211
1225
|
end
|
|
1212
1226
|
else
|
|
1213
|
-
|
|
1227
|
+
model.with_connection do |conn|
|
|
1214
1228
|
conn.unprepared_statement { conn.to_sql(arel) }
|
|
1215
1229
|
end
|
|
1216
1230
|
end
|
|
@@ -1220,12 +1234,12 @@ module ActiveRecord
|
|
|
1220
1234
|
#
|
|
1221
1235
|
# User.where(name: 'Oscar').where_values_hash
|
|
1222
1236
|
# # => {name: "Oscar"}
|
|
1223
|
-
def where_values_hash(relation_table_name =
|
|
1237
|
+
def where_values_hash(relation_table_name = model.table_name) # :nodoc:
|
|
1224
1238
|
where_clause.to_h(relation_table_name)
|
|
1225
1239
|
end
|
|
1226
1240
|
|
|
1227
1241
|
def scope_for_create
|
|
1228
|
-
hash = where_clause.to_h(
|
|
1242
|
+
hash = where_clause.to_h(model.table_name, equality_only: true)
|
|
1229
1243
|
create_with_value.each { |k, v| hash[k.to_s] = v } unless create_with_value.empty?
|
|
1230
1244
|
hash
|
|
1231
1245
|
end
|
|
@@ -1271,6 +1285,10 @@ module ActiveRecord
|
|
|
1271
1285
|
records.blank?
|
|
1272
1286
|
end
|
|
1273
1287
|
|
|
1288
|
+
def readonly?
|
|
1289
|
+
readonly_value
|
|
1290
|
+
end
|
|
1291
|
+
|
|
1274
1292
|
def values
|
|
1275
1293
|
@values.dup
|
|
1276
1294
|
end
|
|
@@ -1289,7 +1307,7 @@ module ActiveRecord
|
|
|
1289
1307
|
end
|
|
1290
1308
|
|
|
1291
1309
|
def empty_scope? # :nodoc:
|
|
1292
|
-
@values ==
|
|
1310
|
+
@values == model.unscoped.values
|
|
1293
1311
|
end
|
|
1294
1312
|
|
|
1295
1313
|
def has_limit_or_offset? # :nodoc:
|
|
@@ -1297,7 +1315,7 @@ module ActiveRecord
|
|
|
1297
1315
|
end
|
|
1298
1316
|
|
|
1299
1317
|
def alias_tracker(joins = [], aliases = nil) # :nodoc:
|
|
1300
|
-
ActiveRecord::Associations::AliasTracker.create(connection_pool, table.name, joins, aliases)
|
|
1318
|
+
ActiveRecord::Associations::AliasTracker.create(model.connection_pool, table.name, joins, aliases)
|
|
1301
1319
|
end
|
|
1302
1320
|
|
|
1303
1321
|
class StrictLoadingScope # :nodoc:
|
|
@@ -1327,46 +1345,46 @@ module ActiveRecord
|
|
|
1327
1345
|
|
|
1328
1346
|
private
|
|
1329
1347
|
def already_in_scope?(registry)
|
|
1330
|
-
@
|
|
1348
|
+
@delegate_to_model && registry.current_scope(model, true)
|
|
1331
1349
|
end
|
|
1332
1350
|
|
|
1333
1351
|
def global_scope?(registry)
|
|
1334
|
-
registry.global_current_scope(
|
|
1352
|
+
registry.global_current_scope(model, true)
|
|
1335
1353
|
end
|
|
1336
1354
|
|
|
1337
1355
|
def current_scope_restoring_block(&block)
|
|
1338
|
-
current_scope =
|
|
1356
|
+
current_scope = model.current_scope(true)
|
|
1339
1357
|
-> record do
|
|
1340
|
-
|
|
1358
|
+
model.current_scope = current_scope
|
|
1341
1359
|
yield record if block_given?
|
|
1342
1360
|
end
|
|
1343
1361
|
end
|
|
1344
1362
|
|
|
1345
1363
|
def _new(attributes, &block)
|
|
1346
|
-
|
|
1364
|
+
model.new(attributes, &block)
|
|
1347
1365
|
end
|
|
1348
1366
|
|
|
1349
1367
|
def _create(attributes, &block)
|
|
1350
|
-
|
|
1368
|
+
model.create(attributes, &block)
|
|
1351
1369
|
end
|
|
1352
1370
|
|
|
1353
1371
|
def _create!(attributes, &block)
|
|
1354
|
-
|
|
1372
|
+
model.create!(attributes, &block)
|
|
1355
1373
|
end
|
|
1356
1374
|
|
|
1357
1375
|
def _scoping(scope, registry, all_queries = false)
|
|
1358
|
-
previous = registry.current_scope(
|
|
1359
|
-
registry.set_current_scope(
|
|
1376
|
+
previous = registry.current_scope(model, true)
|
|
1377
|
+
registry.set_current_scope(model, scope)
|
|
1360
1378
|
|
|
1361
1379
|
if all_queries
|
|
1362
|
-
previous_global = registry.global_current_scope(
|
|
1363
|
-
registry.set_global_current_scope(
|
|
1380
|
+
previous_global = registry.global_current_scope(model, true)
|
|
1381
|
+
registry.set_global_current_scope(model, scope)
|
|
1364
1382
|
end
|
|
1365
1383
|
yield
|
|
1366
1384
|
ensure
|
|
1367
|
-
registry.set_current_scope(
|
|
1385
|
+
registry.set_current_scope(model, previous)
|
|
1368
1386
|
if all_queries
|
|
1369
|
-
registry.set_global_current_scope(
|
|
1387
|
+
registry.set_global_current_scope(model, previous_global)
|
|
1370
1388
|
end
|
|
1371
1389
|
end
|
|
1372
1390
|
|
|
@@ -1378,7 +1396,7 @@ module ActiveRecord
|
|
|
1378
1396
|
value = Arel::Nodes::Grouping.new(value)
|
|
1379
1397
|
end
|
|
1380
1398
|
else
|
|
1381
|
-
type =
|
|
1399
|
+
type = model.type_for_attribute(attr.name)
|
|
1382
1400
|
value = predicate_builder.build_bind_attribute(attr.name, type.cast(value))
|
|
1383
1401
|
end
|
|
1384
1402
|
[attr, value]
|
|
@@ -1425,7 +1443,7 @@ module ActiveRecord
|
|
|
1425
1443
|
if where_clause.contradiction?
|
|
1426
1444
|
[].freeze
|
|
1427
1445
|
elsif eager_loading?
|
|
1428
|
-
|
|
1446
|
+
model.with_connection do |c|
|
|
1429
1447
|
apply_join_dependency do |relation, join_dependency|
|
|
1430
1448
|
if relation.null_relation?
|
|
1431
1449
|
[].freeze
|
|
@@ -1437,8 +1455,8 @@ module ActiveRecord
|
|
|
1437
1455
|
end
|
|
1438
1456
|
end
|
|
1439
1457
|
else
|
|
1440
|
-
|
|
1441
|
-
|
|
1458
|
+
model.with_connection do |c|
|
|
1459
|
+
model._query_by_sql(c, arel, async: async)
|
|
1442
1460
|
end
|
|
1443
1461
|
end
|
|
1444
1462
|
end
|
|
@@ -1451,13 +1469,13 @@ module ActiveRecord
|
|
|
1451
1469
|
@_join_dependency = nil
|
|
1452
1470
|
records
|
|
1453
1471
|
else
|
|
1454
|
-
|
|
1472
|
+
model._load_from_sql(rows, &block).freeze
|
|
1455
1473
|
end
|
|
1456
1474
|
end
|
|
1457
1475
|
|
|
1458
1476
|
def skip_query_cache_if_necessary(&block)
|
|
1459
1477
|
if skip_query_cache_value
|
|
1460
|
-
uncached(&block)
|
|
1478
|
+
model.uncached(&block)
|
|
1461
1479
|
else
|
|
1462
1480
|
yield
|
|
1463
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
|
|
|
@@ -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
|
|
@@ -23,7 +23,7 @@ module ActiveRecord
|
|
|
23
23
|
scope = current_scope
|
|
24
24
|
|
|
25
25
|
if scope
|
|
26
|
-
if self == scope.
|
|
26
|
+
if self == scope.model
|
|
27
27
|
scope.clone
|
|
28
28
|
else
|
|
29
29
|
relation.merge!(scope)
|
|
@@ -191,7 +191,10 @@ module ActiveRecord
|
|
|
191
191
|
private
|
|
192
192
|
def singleton_method_added(name)
|
|
193
193
|
super
|
|
194
|
-
|
|
194
|
+
# Most Kernel extends are both singleton and instance methods so
|
|
195
|
+
# respond_to is a fast check, but we don't want to define methods
|
|
196
|
+
# only on the module (ex. Module#name)
|
|
197
|
+
generate_relation_method(name) if Kernel.respond_to?(name) && (Kernel.method_defined?(name) || Kernel.private_method_defined?(name)) && !ActiveRecord::Relation.method_defined?(name)
|
|
195
198
|
end
|
|
196
199
|
end
|
|
197
200
|
end
|
|
@@ -74,13 +74,13 @@ module ActiveRecord
|
|
|
74
74
|
self
|
|
75
75
|
end
|
|
76
76
|
|
|
77
|
-
def add_bind(obj)
|
|
77
|
+
def add_bind(obj, &)
|
|
78
78
|
@binds << obj
|
|
79
79
|
@parts << Substitute.new
|
|
80
80
|
self
|
|
81
81
|
end
|
|
82
82
|
|
|
83
|
-
def add_binds(binds, proc_for_binds = nil)
|
|
83
|
+
def add_binds(binds, proc_for_binds = nil, &)
|
|
84
84
|
@binds.concat proc_for_binds ? binds.map(&proc_for_binds) : binds
|
|
85
85
|
binds.size.times do |i|
|
|
86
86
|
@parts << ", " unless i == 0
|
|
@@ -133,23 +133,26 @@ module ActiveRecord
|
|
|
133
133
|
relation = (callable || block).call Params.new
|
|
134
134
|
query_builder, binds = connection.cacheable_query(self, relation.arel)
|
|
135
135
|
bind_map = BindMap.new(binds)
|
|
136
|
-
new(query_builder, bind_map, relation.
|
|
136
|
+
new(query_builder, bind_map, relation.model)
|
|
137
137
|
end
|
|
138
138
|
|
|
139
|
-
def initialize(query_builder, bind_map,
|
|
139
|
+
def initialize(query_builder, bind_map, model)
|
|
140
140
|
@query_builder = query_builder
|
|
141
141
|
@bind_map = bind_map
|
|
142
|
-
@
|
|
142
|
+
@model = model
|
|
143
143
|
end
|
|
144
144
|
|
|
145
|
-
def execute(params, connection, allow_retry: false, &block)
|
|
146
|
-
bind_values = bind_map.bind params
|
|
145
|
+
def execute(params, connection, allow_retry: false, async: false, &block)
|
|
146
|
+
bind_values = @bind_map.bind params
|
|
147
|
+
sql = @query_builder.sql_for bind_values, connection
|
|
147
148
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
149
|
+
if async
|
|
150
|
+
@model.async_find_by_sql(sql, bind_values, preparable: true, allow_retry: allow_retry, &block)
|
|
151
|
+
else
|
|
152
|
+
@model.find_by_sql(sql, bind_values, preparable: true, allow_retry: allow_retry, &block)
|
|
153
|
+
end
|
|
151
154
|
rescue ::RangeError
|
|
152
|
-
[]
|
|
155
|
+
async ? Promise.wrap([]) : []
|
|
153
156
|
end
|
|
154
157
|
|
|
155
158
|
def self.unsupported_value?(value)
|
|
@@ -157,8 +160,5 @@ module ActiveRecord
|
|
|
157
160
|
when NilClass, Array, Range, Hash, Relation, Base then true
|
|
158
161
|
end
|
|
159
162
|
end
|
|
160
|
-
|
|
161
|
-
private
|
|
162
|
-
attr_reader :query_builder, :bind_map, :klass
|
|
163
163
|
end
|
|
164
164
|
end
|