activerecord 5.1.7 → 5.2.0.beta1
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 +221 -900
- data/README.rdoc +3 -3
- data/examples/performance.rb +2 -0
- data/examples/simple.rb +2 -0
- data/lib/active_record.rb +10 -3
- data/lib/active_record/aggregations.rb +2 -0
- data/lib/active_record/association_relation.rb +2 -0
- data/lib/active_record/associations.rb +13 -42
- data/lib/active_record/associations/alias_tracker.rb +17 -17
- data/lib/active_record/associations/association.rb +11 -22
- data/lib/active_record/associations/association_scope.rb +32 -44
- data/lib/active_record/associations/belongs_to_association.rb +6 -4
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -1
- data/lib/active_record/associations/builder/association.rb +2 -5
- data/lib/active_record/associations/builder/belongs_to.rb +7 -12
- 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 +41 -33
- data/lib/active_record/associations/collection_proxy.rb +11 -14
- 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 +4 -2
- data/lib/active_record/associations/has_one_association.rb +3 -1
- data/lib/active_record/associations/has_one_through_association.rb +3 -1
- data/lib/active_record/associations/join_dependency.rb +22 -40
- 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/preloader.rb +17 -37
- data/lib/active_record/associations/preloader/association.rb +42 -58
- data/lib/active_record/associations/preloader/through_association.rb +71 -79
- data/lib/active_record/associations/singular_association.rb +14 -10
- data/lib/active_record/associations/through_association.rb +3 -1
- data/lib/active_record/attribute_assignment.rb +2 -0
- data/lib/active_record/attribute_decorators.rb +3 -2
- data/lib/active_record/attribute_methods.rb +47 -7
- 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/attributes.rb +7 -6
- data/lib/active_record/autosave_association.rb +5 -11
- data/lib/active_record/base.rb +2 -0
- data/lib/active_record/callbacks.rb +6 -8
- 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 +10 -5
- 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 +120 -28
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +7 -2
- data/lib/active_record/connection_adapters/abstract/quoting.rb +14 -33
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +13 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +40 -2
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +103 -63
- data/lib/active_record/connection_adapters/abstract/transaction.rb +45 -9
- data/lib/active_record/connection_adapters/abstract_adapter.rb +62 -90
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +75 -138
- 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 +3 -1
- 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 -6
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +30 -30
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +91 -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.rb +2 -1
- 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_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 -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 +3 -5
- 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/quoting.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +11 -7
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +20 -13
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +79 -65
- 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 +47 -82
- data/lib/active_record/connection_adapters/schema_cache.rb +2 -0
- 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 +19 -2
- 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 +34 -89
- 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 +27 -57
- 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 +15 -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 +40 -24
- data/lib/active_record/gem_version.rb +5 -3
- data/lib/active_record/inheritance.rb +6 -5
- 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 +31 -20
- data/lib/active_record/locking/pessimistic.rb +10 -7
- data/lib/active_record/log_subscriber.rb +2 -0
- data/lib/active_record/migration.rb +47 -21
- data/lib/active_record/migration/command_recorder.rb +11 -9
- data/lib/active_record/migration/compatibility.rb +20 -2
- data/lib/active_record/migration/join_table.rb +2 -0
- data/lib/active_record/model_schema.rb +29 -38
- 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 +184 -40
- data/lib/active_record/query_cache.rb +17 -12
- data/lib/active_record/querying.rb +3 -1
- data/lib/active_record/railtie.rb +54 -1
- 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 +41 -28
- data/lib/active_record/readonly_attributes.rb +3 -2
- data/lib/active_record/reflection.rb +100 -182
- data/lib/active_record/relation.rb +61 -193
- data/lib/active_record/relation/batches.rb +20 -5
- data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
- data/lib/active_record/relation/calculations.rb +40 -23
- data/lib/active_record/relation/delegation.rb +10 -27
- data/lib/active_record/relation/finder_methods.rb +53 -49
- data/lib/active_record/relation/from_clause.rb +2 -8
- data/lib/active_record/relation/merger.rb +22 -19
- data/lib/active_record/relation/predicate_builder.rb +42 -79
- 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/query_attribute.rb +9 -2
- data/lib/active_record/relation/query_methods.rb +80 -69
- data/lib/active_record/relation/record_fetch_warning.rb +2 -0
- data/lib/active_record/relation/spawn_methods.rb +2 -0
- data/lib/active_record/relation/where_clause.rb +50 -67
- data/lib/active_record/relation/where_clause_factory.rb +4 -46
- data/lib/active_record/result.rb +2 -0
- data/lib/active_record/runtime_registry.rb +2 -0
- data/lib/active_record/sanitization.rb +15 -9
- data/lib/active_record/schema.rb +3 -1
- data/lib/active_record/schema_dumper.rb +24 -23
- data/lib/active_record/schema_migration.rb +2 -0
- data/lib/active_record/scoping.rb +9 -8
- data/lib/active_record/scoping/default.rb +6 -7
- data/lib/active_record/scoping/named.rb +15 -7
- 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 +2 -0
- data/lib/active_record/suppressor.rb +2 -0
- data/lib/active_record/table_metadata.rb +3 -1
- data/lib/active_record/tasks/database_tasks.rb +23 -12
- 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.rb +4 -1
- 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_caster.rb +2 -0
- data/lib/active_record/type_caster/connection.rb +2 -0
- data/lib/active_record/type_caster/map.rb +2 -0
- data/lib/active_record/validations.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 +36 -6
- data/lib/active_record/version.rb +2 -0
- data/lib/rails/generators/active_record.rb +3 -1
- 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.rb +2 -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/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
- metadata +25 -38
- 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.rb +0 -240
- data/lib/active_record/attribute/user_provided_default.rb +0 -30
- data/lib/active_record/attribute_mutation_tracker.rb +0 -122
- data/lib/active_record/attribute_set.rb +0 -113
- 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/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
|
require "active_record/associations/join_dependency/join_part"
|
2
4
|
|
3
5
|
module ActiveRecord
|
@@ -9,11 +11,12 @@ module ActiveRecord
|
|
9
11
|
|
10
12
|
attr_accessor :tables
|
11
13
|
|
12
|
-
def initialize(reflection, children)
|
14
|
+
def initialize(reflection, children, alias_tracker)
|
13
15
|
super(reflection.klass, children)
|
14
16
|
|
15
|
-
@
|
16
|
-
@
|
17
|
+
@alias_tracker = alias_tracker
|
18
|
+
@reflection = reflection
|
19
|
+
@tables = nil
|
17
20
|
end
|
18
21
|
|
19
22
|
def match?(other)
|
@@ -21,11 +24,8 @@ module ActiveRecord
|
|
21
24
|
super && reflection == other.reflection
|
22
25
|
end
|
23
26
|
|
24
|
-
JoinInformation = Struct.new :joins, :binds
|
25
|
-
|
26
27
|
def join_constraints(foreign_table, foreign_klass, join_type, tables, chain)
|
27
28
|
joins = []
|
28
|
-
binds = []
|
29
29
|
tables = tables.reverse
|
30
30
|
|
31
31
|
# The chain starts with the target table, but we want to end with it here (makes
|
@@ -34,71 +34,32 @@ module ActiveRecord
|
|
34
34
|
table = tables.shift
|
35
35
|
klass = reflection.klass
|
36
36
|
|
37
|
-
|
38
|
-
key = join_keys.key
|
39
|
-
foreign_key = join_keys.foreign_key
|
40
|
-
|
41
|
-
constraint = build_constraint(klass, table, key, foreign_table, foreign_key)
|
42
|
-
|
43
|
-
rel = reflection.join_scope(table)
|
37
|
+
constraint = reflection.build_join_constraint(table, foreign_table)
|
44
38
|
|
45
|
-
|
46
|
-
binds += rel.bound_attributes
|
47
|
-
constraint = constraint.and rel.arel.constraints
|
48
|
-
end
|
39
|
+
joins << table.create_join(table, table.create_on(constraint), join_type)
|
49
40
|
|
50
|
-
|
51
|
-
|
52
|
-
column = klass.columns_hash[reflection.type.to_s]
|
41
|
+
join_scope = reflection.join_scope(table, foreign_klass)
|
42
|
+
arel = join_scope.arel(alias_tracker.aliases)
|
53
43
|
|
54
|
-
|
55
|
-
|
44
|
+
if arel.constraints.any?
|
45
|
+
joins.concat arel.join_sources
|
46
|
+
right = joins.last.right
|
47
|
+
right.expr = right.expr.and(arel.constraints)
|
56
48
|
end
|
57
49
|
|
58
|
-
joins << table.create_join(table, table.create_on(constraint), join_type)
|
59
|
-
|
60
50
|
# The current table in this iteration becomes the foreign table in the next
|
61
51
|
foreign_table, foreign_klass = table, klass
|
62
52
|
end
|
63
53
|
|
64
|
-
|
65
|
-
end
|
66
|
-
|
67
|
-
# Builds equality condition.
|
68
|
-
#
|
69
|
-
# Example:
|
70
|
-
#
|
71
|
-
# class Physician < ActiveRecord::Base
|
72
|
-
# has_many :appointments
|
73
|
-
# end
|
74
|
-
#
|
75
|
-
# If I execute `Physician.joins(:appointments).to_a` then
|
76
|
-
# klass # => Physician
|
77
|
-
# table # => #<Arel::Table @name="appointments" ...>
|
78
|
-
# key # => physician_id
|
79
|
-
# foreign_table # => #<Arel::Table @name="physicians" ...>
|
80
|
-
# foreign_key # => id
|
81
|
-
#
|
82
|
-
def build_constraint(klass, table, key, foreign_table, foreign_key)
|
83
|
-
constraint = table[key].eq(foreign_table[foreign_key])
|
84
|
-
|
85
|
-
if klass.finder_needs_type_condition?
|
86
|
-
constraint = table.create_and([
|
87
|
-
constraint,
|
88
|
-
klass.send(:type_condition, table)
|
89
|
-
])
|
90
|
-
end
|
91
|
-
|
92
|
-
constraint
|
54
|
+
joins
|
93
55
|
end
|
94
56
|
|
95
57
|
def table
|
96
58
|
tables.first
|
97
59
|
end
|
98
60
|
|
99
|
-
|
100
|
-
|
101
|
-
end
|
61
|
+
protected
|
62
|
+
attr_reader :alias_tracker
|
102
63
|
end
|
103
64
|
end
|
104
65
|
end
|
@@ -1,20 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "active_record/associations/join_dependency/join_part"
|
2
4
|
|
3
5
|
module ActiveRecord
|
4
6
|
module Associations
|
5
7
|
class JoinDependency # :nodoc:
|
6
8
|
class JoinBase < JoinPart # :nodoc:
|
7
|
-
|
8
|
-
return true if self == other
|
9
|
-
super && base_klass == other.base_klass
|
10
|
-
end
|
9
|
+
attr_reader :table
|
11
10
|
|
12
|
-
def table
|
13
|
-
base_klass
|
11
|
+
def initialize(base_klass, table, children)
|
12
|
+
super(base_klass, children)
|
13
|
+
@table = table
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
17
|
-
|
16
|
+
def match?(other)
|
17
|
+
return true if self == other
|
18
|
+
super && base_klass == other.base_klass
|
18
19
|
end
|
19
20
|
end
|
20
21
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module Associations
|
3
5
|
class JoinDependency # :nodoc:
|
@@ -22,10 +24,6 @@ module ActiveRecord
|
|
22
24
|
@children = children
|
23
25
|
end
|
24
26
|
|
25
|
-
def name
|
26
|
-
reflection.name
|
27
|
-
end
|
28
|
-
|
29
27
|
def match?(other)
|
30
28
|
self.class == other.class
|
31
29
|
end
|
@@ -40,11 +38,6 @@ module ActiveRecord
|
|
40
38
|
raise NotImplementedError
|
41
39
|
end
|
42
40
|
|
43
|
-
# The alias for the active_record's table
|
44
|
-
def aliased_table_name
|
45
|
-
raise NotImplementedError
|
46
|
-
end
|
47
|
-
|
48
41
|
def extract_record(row, column_names_with_alias)
|
49
42
|
# This code is performance critical as it is called per row.
|
50
43
|
# see: https://github.com/rails/rails/pull/12185
|
@@ -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 = 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,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
|
+
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
|
@@ -119,15 +95,23 @@ 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
|
@@ -137,8 +121,8 @@ module ActiveRecord
|
|
137
121
|
scope.where!(reflection.type => model.base_class.sti_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
|