activerecord 7.0.8.7 → 7.1.0.beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1339 -1572
- data/MIT-LICENSE +1 -1
- data/README.rdoc +15 -16
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/association.rb +18 -3
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +14 -6
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +21 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +17 -9
- data/lib/active_record/associations/collection_proxy.rb +16 -11
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +20 -13
- data/lib/active_record/associations/has_many_through_association.rb +10 -6
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency.rb +10 -8
- data/lib/active_record/associations/preloader/association.rb +27 -6
- data/lib/active_record/associations/preloader.rb +12 -9
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +193 -97
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/dirty.rb +40 -26
- data/lib/active_record/attribute_methods/primary_key.rb +76 -24
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +18 -5
- data/lib/active_record/attribute_methods/serialization.rb +150 -31
- data/lib/active_record/attribute_methods/write.rb +3 -3
- data/lib/active_record/attribute_methods.rb +105 -21
- data/lib/active_record/attributes.rb +3 -3
- data/lib/active_record/autosave_association.rb +55 -9
- data/lib/active_record/base.rb +7 -2
- data/lib/active_record/callbacks.rb +10 -24
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -42
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +163 -88
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +63 -43
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +109 -32
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +60 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +41 -6
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +289 -122
- data/lib/active_record/connection_adapters/abstract/transaction.rb +280 -58
- data/lib/active_record/connection_adapters/abstract_adapter.rb +502 -91
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +200 -108
- data/lib/active_record/connection_adapters/column.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +22 -143
- data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -12
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +17 -12
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
- data/lib/active_record/connection_adapters/pool_config.rb +14 -5
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +76 -29
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +9 -6
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +351 -54
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +336 -168
- data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
- data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +42 -36
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +4 -3
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +1 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +26 -7
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +162 -77
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
- data/lib/active_record/connection_adapters.rb +3 -1
- data/lib/active_record/connection_handling.rb +71 -94
- data/lib/active_record/core.rb +128 -138
- data/lib/active_record/counter_cache.rb +46 -25
- data/lib/active_record/database_configurations/database_config.rb +9 -3
- data/lib/active_record/database_configurations/hash_config.rb +22 -12
- data/lib/active_record/database_configurations/url_config.rb +17 -11
- data/lib/active_record/database_configurations.rb +86 -33
- data/lib/active_record/delegated_type.rb +8 -3
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +2 -0
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
- data/lib/active_record/encryption/config.rb +25 -1
- data/lib/active_record/encryption/configurable.rb +12 -19
- data/lib/active_record/encryption/context.rb +10 -3
- data/lib/active_record/encryption/contexts.rb +5 -1
- data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
- data/lib/active_record/encryption/encryptable_record.rb +36 -18
- data/lib/active_record/encryption/encrypted_attribute_type.rb +17 -6
- data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -54
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +2 -2
- data/lib/active_record/encryption/key_generator.rb +12 -1
- data/lib/active_record/encryption/message_serializer.rb +2 -0
- data/lib/active_record/encryption/properties.rb +3 -3
- data/lib/active_record/encryption/scheme.rb +19 -22
- data/lib/active_record/encryption.rb +1 -0
- data/lib/active_record/enum.rb +113 -26
- data/lib/active_record/errors.rb +89 -15
- data/lib/active_record/explain.rb +23 -3
- data/lib/active_record/fixture_set/model_metadata.rb +14 -4
- data/lib/active_record/fixture_set/render_context.rb +2 -0
- data/lib/active_record/fixture_set/table_row.rb +29 -8
- data/lib/active_record/fixtures.rb +119 -71
- data/lib/active_record/future_result.rb +30 -5
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +30 -16
- data/lib/active_record/insert_all.rb +55 -8
- data/lib/active_record/integration.rb +8 -8
- data/lib/active_record/internal_metadata.rb +118 -30
- data/lib/active_record/locking/pessimistic.rb +5 -2
- data/lib/active_record/log_subscriber.rb +29 -12
- data/lib/active_record/marshalling.rb +56 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
- data/lib/active_record/middleware/database_selector.rb +5 -7
- data/lib/active_record/middleware/shard_selector.rb +3 -1
- data/lib/active_record/migration/command_recorder.rb +100 -4
- data/lib/active_record/migration/compatibility.rb +131 -5
- data/lib/active_record/migration/default_strategy.rb +23 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration.rb +213 -109
- data/lib/active_record/model_schema.rb +47 -27
- data/lib/active_record/nested_attributes.rb +28 -3
- data/lib/active_record/normalization.rb +158 -0
- data/lib/active_record/persistence.rb +183 -33
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +3 -21
- data/lib/active_record/query_logs.rb +77 -52
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +15 -2
- data/lib/active_record/railtie.rb +107 -45
- data/lib/active_record/railties/controller_runtime.rb +10 -5
- data/lib/active_record/railties/databases.rake +139 -145
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +32 -5
- data/lib/active_record/reflection.rb +169 -45
- data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
- data/lib/active_record/relation/batches.rb +190 -61
- data/lib/active_record/relation/calculations.rb +152 -63
- data/lib/active_record/relation/delegation.rb +22 -8
- data/lib/active_record/relation/finder_methods.rb +85 -15
- data/lib/active_record/relation/merger.rb +2 -0
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -2
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +26 -14
- data/lib/active_record/relation/query_attribute.rb +2 -1
- data/lib/active_record/relation/query_methods.rb +351 -62
- data/lib/active_record/relation/spawn_methods.rb +18 -1
- data/lib/active_record/relation.rb +76 -35
- data/lib/active_record/result.rb +19 -5
- data/lib/active_record/runtime_registry.rb +10 -1
- data/lib/active_record/sanitization.rb +51 -11
- data/lib/active_record/schema.rb +2 -3
- data/lib/active_record/schema_dumper.rb +41 -7
- data/lib/active_record/schema_migration.rb +68 -33
- data/lib/active_record/scoping/default.rb +15 -5
- data/lib/active_record/scoping/named.rb +2 -2
- data/lib/active_record/scoping.rb +2 -1
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/signed_id.rb +7 -5
- data/lib/active_record/store.rb +8 -8
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +10 -1
- data/lib/active_record/tasks/database_tasks.rb +127 -105
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
- data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -7
- data/lib/active_record/test_fixtures.rb +113 -96
- data/lib/active_record/timestamp.rb +26 -14
- data/lib/active_record/token_for.rb +113 -0
- data/lib/active_record/touch_later.rb +11 -6
- data/lib/active_record/transactions.rb +36 -10
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/numericality.rb +5 -4
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +47 -2
- data/lib/active_record/validations.rb +8 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +121 -16
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -1
- data/lib/arel/nodes/bound_sql_literal.rb +61 -0
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/homogeneous_in.rb +0 -8
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/node.rb +111 -2
- data/lib/arel/nodes/sql_literal.rb +6 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes.rb +4 -0
- data/lib/arel/predications.rb +2 -0
- data/lib/arel/table.rb +9 -5
- data/lib/arel/visitors/mysql.rb +8 -1
- data/lib/arel/visitors/to_sql.rb +81 -17
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +16 -2
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/migration.rb +3 -1
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
- metadata +52 -17
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
@@ -3,6 +3,7 @@
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Associations
|
5
5
|
# = Active Record Has Many Association
|
6
|
+
#
|
6
7
|
# This is the proxy that handles a has many association.
|
7
8
|
#
|
8
9
|
# If the association has a <tt>:through</tt> option further specialization
|
@@ -33,20 +34,24 @@ module ActiveRecord
|
|
33
34
|
|
34
35
|
unless target.empty?
|
35
36
|
association_class = target.first.class
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
37
|
+
if association_class.query_constraints_list
|
38
|
+
primary_key_column = association_class.query_constraints_list.map(&:to_sym)
|
39
|
+
ids = target.collect { |assoc| primary_key_column.map { |col| assoc.public_send(col) } }
|
40
|
+
else
|
41
|
+
primary_key_column = association_class.primary_key.to_sym
|
42
|
+
ids = target.collect { |assoc| assoc.public_send(primary_key_column) }
|
40
43
|
end
|
41
44
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
45
|
+
ids.each_slice(owner.class.destroy_association_async_batch_size || ids.size) do |ids_batch|
|
46
|
+
enqueue_destroy_association(
|
47
|
+
owner_model_name: owner.class.to_s,
|
48
|
+
owner_id: owner.id,
|
49
|
+
association_class: reflection.klass.to_s,
|
50
|
+
association_ids: ids_batch,
|
51
|
+
association_primary_key_column: primary_key_column,
|
52
|
+
ensuring_owner_was_method: options.fetch(:ensuring_owner_was, nil)
|
53
|
+
)
|
54
|
+
end
|
50
55
|
end
|
51
56
|
else
|
52
57
|
delete_all
|
@@ -124,7 +129,9 @@ module ActiveRecord
|
|
124
129
|
records.each(&:destroy!)
|
125
130
|
update_counter(-records.length) unless reflection.inverse_updates_counter_cache?
|
126
131
|
else
|
127
|
-
|
132
|
+
query_constraints = reflection.klass.composite_query_constraints_list
|
133
|
+
values = records.map { |r| query_constraints.map { |col| r._read_attribute(col) } }
|
134
|
+
scope = self.scope.where(query_constraints => values)
|
128
135
|
update_counter(-delete_count(method, scope))
|
129
136
|
end
|
130
137
|
end
|
@@ -59,9 +59,10 @@ module ActiveRecord
|
|
59
59
|
|
60
60
|
attributes = through_scope_attributes
|
61
61
|
attributes[source_reflection.name] = record
|
62
|
-
attributes[source_reflection.foreign_type] = options[:source_type] if options[:source_type]
|
63
62
|
|
64
|
-
through_association.build(attributes)
|
63
|
+
through_association.build(attributes).tap do |new_record|
|
64
|
+
new_record.send("#{source_reflection.foreign_type}=", options[:source_type]) if options[:source_type]
|
65
|
+
end
|
65
66
|
end
|
66
67
|
end
|
67
68
|
|
@@ -69,9 +70,12 @@ module ActiveRecord
|
|
69
70
|
|
70
71
|
def through_scope_attributes
|
71
72
|
scope = through_scope || self.scope
|
72
|
-
scope.where_values_hash(through_association.reflection.
|
73
|
-
|
74
|
-
|
73
|
+
attributes = scope.where_values_hash(through_association.reflection.klass.table_name)
|
74
|
+
except_keys = [
|
75
|
+
*Array(through_association.reflection.foreign_key),
|
76
|
+
through_association.reflection.klass.inheritance_column
|
77
|
+
]
|
78
|
+
attributes.except!(*except_keys)
|
75
79
|
end
|
76
80
|
|
77
81
|
def save_through_record(record)
|
@@ -109,7 +113,7 @@ module ActiveRecord
|
|
109
113
|
end
|
110
114
|
|
111
115
|
def target_reflection_has_associated_record?
|
112
|
-
!(through_reflection.belongs_to? &&
|
116
|
+
!(through_reflection.belongs_to? && Array(through_reflection.foreign_key).all? { |foreign_key_column| owner[foreign_key_column].blank? })
|
113
117
|
end
|
114
118
|
|
115
119
|
def update_through_counter?(method)
|
@@ -33,8 +33,13 @@ module ActiveRecord
|
|
33
33
|
target.destroy
|
34
34
|
throw(:abort) unless target.destroyed?
|
35
35
|
when :destroy_async
|
36
|
-
|
37
|
-
|
36
|
+
if target.class.query_constraints_list
|
37
|
+
primary_key_column = target.class.query_constraints_list.map(&:to_sym)
|
38
|
+
id = primary_key_column.map { |col| target.public_send(col) }
|
39
|
+
else
|
40
|
+
primary_key_column = target.class.primary_key.to_sym
|
41
|
+
id = target.public_send(primary_key_column)
|
42
|
+
end
|
38
43
|
|
39
44
|
enqueue_destroy_association(
|
40
45
|
owner_model_name: owner.class.to_s,
|
@@ -112,7 +117,9 @@ module ActiveRecord
|
|
112
117
|
end
|
113
118
|
|
114
119
|
def nullify_owner_attributes(record)
|
115
|
-
|
120
|
+
Array(reflection.foreign_key).each do |foreign_key_column|
|
121
|
+
record[foreign_key_column] = nil unless foreign_key_column.in?(Array(record.class.primary_key))
|
122
|
+
end
|
116
123
|
end
|
117
124
|
|
118
125
|
def transaction_if(value, &block)
|
@@ -253,22 +253,24 @@ module ActiveRecord
|
|
253
253
|
end
|
254
254
|
|
255
255
|
if node.primary_key
|
256
|
-
|
257
|
-
|
256
|
+
keys = Array(node.primary_key).map { |column| aliases.column_alias(node, column) }
|
257
|
+
ids = keys.map { |key| row[key] }
|
258
258
|
else
|
259
|
-
|
260
|
-
|
259
|
+
keys = Array(node.reflection.join_primary_key).map { |column| aliases.column_alias(node, column.to_s) }
|
260
|
+
ids = keys.map { nil } # Avoid id-based model caching.
|
261
261
|
end
|
262
262
|
|
263
|
-
if row[key].nil?
|
263
|
+
if keys.any? { |key| row[key].nil? }
|
264
264
|
nil_association = ar_parent.association(node.reflection.name)
|
265
265
|
nil_association.loaded!
|
266
266
|
next
|
267
267
|
end
|
268
268
|
|
269
|
-
|
270
|
-
model =
|
271
|
-
|
269
|
+
ids.each do |id|
|
270
|
+
unless model = seen[ar_parent][node][id]
|
271
|
+
model = construct_model(ar_parent, node, row, model_cache, id, strict_loading_value)
|
272
|
+
seen[ar_parent][node][id] = model if id
|
273
|
+
end
|
272
274
|
end
|
273
275
|
|
274
276
|
construct(model, node, row, seen, model_cache, strict_loading_value)
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :enddoc:
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
module Associations
|
5
7
|
class Preloader
|
@@ -36,7 +38,19 @@ module ActiveRecord
|
|
36
38
|
end
|
37
39
|
|
38
40
|
def load_records_for_keys(keys, &block)
|
39
|
-
|
41
|
+
if association_key_name.is_a?(Array)
|
42
|
+
query_constraints = Hash.new { |hsh, key| hsh[key] = Set.new }
|
43
|
+
|
44
|
+
keys.each_with_object(query_constraints) do |values_set, constraints|
|
45
|
+
association_key_name.zip(values_set).each do |key_name, value|
|
46
|
+
constraints[key_name] << value
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
scope.where(query_constraints)
|
51
|
+
else
|
52
|
+
scope.where(association_key_name => keys)
|
53
|
+
end.load(&block)
|
40
54
|
end
|
41
55
|
end
|
42
56
|
|
@@ -151,7 +165,7 @@ module ActiveRecord
|
|
151
165
|
|
152
166
|
def owners_by_key
|
153
167
|
@owners_by_key ||= owners.each_with_object({}) do |owner, result|
|
154
|
-
key =
|
168
|
+
key = derive_key(owner, owner_key_name)
|
155
169
|
(result[key] ||= []) << owner if key
|
156
170
|
end
|
157
171
|
end
|
@@ -169,7 +183,7 @@ module ActiveRecord
|
|
169
183
|
end
|
170
184
|
|
171
185
|
def set_inverse(record)
|
172
|
-
if owners = owners_by_key[
|
186
|
+
if owners = owners_by_key[derive_key(record, association_key_name)]
|
173
187
|
# Processing only the first owner
|
174
188
|
# because the record is modified but not an owner
|
175
189
|
association = owners.first.association(reflection.name)
|
@@ -182,11 +196,10 @@ module ActiveRecord
|
|
182
196
|
# #compare_by_identity makes such owners different hash keys
|
183
197
|
@records_by_owner = {}.compare_by_identity
|
184
198
|
raw_records ||= loader_query.records_for([self])
|
185
|
-
|
186
199
|
@preloaded_records = raw_records.select do |record|
|
187
200
|
assignments = false
|
188
201
|
|
189
|
-
owners_by_key[
|
202
|
+
owners_by_key[derive_key(record, association_key_name)]&.each do |owner|
|
190
203
|
entries = (@records_by_owner[owner] ||= [])
|
191
204
|
|
192
205
|
if reflection.collection? || entries.empty?
|
@@ -206,7 +219,7 @@ module ActiveRecord
|
|
206
219
|
return if reflection.collection?
|
207
220
|
|
208
221
|
unscoped_records.select { |r| r[association_key_name].present? }.each do |record|
|
209
|
-
owners = owners_by_key[
|
222
|
+
owners = owners_by_key[derive_key(record, association_key_name)]
|
210
223
|
owners&.each_with_index do |owner, i|
|
211
224
|
association = owner.association(reflection.name)
|
212
225
|
association.target = record
|
@@ -246,6 +259,14 @@ module ActiveRecord
|
|
246
259
|
@key_conversion_required
|
247
260
|
end
|
248
261
|
|
262
|
+
def derive_key(owner, key)
|
263
|
+
if key.is_a?(Array)
|
264
|
+
key.map { |k| convert_key(owner._read_attribute(k)) }
|
265
|
+
else
|
266
|
+
convert_key(owner._read_attribute(key))
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
249
270
|
def convert_key(key)
|
250
271
|
if key_conversion_required?
|
251
272
|
key.to_s
|
@@ -4,6 +4,8 @@ require "active_support/core_ext/enumerable"
|
|
4
4
|
|
5
5
|
module ActiveRecord
|
6
6
|
module Associations
|
7
|
+
# = Active Record \Preloader
|
8
|
+
#
|
7
9
|
# Implements the details of eager loading of Active Record associations.
|
8
10
|
#
|
9
11
|
# Suppose that you have the following two Active Record models:
|
@@ -22,8 +24,8 @@ module ActiveRecord
|
|
22
24
|
#
|
23
25
|
# Author.includes(:books).where(name: ['bell hooks', 'Homer']).to_a
|
24
26
|
#
|
25
|
-
#
|
26
|
-
#
|
27
|
+
# # SELECT `authors`.* FROM `authors` WHERE `name` IN ('bell hooks', 'Homer')
|
28
|
+
# # SELECT `books`.* FROM `books` WHERE `author_id` IN (2, 5)
|
27
29
|
#
|
28
30
|
# Active Record saves the ids of the records from the first query to use in
|
29
31
|
# the second. Depending on the number of associations involved there can be
|
@@ -33,11 +35,11 @@ module ActiveRecord
|
|
33
35
|
# Record will fall back to a slightly more resource-intensive single query:
|
34
36
|
#
|
35
37
|
# Author.includes(:books).where(books: {title: 'Illiad'}).to_a
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
38
|
+
# # SELECT `authors`.`id` AS t0_r0, `authors`.`name` AS t0_r1, `authors`.`age` AS t0_r2,
|
39
|
+
# # `books`.`id` AS t1_r0, `books`.`title` AS t1_r1, `books`.`sales` AS t1_r2
|
40
|
+
# # FROM `authors`
|
41
|
+
# # LEFT OUTER JOIN `books` ON `authors`.`id` = `books`.`author_id`
|
42
|
+
# # WHERE `books`.`title` = 'Illiad'
|
41
43
|
#
|
42
44
|
# This could result in many rows that contain redundant data and it performs poorly at scale
|
43
45
|
# and is therefore only used when necessary.
|
@@ -80,8 +82,9 @@ module ActiveRecord
|
|
80
82
|
# example, specifying <tt>{ author: :avatar }</tt> will preload a
|
81
83
|
# book's author, as well as that author's avatar.
|
82
84
|
#
|
83
|
-
# +:associations+ has the same format as the
|
84
|
-
#
|
85
|
+
# +:associations+ has the same format as the arguments to
|
86
|
+
# ActiveRecord::QueryMethods#includes. So +associations+ could look like
|
87
|
+
# this:
|
85
88
|
#
|
86
89
|
# :books
|
87
90
|
# [ :books, :author ]
|
@@ -7,6 +7,10 @@ module ActiveRecord
|
|
7
7
|
delegate :source_reflection, to: :reflection
|
8
8
|
|
9
9
|
private
|
10
|
+
def transaction(&block)
|
11
|
+
through_reflection.klass.transaction(&block)
|
12
|
+
end
|
13
|
+
|
10
14
|
def through_reflection
|
11
15
|
@through_reflection ||= begin
|
12
16
|
refl = reflection.through_reflection
|
@@ -55,12 +59,11 @@ module ActiveRecord
|
|
55
59
|
|
56
60
|
association_primary_key = source_reflection.association_primary_key(reflection.klass)
|
57
61
|
|
58
|
-
if association_primary_key == reflection.klass.
|
62
|
+
if Array(association_primary_key) == reflection.klass.composite_query_constraints_list && !options[:source_type]
|
59
63
|
join_attributes = { source_reflection.name => records }
|
60
64
|
else
|
61
|
-
|
62
|
-
|
63
|
-
}
|
65
|
+
assoc_pk_values = records.map { |record| record._read_attribute(association_primary_key) }
|
66
|
+
join_attributes = { source_reflection.foreign_key => assoc_pk_values }
|
64
67
|
end
|
65
68
|
|
66
69
|
if options[:source_type]
|
@@ -78,12 +81,16 @@ module ActiveRecord
|
|
78
81
|
# to try to properly support stale-checking for nested associations.
|
79
82
|
def stale_state
|
80
83
|
if through_reflection.belongs_to?
|
81
|
-
|
84
|
+
Array(through_reflection.foreign_key).filter_map do |foreign_key_column|
|
85
|
+
owner[foreign_key_column] && owner[foreign_key_column].to_s
|
86
|
+
end.presence
|
82
87
|
end
|
83
88
|
end
|
84
89
|
|
85
90
|
def foreign_key_present?
|
86
|
-
through_reflection.belongs_to? &&
|
91
|
+
through_reflection.belongs_to? && Array(through_reflection.foreign_key).all? do |foreign_key_column|
|
92
|
+
!owner[foreign_key_column].nil?
|
93
|
+
end
|
87
94
|
end
|
88
95
|
|
89
96
|
def ensure_mutable
|
@@ -107,11 +114,15 @@ module ActiveRecord
|
|
107
114
|
end
|
108
115
|
|
109
116
|
def build_record(attributes)
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
117
|
+
if source_reflection.collection?
|
118
|
+
inverse = source_reflection.inverse_of
|
119
|
+
target = through_association.target
|
120
|
+
|
121
|
+
if inverse && target && !target.is_a?(Array)
|
122
|
+
Array(target.id).zip(Array(inverse.foreign_key)).map do |primary_key_value, foreign_key_column|
|
123
|
+
attributes[foreign_key_column] = primary_key_value
|
124
|
+
end
|
125
|
+
end
|
115
126
|
end
|
116
127
|
|
117
128
|
super
|