activerecord 7.2.2 → 8.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +239 -878
- 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 +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/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 +2 -7
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +2 -12
- data/lib/active_record/attribute_methods.rb +1 -1
- data/lib/active_record/autosave_association.rb +69 -27
- 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 +0 -9
- 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 +33 -6
- data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -5
- data/lib/active_record/connection_adapters/abstract_adapter.rb +24 -26
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +21 -39
- 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 +43 -45
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +42 -98
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -8
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +64 -42
- 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 +45 -95
- 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/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 -1
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +53 -12
- 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 +22 -0
- data/lib/active_record/core.rb +18 -14
- data/lib/active_record/database_configurations/database_config.rb +4 -0
- data/lib/active_record/database_configurations/hash_config.rb +8 -0
- 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 +15 -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 +14 -10
- 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 +27 -10
- data/lib/active_record/migration/compatibility.rb +5 -2
- data/lib/active_record/migration.rb +35 -38
- 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 +2 -17
- 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 +115 -65
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- 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 +12 -12
- 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 +48 -47
- data/lib/active_record/tasks/mysql_database_tasks.rb +0 -2
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -2
- data/lib/active_record/test_fixtures.rb +12 -0
- data/lib/active_record/token_for.rb +1 -1
- data/lib/active_record/validations/uniqueness.rb +8 -8
- data/lib/active_record.rb +15 -45
- data/lib/arel/collectors/bind.rb +1 -1
- data/lib/arel/table.rb +3 -7
- metadata +11 -12
- data/lib/active_record/relation/record_fetch_warning.rb +0 -52
@@ -22,6 +22,9 @@ module ActiveRecord
|
|
22
22
|
end
|
23
23
|
|
24
24
|
module DelegateCache # :nodoc:
|
25
|
+
@delegate_base_methods = true
|
26
|
+
singleton_class.attr_accessor :delegate_base_methods
|
27
|
+
|
25
28
|
def relation_delegate_class(klass)
|
26
29
|
@relation_delegate_cache[klass]
|
27
30
|
end
|
@@ -75,12 +78,12 @@ module ActiveRecord
|
|
75
78
|
if /\A[a-zA-Z_]\w*[!?]?\z/.match?(method) && !::ActiveSupport::Delegation::RESERVED_METHOD_NAMES.include?(method.to_s)
|
76
79
|
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
77
80
|
def #{method}(...)
|
78
|
-
scoping {
|
81
|
+
scoping { model.#{method}(...) }
|
79
82
|
end
|
80
83
|
RUBY
|
81
84
|
else
|
82
85
|
define_method(method) do |*args, **kwargs, &block|
|
83
|
-
scoping {
|
86
|
+
scoping { model.public_send(method, *args, **kwargs, &block) }
|
84
87
|
end
|
85
88
|
end
|
86
89
|
end
|
@@ -92,15 +95,15 @@ module ActiveRecord
|
|
92
95
|
|
93
96
|
# This module creates compiled delegation methods dynamically at runtime, which makes
|
94
97
|
# subsequent calls to that method faster by avoiding method_missing. The delegations
|
95
|
-
# may vary depending on the
|
96
|
-
# for each different
|
98
|
+
# may vary depending on the model of a relation, so we create a subclass of Relation
|
99
|
+
# for each different model, and the delegations are compiled into that subclass only.
|
97
100
|
|
98
101
|
delegate :to_xml, :encode_with, :length, :each, :join, :intersect?,
|
99
102
|
:[], :&, :|, :+, :-, :sample, :reverse, :rotate, :compact, :in_groups, :in_groups_of,
|
100
103
|
:to_sentence, :to_fs, :to_formatted_s, :as_json,
|
101
104
|
:shuffle, :split, :slice, :index, :rindex, to: :records
|
102
105
|
|
103
|
-
delegate :primary_key, :
|
106
|
+
delegate :primary_key, :with_connection, :connection, :table_name, :transaction, :sanitize_sql_like, :unscoped, :name, to: :model
|
104
107
|
|
105
108
|
module ClassSpecificRelation # :nodoc:
|
106
109
|
extend ActiveSupport::Concern
|
@@ -113,11 +116,19 @@ module ActiveRecord
|
|
113
116
|
|
114
117
|
private
|
115
118
|
def method_missing(method, ...)
|
116
|
-
if
|
117
|
-
|
118
|
-
|
119
|
+
if model.respond_to?(method)
|
120
|
+
if !DelegateCache.delegate_base_methods && Base.respond_to?(method)
|
121
|
+
# A common mistake in Active Record's own code is to call `ActiveRecord::Base`
|
122
|
+
# class methods on Association. It works because it's automatically delegated, but
|
123
|
+
# can introduce subtle bugs because it sets the global scope.
|
124
|
+
# We can't deprecate this behavior because gems might depend on it, however we
|
125
|
+
# can ban it from Active Record's own test suite to avoid regressions.
|
126
|
+
raise NotImplementedError, "Active Record code shouldn't rely on association delegation into ActiveRecord::Base methods"
|
127
|
+
elsif !Delegation.uncacheable_methods.include?(method)
|
128
|
+
model.generate_relation_method(method)
|
119
129
|
end
|
120
|
-
|
130
|
+
|
131
|
+
scoping { model.public_send(method, ...) }
|
121
132
|
else
|
122
133
|
super
|
123
134
|
end
|
@@ -125,19 +136,19 @@ module ActiveRecord
|
|
125
136
|
end
|
126
137
|
|
127
138
|
module ClassMethods # :nodoc:
|
128
|
-
def create(
|
129
|
-
relation_class_for(
|
139
|
+
def create(model, ...)
|
140
|
+
relation_class_for(model).new(model, ...)
|
130
141
|
end
|
131
142
|
|
132
143
|
private
|
133
|
-
def relation_class_for(
|
134
|
-
|
144
|
+
def relation_class_for(model)
|
145
|
+
model.relation_delegate_class(self)
|
135
146
|
end
|
136
147
|
end
|
137
148
|
|
138
149
|
private
|
139
150
|
def respond_to_missing?(method, _)
|
140
|
-
super ||
|
151
|
+
super || model.respond_to?(method)
|
141
152
|
end
|
142
153
|
end
|
143
154
|
end
|
@@ -145,10 +145,10 @@ module ActiveRecord
|
|
145
145
|
|
146
146
|
if found.nil?
|
147
147
|
raise_record_not_found_exception!
|
148
|
-
elsif undesired.
|
149
|
-
raise ActiveRecord::SoleRecordExceeded.new(self)
|
150
|
-
else
|
148
|
+
elsif undesired.nil?
|
151
149
|
found
|
150
|
+
else
|
151
|
+
raise ActiveRecord::SoleRecordExceeded.new(model)
|
152
152
|
end
|
153
153
|
end
|
154
154
|
|
@@ -376,7 +376,7 @@ module ActiveRecord
|
|
376
376
|
|
377
377
|
skip_query_cache_if_necessary do
|
378
378
|
with_connection do |c|
|
379
|
-
c.select_rows(relation.arel, "#{name} Exists?").size == 1
|
379
|
+
c.select_rows(relation.arel, "#{model.name} Exists?").size == 1
|
380
380
|
end
|
381
381
|
end
|
382
382
|
end
|
@@ -389,7 +389,7 @@ module ActiveRecord
|
|
389
389
|
def include?(record)
|
390
390
|
# The existing implementation relies on receiving an Active Record instance as the input parameter named record.
|
391
391
|
# Any non-Active Record object passed to this implementation is guaranteed to return `false`.
|
392
|
-
return false unless record.is_a?(
|
392
|
+
return false unless record.is_a?(model)
|
393
393
|
|
394
394
|
if loaded? || offset_value || limit_value || having_clause.any?
|
395
395
|
records.include?(record)
|
@@ -415,9 +415,9 @@ module ActiveRecord
|
|
415
415
|
# the expected number of results should be provided in the +expected_size+
|
416
416
|
# argument.
|
417
417
|
def raise_record_not_found_exception!(ids = nil, result_size = nil, expected_size = nil, key = primary_key, not_found_ids = nil) # :nodoc:
|
418
|
-
conditions = " [#{arel.where_sql(
|
418
|
+
conditions = " [#{arel.where_sql(model)}]" unless where_clause.empty?
|
419
419
|
|
420
|
-
name =
|
420
|
+
name = model.name
|
421
421
|
|
422
422
|
if ids.nil?
|
423
423
|
error = +"Couldn't find #{name}"
|
@@ -471,7 +471,7 @@ module ActiveRecord
|
|
471
471
|
)
|
472
472
|
)
|
473
473
|
relation = skip_query_cache_if_necessary do
|
474
|
-
|
474
|
+
model.with_connection do |c|
|
475
475
|
c.distinct_relation_for_primary_key(relation)
|
476
476
|
end
|
477
477
|
end
|
@@ -489,9 +489,9 @@ module ActiveRecord
|
|
489
489
|
end
|
490
490
|
|
491
491
|
def find_with_ids(*ids)
|
492
|
-
raise UnknownPrimaryKey.new(
|
492
|
+
raise UnknownPrimaryKey.new(model) if primary_key.nil?
|
493
493
|
|
494
|
-
expects_array = if
|
494
|
+
expects_array = if model.composite_primary_key?
|
495
495
|
ids.first.first.is_a?(Array)
|
496
496
|
else
|
497
497
|
ids.first.is_a?(Array)
|
@@ -503,7 +503,7 @@ module ActiveRecord
|
|
503
503
|
|
504
504
|
ids = ids.compact.uniq
|
505
505
|
|
506
|
-
model_name =
|
506
|
+
model_name = model.name
|
507
507
|
|
508
508
|
case ids.size
|
509
509
|
when 0
|
@@ -525,7 +525,7 @@ module ActiveRecord
|
|
525
525
|
MSG
|
526
526
|
end
|
527
527
|
|
528
|
-
relation = if
|
528
|
+
relation = if model.composite_primary_key?
|
529
529
|
where(primary_key.zip(id).to_h)
|
530
530
|
else
|
531
531
|
where(primary_key => id)
|
@@ -573,7 +573,7 @@ module ActiveRecord
|
|
573
573
|
result = relation.records
|
574
574
|
|
575
575
|
if result.size == ids.size
|
576
|
-
result.in_order_of(:id, ids.map { |id|
|
576
|
+
result.in_order_of(:id, ids.map { |id| model.type_for_attribute(primary_key).cast(id) })
|
577
577
|
else
|
578
578
|
raise_record_not_found_exception!(ids, result.size, ids.size)
|
579
579
|
end
|
@@ -638,7 +638,7 @@ module ActiveRecord
|
|
638
638
|
end
|
639
639
|
|
640
640
|
def ordered_relation
|
641
|
-
if order_values.empty? && (implicit_order_column || !query_constraints_list.nil? || primary_key)
|
641
|
+
if order_values.empty? && (model.implicit_order_column || !model.query_constraints_list.nil? || primary_key)
|
642
642
|
order(_order_columns.map { |column| table[column].asc })
|
643
643
|
else
|
644
644
|
self
|
@@ -648,11 +648,11 @@ module ActiveRecord
|
|
648
648
|
def _order_columns
|
649
649
|
oc = []
|
650
650
|
|
651
|
-
oc << implicit_order_column if implicit_order_column
|
652
|
-
oc << query_constraints_list if query_constraints_list
|
651
|
+
oc << model.implicit_order_column if model.implicit_order_column
|
652
|
+
oc << model.query_constraints_list if model.query_constraints_list
|
653
653
|
|
654
|
-
if primary_key && query_constraints_list.nil?
|
655
|
-
oc << primary_key
|
654
|
+
if model.primary_key && model.query_constraints_list.nil?
|
655
|
+
oc << model.primary_key
|
656
656
|
end
|
657
657
|
|
658
658
|
oc.flatten.uniq.compact
|
@@ -24,7 +24,7 @@ module ActiveRecord
|
|
24
24
|
# the values.
|
25
25
|
def other
|
26
26
|
other = Relation.create(
|
27
|
-
relation.
|
27
|
+
relation.model,
|
28
28
|
table: relation.table,
|
29
29
|
predicate_builder: relation.predicate_builder
|
30
30
|
)
|
@@ -84,7 +84,7 @@ module ActiveRecord
|
|
84
84
|
def merge_select_values
|
85
85
|
return if other.select_values.empty?
|
86
86
|
|
87
|
-
if other.
|
87
|
+
if other.model == relation.model
|
88
88
|
relation.select_values |= other.select_values
|
89
89
|
else
|
90
90
|
relation.select_values |= other.instance_eval do
|
@@ -96,12 +96,12 @@ module ActiveRecord
|
|
96
96
|
def merge_preloads
|
97
97
|
return if other.preload_values.empty? && other.includes_values.empty?
|
98
98
|
|
99
|
-
if other.
|
99
|
+
if other.model == relation.model
|
100
100
|
relation.preload_values |= other.preload_values unless other.preload_values.empty?
|
101
101
|
relation.includes_values |= other.includes_values unless other.includes_values.empty?
|
102
102
|
else
|
103
|
-
reflection = relation.
|
104
|
-
r.class_name == other.
|
103
|
+
reflection = relation.model.reflect_on_all_associations.find do |r|
|
104
|
+
r.class_name == other.model.name
|
105
105
|
end || return
|
106
106
|
|
107
107
|
unless other.preload_values.empty?
|
@@ -117,7 +117,7 @@ module ActiveRecord
|
|
117
117
|
def merge_joins
|
118
118
|
return if other.joins_values.empty?
|
119
119
|
|
120
|
-
if other.
|
120
|
+
if other.model == relation.model
|
121
121
|
relation.joins_values |= other.joins_values
|
122
122
|
else
|
123
123
|
associations, others = other.joins_values.partition do |join|
|
@@ -136,7 +136,7 @@ module ActiveRecord
|
|
136
136
|
def merge_outer_joins
|
137
137
|
return if other.left_outer_joins_values.empty?
|
138
138
|
|
139
|
-
if other.
|
139
|
+
if other.model == relation.model
|
140
140
|
relation.left_outer_joins_values |= other.left_outer_joins_values
|
141
141
|
else
|
142
142
|
associations, others = other.left_outer_joins_values.partition do |join|
|
@@ -185,7 +185,7 @@ module ActiveRecord
|
|
185
185
|
|
186
186
|
def replace_from_clause?
|
187
187
|
relation.from_clause.empty? && !other.from_clause.empty? &&
|
188
|
-
relation.
|
188
|
+
relation.model.base_class == other.model.base_class
|
189
189
|
end
|
190
190
|
end
|
191
191
|
end
|
@@ -9,10 +9,11 @@ module ActiveRecord
|
|
9
9
|
end
|
10
10
|
|
11
11
|
if value.select_values.empty?
|
12
|
-
|
13
|
-
|
12
|
+
model = value.model
|
13
|
+
if model.composite_primary_key?
|
14
|
+
raise ArgumentError, "Cannot map composite primary key #{model.primary_key} to #{attribute.name}"
|
14
15
|
else
|
15
|
-
value = value.select(value.table[
|
16
|
+
value = value.select(value.table[model.primary_key])
|
16
17
|
end
|
17
18
|
end
|
18
19
|
|
@@ -72,11 +72,24 @@ module ActiveRecord
|
|
72
72
|
table.associated_table(table_name, &block).arel_table[column_name]
|
73
73
|
end
|
74
74
|
|
75
|
+
def with(table)
|
76
|
+
other = dup
|
77
|
+
other.table = table
|
78
|
+
other
|
79
|
+
end
|
80
|
+
|
75
81
|
protected
|
82
|
+
attr_writer :table
|
83
|
+
|
76
84
|
def expand_from_hash(attributes, &block)
|
77
85
|
return ["1=0"] if attributes.empty?
|
78
86
|
|
79
87
|
attributes.flat_map do |key, value|
|
88
|
+
if key.is_a?(Array) && key.size == 1
|
89
|
+
key = key.first
|
90
|
+
value = value.flatten
|
91
|
+
end
|
92
|
+
|
80
93
|
if key.is_a?(Array)
|
81
94
|
queries = Array(value).map do |ids_set|
|
82
95
|
raise ArgumentError, "Expected corresponding value for #{key} to be an Array" unless ids_set.is_a?(Array)
|