activerecord 5.1.0 → 5.2.0.rc1
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 +5 -5
- data/CHANGELOG.md +410 -530
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -4
- data/examples/performance.rb +2 -0
- data/examples/simple.rb +2 -0
- data/lib/active_record/aggregations.rb +6 -5
- data/lib/active_record/association_relation.rb +4 -2
- data/lib/active_record/associations/alias_tracker.rb +23 -32
- data/lib/active_record/associations/association.rb +20 -21
- data/lib/active_record/associations/association_scope.rb +49 -49
- data/lib/active_record/associations/belongs_to_association.rb +12 -10
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +4 -7
- data/lib/active_record/associations/builder/association.rb +4 -7
- data/lib/active_record/associations/builder/belongs_to.rb +10 -6
- data/lib/active_record/associations/builder/collection_association.rb +1 -1
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -1
- data/lib/active_record/associations/builder/has_many.rb +2 -0
- data/lib/active_record/associations/builder/has_one.rb +2 -0
- data/lib/active_record/associations/builder/singular_association.rb +2 -0
- data/lib/active_record/associations/collection_association.rb +50 -41
- data/lib/active_record/associations/collection_proxy.rb +22 -39
- data/lib/active_record/associations/foreign_association.rb +2 -0
- data/lib/active_record/associations/has_many_association.rb +4 -2
- data/lib/active_record/associations/has_many_through_association.rb +12 -18
- data/lib/active_record/associations/has_one_association.rb +5 -1
- data/lib/active_record/associations/has_one_through_association.rb +8 -7
- data/lib/active_record/associations/join_dependency/join_association.rb +17 -64
- data/lib/active_record/associations/join_dependency/join_base.rb +9 -8
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -9
- data/lib/active_record/associations/join_dependency.rb +27 -44
- data/lib/active_record/associations/preloader/association.rb +53 -92
- data/lib/active_record/associations/preloader/through_association.rb +72 -73
- data/lib/active_record/associations/preloader.rb +17 -37
- data/lib/active_record/associations/singular_association.rb +14 -10
- data/lib/active_record/associations/through_association.rb +26 -11
- data/lib/active_record/associations.rb +68 -76
- data/lib/active_record/attribute_assignment.rb +2 -0
- data/lib/active_record/attribute_decorators.rb +3 -2
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -0
- data/lib/active_record/attribute_methods/dirty.rb +24 -214
- data/lib/active_record/attribute_methods/primary_key.rb +10 -13
- data/lib/active_record/attribute_methods/query.rb +2 -0
- data/lib/active_record/attribute_methods/read.rb +8 -2
- data/lib/active_record/attribute_methods/serialization.rb +23 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +6 -8
- data/lib/active_record/attribute_methods/write.rb +22 -19
- data/lib/active_record/attribute_methods.rb +48 -12
- data/lib/active_record/attributes.rb +7 -6
- data/lib/active_record/autosave_association.rb +8 -11
- data/lib/active_record/base.rb +2 -0
- data/lib/active_record/callbacks.rb +8 -6
- data/lib/active_record/coders/json.rb +2 -0
- data/lib/active_record/coders/yaml_column.rb +2 -0
- data/lib/active_record/collection_cache_key.rb +14 -10
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +110 -35
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +175 -33
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +8 -2
- data/lib/active_record/connection_adapters/abstract/quoting.rb +13 -24
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -6
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +58 -3
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +165 -85
- data/lib/active_record/connection_adapters/abstract/transaction.rb +45 -9
- data/lib/active_record/connection_adapters/abstract_adapter.rb +83 -97
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +118 -180
- data/lib/active_record/connection_adapters/column.rb +4 -2
- data/lib/active_record/connection_adapters/connection_specification.rb +17 -3
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +11 -17
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +9 -10
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +5 -3
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +7 -10
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +30 -23
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +106 -1
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +2 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +6 -32
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +4 -2
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +4 -2
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +22 -1
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +14 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +24 -11
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +20 -13
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +269 -126
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +64 -85
- data/lib/active_record/connection_adapters/schema_cache.rb +4 -2
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +2 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +2 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +18 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +6 -15
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +3 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +71 -1
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +92 -95
- data/lib/active_record/connection_adapters/statement_pool.rb +2 -0
- data/lib/active_record/connection_handling.rb +4 -2
- data/lib/active_record/core.rb +39 -60
- data/lib/active_record/counter_cache.rb +3 -2
- data/lib/active_record/define_callbacks.rb +5 -3
- data/lib/active_record/dynamic_matchers.rb +9 -9
- data/lib/active_record/enum.rb +17 -13
- data/lib/active_record/errors.rb +42 -3
- data/lib/active_record/explain.rb +3 -1
- data/lib/active_record/explain_registry.rb +2 -0
- data/lib/active_record/explain_subscriber.rb +2 -0
- data/lib/active_record/fixture_set/file.rb +2 -0
- data/lib/active_record/fixtures.rb +67 -60
- data/lib/active_record/gem_version.rb +4 -2
- data/lib/active_record/inheritance.rb +9 -9
- data/lib/active_record/integration.rb +58 -19
- data/lib/active_record/internal_metadata.rb +2 -0
- data/lib/active_record/legacy_yaml_adapter.rb +3 -1
- data/lib/active_record/locking/optimistic.rb +8 -6
- data/lib/active_record/locking/pessimistic.rb +9 -6
- data/lib/active_record/log_subscriber.rb +46 -4
- data/lib/active_record/migration/command_recorder.rb +11 -9
- data/lib/active_record/migration/compatibility.rb +74 -22
- data/lib/active_record/migration/join_table.rb +2 -0
- data/lib/active_record/migration.rb +181 -137
- data/lib/active_record/model_schema.rb +73 -58
- data/lib/active_record/nested_attributes.rb +18 -6
- data/lib/active_record/no_touching.rb +3 -1
- data/lib/active_record/null_relation.rb +2 -0
- data/lib/active_record/persistence.rb +153 -18
- data/lib/active_record/query_cache.rb +17 -12
- data/lib/active_record/querying.rb +4 -2
- data/lib/active_record/railtie.rb +61 -3
- data/lib/active_record/railties/console_sandbox.rb +2 -0
- data/lib/active_record/railties/controller_runtime.rb +2 -0
- data/lib/active_record/railties/databases.rake +47 -37
- data/lib/active_record/readonly_attributes.rb +3 -2
- data/lib/active_record/reflection.rb +131 -204
- data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
- data/lib/active_record/relation/batches.rb +32 -17
- data/lib/active_record/relation/calculations.rb +58 -20
- data/lib/active_record/relation/delegation.rb +10 -29
- data/lib/active_record/relation/finder_methods.rb +74 -85
- data/lib/active_record/relation/from_clause.rb +2 -8
- data/lib/active_record/relation/merger.rb +51 -20
- data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -7
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +12 -1
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +54 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -6
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
- data/lib/active_record/relation/predicate_builder.rb +53 -78
- data/lib/active_record/relation/query_attribute.rb +9 -2
- data/lib/active_record/relation/query_methods.rb +101 -95
- data/lib/active_record/relation/record_fetch_warning.rb +2 -0
- data/lib/active_record/relation/spawn_methods.rb +3 -1
- data/lib/active_record/relation/where_clause.rb +65 -67
- data/lib/active_record/relation/where_clause_factory.rb +5 -48
- data/lib/active_record/relation.rb +99 -202
- data/lib/active_record/result.rb +2 -0
- data/lib/active_record/runtime_registry.rb +2 -0
- data/lib/active_record/sanitization.rb +129 -121
- data/lib/active_record/schema.rb +4 -2
- data/lib/active_record/schema_dumper.rb +36 -26
- data/lib/active_record/schema_migration.rb +2 -0
- data/lib/active_record/scoping/default.rb +10 -7
- data/lib/active_record/scoping/named.rb +38 -12
- data/lib/active_record/scoping.rb +12 -10
- data/lib/active_record/secure_token.rb +2 -0
- data/lib/active_record/serialization.rb +2 -0
- data/lib/active_record/statement_cache.rb +22 -12
- data/lib/active_record/store.rb +3 -1
- data/lib/active_record/suppressor.rb +2 -0
- data/lib/active_record/table_metadata.rb +12 -3
- data/lib/active_record/tasks/database_tasks.rb +37 -25
- data/lib/active_record/tasks/mysql_database_tasks.rb +11 -50
- data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -3
- data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -3
- data/lib/active_record/timestamp.rb +5 -5
- data/lib/active_record/touch_later.rb +2 -0
- data/lib/active_record/transactions.rb +9 -7
- data/lib/active_record/translation.rb +2 -0
- data/lib/active_record/type/adapter_specific_registry.rb +2 -0
- data/lib/active_record/type/date.rb +2 -0
- data/lib/active_record/type/date_time.rb +2 -0
- data/lib/active_record/type/decimal_without_scale.rb +2 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +2 -0
- data/lib/active_record/type/internal/timezone.rb +2 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +2 -0
- data/lib/active_record/type/text.rb +2 -0
- data/lib/active_record/type/time.rb +2 -0
- data/lib/active_record/type/type_map.rb +2 -0
- data/lib/active_record/type/unsigned_integer.rb +2 -0
- data/lib/active_record/type.rb +4 -1
- data/lib/active_record/type_caster/connection.rb +2 -0
- data/lib/active_record/type_caster/map.rb +3 -1
- data/lib/active_record/type_caster.rb +2 -0
- data/lib/active_record/validations/absence.rb +2 -0
- data/lib/active_record/validations/associated.rb +2 -0
- data/lib/active_record/validations/length.rb +2 -0
- data/lib/active_record/validations/presence.rb +2 -0
- data/lib/active_record/validations/uniqueness.rb +35 -5
- data/lib/active_record/validations.rb +2 -0
- data/lib/active_record/version.rb +2 -0
- data/lib/active_record.rb +11 -4
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
- data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -1
- data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +0 -0
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +0 -0
- data/lib/rails/generators/active_record/migration.rb +2 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +2 -23
- data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +0 -0
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
- data/lib/rails/generators/active_record.rb +3 -1
- metadata +25 -37
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -15
- data/lib/active_record/associations/preloader/collection_association.rb +0 -17
- data/lib/active_record/associations/preloader/has_many.rb +0 -15
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
- data/lib/active_record/associations/preloader/has_one.rb +0 -15
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -18
- data/lib/active_record/attribute/user_provided_default.rb +0 -30
- data/lib/active_record/attribute.rb +0 -240
- data/lib/active_record/attribute_mutation_tracker.rb +0 -113
- data/lib/active_record/attribute_set/builder.rb +0 -124
- data/lib/active_record/attribute_set/yaml_encoder.rb +0 -41
- data/lib/active_record/attribute_set.rb +0 -113
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -10
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -59
- data/lib/active_record/type/internal/abstract_json.rb +0 -33
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module Associations
|
3
5
|
class JoinDependency # :nodoc:
|
@@ -33,20 +35,14 @@ module ActiveRecord
|
|
33
35
|
end
|
34
36
|
|
35
37
|
Table = Struct.new(:node, :columns) do # :nodoc:
|
36
|
-
def table
|
37
|
-
Arel::Nodes::TableAlias.new node.table, node.aliased_table_name
|
38
|
-
end
|
39
|
-
|
40
38
|
def column_aliases
|
41
|
-
t = table
|
39
|
+
t = node.table
|
42
40
|
columns.map { |column| t[column.name].as Arel.sql column.alias }
|
43
41
|
end
|
44
42
|
end
|
45
43
|
Column = Struct.new(:name, :alias)
|
46
44
|
end
|
47
45
|
|
48
|
-
attr_reader :alias_tracker, :base_klass, :join_root
|
49
|
-
|
50
46
|
def self.make_tree(associations)
|
51
47
|
hash = {}
|
52
48
|
walk_tree associations, hash
|
@@ -92,11 +88,11 @@ module ActiveRecord
|
|
92
88
|
# associations # => [:appointments]
|
93
89
|
# joins # => []
|
94
90
|
#
|
95
|
-
def initialize(base, associations,
|
96
|
-
@alias_tracker =
|
91
|
+
def initialize(base, table, associations, alias_tracker, eager_loading: true)
|
92
|
+
@alias_tracker = alias_tracker
|
97
93
|
@eager_loading = eager_loading
|
98
94
|
tree = self.class.make_tree associations
|
99
|
-
@join_root = JoinBase.new
|
95
|
+
@join_root = JoinBase.new(base, table, build(tree, base))
|
100
96
|
@join_root.children.each { |child| construct_tables! @join_root, child }
|
101
97
|
end
|
102
98
|
|
@@ -104,29 +100,24 @@ module ActiveRecord
|
|
104
100
|
join_root.drop(1).map!(&:reflection)
|
105
101
|
end
|
106
102
|
|
107
|
-
def join_constraints(
|
103
|
+
def join_constraints(joins_to_add, join_type)
|
108
104
|
joins = join_root.children.flat_map { |child|
|
109
|
-
|
110
|
-
if join_type == Arel::Nodes::OuterJoin
|
111
|
-
make_left_outer_joins join_root, child
|
112
|
-
else
|
113
|
-
make_inner_joins join_root, child
|
114
|
-
end
|
105
|
+
make_join_constraints(join_root, child, join_type)
|
115
106
|
}
|
116
107
|
|
117
|
-
joins.concat
|
108
|
+
joins.concat joins_to_add.flat_map { |oj|
|
118
109
|
if join_root.match? oj.join_root
|
119
110
|
walk join_root, oj.join_root
|
120
111
|
else
|
121
112
|
oj.join_root.children.flat_map { |child|
|
122
|
-
|
113
|
+
make_join_constraints(oj.join_root, child, join_type)
|
123
114
|
}
|
124
115
|
end
|
125
116
|
}
|
126
117
|
end
|
127
118
|
|
128
119
|
def aliases
|
129
|
-
Aliases.new join_root.each_with_index.map { |join_part, i|
|
120
|
+
@aliases ||= Aliases.new join_root.each_with_index.map { |join_part, i|
|
130
121
|
columns = join_part.column_names.each_with_index.map { |column_name, j|
|
131
122
|
Aliases::Column.new column_name, "t#{i}_r#{j}"
|
132
123
|
}
|
@@ -134,7 +125,7 @@ module ActiveRecord
|
|
134
125
|
}
|
135
126
|
end
|
136
127
|
|
137
|
-
def instantiate(result_set,
|
128
|
+
def instantiate(result_set, &block)
|
138
129
|
primary_key = aliases.column_alias(join_root, join_root.primary_key)
|
139
130
|
|
140
131
|
seen = Hash.new { |i, object_id|
|
@@ -157,7 +148,7 @@ module ActiveRecord
|
|
157
148
|
message_bus.instrument("instantiation.active_record", payload) do
|
158
149
|
result_set.each { |row_hash|
|
159
150
|
parent_key = primary_key ? row_hash[primary_key] : row_hash
|
160
|
-
parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases)
|
151
|
+
parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases, &block)
|
161
152
|
construct(parent, join_root, row_hash, result_set, seen, model_cache, aliases)
|
162
153
|
}
|
163
154
|
end
|
@@ -165,6 +156,9 @@ module ActiveRecord
|
|
165
156
|
parents.values
|
166
157
|
end
|
167
158
|
|
159
|
+
protected
|
160
|
+
attr_reader :alias_tracker, :base_klass, :join_root
|
161
|
+
|
168
162
|
private
|
169
163
|
|
170
164
|
def make_constraints(parent, child, tables, join_type)
|
@@ -175,34 +169,23 @@ module ActiveRecord
|
|
175
169
|
end
|
176
170
|
|
177
171
|
def make_outer_joins(parent, child)
|
178
|
-
tables = table_aliases_for(parent, child)
|
179
|
-
join_type = Arel::Nodes::OuterJoin
|
180
|
-
info = make_constraints parent, child, tables, join_type
|
181
|
-
|
182
|
-
[info] + child.children.flat_map { |c| make_outer_joins(child, c) }
|
183
|
-
end
|
184
|
-
|
185
|
-
def make_left_outer_joins(parent, child)
|
186
|
-
tables = child.tables
|
187
172
|
join_type = Arel::Nodes::OuterJoin
|
188
|
-
|
189
|
-
|
190
|
-
[info] + child.children.flat_map { |c| make_left_outer_joins(child, c) }
|
173
|
+
make_join_constraints(parent, child, join_type, true)
|
191
174
|
end
|
192
175
|
|
193
|
-
def
|
194
|
-
tables
|
195
|
-
|
196
|
-
info = make_constraints parent, child, tables, join_type
|
176
|
+
def make_join_constraints(parent, child, join_type, aliasing = false)
|
177
|
+
tables = aliasing ? table_aliases_for(parent, child) : child.tables
|
178
|
+
joins = make_constraints(parent, child, tables, join_type)
|
197
179
|
|
198
|
-
|
180
|
+
joins.concat child.children.flat_map { |c| make_join_constraints(child, c, join_type, aliasing) }
|
199
181
|
end
|
200
182
|
|
201
183
|
def table_aliases_for(parent, node)
|
202
184
|
node.reflection.chain.map { |reflection|
|
203
185
|
alias_tracker.aliased_table_for(
|
204
186
|
reflection.table_name,
|
205
|
-
table_alias_for(reflection, parent, reflection != node.reflection)
|
187
|
+
table_alias_for(reflection, parent, reflection != node.reflection),
|
188
|
+
reflection.klass.type_caster
|
206
189
|
)
|
207
190
|
}
|
208
191
|
end
|
@@ -214,8 +197,7 @@ module ActiveRecord
|
|
214
197
|
|
215
198
|
def table_alias_for(reflection, parent, join)
|
216
199
|
name = "#{reflection.plural_name}_#{parent.table_name}"
|
217
|
-
|
218
|
-
name
|
200
|
+
join ? "#{name}_join" : name
|
219
201
|
end
|
220
202
|
|
221
203
|
def walk(left, right)
|
@@ -243,7 +225,7 @@ module ActiveRecord
|
|
243
225
|
raise EagerLoadPolymorphicError.new(reflection)
|
244
226
|
end
|
245
227
|
|
246
|
-
JoinAssociation.new
|
228
|
+
JoinAssociation.new(reflection, build(right, reflection.klass), alias_tracker)
|
247
229
|
end.compact
|
248
230
|
end
|
249
231
|
|
@@ -275,7 +257,8 @@ module ActiveRecord
|
|
275
257
|
else
|
276
258
|
model = construct_model(ar_parent, node, row, model_cache, id, aliases)
|
277
259
|
|
278
|
-
if node.reflection.
|
260
|
+
if node.reflection.scope &&
|
261
|
+
node.reflection.scope_for(node.base_klass.unscoped).readonly_value
|
279
262
|
model.readonly!
|
280
263
|
end
|
281
264
|
|
@@ -1,8 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module Associations
|
3
5
|
class Preloader
|
4
6
|
class Association #:nodoc:
|
5
|
-
attr_reader :owners, :reflection, :preload_scope, :model, :klass
|
6
7
|
attr_reader :preloaded_records
|
7
8
|
|
8
9
|
def initialize(klass, owners, reflection, preload_scope)
|
@@ -11,86 +12,65 @@ module ActiveRecord
|
|
11
12
|
@reflection = reflection
|
12
13
|
@preload_scope = preload_scope
|
13
14
|
@model = owners.first && owners.first.class
|
14
|
-
@scope = nil
|
15
15
|
@preloaded_records = []
|
16
16
|
end
|
17
17
|
|
18
18
|
def run(preloader)
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
end
|
25
|
-
|
26
|
-
def scope
|
27
|
-
@scope ||= build_scope
|
28
|
-
end
|
29
|
-
|
30
|
-
def records_for(ids)
|
31
|
-
scope.where(association_key_name => ids)
|
32
|
-
end
|
33
|
-
|
34
|
-
def table
|
35
|
-
klass.arel_table
|
36
|
-
end
|
37
|
-
|
38
|
-
# The name of the key on the associated records
|
39
|
-
def association_key_name
|
40
|
-
raise NotImplementedError
|
41
|
-
end
|
42
|
-
|
43
|
-
# This is overridden by HABTM as the condition should be on the foreign_key column in
|
44
|
-
# the join table
|
45
|
-
def association_key
|
46
|
-
klass.arel_attribute(association_key_name, table)
|
47
|
-
end
|
19
|
+
records = load_records do |record|
|
20
|
+
owner = owners_by_key[convert_key(record[association_key_name])]
|
21
|
+
association = owner.association(reflection.name)
|
22
|
+
association.set_inverse_instance(record)
|
23
|
+
end
|
48
24
|
|
49
|
-
|
50
|
-
|
51
|
-
|
25
|
+
owners.each do |owner|
|
26
|
+
associate_records_to_owner(owner, records[convert_key(owner[owner_key_name])] || [])
|
27
|
+
end
|
52
28
|
end
|
53
29
|
|
54
|
-
|
55
|
-
reflection
|
56
|
-
end
|
30
|
+
protected
|
31
|
+
attr_reader :owners, :reflection, :preload_scope, :model, :klass
|
57
32
|
|
58
33
|
private
|
34
|
+
# The name of the key on the associated records
|
35
|
+
def association_key_name
|
36
|
+
reflection.join_primary_key(klass)
|
37
|
+
end
|
59
38
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
association.set_inverse_instance(record)
|
65
|
-
end
|
39
|
+
# The name of the key on the model which declares the association
|
40
|
+
def owner_key_name
|
41
|
+
reflection.join_foreign_key
|
42
|
+
end
|
66
43
|
|
67
|
-
|
68
|
-
|
44
|
+
def associate_records_to_owner(owner, records)
|
45
|
+
association = owner.association(reflection.name)
|
46
|
+
if reflection.collection?
|
47
|
+
association.loaded!
|
48
|
+
association.target.concat(records)
|
49
|
+
else
|
50
|
+
association.target = records.first
|
69
51
|
end
|
70
52
|
end
|
71
53
|
|
72
54
|
def owner_keys
|
73
|
-
|
74
|
-
@owner_keys = owners.map do |owner|
|
75
|
-
owner[owner_key_name]
|
76
|
-
end
|
77
|
-
@owner_keys.uniq!
|
78
|
-
@owner_keys.compact!
|
79
|
-
end
|
80
|
-
@owner_keys
|
55
|
+
@owner_keys ||= owners_by_key.keys
|
81
56
|
end
|
82
57
|
|
83
58
|
def owners_by_key
|
84
59
|
unless defined?(@owners_by_key)
|
85
60
|
@owners_by_key = owners.each_with_object({}) do |owner, h|
|
86
|
-
|
61
|
+
key = convert_key(owner[owner_key_name])
|
62
|
+
h[key] = owner if key
|
87
63
|
end
|
88
64
|
end
|
89
65
|
@owners_by_key
|
90
66
|
end
|
91
67
|
|
92
68
|
def key_conversion_required?
|
93
|
-
@key_conversion_required
|
69
|
+
unless defined?(@key_conversion_required)
|
70
|
+
@key_conversion_required = (association_key_type != owner_key_type)
|
71
|
+
end
|
72
|
+
|
73
|
+
@key_conversion_required
|
94
74
|
end
|
95
75
|
|
96
76
|
def convert_key(key)
|
@@ -102,11 +82,11 @@ module ActiveRecord
|
|
102
82
|
end
|
103
83
|
|
104
84
|
def association_key_type
|
105
|
-
@klass.type_for_attribute(association_key_name
|
85
|
+
@klass.type_for_attribute(association_key_name).type
|
106
86
|
end
|
107
87
|
|
108
88
|
def owner_key_type
|
109
|
-
@model.type_for_attribute(owner_key_name
|
89
|
+
@model.type_for_attribute(owner_key_name).type
|
110
90
|
end
|
111
91
|
|
112
92
|
def load_records(&block)
|
@@ -115,54 +95,35 @@ module ActiveRecord
|
|
115
95
|
# Make several smaller queries if necessary or make one query if the adapter supports it
|
116
96
|
slices = owner_keys.each_slice(klass.connection.in_clause_length || owner_keys.size)
|
117
97
|
@preloaded_records = slices.flat_map do |slice|
|
118
|
-
records_for(slice
|
98
|
+
records_for(slice, &block)
|
119
99
|
end
|
120
100
|
@preloaded_records.group_by do |record|
|
121
101
|
convert_key(record[association_key_name])
|
122
102
|
end
|
123
103
|
end
|
124
104
|
|
125
|
-
def
|
126
|
-
|
105
|
+
def records_for(ids, &block)
|
106
|
+
scope.where(association_key_name => ids).load(&block)
|
127
107
|
end
|
128
108
|
|
129
|
-
def
|
130
|
-
scope
|
131
|
-
|
132
|
-
values = reflection_scope.values
|
133
|
-
preload_values = preload_scope.values
|
134
|
-
|
135
|
-
scope.where_clause = reflection_scope.where_clause + preload_scope.where_clause
|
136
|
-
scope.references_values = Array(values[:references]) + Array(preload_values[:references])
|
137
|
-
|
138
|
-
if preload_values[:select] || values[:select]
|
139
|
-
scope._select!(preload_values[:select] || values[:select])
|
140
|
-
end
|
141
|
-
scope.includes! preload_values[:includes] || values[:includes]
|
142
|
-
if preload_scope.joins_values.any?
|
143
|
-
scope.joins!(preload_scope.joins_values)
|
144
|
-
else
|
145
|
-
scope.joins!(reflection_scope.joins_values)
|
146
|
-
end
|
147
|
-
|
148
|
-
if order_values = preload_values[:order] || values[:order]
|
149
|
-
scope.order!(order_values)
|
150
|
-
end
|
109
|
+
def scope
|
110
|
+
@scope ||= build_scope
|
111
|
+
end
|
151
112
|
|
152
|
-
|
153
|
-
|
154
|
-
|
113
|
+
def reflection_scope
|
114
|
+
@reflection_scope ||= reflection.scope ? reflection.scope_for(klass.unscoped) : klass.unscoped
|
115
|
+
end
|
155
116
|
|
156
|
-
|
157
|
-
|
158
|
-
end
|
117
|
+
def build_scope
|
118
|
+
scope = klass.scope_for_association
|
159
119
|
|
160
|
-
if
|
161
|
-
scope.where!(
|
120
|
+
if reflection.type
|
121
|
+
scope.where!(reflection.type => model.base_class.sti_name)
|
162
122
|
end
|
163
123
|
|
164
|
-
scope.
|
165
|
-
|
124
|
+
scope.merge!(reflection_scope) if reflection.scope
|
125
|
+
scope.merge!(preload_scope) if preload_scope
|
126
|
+
scope
|
166
127
|
end
|
167
128
|
end
|
168
129
|
end
|
@@ -1,106 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module Associations
|
3
5
|
class Preloader
|
4
|
-
|
5
|
-
def
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
def associated_records_by_owner(preloader)
|
14
|
-
preloader.preload(owners,
|
15
|
-
through_reflection.name,
|
16
|
-
through_scope)
|
17
|
-
|
18
|
-
through_records = owners.map do |owner|
|
19
|
-
association = owner.association through_reflection.name
|
20
|
-
|
21
|
-
center = target_records_from_association(association)
|
22
|
-
[owner, Array(center)]
|
23
|
-
end
|
24
|
-
|
25
|
-
reset_association owners, through_reflection.name
|
26
|
-
|
27
|
-
middle_records = through_records.flat_map { |(_, rec)| rec }
|
28
|
-
|
29
|
-
preloaders = preloader.preload(middle_records,
|
30
|
-
source_reflection.name,
|
31
|
-
reflection_scope)
|
32
|
-
|
6
|
+
class ThroughAssociation < Association # :nodoc:
|
7
|
+
def run(preloader)
|
8
|
+
already_loaded = owners.first.association(through_reflection.name).loaded?
|
9
|
+
through_scope = through_scope()
|
10
|
+
reflection_scope = target_reflection_scope
|
11
|
+
through_preloaders = preloader.preload(owners, through_reflection.name, through_scope)
|
12
|
+
middle_records = through_preloaders.flat_map(&:preloaded_records)
|
13
|
+
preloaders = preloader.preload(middle_records, source_reflection.name, reflection_scope)
|
33
14
|
@preloaded_records = preloaders.flat_map(&:preloaded_records)
|
34
15
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
pl_to_middle = center.group_by { |record| middle_to_pl[record] }
|
43
|
-
|
44
|
-
records_by_owner[lhs] = pl_to_middle.flat_map do |pl, middles|
|
45
|
-
rhs_records = middles.flat_map { |r|
|
46
|
-
association = r.association source_reflection.name
|
47
|
-
|
48
|
-
target_records_from_association(association)
|
49
|
-
}.compact
|
50
|
-
|
51
|
-
# Respect the order on `reflection_scope` if it exists, else use the natural order.
|
52
|
-
if reflection_scope.values[:order].present?
|
53
|
-
@id_map ||= id_to_index_map @preloaded_records
|
54
|
-
rhs_records.sort_by { |rhs| @id_map[rhs] }
|
55
|
-
else
|
56
|
-
rhs_records
|
16
|
+
owners.each do |owner|
|
17
|
+
through_records = Array(owner.association(through_reflection.name).target)
|
18
|
+
if already_loaded
|
19
|
+
if source_type = reflection.options[:source_type]
|
20
|
+
through_records = through_records.select do |record|
|
21
|
+
record[reflection.foreign_type] == source_type
|
22
|
+
end
|
57
23
|
end
|
24
|
+
else
|
25
|
+
owner.association(through_reflection.name).reset if through_scope
|
26
|
+
end
|
27
|
+
result = through_records.flat_map do |record|
|
28
|
+
association = record.association(source_reflection.name)
|
29
|
+
target = association.target
|
30
|
+
association.reset if preload_scope
|
31
|
+
target
|
32
|
+
end
|
33
|
+
result.compact!
|
34
|
+
if reflection_scope
|
35
|
+
result.sort_by! { |rhs| preload_index[rhs] } if reflection_scope.order_values.any?
|
36
|
+
result.uniq! if reflection_scope.distinct_value
|
58
37
|
end
|
38
|
+
associate_records_to_owner(owner, result)
|
59
39
|
end
|
60
40
|
end
|
61
41
|
|
62
42
|
private
|
63
|
-
|
64
|
-
|
65
|
-
id_map = {}
|
66
|
-
ids.each_with_index { |id, index| id_map[id] = index }
|
67
|
-
id_map
|
43
|
+
def through_reflection
|
44
|
+
reflection.through_reflection
|
68
45
|
end
|
69
46
|
|
70
|
-
def
|
71
|
-
|
72
|
-
|
47
|
+
def source_reflection
|
48
|
+
reflection.source_reflection
|
49
|
+
end
|
73
50
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
owner.association(association_name).reset
|
78
|
-
}
|
51
|
+
def preload_index
|
52
|
+
@preload_index ||= @preloaded_records.each_with_object({}).with_index do |(id, result), index|
|
53
|
+
result[id] = index
|
79
54
|
end
|
80
55
|
end
|
81
56
|
|
82
57
|
def through_scope
|
83
58
|
scope = through_reflection.klass.unscoped
|
59
|
+
options = reflection.options
|
84
60
|
|
85
61
|
if options[:source_type]
|
86
62
|
scope.where! reflection.foreign_type => options[:source_type]
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
63
|
+
elsif !reflection_scope.where_clause.empty?
|
64
|
+
scope.where_clause = reflection_scope.where_clause
|
65
|
+
values = reflection_scope.values
|
66
|
+
|
67
|
+
if includes = values[:includes]
|
68
|
+
scope.includes!(source_reflection.name => includes)
|
69
|
+
else
|
70
|
+
scope.includes!(source_reflection.name)
|
91
71
|
end
|
92
72
|
|
93
|
-
|
94
|
-
|
73
|
+
if values[:references] && !values[:references].empty?
|
74
|
+
scope.references!(values[:references])
|
75
|
+
else
|
76
|
+
scope.references!(source_reflection.table_name)
|
77
|
+
end
|
78
|
+
|
79
|
+
if joins = values[:joins]
|
80
|
+
scope.joins!(source_reflection.name => joins)
|
81
|
+
end
|
82
|
+
|
83
|
+
if left_outer_joins = values[:left_outer_joins]
|
84
|
+
scope.left_outer_joins!(source_reflection.name => left_outer_joins)
|
85
|
+
end
|
86
|
+
|
87
|
+
if scope.eager_loading? && order_values = values[:order]
|
95
88
|
scope = scope.order(order_values)
|
96
89
|
end
|
97
90
|
end
|
98
91
|
|
99
|
-
scope
|
92
|
+
scope unless scope.empty_scope?
|
100
93
|
end
|
101
94
|
|
102
|
-
def
|
103
|
-
|
95
|
+
def target_reflection_scope
|
96
|
+
if preload_scope
|
97
|
+
reflection_scope.merge(preload_scope)
|
98
|
+
elsif reflection.scope
|
99
|
+
reflection_scope
|
100
|
+
else
|
101
|
+
nil
|
102
|
+
end
|
104
103
|
end
|
105
104
|
end
|
106
105
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module Associations
|
3
5
|
# Implements the details of eager loading of Active Record associations.
|
@@ -42,20 +44,10 @@ module ActiveRecord
|
|
42
44
|
extend ActiveSupport::Autoload
|
43
45
|
|
44
46
|
eager_autoload do
|
45
|
-
autoload :Association,
|
46
|
-
autoload :
|
47
|
-
autoload :CollectionAssociation, "active_record/associations/preloader/collection_association"
|
48
|
-
autoload :ThroughAssociation, "active_record/associations/preloader/through_association"
|
49
|
-
|
50
|
-
autoload :HasMany, "active_record/associations/preloader/has_many"
|
51
|
-
autoload :HasManyThrough, "active_record/associations/preloader/has_many_through"
|
52
|
-
autoload :HasOne, "active_record/associations/preloader/has_one"
|
53
|
-
autoload :HasOneThrough, "active_record/associations/preloader/has_one_through"
|
54
|
-
autoload :BelongsTo, "active_record/associations/preloader/belongs_to"
|
47
|
+
autoload :Association, "active_record/associations/preloader/association"
|
48
|
+
autoload :ThroughAssociation, "active_record/associations/preloader/through_association"
|
55
49
|
end
|
56
50
|
|
57
|
-
NULL_RELATION = Struct.new(:values, :where_clause, :joins_values).new({}, Relation::WhereClause.empty, [])
|
58
|
-
|
59
51
|
# Eager loads the named associations for the given Active Record record(s).
|
60
52
|
#
|
61
53
|
# In this description, 'association name' shall refer to the name passed
|
@@ -91,14 +83,13 @@ module ActiveRecord
|
|
91
83
|
# { author: :avatar }
|
92
84
|
# [ :books, { author: :avatar } ]
|
93
85
|
def preload(records, associations, preload_scope = nil)
|
94
|
-
records
|
95
|
-
associations = Array.wrap(associations)
|
96
|
-
preload_scope = preload_scope || NULL_RELATION
|
86
|
+
records = Array.wrap(records).compact
|
97
87
|
|
98
88
|
if records.empty?
|
99
89
|
[]
|
100
90
|
else
|
101
|
-
|
91
|
+
records.uniq!
|
92
|
+
Array.wrap(associations).flat_map { |association|
|
102
93
|
preloaders_on association, records, preload_scope
|
103
94
|
}
|
104
95
|
end
|
@@ -147,7 +138,7 @@ module ActiveRecord
|
|
147
138
|
def preloaders_for_one(association, records, scope)
|
148
139
|
grouped_records(association, records).flat_map do |reflection, klasses|
|
149
140
|
klasses.map do |rhs_klass, rs|
|
150
|
-
loader = preloader_for(reflection, rs
|
141
|
+
loader = preloader_for(reflection, rs).new(rhs_klass, rs, reflection, scope)
|
151
142
|
loader.run self
|
152
143
|
loader
|
153
144
|
end
|
@@ -159,6 +150,7 @@ module ActiveRecord
|
|
159
150
|
records.each do |record|
|
160
151
|
next unless record
|
161
152
|
assoc = record.association(association)
|
153
|
+
next unless assoc.klass
|
162
154
|
klasses = h[assoc.reflection] ||= {}
|
163
155
|
(klasses[assoc.klass] ||= []) << record
|
164
156
|
end
|
@@ -166,8 +158,6 @@ module ActiveRecord
|
|
166
158
|
end
|
167
159
|
|
168
160
|
class AlreadyLoaded # :nodoc:
|
169
|
-
attr_reader :owners, :reflection
|
170
|
-
|
171
161
|
def initialize(klass, owners, reflection, preload_scope)
|
172
162
|
@owners = owners
|
173
163
|
@reflection = reflection
|
@@ -178,34 +168,24 @@ module ActiveRecord
|
|
178
168
|
def preloaded_records
|
179
169
|
owners.flat_map { |owner| owner.association(reflection.name).target }
|
180
170
|
end
|
181
|
-
end
|
182
171
|
|
183
|
-
|
184
|
-
|
185
|
-
def self.run(preloader); end
|
186
|
-
def self.preloaded_records; []; end
|
187
|
-
def self.owners; []; end
|
172
|
+
protected
|
173
|
+
attr_reader :owners, :reflection
|
188
174
|
end
|
189
175
|
|
190
176
|
# Returns a class containing the logic needed to load preload the data
|
191
|
-
# and attach it to a relation.
|
192
|
-
# +Preloader::HasManyThrough+. The class returned implements a `run` method
|
177
|
+
# and attach it to a relation. The class returned implements a `run` method
|
193
178
|
# that accepts a preloader.
|
194
|
-
def preloader_for(reflection, owners
|
195
|
-
return NullPreloader unless rhs_klass
|
196
|
-
|
179
|
+
def preloader_for(reflection, owners)
|
197
180
|
if owners.first.association(reflection.name).loaded?
|
198
181
|
return AlreadyLoaded
|
199
182
|
end
|
200
183
|
reflection.check_preloadable!
|
201
184
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
reflection.options[:through] ? HasOneThrough : HasOne
|
207
|
-
when :belongs_to
|
208
|
-
BelongsTo
|
185
|
+
if reflection.options[:through]
|
186
|
+
ThroughAssociation
|
187
|
+
else
|
188
|
+
Association
|
209
189
|
end
|
210
190
|
end
|
211
191
|
end
|