activerecord 5.1.7 → 5.2.0
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 +372 -765
- data/MIT-LICENSE +1 -1
- data/README.rdoc +5 -5
- 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 +19 -27
- data/lib/active_record/associations/association.rb +16 -27
- data/lib/active_record/associations/association_scope.rb +38 -50
- data/lib/active_record/associations/belongs_to_association.rb +20 -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 +4 -5
- 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 +43 -35
- data/lib/active_record/associations/collection_proxy.rb +12 -15
- data/lib/active_record/associations/foreign_association.rb +2 -0
- data/lib/active_record/associations/has_many_association.rb +3 -1
- data/lib/active_record/associations/has_many_through_association.rb +7 -18
- data/lib/active_record/associations/has_one_association.rb +4 -1
- data/lib/active_record/associations/has_one_through_association.rb +8 -7
- data/lib/active_record/associations/join_dependency/join_association.rb +17 -56
- 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 +23 -43
- data/lib/active_record/associations/preloader/association.rb +45 -61
- data/lib/active_record/associations/preloader/through_association.rb +71 -79
- 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 +25 -10
- data/lib/active_record/associations.rb +31 -54
- data/lib/active_record/attribute_assignment.rb +2 -5
- 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 +25 -214
- data/lib/active_record/attribute_methods/primary_key.rb +7 -6
- 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 +21 -9
- data/lib/active_record/attribute_methods.rb +65 -24
- data/lib/active_record/attributes.rb +6 -5
- data/lib/active_record/autosave_association.rb +8 -11
- data/lib/active_record/base.rb +2 -0
- data/lib/active_record/callbacks.rb +8 -10
- 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 +11 -7
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +111 -38
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +157 -29
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +7 -2
- data/lib/active_record/connection_adapters/abstract/quoting.rb +13 -32
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +57 -2
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +158 -78
- data/lib/active_record/connection_adapters/abstract/transaction.rb +45 -9
- data/lib/active_record/connection_adapters/abstract_adapter.rb +81 -96
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +111 -183
- data/lib/active_record/connection_adapters/column.rb +3 -1
- 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 -2
- 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 -30
- 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 +2 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -11
- 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.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +2 -0
- 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 -1
- 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 -6
- 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 +3 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +18 -0
- 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 +246 -110
- 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 +58 -82
- 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 -1
- 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 +80 -90
- 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 +15 -12
- 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 +54 -21
- 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 +49 -19
- 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 +14 -17
- data/lib/active_record/locking/pessimistic.rb +9 -6
- data/lib/active_record/log_subscriber.rb +43 -0
- data/lib/active_record/migration/command_recorder.rb +11 -9
- data/lib/active_record/migration/compatibility.rb +40 -2
- data/lib/active_record/migration/join_table.rb +2 -0
- data/lib/active_record/migration.rb +189 -139
- data/lib/active_record/model_schema.rb +16 -21
- 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 +166 -16
- data/lib/active_record/query_cache.rb +11 -6
- data/lib/active_record/querying.rb +3 -1
- 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 +46 -36
- data/lib/active_record/readonly_attributes.rb +3 -2
- data/lib/active_record/reflection.rb +110 -192
- data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
- data/lib/active_record/relation/batches.rb +20 -5
- data/lib/active_record/relation/calculations.rb +30 -8
- data/lib/active_record/relation/delegation.rb +15 -27
- data/lib/active_record/relation/finder_methods.rb +75 -78
- 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 +56 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +26 -9
- 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 +26 -2
- data/lib/active_record/relation/query_methods.rb +89 -88
- 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 -68
- data/lib/active_record/relation/where_clause_factory.rb +5 -48
- data/lib/active_record/relation.rb +95 -208
- 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 +6 -7
- data/lib/active_record/scoping/named.rb +21 -7
- data/lib/active_record/scoping.rb +9 -8
- 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 +26 -15
- data/lib/active_record/tasks/mysql_database_tasks.rb +9 -48
- data/lib/active_record/tasks/postgresql_database_tasks.rb +10 -2
- data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -3
- data/lib/active_record/timestamp.rb +5 -12
- 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 -4
- 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 +24 -36
- 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 -122
- data/lib/active_record/attribute_set/builder.rb +0 -126
- 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 -37
@@ -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,10 @@ module ActiveRecord
|
|
92
88
|
# associations # => [:appointments]
|
93
89
|
# joins # => []
|
94
90
|
#
|
95
|
-
def initialize(base,
|
96
|
-
@alias_tracker =
|
97
|
-
@eager_loading = eager_loading
|
91
|
+
def initialize(base, table, associations, alias_tracker)
|
92
|
+
@alias_tracker = alias_tracker
|
98
93
|
tree = self.class.make_tree associations
|
99
|
-
@join_root = JoinBase.new
|
94
|
+
@join_root = JoinBase.new(base, table, build(tree, base))
|
100
95
|
@join_root.children.each { |child| construct_tables! @join_root, child }
|
101
96
|
end
|
102
97
|
|
@@ -104,22 +99,17 @@ module ActiveRecord
|
|
104
99
|
join_root.drop(1).map!(&:reflection)
|
105
100
|
end
|
106
101
|
|
107
|
-
def join_constraints(
|
102
|
+
def join_constraints(joins_to_add, join_type)
|
108
103
|
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
|
104
|
+
make_join_constraints(join_root, child, join_type)
|
115
105
|
}
|
116
106
|
|
117
|
-
joins.concat
|
107
|
+
joins.concat joins_to_add.flat_map { |oj|
|
118
108
|
if join_root.match? oj.join_root
|
119
109
|
walk join_root, oj.join_root
|
120
110
|
else
|
121
111
|
oj.join_root.children.flat_map { |child|
|
122
|
-
|
112
|
+
make_join_constraints(oj.join_root, child, join_type)
|
123
113
|
}
|
124
114
|
end
|
125
115
|
}
|
@@ -165,6 +155,9 @@ module ActiveRecord
|
|
165
155
|
parents.values
|
166
156
|
end
|
167
157
|
|
158
|
+
protected
|
159
|
+
attr_reader :alias_tracker, :base_klass, :join_root
|
160
|
+
|
168
161
|
private
|
169
162
|
|
170
163
|
def make_constraints(parent, child, tables, join_type)
|
@@ -175,27 +168,15 @@ module ActiveRecord
|
|
175
168
|
end
|
176
169
|
|
177
170
|
def make_outer_joins(parent, child)
|
178
|
-
tables = table_aliases_for(parent, child)
|
179
171
|
join_type = Arel::Nodes::OuterJoin
|
180
|
-
|
181
|
-
|
182
|
-
[info] + child.children.flat_map { |c| make_outer_joins(child, c) }
|
172
|
+
make_join_constraints(parent, child, join_type, true)
|
183
173
|
end
|
184
174
|
|
185
|
-
def
|
186
|
-
tables
|
187
|
-
|
188
|
-
info = make_constraints parent, child, tables, join_type
|
175
|
+
def make_join_constraints(parent, child, join_type, aliasing = false)
|
176
|
+
tables = aliasing ? table_aliases_for(parent, child) : child.tables
|
177
|
+
joins = make_constraints(parent, child, tables, join_type)
|
189
178
|
|
190
|
-
|
191
|
-
end
|
192
|
-
|
193
|
-
def make_inner_joins(parent, child)
|
194
|
-
tables = child.tables
|
195
|
-
join_type = Arel::Nodes::InnerJoin
|
196
|
-
info = make_constraints parent, child, tables, join_type
|
197
|
-
|
198
|
-
[info] + child.children.flat_map { |c| make_inner_joins(child, c) }
|
179
|
+
joins.concat child.children.flat_map { |c| make_join_constraints(child, c, join_type, aliasing) }
|
199
180
|
end
|
200
181
|
|
201
182
|
def table_aliases_for(parent, node)
|
@@ -215,8 +196,7 @@ module ActiveRecord
|
|
215
196
|
|
216
197
|
def table_alias_for(reflection, parent, join)
|
217
198
|
name = "#{reflection.plural_name}_#{parent.table_name}"
|
218
|
-
|
219
|
-
name
|
199
|
+
join ? "#{name}_join" : name
|
220
200
|
end
|
221
201
|
|
222
202
|
def walk(left, right)
|
@@ -240,12 +220,11 @@ module ActiveRecord
|
|
240
220
|
reflection.check_eager_loadable!
|
241
221
|
|
242
222
|
if reflection.polymorphic?
|
243
|
-
next unless @eager_loading
|
244
223
|
raise EagerLoadPolymorphicError.new(reflection)
|
245
224
|
end
|
246
225
|
|
247
|
-
JoinAssociation.new
|
248
|
-
end
|
226
|
+
JoinAssociation.new(reflection, build(right, reflection.klass), alias_tracker)
|
227
|
+
end
|
249
228
|
end
|
250
229
|
|
251
230
|
def construct(ar_parent, parent, row, rs, seen, model_cache, aliases)
|
@@ -276,7 +255,8 @@ module ActiveRecord
|
|
276
255
|
else
|
277
256
|
model = construct_model(ar_parent, node, row, model_cache, id, aliases)
|
278
257
|
|
279
|
-
if node.reflection.
|
258
|
+
if node.reflection.scope &&
|
259
|
+
node.reflection.scope_for(node.base_klass.unscoped).readonly_value
|
280
260
|
model.readonly!
|
281
261
|
end
|
282
262
|
|
@@ -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,79 +12,54 @@ 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
|
+
association.loaded!
|
47
|
+
if reflection.collection?
|
48
|
+
association.target.concat(records)
|
49
|
+
else
|
50
|
+
association.target = records.first unless records.empty?
|
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
|
@@ -106,11 +82,11 @@ module ActiveRecord
|
|
106
82
|
end
|
107
83
|
|
108
84
|
def association_key_type
|
109
|
-
@klass.type_for_attribute(association_key_name
|
85
|
+
@klass.type_for_attribute(association_key_name).type
|
110
86
|
end
|
111
87
|
|
112
88
|
def owner_key_type
|
113
|
-
@model.type_for_attribute(owner_key_name
|
89
|
+
@model.type_for_attribute(owner_key_name).type
|
114
90
|
end
|
115
91
|
|
116
92
|
def load_records(&block)
|
@@ -119,26 +95,34 @@ module ActiveRecord
|
|
119
95
|
# Make several smaller queries if necessary or make one query if the adapter supports it
|
120
96
|
slices = owner_keys.each_slice(klass.connection.in_clause_length || owner_keys.size)
|
121
97
|
@preloaded_records = slices.flat_map do |slice|
|
122
|
-
records_for(slice
|
98
|
+
records_for(slice, &block)
|
123
99
|
end
|
124
100
|
@preloaded_records.group_by do |record|
|
125
101
|
convert_key(record[association_key_name])
|
126
102
|
end
|
127
103
|
end
|
128
104
|
|
105
|
+
def records_for(ids, &block)
|
106
|
+
scope.where(association_key_name => ids).load(&block)
|
107
|
+
end
|
108
|
+
|
109
|
+
def scope
|
110
|
+
@scope ||= build_scope
|
111
|
+
end
|
112
|
+
|
129
113
|
def reflection_scope
|
130
|
-
@reflection_scope ||= reflection.scope_for(klass)
|
114
|
+
@reflection_scope ||= reflection.scope ? reflection.scope_for(klass.unscoped) : klass.unscoped
|
131
115
|
end
|
132
116
|
|
133
117
|
def build_scope
|
134
118
|
scope = klass.scope_for_association
|
135
119
|
|
136
120
|
if reflection.type
|
137
|
-
scope.where!(reflection.type => model.
|
121
|
+
scope.where!(reflection.type => model.polymorphic_name)
|
138
122
|
end
|
139
123
|
|
140
|
-
scope.merge!(reflection_scope)
|
141
|
-
scope.merge!(preload_scope) if preload_scope
|
124
|
+
scope.merge!(reflection_scope) if reflection.scope
|
125
|
+
scope.merge!(preload_scope) if preload_scope
|
142
126
|
scope
|
143
127
|
end
|
144
128
|
end
|
@@ -1,113 +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
|
84
|
-
|
59
|
+
options = reflection.options
|
85
60
|
|
86
61
|
if options[:source_type]
|
87
62
|
scope.where! reflection.foreign_type => options[:source_type]
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
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)
|
71
|
+
end
|
72
|
+
|
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)
|
98
85
|
end
|
99
86
|
|
100
|
-
scope.references! values[:references]
|
101
87
|
if scope.eager_loading? && order_values = values[:order]
|
102
88
|
scope = scope.order(order_values)
|
103
89
|
end
|
104
90
|
end
|
105
91
|
|
106
|
-
scope
|
92
|
+
scope unless scope.empty_scope?
|
107
93
|
end
|
108
94
|
|
109
|
-
def
|
110
|
-
|
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
|
111
103
|
end
|
112
104
|
end
|
113
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
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module Associations
|
3
5
|
class SingularAssociation < Association #:nodoc:
|
@@ -30,24 +32,22 @@ module ActiveRecord
|
|
30
32
|
end
|
31
33
|
|
32
34
|
private
|
33
|
-
|
34
|
-
|
35
|
-
scope.scope_for_create.stringify_keys.except(klass.primary_key)
|
35
|
+
def scope_for_create
|
36
|
+
super.except!(klass.primary_key)
|
36
37
|
end
|
37
38
|
|
38
39
|
def find_target
|
39
|
-
|
40
|
+
scope = self.scope
|
41
|
+
return scope.take if skip_statement_cache?(scope)
|
40
42
|
|
41
43
|
conn = klass.connection
|
42
|
-
sc = reflection.association_scope_cache(conn, owner) do
|
43
|
-
|
44
|
-
|
45
|
-
target_scope.merge(as.scope(self, conn)).limit(1)
|
46
|
-
}
|
44
|
+
sc = reflection.association_scope_cache(conn, owner) do |params|
|
45
|
+
as = AssociationScope.create { params.bind }
|
46
|
+
target_scope.merge!(as.scope(self)).limit(1)
|
47
47
|
end
|
48
48
|
|
49
49
|
binds = AssociationScope.get_bind_values(owner, reflection.chain)
|
50
|
-
sc.execute(binds,
|
50
|
+
sc.execute(binds, conn) do |record|
|
51
51
|
set_inverse_instance record
|
52
52
|
end.first
|
53
53
|
rescue ::RangeError
|
@@ -63,6 +63,10 @@ module ActiveRecord
|
|
63
63
|
end
|
64
64
|
|
65
65
|
def _create_record(attributes, raise_error = false)
|
66
|
+
unless owner.persisted?
|
67
|
+
raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved"
|
68
|
+
end
|
69
|
+
|
66
70
|
record = build_record(attributes)
|
67
71
|
yield(record) if block_given?
|
68
72
|
saved = record.save
|