activerecord 6.0.1 → 6.1.7
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1314 -633
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -4
- data/lib/active_record/aggregations.rb +5 -6
- data/lib/active_record/association_relation.rb +26 -15
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +55 -37
- data/lib/active_record/associations/association_scope.rb +19 -15
- data/lib/active_record/associations/belongs_to_association.rb +23 -10
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -3
- data/lib/active_record/associations/builder/association.rb +32 -5
- data/lib/active_record/associations/builder/belongs_to.rb +10 -7
- data/lib/active_record/associations/builder/collection_association.rb +5 -4
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -3
- data/lib/active_record/associations/builder/has_many.rb +6 -2
- data/lib/active_record/associations/builder/has_one.rb +11 -14
- data/lib/active_record/associations/builder/singular_association.rb +1 -1
- data/lib/active_record/associations/collection_association.rb +38 -13
- data/lib/active_record/associations/collection_proxy.rb +14 -7
- data/lib/active_record/associations/foreign_association.rb +13 -0
- data/lib/active_record/associations/has_many_association.rb +24 -3
- data/lib/active_record/associations/has_many_through_association.rb +10 -4
- data/lib/active_record/associations/has_one_association.rb +15 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +39 -16
- data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
- data/lib/active_record/associations/join_dependency.rb +73 -42
- data/lib/active_record/associations/preloader/association.rb +49 -25
- data/lib/active_record/associations/preloader/through_association.rb +2 -2
- data/lib/active_record/associations/preloader.rb +12 -7
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +119 -12
- data/lib/active_record/attribute_assignment.rb +10 -9
- data/lib/active_record/attribute_methods/before_type_cast.rb +13 -10
- data/lib/active_record/attribute_methods/dirty.rb +3 -13
- data/lib/active_record/attribute_methods/primary_key.rb +6 -4
- data/lib/active_record/attribute_methods/query.rb +3 -6
- data/lib/active_record/attribute_methods/read.rb +8 -12
- data/lib/active_record/attribute_methods/serialization.rb +11 -6
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
- data/lib/active_record/attribute_methods/write.rb +12 -21
- data/lib/active_record/attribute_methods.rb +64 -54
- data/lib/active_record/attributes.rb +33 -9
- data/lib/active_record/autosave_association.rb +56 -41
- data/lib/active_record/base.rb +2 -14
- data/lib/active_record/callbacks.rb +153 -24
- data/lib/active_record/coders/yaml_column.rb +24 -3
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +190 -136
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +83 -38
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -9
- data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +152 -116
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +145 -52
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +267 -105
- data/lib/active_record/connection_adapters/abstract/transaction.rb +94 -36
- data/lib/active_record/connection_adapters/abstract_adapter.rb +63 -77
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +136 -111
- data/lib/active_record/connection_adapters/column.rb +15 -1
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +30 -36
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
- data/lib/active_record/connection_adapters/mysql/quoting.rb +18 -3
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -7
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +5 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +20 -13
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -13
- data/lib/active_record/connection_adapters/pool_config.rb +73 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +21 -56
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -6
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +7 -3
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +72 -54
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +80 -66
- data/lib/active_record/connection_adapters/schema_cache.rb +130 -15
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +38 -12
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -5
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +57 -57
- data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
- data/lib/active_record/connection_adapters.rb +52 -0
- data/lib/active_record/connection_handling.rb +218 -87
- data/lib/active_record/core.rb +269 -68
- data/lib/active_record/counter_cache.rb +4 -1
- data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -0
- data/lib/active_record/database_configurations/database_config.rb +52 -9
- data/lib/active_record/database_configurations/hash_config.rb +54 -8
- data/lib/active_record/database_configurations/url_config.rb +15 -41
- data/lib/active_record/database_configurations.rb +125 -85
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/dynamic_matchers.rb +2 -3
- data/lib/active_record/enum.rb +80 -38
- data/lib/active_record/errors.rb +47 -12
- data/lib/active_record/explain.rb +9 -5
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +10 -17
- data/lib/active_record/fixture_set/model_metadata.rb +1 -2
- data/lib/active_record/fixture_set/render_context.rb +1 -1
- data/lib/active_record/fixture_set/table_row.rb +2 -3
- data/lib/active_record/fixture_set/table_rows.rb +0 -1
- data/lib/active_record/fixtures.rb +58 -12
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +40 -21
- data/lib/active_record/insert_all.rb +42 -9
- data/lib/active_record/integration.rb +3 -5
- data/lib/active_record/internal_metadata.rb +18 -7
- data/lib/active_record/legacy_yaml_adapter.rb +7 -3
- data/lib/active_record/locking/optimistic.rb +33 -18
- data/lib/active_record/locking/pessimistic.rb +6 -2
- data/lib/active_record/log_subscriber.rb +28 -9
- data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +6 -2
- data/lib/active_record/middleware/database_selector.rb +4 -2
- data/lib/active_record/migration/command_recorder.rb +53 -45
- data/lib/active_record/migration/compatibility.rb +75 -21
- data/lib/active_record/migration/join_table.rb +0 -1
- data/lib/active_record/migration.rb +115 -85
- data/lib/active_record/model_schema.rb +117 -15
- data/lib/active_record/nested_attributes.rb +2 -5
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/null_relation.rb +0 -1
- data/lib/active_record/persistence.rb +50 -46
- data/lib/active_record/query_cache.rb +15 -5
- data/lib/active_record/querying.rb +12 -7
- data/lib/active_record/railtie.rb +65 -45
- data/lib/active_record/railties/console_sandbox.rb +2 -4
- data/lib/active_record/railties/databases.rake +280 -99
- data/lib/active_record/readonly_attributes.rb +4 -0
- data/lib/active_record/reflection.rb +77 -63
- data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
- data/lib/active_record/relation/batches.rb +38 -32
- data/lib/active_record/relation/calculations.rb +106 -45
- data/lib/active_record/relation/delegation.rb +9 -7
- data/lib/active_record/relation/finder_methods.rb +45 -16
- data/lib/active_record/relation/from_clause.rb +5 -1
- data/lib/active_record/relation/merger.rb +27 -26
- data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -6
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +59 -40
- data/lib/active_record/relation/query_methods.rb +339 -188
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +8 -8
- data/lib/active_record/relation/where_clause.rb +111 -62
- data/lib/active_record/relation.rb +116 -83
- data/lib/active_record/result.rb +41 -34
- data/lib/active_record/runtime_registry.rb +2 -2
- data/lib/active_record/sanitization.rb +6 -17
- data/lib/active_record/schema_dumper.rb +34 -4
- data/lib/active_record/schema_migration.rb +2 -8
- data/lib/active_record/scoping/default.rb +1 -4
- data/lib/active_record/scoping/named.rb +7 -18
- data/lib/active_record/scoping.rb +0 -1
- data/lib/active_record/secure_token.rb +16 -8
- data/lib/active_record/serialization.rb +5 -3
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +20 -4
- data/lib/active_record/store.rb +9 -4
- data/lib/active_record/suppressor.rb +2 -2
- data/lib/active_record/table_metadata.rb +42 -36
- data/lib/active_record/tasks/database_tasks.rb +140 -113
- data/lib/active_record/tasks/mysql_database_tasks.rb +34 -36
- data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -27
- data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -10
- data/lib/active_record/test_databases.rb +5 -4
- data/lib/active_record/test_fixtures.rb +87 -20
- data/lib/active_record/timestamp.rb +4 -7
- data/lib/active_record/touch_later.rb +20 -21
- data/lib/active_record/transactions.rb +25 -72
- data/lib/active_record/type/adapter_specific_registry.rb +2 -5
- data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
- data/lib/active_record/type/serialized.rb +6 -3
- data/lib/active_record/type/time.rb +10 -0
- data/lib/active_record/type/type_map.rb +0 -1
- data/lib/active_record/type/unsigned_integer.rb +0 -1
- data/lib/active_record/type.rb +8 -2
- data/lib/active_record/type_caster/connection.rb +0 -1
- data/lib/active_record/type_caster/map.rb +8 -5
- data/lib/active_record/validations/associated.rb +1 -2
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +24 -4
- data/lib/active_record/validations.rb +3 -3
- data/lib/active_record.rb +7 -13
- data/lib/arel/attributes/attribute.rb +4 -0
- data/lib/arel/collectors/bind.rb +5 -0
- data/lib/arel/collectors/composite.rb +8 -0
- data/lib/arel/collectors/sql_string.rb +7 -0
- data/lib/arel/collectors/substitute_binds.rb +7 -0
- data/lib/arel/nodes/binary.rb +82 -8
- data/lib/arel/nodes/bind_param.rb +8 -0
- data/lib/arel/nodes/casted.rb +21 -9
- data/lib/arel/nodes/equality.rb +6 -9
- data/lib/arel/nodes/grouping.rb +3 -0
- data/lib/arel/nodes/homogeneous_in.rb +76 -0
- data/lib/arel/nodes/in.rb +8 -1
- data/lib/arel/nodes/infix_operation.rb +13 -1
- data/lib/arel/nodes/join_source.rb +1 -1
- data/lib/arel/nodes/node.rb +7 -6
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/sql_literal.rb +3 -0
- data/lib/arel/nodes/table_alias.rb +7 -3
- data/lib/arel/nodes/unary.rb +0 -1
- data/lib/arel/nodes.rb +3 -1
- data/lib/arel/predications.rb +17 -24
- data/lib/arel/select_manager.rb +1 -2
- data/lib/arel/table.rb +13 -5
- data/lib/arel/visitors/dot.rb +14 -3
- data/lib/arel/visitors/mysql.rb +11 -1
- data/lib/arel/visitors/postgresql.rb +15 -5
- data/lib/arel/visitors/sqlite.rb +0 -1
- data/lib/arel/visitors/to_sql.rb +89 -79
- data/lib/arel/visitors/visitor.rb +0 -1
- data/lib/arel/visitors.rb +0 -7
- data/lib/arel.rb +5 -9
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
- data/lib/rails/generators/active_record/migration.rb +6 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +38 -2
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- metadata +30 -29
- data/lib/active_record/attribute_decorators.rb +0 -90
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
- data/lib/active_record/relation/where_clause_factory.rb +0 -33
- data/lib/arel/attributes.rb +0 -22
- data/lib/arel/visitors/depth_first.rb +0 -204
- data/lib/arel/visitors/ibm_db.rb +0 -34
- data/lib/arel/visitors/informix.rb +0 -62
- data/lib/arel/visitors/mssql.rb +0 -157
- data/lib/arel/visitors/oracle.rb +0 -159
- data/lib/arel/visitors/oracle12.rb +0 -66
- data/lib/arel/visitors/where_sql.rb +0 -23
@@ -34,7 +34,7 @@ module ActiveRecord
|
|
34
34
|
Table = Struct.new(:node, :columns) do # :nodoc:
|
35
35
|
def column_aliases
|
36
36
|
t = node.table
|
37
|
-
columns.map { |column| t[column.name].as
|
37
|
+
columns.map { |column| t[column.name].as(column.alias) }
|
38
38
|
end
|
39
39
|
end
|
40
40
|
Column = Struct.new(:name, :alias)
|
@@ -78,14 +78,18 @@ module ActiveRecord
|
|
78
78
|
join_root.drop(1).map!(&:reflection)
|
79
79
|
end
|
80
80
|
|
81
|
-
def join_constraints(joins_to_add, alias_tracker)
|
81
|
+
def join_constraints(joins_to_add, alias_tracker, references)
|
82
82
|
@alias_tracker = alias_tracker
|
83
|
+
@joined_tables = {}
|
84
|
+
@references = {}
|
85
|
+
|
86
|
+
references.each do |table_name|
|
87
|
+
@references[table_name.to_sym] = table_name if table_name.is_a?(Arel::Nodes::SqlLiteral)
|
88
|
+
end unless references.empty?
|
83
89
|
|
84
|
-
construct_tables!(join_root)
|
85
90
|
joins = make_join_constraints(join_root, join_type)
|
86
91
|
|
87
92
|
joins.concat joins_to_add.flat_map { |oj|
|
88
|
-
construct_tables!(oj.join_root)
|
89
93
|
if join_root.match? oj.join_root
|
90
94
|
walk(join_root, oj.join_root, oj.join_type)
|
91
95
|
else
|
@@ -94,18 +98,35 @@ module ActiveRecord
|
|
94
98
|
}
|
95
99
|
end
|
96
100
|
|
97
|
-
def instantiate(result_set, &block)
|
101
|
+
def instantiate(result_set, strict_loading_value, &block)
|
98
102
|
primary_key = aliases.column_alias(join_root, join_root.primary_key)
|
99
103
|
|
100
|
-
seen = Hash.new { |i,
|
101
|
-
i[
|
104
|
+
seen = Hash.new { |i, parent|
|
105
|
+
i[parent] = Hash.new { |j, child_class|
|
102
106
|
j[child_class] = {}
|
103
107
|
}
|
104
|
-
}
|
108
|
+
}.compare_by_identity
|
105
109
|
|
106
110
|
model_cache = Hash.new { |h, klass| h[klass] = {} }
|
107
111
|
parents = model_cache[join_root]
|
108
|
-
|
112
|
+
|
113
|
+
column_aliases = aliases.column_aliases(join_root)
|
114
|
+
column_names = []
|
115
|
+
|
116
|
+
result_set.columns.each do |name|
|
117
|
+
column_names << name unless /\At\d+_r\d+\z/.match?(name)
|
118
|
+
end
|
119
|
+
|
120
|
+
if column_names.empty?
|
121
|
+
column_types = {}
|
122
|
+
else
|
123
|
+
column_types = result_set.column_types
|
124
|
+
unless column_types.empty?
|
125
|
+
attribute_types = join_root.attribute_types
|
126
|
+
column_types = column_types.slice(*column_names).delete_if { |k, _| attribute_types.key?(k) }
|
127
|
+
end
|
128
|
+
column_aliases += column_names.map! { |name| Aliases::Column.new(name, name) }
|
129
|
+
end
|
109
130
|
|
110
131
|
message_bus = ActiveSupport::Notifications.instrumenter
|
111
132
|
|
@@ -117,8 +138,8 @@ module ActiveRecord
|
|
117
138
|
message_bus.instrument("instantiation.active_record", payload) do
|
118
139
|
result_set.each { |row_hash|
|
119
140
|
parent_key = primary_key ? row_hash[primary_key] : row_hash
|
120
|
-
parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases, &block)
|
121
|
-
construct(parent, join_root, row_hash, seen, model_cache)
|
141
|
+
parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases, column_types, &block)
|
142
|
+
construct(parent, join_root, row_hash, seen, model_cache, strict_loading_value)
|
122
143
|
}
|
123
144
|
end
|
124
145
|
|
@@ -126,30 +147,36 @@ module ActiveRecord
|
|
126
147
|
end
|
127
148
|
|
128
149
|
def apply_column_aliases(relation)
|
150
|
+
@join_root_alias = relation.select_values.empty?
|
129
151
|
relation._select!(-> { aliases.columns })
|
130
152
|
end
|
131
153
|
|
154
|
+
def each(&block)
|
155
|
+
join_root.each(&block)
|
156
|
+
end
|
157
|
+
|
132
158
|
protected
|
133
159
|
attr_reader :join_root, :join_type
|
134
160
|
|
135
161
|
private
|
136
|
-
attr_reader :alias_tracker
|
162
|
+
attr_reader :alias_tracker, :join_root_alias
|
137
163
|
|
138
164
|
def aliases
|
139
165
|
@aliases ||= Aliases.new join_root.each_with_index.map { |join_part, i|
|
140
|
-
|
166
|
+
column_names = if join_part == join_root && !join_root_alias
|
167
|
+
primary_key = join_root.primary_key
|
168
|
+
primary_key ? [primary_key] : []
|
169
|
+
else
|
170
|
+
join_part.column_names
|
171
|
+
end
|
172
|
+
|
173
|
+
columns = column_names.each_with_index.map { |column_name, j|
|
141
174
|
Aliases::Column.new column_name, "t#{i}_r#{j}"
|
142
175
|
}
|
143
176
|
Aliases::Table.new(join_part, columns)
|
144
177
|
}
|
145
178
|
end
|
146
179
|
|
147
|
-
def construct_tables!(join_root)
|
148
|
-
join_root.each_children do |parent, child|
|
149
|
-
child.tables = table_aliases_for(parent, child)
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
180
|
def make_join_constraints(join_root, join_type)
|
154
181
|
join_root.children.flat_map do |child|
|
155
182
|
make_constraints(join_root, child, join_type)
|
@@ -159,23 +186,25 @@ module ActiveRecord
|
|
159
186
|
def make_constraints(parent, child, join_type)
|
160
187
|
foreign_table = parent.table
|
161
188
|
foreign_klass = parent.base_klass
|
162
|
-
|
163
|
-
|
164
|
-
|
189
|
+
child.join_constraints(foreign_table, foreign_klass, join_type, alias_tracker) do |reflection|
|
190
|
+
table, terminated = @joined_tables[reflection]
|
191
|
+
root = reflection == child.reflection
|
165
192
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
193
|
+
if table && (!root || !terminated)
|
194
|
+
@joined_tables[reflection] = [table, root] if root
|
195
|
+
next table, true
|
196
|
+
end
|
197
|
+
|
198
|
+
table_name = @references[reflection.name.to_sym]&.to_s
|
199
|
+
|
200
|
+
table = alias_tracker.aliased_table_for(reflection.klass.arel_table, table_name) do
|
201
|
+
name = reflection.alias_candidate(parent.table_name)
|
202
|
+
root ? name : "#{name}_join"
|
203
|
+
end
|
175
204
|
|
176
|
-
|
177
|
-
|
178
|
-
|
205
|
+
@joined_tables[reflection] ||= [table, root] if join_type == Arel::Nodes::OuterJoin
|
206
|
+
table
|
207
|
+
end.concat child.children.flat_map { |c| make_constraints(child, c, join_type) }
|
179
208
|
end
|
180
209
|
|
181
210
|
def walk(left, right, join_type)
|
@@ -206,7 +235,7 @@ module ActiveRecord
|
|
206
235
|
end
|
207
236
|
end
|
208
237
|
|
209
|
-
def construct(ar_parent, parent, row, seen, model_cache)
|
238
|
+
def construct(ar_parent, parent, row, seen, model_cache, strict_loading_value)
|
210
239
|
return if ar_parent.nil?
|
211
240
|
|
212
241
|
parent.children.each do |node|
|
@@ -215,7 +244,7 @@ module ActiveRecord
|
|
215
244
|
other.loaded!
|
216
245
|
elsif ar_parent.association_cached?(node.reflection.name)
|
217
246
|
model = ar_parent.association(node.reflection.name).target
|
218
|
-
construct(model, node, row, seen, model_cache)
|
247
|
+
construct(model, node, row, seen, model_cache, strict_loading_value)
|
219
248
|
next
|
220
249
|
end
|
221
250
|
|
@@ -227,24 +256,25 @@ module ActiveRecord
|
|
227
256
|
next
|
228
257
|
end
|
229
258
|
|
230
|
-
model = seen[ar_parent
|
259
|
+
model = seen[ar_parent][node][id]
|
231
260
|
|
232
261
|
if model
|
233
|
-
construct(model, node, row, seen, model_cache)
|
262
|
+
construct(model, node, row, seen, model_cache, strict_loading_value)
|
234
263
|
else
|
235
|
-
model = construct_model(ar_parent, node, row, model_cache, id)
|
264
|
+
model = construct_model(ar_parent, node, row, model_cache, id, strict_loading_value)
|
236
265
|
|
237
|
-
seen[ar_parent
|
238
|
-
construct(model, node, row, seen, model_cache)
|
266
|
+
seen[ar_parent][node][id] = model
|
267
|
+
construct(model, node, row, seen, model_cache, strict_loading_value)
|
239
268
|
end
|
240
269
|
end
|
241
270
|
end
|
242
271
|
|
243
|
-
def construct_model(record, node, row, model_cache, id)
|
272
|
+
def construct_model(record, node, row, model_cache, id, strict_loading_value)
|
244
273
|
other = record.association(node.reflection.name)
|
245
274
|
|
246
275
|
model = model_cache[node][id] ||=
|
247
276
|
node.instantiate(row, aliases.column_aliases(node)) do |m|
|
277
|
+
m.strict_loading! if strict_loading_value
|
248
278
|
other.set_inverse_instance(m)
|
249
279
|
end
|
250
280
|
|
@@ -255,6 +285,7 @@ module ActiveRecord
|
|
255
285
|
end
|
256
286
|
|
257
287
|
model.readonly! if node.readonly?
|
288
|
+
model.strict_loading! if node.strict_loading?
|
258
289
|
model
|
259
290
|
end
|
260
291
|
end
|
@@ -4,46 +4,62 @@ module ActiveRecord
|
|
4
4
|
module Associations
|
5
5
|
class Preloader
|
6
6
|
class Association #:nodoc:
|
7
|
-
def initialize(klass, owners, reflection, preload_scope)
|
7
|
+
def initialize(klass, owners, reflection, preload_scope, associate_by_default = true)
|
8
8
|
@klass = klass
|
9
|
-
@owners = owners
|
9
|
+
@owners = owners.uniq(&:__id__)
|
10
10
|
@reflection = reflection
|
11
11
|
@preload_scope = preload_scope
|
12
|
+
@associate = associate_by_default || !preload_scope || preload_scope.empty_scope?
|
12
13
|
@model = owners.first && owners.first.class
|
13
14
|
end
|
14
15
|
|
15
16
|
def run
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
# the association can not be marked as loaded
|
23
|
-
# Loading into a Hash instead
|
24
|
-
records_by_owner
|
25
|
-
end
|
17
|
+
records = records_by_owner
|
18
|
+
|
19
|
+
owners.each do |owner|
|
20
|
+
associate_records_to_owner(owner, records[owner] || [])
|
21
|
+
end if @associate
|
22
|
+
|
26
23
|
self
|
27
24
|
end
|
28
25
|
|
29
26
|
def records_by_owner
|
30
|
-
|
31
|
-
|
32
|
-
@records_by_owner
|
33
|
-
owners_by_key[convert_key(record[association_key_name])].each do |owner|
|
34
|
-
(result[owner] ||= []) << record
|
35
|
-
end
|
36
|
-
end
|
27
|
+
load_records unless defined?(@records_by_owner)
|
28
|
+
|
29
|
+
@records_by_owner
|
37
30
|
end
|
38
31
|
|
39
32
|
def preloaded_records
|
40
|
-
|
41
|
-
|
33
|
+
load_records unless defined?(@preloaded_records)
|
34
|
+
|
35
|
+
@preloaded_records
|
42
36
|
end
|
43
37
|
|
44
38
|
private
|
45
39
|
attr_reader :owners, :reflection, :preload_scope, :model, :klass
|
46
40
|
|
41
|
+
def load_records
|
42
|
+
# owners can be duplicated when a relation has a collection association join
|
43
|
+
# #compare_by_identity makes such owners different hash keys
|
44
|
+
@records_by_owner = {}.compare_by_identity
|
45
|
+
raw_records = owner_keys.empty? ? [] : records_for(owner_keys)
|
46
|
+
|
47
|
+
@preloaded_records = raw_records.select do |record|
|
48
|
+
assignments = false
|
49
|
+
|
50
|
+
owners_by_key[convert_key(record[association_key_name])].each do |owner|
|
51
|
+
entries = (@records_by_owner[owner] ||= [])
|
52
|
+
|
53
|
+
if reflection.collection? || entries.empty?
|
54
|
+
entries << record
|
55
|
+
assignments = true
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
assignments
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
47
63
|
# The name of the key on the associated records
|
48
64
|
def association_key_name
|
49
65
|
reflection.join_primary_key(klass)
|
@@ -113,7 +129,7 @@ module ActiveRecord
|
|
113
129
|
end
|
114
130
|
|
115
131
|
def reflection_scope
|
116
|
-
@reflection_scope ||= reflection.
|
132
|
+
@reflection_scope ||= reflection.join_scopes(klass.arel_table, klass.predicate_builder, klass).inject(klass.unscoped, &:merge!)
|
117
133
|
end
|
118
134
|
|
119
135
|
def build_scope
|
@@ -123,9 +139,17 @@ module ActiveRecord
|
|
123
139
|
scope.where!(reflection.type => model.polymorphic_name)
|
124
140
|
end
|
125
141
|
|
126
|
-
scope.merge!(reflection_scope)
|
127
|
-
|
128
|
-
|
142
|
+
scope.merge!(reflection_scope) unless reflection_scope.empty_scope?
|
143
|
+
|
144
|
+
if preload_scope && !preload_scope.empty_scope?
|
145
|
+
scope.merge!(preload_scope)
|
146
|
+
end
|
147
|
+
|
148
|
+
if preload_scope && preload_scope.strict_loading_value
|
149
|
+
scope.strict_loading
|
150
|
+
else
|
151
|
+
scope
|
152
|
+
end
|
129
153
|
end
|
130
154
|
end
|
131
155
|
end
|
@@ -4,7 +4,7 @@ module ActiveRecord
|
|
4
4
|
module Associations
|
5
5
|
class Preloader
|
6
6
|
class ThroughAssociation < Association # :nodoc:
|
7
|
-
PRELOADER = ActiveRecord::Associations::Preloader.new
|
7
|
+
PRELOADER = ActiveRecord::Associations::Preloader.new(associate_by_default: false)
|
8
8
|
|
9
9
|
def initialize(*)
|
10
10
|
super
|
@@ -90,7 +90,7 @@ module ActiveRecord
|
|
90
90
|
end
|
91
91
|
|
92
92
|
if values[:references] && !values[:references].empty?
|
93
|
-
scope.
|
93
|
+
scope.references_values |= values[:references]
|
94
94
|
else
|
95
95
|
scope.references!(source_reflection.table_name)
|
96
96
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/core_ext/enumerable"
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
module Associations
|
5
7
|
# Implements the details of eager loading of Active Record associations.
|
@@ -58,7 +60,7 @@ module ActiveRecord
|
|
58
60
|
# == Parameters
|
59
61
|
# +records+ is an array of ActiveRecord::Base. This array needs not be flat,
|
60
62
|
# i.e. +records+ itself may also contain arrays of records. In any case,
|
61
|
-
# +preload_associations+ will preload
|
63
|
+
# +preload_associations+ will preload all associations records by
|
62
64
|
# flattening +records+.
|
63
65
|
#
|
64
66
|
# +associations+ specifies one or more associations that you want to
|
@@ -94,8 +96,11 @@ module ActiveRecord
|
|
94
96
|
end
|
95
97
|
end
|
96
98
|
|
97
|
-
|
99
|
+
def initialize(associate_by_default: true)
|
100
|
+
@associate_by_default = associate_by_default
|
101
|
+
end
|
98
102
|
|
103
|
+
private
|
99
104
|
# Loads all the given data into +records+ for the +association+.
|
100
105
|
def preloaders_on(association, records, scope, polymorphic_parent = false)
|
101
106
|
case association
|
@@ -143,7 +148,7 @@ module ActiveRecord
|
|
143
148
|
|
144
149
|
def preloaders_for_reflection(reflection, records, scope)
|
145
150
|
records.group_by { |record| record.association(reflection.name).klass }.map do |rhs_klass, rs|
|
146
|
-
preloader_for(reflection, rs).new(rhs_klass, rs, reflection, scope).run
|
151
|
+
preloader_for(reflection, rs).new(rhs_klass, rs, reflection, scope, @associate_by_default).run
|
147
152
|
end
|
148
153
|
end
|
149
154
|
|
@@ -158,7 +163,7 @@ module ActiveRecord
|
|
158
163
|
end
|
159
164
|
|
160
165
|
class AlreadyLoaded # :nodoc:
|
161
|
-
def initialize(klass, owners, reflection, preload_scope)
|
166
|
+
def initialize(klass, owners, reflection, preload_scope, associate_by_default = true)
|
162
167
|
@owners = owners
|
163
168
|
@reflection = reflection
|
164
169
|
end
|
@@ -172,8 +177,8 @@ module ActiveRecord
|
|
172
177
|
end
|
173
178
|
|
174
179
|
def records_by_owner
|
175
|
-
@records_by_owner ||= owners.
|
176
|
-
|
180
|
+
@records_by_owner ||= owners.index_with do |owner|
|
181
|
+
Array(owner.association(reflection.name).target)
|
177
182
|
end
|
178
183
|
end
|
179
184
|
|
@@ -185,7 +190,7 @@ module ActiveRecord
|
|
185
190
|
# and attach it to a relation. The class returned implements a `run` method
|
186
191
|
# that accepts a preloader.
|
187
192
|
def preloader_for(reflection, owners)
|
188
|
-
if owners.
|
193
|
+
if owners.all? { |o| o.association(reflection.name).loaded? }
|
189
194
|
return AlreadyLoaded
|
190
195
|
end
|
191
196
|
reflection.check_preloadable!
|
@@ -32,7 +32,7 @@ module ActiveRecord
|
|
32
32
|
reflection.chain.drop(1).each do |reflection|
|
33
33
|
relation = reflection.klass.scope_for_association
|
34
34
|
scope.merge!(
|
35
|
-
relation.except(:select, :create_with, :includes, :preload, :joins, :
|
35
|
+
relation.except(:select, :create_with, :includes, :preload, :eager_load, :joins, :left_outer_joins)
|
36
36
|
)
|
37
37
|
end
|
38
38
|
scope
|