activerecord 5.2.6 → 6.1.3.2
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 +1038 -571
- data/MIT-LICENSE +3 -1
- data/README.rdoc +7 -5
- data/examples/performance.rb +1 -1
- data/lib/active_record.rb +13 -12
- data/lib/active_record/aggregations.rb +9 -8
- data/lib/active_record/association_relation.rb +30 -10
- data/lib/active_record/associations.rb +137 -25
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +95 -42
- data/lib/active_record/associations/association_scope.rb +23 -21
- data/lib/active_record/associations/belongs_to_association.rb +54 -46
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -6
- data/lib/active_record/associations/builder/association.rb +45 -22
- data/lib/active_record/associations/builder/belongs_to.rb +29 -59
- data/lib/active_record/associations/builder/collection_association.rb +8 -17
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -41
- data/lib/active_record/associations/builder/has_many.rb +8 -2
- data/lib/active_record/associations/builder/has_one.rb +33 -2
- data/lib/active_record/associations/builder/singular_association.rb +3 -1
- data/lib/active_record/associations/collection_association.rb +31 -29
- data/lib/active_record/associations/collection_proxy.rb +25 -21
- data/lib/active_record/associations/foreign_association.rb +20 -0
- data/lib/active_record/associations/has_many_association.rb +26 -13
- data/lib/active_record/associations/has_many_through_association.rb +24 -18
- data/lib/active_record/associations/has_one_association.rb +43 -31
- data/lib/active_record/associations/has_one_through_association.rb +5 -5
- data/lib/active_record/associations/join_dependency.rb +91 -60
- data/lib/active_record/associations/join_dependency/join_association.rb +44 -22
- data/lib/active_record/associations/join_dependency/join_part.rb +5 -5
- data/lib/active_record/associations/preloader.rb +47 -34
- data/lib/active_record/associations/preloader/association.rb +71 -43
- data/lib/active_record/associations/preloader/through_association.rb +49 -40
- data/lib/active_record/associations/singular_association.rb +3 -17
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/attribute_assignment.rb +17 -19
- data/lib/active_record/attribute_methods.rb +81 -143
- data/lib/active_record/attribute_methods/before_type_cast.rb +13 -7
- data/lib/active_record/attribute_methods/dirty.rb +101 -40
- data/lib/active_record/attribute_methods/primary_key.rb +20 -25
- data/lib/active_record/attribute_methods/query.rb +4 -8
- data/lib/active_record/attribute_methods/read.rb +14 -56
- data/lib/active_record/attribute_methods/serialization.rb +12 -7
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
- data/lib/active_record/attribute_methods/write.rb +18 -34
- data/lib/active_record/attributes.rb +46 -9
- data/lib/active_record/autosave_association.rb +57 -42
- data/lib/active_record/base.rb +4 -17
- data/lib/active_record/callbacks.rb +158 -43
- data/lib/active_record/coders/yaml_column.rb +1 -2
- data/lib/active_record/connection_adapters.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +272 -130
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -36
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +167 -146
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +18 -14
- data/lib/active_record/connection_adapters/abstract/quoting.rb +98 -47
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -110
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +207 -90
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -4
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +385 -144
- data/lib/active_record/connection_adapters/abstract/transaction.rb +155 -68
- data/lib/active_record/connection_adapters/abstract_adapter.rb +228 -98
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +243 -275
- data/lib/active_record/connection_adapters/column.rb +30 -12
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +86 -32
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
- data/lib/active_record/connection_adapters/mysql/quoting.rb +59 -7
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +34 -10
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +48 -32
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +18 -7
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +139 -19
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +14 -9
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +53 -18
- data/lib/active_record/connection_adapters/pool_config.rb +73 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +37 -28
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +38 -54
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +3 -4
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +3 -4
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +25 -7
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +15 -3
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +47 -10
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +19 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +120 -100
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +31 -26
- data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -120
- data/lib/active_record/connection_adapters/schema_cache.rb +127 -21
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +19 -6
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +144 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -7
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +77 -13
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +174 -186
- data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
- data/lib/active_record/connection_handling.rb +293 -33
- data/lib/active_record/core.rb +323 -97
- data/lib/active_record/counter_cache.rb +8 -30
- data/lib/active_record/database_configurations.rb +272 -0
- data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
- data/lib/active_record/database_configurations/database_config.rb +80 -0
- data/lib/active_record/database_configurations/hash_config.rb +96 -0
- data/lib/active_record/database_configurations/url_config.rb +53 -0
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/dynamic_matchers.rb +3 -4
- data/lib/active_record/enum.rb +111 -37
- data/lib/active_record/errors.rb +62 -19
- data/lib/active_record/explain.rb +10 -6
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +10 -17
- data/lib/active_record/fixture_set/model_metadata.rb +32 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +152 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +200 -481
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +53 -24
- data/lib/active_record/insert_all.rb +208 -0
- data/lib/active_record/integration.rb +67 -17
- data/lib/active_record/internal_metadata.rb +26 -9
- data/lib/active_record/legacy_yaml_adapter.rb +7 -3
- data/lib/active_record/locking/optimistic.rb +37 -23
- data/lib/active_record/locking/pessimistic.rb +9 -5
- data/lib/active_record/log_subscriber.rb +35 -35
- data/lib/active_record/middleware/database_selector.rb +77 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
- data/lib/active_record/migration.rb +206 -157
- data/lib/active_record/migration/command_recorder.rb +96 -44
- data/lib/active_record/migration/compatibility.rb +142 -64
- data/lib/active_record/migration/join_table.rb +0 -1
- data/lib/active_record/model_schema.rb +148 -22
- data/lib/active_record/nested_attributes.rb +4 -7
- data/lib/active_record/no_touching.rb +8 -1
- data/lib/active_record/null_relation.rb +0 -1
- data/lib/active_record/persistence.rb +267 -59
- data/lib/active_record/query_cache.rb +21 -4
- data/lib/active_record/querying.rb +40 -23
- data/lib/active_record/railtie.rb +115 -58
- data/lib/active_record/railties/console_sandbox.rb +2 -4
- data/lib/active_record/railties/controller_runtime.rb +30 -35
- data/lib/active_record/railties/databases.rake +408 -78
- data/lib/active_record/readonly_attributes.rb +4 -0
- data/lib/active_record/reflection.rb +109 -93
- data/lib/active_record/relation.rb +374 -104
- data/lib/active_record/relation/batches.rb +44 -35
- data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
- data/lib/active_record/relation/calculations.rb +153 -90
- data/lib/active_record/relation/delegation.rb +35 -50
- data/lib/active_record/relation/finder_methods.rb +64 -39
- data/lib/active_record/relation/from_clause.rb +5 -1
- data/lib/active_record/relation/merger.rb +32 -40
- data/lib/active_record/relation/predicate_builder.rb +62 -45
- data/lib/active_record/relation/predicate_builder/array_handler.rb +13 -13
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +5 -9
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +11 -10
- data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/query_attribute.rb +13 -8
- data/lib/active_record/relation/query_methods.rb +475 -186
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +9 -9
- data/lib/active_record/relation/where_clause.rb +111 -61
- data/lib/active_record/result.rb +64 -38
- data/lib/active_record/runtime_registry.rb +2 -2
- data/lib/active_record/sanitization.rb +22 -41
- data/lib/active_record/schema.rb +2 -11
- data/lib/active_record/schema_dumper.rb +54 -9
- data/lib/active_record/schema_migration.rb +7 -9
- data/lib/active_record/scoping.rb +8 -9
- data/lib/active_record/scoping/default.rb +4 -6
- data/lib/active_record/scoping/named.rb +17 -24
- data/lib/active_record/secure_token.rb +16 -8
- data/lib/active_record/serialization.rb +5 -3
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +49 -6
- data/lib/active_record/store.rb +88 -9
- data/lib/active_record/suppressor.rb +2 -2
- data/lib/active_record/table_metadata.rb +42 -43
- data/lib/active_record/tasks/database_tasks.rb +277 -81
- data/lib/active_record/tasks/mysql_database_tasks.rb +37 -39
- data/lib/active_record/tasks/postgresql_database_tasks.rb +27 -32
- data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -17
- data/lib/active_record/test_databases.rb +24 -0
- data/lib/active_record/test_fixtures.rb +246 -0
- data/lib/active_record/timestamp.rb +43 -32
- data/lib/active_record/touch_later.rb +23 -22
- data/lib/active_record/transactions.rb +62 -118
- data/lib/active_record/translation.rb +1 -1
- data/lib/active_record/type.rb +10 -5
- data/lib/active_record/type/adapter_specific_registry.rb +3 -13
- data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
- data/lib/active_record/type/serialized.rb +6 -3
- data/lib/active_record/type/time.rb +10 -0
- data/lib/active_record/type/type_map.rb +0 -1
- data/lib/active_record/type/unsigned_integer.rb +0 -1
- data/lib/active_record/type_caster/connection.rb +15 -15
- data/lib/active_record/type_caster/map.rb +8 -8
- data/lib/active_record/validations.rb +4 -3
- data/lib/active_record/validations/associated.rb +1 -2
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +38 -30
- data/lib/arel.rb +54 -0
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +41 -0
- data/lib/arel/collectors/bind.rb +29 -0
- data/lib/arel/collectors/composite.rb +39 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +27 -0
- data/lib/arel/collectors/substitute_binds.rb +35 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes.rb +70 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +126 -0
- data/lib/arel/nodes/bind_param.rb +44 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +62 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +15 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +11 -0
- data/lib/arel/nodes/homogeneous_in.rb +72 -0
- data/lib/arel/nodes/in.rb +15 -0
- data/lib/arel/nodes/infix_operation.rb +92 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +51 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +19 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +31 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +44 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +250 -0
- data/lib/arel/select_manager.rb +270 -0
- data/lib/arel/table.rb +118 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors.rb +13 -0
- data/lib/arel/visitors/dot.rb +308 -0
- data/lib/arel/visitors/mysql.rb +93 -0
- data/lib/arel/visitors/postgresql.rb +120 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +899 -0
- data/lib/arel/visitors/visitor.rb +45 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
- data/lib/rails/generators/active_record/migration.rb +19 -2
- data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -5
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +3 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +7 -5
- data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +119 -34
- data/lib/active_record/attribute_decorators.rb +0 -90
- data/lib/active_record/collection_cache_key.rb +0 -53
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -287
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -33
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -19
- data/lib/active_record/relation/where_clause_factory.rb +0 -34
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_record/associations/join_dependency/join_part"
|
4
|
+
require "active_support/core_ext/array/extract"
|
4
5
|
|
5
6
|
module ActiveRecord
|
6
7
|
module Associations
|
@@ -13,7 +14,6 @@ module ActiveRecord
|
|
13
14
|
super(reflection.klass, children)
|
14
15
|
|
15
16
|
@reflection = reflection
|
16
|
-
@tables = nil
|
17
17
|
end
|
18
18
|
|
19
19
|
def match?(other)
|
@@ -23,26 +23,47 @@ module ActiveRecord
|
|
23
23
|
|
24
24
|
def join_constraints(foreign_table, foreign_klass, join_type, alias_tracker)
|
25
25
|
joins = []
|
26
|
+
chain = []
|
27
|
+
|
28
|
+
reflection.chain.each do |reflection|
|
29
|
+
table, terminated = yield reflection
|
30
|
+
@table ||= table
|
31
|
+
|
32
|
+
if terminated
|
33
|
+
foreign_table, foreign_klass = table, reflection.klass
|
34
|
+
break
|
35
|
+
end
|
36
|
+
|
37
|
+
chain << [reflection, table]
|
38
|
+
end
|
26
39
|
|
27
40
|
# The chain starts with the target table, but we want to end with it here (makes
|
28
41
|
# more sense in this context), so we reverse
|
29
|
-
|
30
|
-
table = tables[-i]
|
42
|
+
chain.reverse_each do |reflection, table|
|
31
43
|
klass = reflection.klass
|
32
44
|
|
33
|
-
|
45
|
+
scope = reflection.join_scope(table, foreign_table, foreign_klass)
|
46
|
+
|
47
|
+
unless scope.references_values.empty?
|
48
|
+
associations = scope.eager_load_values | scope.includes_values
|
49
|
+
|
50
|
+
unless associations.empty?
|
51
|
+
scope.joins! scope.construct_join_dependency(associations, Arel::Nodes::OuterJoin)
|
52
|
+
end
|
53
|
+
end
|
34
54
|
|
35
|
-
arel =
|
55
|
+
arel = scope.arel(alias_tracker.aliases)
|
36
56
|
nodes = arel.constraints.first
|
37
57
|
|
38
|
-
|
39
|
-
|
58
|
+
if nodes.is_a?(Arel::Nodes::And)
|
59
|
+
others = nodes.children.extract! do |node|
|
60
|
+
!Arel.fetch_attribute(node) { |attr| attr.relation.name == table.name }
|
61
|
+
end
|
40
62
|
end
|
41
|
-
nodes = table.create_and(children)
|
42
63
|
|
43
|
-
joins <<
|
64
|
+
joins << join_type.new(table, Arel::Nodes::On.new(nodes))
|
44
65
|
|
45
|
-
|
66
|
+
if others && !others.empty?
|
46
67
|
joins.concat arel.join_sources
|
47
68
|
append_constraints(joins.last, others)
|
48
69
|
end
|
@@ -54,25 +75,26 @@ module ActiveRecord
|
|
54
75
|
joins
|
55
76
|
end
|
56
77
|
|
57
|
-
def
|
58
|
-
@
|
59
|
-
|
78
|
+
def readonly?
|
79
|
+
return @readonly if defined?(@readonly)
|
80
|
+
|
81
|
+
@readonly = reflection.scope && reflection.scope_for(base_klass.unscoped).readonly_value
|
60
82
|
end
|
61
83
|
|
62
|
-
|
63
|
-
|
64
|
-
case value
|
65
|
-
when Arel::Nodes::Between, Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual, Arel::Nodes::LessThan, Arel::Nodes::LessThanOrEqual, Arel::Nodes::GreaterThan, Arel::Nodes::GreaterThanOrEqual
|
66
|
-
yield value.left.is_a?(Arel::Attributes::Attribute) ? value.left : value.right
|
67
|
-
end
|
68
|
-
end
|
84
|
+
def strict_loading?
|
85
|
+
return @strict_loading if defined?(@strict_loading)
|
69
86
|
|
87
|
+
@strict_loading = reflection.scope && reflection.scope_for(base_klass.unscoped).strict_loading_value
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
70
91
|
def append_constraints(join, constraints)
|
71
92
|
if join.is_a?(Arel::Nodes::StringJoin)
|
72
|
-
join_string =
|
93
|
+
join_string = Arel::Nodes::And.new(constraints.unshift join.left)
|
73
94
|
join.left = Arel.sql(base_klass.connection.visitor.compile(join_string))
|
74
95
|
else
|
75
|
-
join.right
|
96
|
+
right = join.right
|
97
|
+
right.expr = Arel::Nodes::And.new(constraints.unshift right.expr)
|
76
98
|
end
|
77
99
|
end
|
78
100
|
end
|
@@ -17,7 +17,7 @@ module ActiveRecord
|
|
17
17
|
# association.
|
18
18
|
attr_reader :base_klass, :children
|
19
19
|
|
20
|
-
delegate :table_name, :column_names, :primary_key, to: :base_klass
|
20
|
+
delegate :table_name, :column_names, :primary_key, :attribute_types, to: :base_klass
|
21
21
|
|
22
22
|
def initialize(base_klass, children)
|
23
23
|
@base_klass = base_klass
|
@@ -54,16 +54,16 @@ module ActiveRecord
|
|
54
54
|
length = column_names_with_alias.length
|
55
55
|
|
56
56
|
while index < length
|
57
|
-
|
58
|
-
hash[
|
57
|
+
column = column_names_with_alias[index]
|
58
|
+
hash[column.name] = row[column.alias]
|
59
59
|
index += 1
|
60
60
|
end
|
61
61
|
|
62
62
|
hash
|
63
63
|
end
|
64
64
|
|
65
|
-
def instantiate(row, aliases, &block)
|
66
|
-
base_klass.instantiate(extract_record(row, aliases), &block)
|
65
|
+
def instantiate(row, aliases, column_types = {}, &block)
|
66
|
+
base_klass.instantiate(extract_record(row, aliases), column_types, &block)
|
67
67
|
end
|
68
68
|
end
|
69
69
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/core_ext/enumerable"
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
module Associations
|
5
7
|
# Implements the details of eager loading of Active Record associations.
|
@@ -58,7 +60,7 @@ module ActiveRecord
|
|
58
60
|
# == Parameters
|
59
61
|
# +records+ is an array of ActiveRecord::Base. This array needs not be flat,
|
60
62
|
# i.e. +records+ itself may also contain arrays of records. In any case,
|
61
|
-
# +preload_associations+ will preload
|
63
|
+
# +preload_associations+ will preload all associations records by
|
62
64
|
# flattening +records+.
|
63
65
|
#
|
64
66
|
# +associations+ specifies one or more associations that you want to
|
@@ -88,44 +90,46 @@ module ActiveRecord
|
|
88
90
|
if records.empty?
|
89
91
|
[]
|
90
92
|
else
|
91
|
-
records.uniq!
|
92
93
|
Array.wrap(associations).flat_map { |association|
|
93
94
|
preloaders_on association, records, preload_scope
|
94
95
|
}
|
95
96
|
end
|
96
97
|
end
|
97
98
|
|
98
|
-
|
99
|
+
def initialize(associate_by_default: true)
|
100
|
+
@associate_by_default = associate_by_default
|
101
|
+
end
|
99
102
|
|
103
|
+
private
|
100
104
|
# Loads all the given data into +records+ for the +association+.
|
101
|
-
def preloaders_on(association, records, scope)
|
105
|
+
def preloaders_on(association, records, scope, polymorphic_parent = false)
|
102
106
|
case association
|
103
107
|
when Hash
|
104
|
-
preloaders_for_hash(association, records, scope)
|
105
|
-
when Symbol
|
106
|
-
preloaders_for_one(association, records, scope)
|
107
|
-
when String
|
108
|
-
preloaders_for_one(association.to_sym, records, scope)
|
108
|
+
preloaders_for_hash(association, records, scope, polymorphic_parent)
|
109
|
+
when Symbol, String
|
110
|
+
preloaders_for_one(association, records, scope, polymorphic_parent)
|
109
111
|
else
|
110
112
|
raise ArgumentError, "#{association.inspect} was not recognized for preload"
|
111
113
|
end
|
112
114
|
end
|
113
115
|
|
114
|
-
def preloaders_for_hash(association, records, scope)
|
116
|
+
def preloaders_for_hash(association, records, scope, polymorphic_parent)
|
115
117
|
association.flat_map { |parent, child|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
118
|
+
grouped_records(parent, records, polymorphic_parent).flat_map do |reflection, reflection_records|
|
119
|
+
loaders = preloaders_for_reflection(reflection, reflection_records, scope)
|
120
|
+
recs = loaders.flat_map(&:preloaded_records).uniq
|
121
|
+
child_polymorphic_parent = reflection && reflection.options[:polymorphic]
|
122
|
+
loaders.concat Array.wrap(child).flat_map { |assoc|
|
123
|
+
preloaders_on assoc, recs, scope, child_polymorphic_parent
|
124
|
+
}
|
125
|
+
loaders
|
126
|
+
end
|
123
127
|
}
|
124
128
|
end
|
125
129
|
|
126
130
|
# Loads all the given data into +records+ for a singular +association+.
|
127
131
|
#
|
128
|
-
# Functions by instantiating a preloader class such as Preloader::
|
132
|
+
# Functions by instantiating a preloader class such as Preloader::Association and
|
129
133
|
# call the +run+ method for each passed in class in the +records+ argument.
|
130
134
|
#
|
131
135
|
# Not all records have the same class, so group then preload group on the reflection
|
@@ -135,41 +139,50 @@ module ActiveRecord
|
|
135
139
|
# Additionally, polymorphic belongs_to associations can have multiple associated
|
136
140
|
# classes, depending on the polymorphic_type field. So we group by the classes as
|
137
141
|
# well.
|
138
|
-
def preloaders_for_one(association, records, scope)
|
139
|
-
grouped_records(association, records
|
140
|
-
|
141
|
-
|
142
|
-
loader.run self
|
143
|
-
loader
|
142
|
+
def preloaders_for_one(association, records, scope, polymorphic_parent)
|
143
|
+
grouped_records(association, records, polymorphic_parent)
|
144
|
+
.flat_map do |reflection, reflection_records|
|
145
|
+
preloaders_for_reflection reflection, reflection_records, scope
|
144
146
|
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def preloaders_for_reflection(reflection, records, scope)
|
150
|
+
records.group_by { |record| record.association(reflection.name).klass }.map do |rhs_klass, rs|
|
151
|
+
preloader_for(reflection, rs).new(rhs_klass, rs, reflection, scope, @associate_by_default).run
|
145
152
|
end
|
146
153
|
end
|
147
154
|
|
148
|
-
def grouped_records(association, records)
|
155
|
+
def grouped_records(association, records, polymorphic_parent)
|
149
156
|
h = {}
|
150
157
|
records.each do |record|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
klasses = h[assoc.reflection] ||= {}
|
155
|
-
(klasses[assoc.klass] ||= []) << record
|
158
|
+
reflection = record.class._reflect_on_association(association)
|
159
|
+
next if polymorphic_parent && !reflection || !record.association(association).klass
|
160
|
+
(h[reflection] ||= []) << record
|
156
161
|
end
|
157
162
|
h
|
158
163
|
end
|
159
164
|
|
160
165
|
class AlreadyLoaded # :nodoc:
|
161
|
-
def initialize(klass, owners, reflection, preload_scope)
|
166
|
+
def initialize(klass, owners, reflection, preload_scope, associate_by_default = true)
|
162
167
|
@owners = owners
|
163
168
|
@reflection = reflection
|
164
169
|
end
|
165
170
|
|
166
|
-
def run
|
171
|
+
def run
|
172
|
+
self
|
173
|
+
end
|
167
174
|
|
168
175
|
def preloaded_records
|
169
|
-
|
176
|
+
@preloaded_records ||= records_by_owner.flat_map(&:last)
|
177
|
+
end
|
178
|
+
|
179
|
+
def records_by_owner
|
180
|
+
@records_by_owner ||= owners.index_with do |owner|
|
181
|
+
Array(owner.association(reflection.name).target)
|
182
|
+
end
|
170
183
|
end
|
171
184
|
|
172
|
-
|
185
|
+
private
|
173
186
|
attr_reader :owners, :reflection
|
174
187
|
end
|
175
188
|
|
@@ -4,33 +4,62 @@ module ActiveRecord
|
|
4
4
|
module Associations
|
5
5
|
class Preloader
|
6
6
|
class Association #:nodoc:
|
7
|
-
|
8
|
-
|
9
|
-
def initialize(klass, owners, reflection, preload_scope)
|
7
|
+
def initialize(klass, owners, reflection, preload_scope, associate_by_default = true)
|
10
8
|
@klass = klass
|
11
|
-
@owners = owners
|
9
|
+
@owners = owners.uniq(&:__id__)
|
12
10
|
@reflection = reflection
|
13
11
|
@preload_scope = preload_scope
|
12
|
+
@associate = associate_by_default || !preload_scope || preload_scope.empty_scope?
|
14
13
|
@model = owners.first && owners.first.class
|
15
|
-
@preloaded_records = []
|
16
14
|
end
|
17
15
|
|
18
|
-
def run
|
19
|
-
records =
|
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
|
16
|
+
def run
|
17
|
+
records = records_by_owner
|
24
18
|
|
25
19
|
owners.each do |owner|
|
26
|
-
associate_records_to_owner(owner, records[
|
27
|
-
end
|
20
|
+
associate_records_to_owner(owner, records[owner] || [])
|
21
|
+
end if @associate
|
22
|
+
|
23
|
+
self
|
28
24
|
end
|
29
25
|
|
30
|
-
|
31
|
-
|
26
|
+
def records_by_owner
|
27
|
+
load_records unless defined?(@records_by_owner)
|
28
|
+
|
29
|
+
@records_by_owner
|
30
|
+
end
|
31
|
+
|
32
|
+
def preloaded_records
|
33
|
+
load_records unless defined?(@preloaded_records)
|
34
|
+
|
35
|
+
@preloaded_records
|
36
|
+
end
|
32
37
|
|
33
38
|
private
|
39
|
+
attr_reader :owners, :reflection, :preload_scope, :model, :klass
|
40
|
+
|
41
|
+
def load_records
|
42
|
+
# owners can be duplicated when a relation has a collection association join
|
43
|
+
# #compare_by_identity makes such owners different hash keys
|
44
|
+
@records_by_owner = {}.compare_by_identity
|
45
|
+
raw_records = owner_keys.empty? ? [] : records_for(owner_keys)
|
46
|
+
|
47
|
+
@preloaded_records = raw_records.select do |record|
|
48
|
+
assignments = false
|
49
|
+
|
50
|
+
owners_by_key[convert_key(record[association_key_name])].each do |owner|
|
51
|
+
entries = (@records_by_owner[owner] ||= [])
|
52
|
+
|
53
|
+
if reflection.collection? || entries.empty?
|
54
|
+
entries << record
|
55
|
+
assignments = true
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
assignments
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
34
63
|
# The name of the key on the associated records
|
35
64
|
def association_key_name
|
36
65
|
reflection.join_primary_key(klass)
|
@@ -43,11 +72,10 @@ module ActiveRecord
|
|
43
72
|
|
44
73
|
def associate_records_to_owner(owner, records)
|
45
74
|
association = owner.association(reflection.name)
|
46
|
-
association.loaded!
|
47
75
|
if reflection.collection?
|
48
|
-
association.target
|
76
|
+
association.target = records
|
49
77
|
else
|
50
|
-
association.target = records.first
|
78
|
+
association.target = records.first
|
51
79
|
end
|
52
80
|
end
|
53
81
|
|
@@ -56,13 +84,10 @@ module ActiveRecord
|
|
56
84
|
end
|
57
85
|
|
58
86
|
def owners_by_key
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
h[key] = owner if key
|
63
|
-
end
|
87
|
+
@owners_by_key ||= owners.each_with_object({}) do |owner, result|
|
88
|
+
key = convert_key(owner[owner_key_name])
|
89
|
+
(result[key] ||= []) << owner if key
|
64
90
|
end
|
65
|
-
@owners_by_key
|
66
91
|
end
|
67
92
|
|
68
93
|
def key_conversion_required?
|
@@ -89,41 +114,44 @@ module ActiveRecord
|
|
89
114
|
@model.type_for_attribute(owner_key_name).type
|
90
115
|
end
|
91
116
|
|
92
|
-
def
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
end
|
100
|
-
@preloaded_records.group_by do |record|
|
101
|
-
convert_key(record[association_key_name])
|
117
|
+
def records_for(ids)
|
118
|
+
scope.where(association_key_name => ids).load do |record|
|
119
|
+
# Processing only the first owner
|
120
|
+
# because the record is modified but not an owner
|
121
|
+
owner = owners_by_key[convert_key(record[association_key_name])].first
|
122
|
+
association = owner.association(reflection.name)
|
123
|
+
association.set_inverse_instance(record)
|
102
124
|
end
|
103
125
|
end
|
104
126
|
|
105
|
-
def records_for(ids, &block)
|
106
|
-
scope.where(association_key_name => ids).load(&block)
|
107
|
-
end
|
108
|
-
|
109
127
|
def scope
|
110
128
|
@scope ||= build_scope
|
111
129
|
end
|
112
130
|
|
113
131
|
def reflection_scope
|
114
|
-
@reflection_scope ||=
|
132
|
+
@reflection_scope ||= begin
|
133
|
+
reflection.join_scopes(klass.arel_table, klass.predicate_builder, klass).inject(&:merge!) || klass.unscoped
|
134
|
+
end
|
115
135
|
end
|
116
136
|
|
117
137
|
def build_scope
|
118
138
|
scope = klass.scope_for_association
|
119
139
|
|
120
|
-
if reflection.type
|
140
|
+
if reflection.type && !reflection.through_reflection?
|
121
141
|
scope.where!(reflection.type => model.polymorphic_name)
|
122
142
|
end
|
123
143
|
|
124
|
-
scope.merge!(reflection_scope)
|
125
|
-
|
126
|
-
|
144
|
+
scope.merge!(reflection_scope) unless reflection_scope.empty_scope?
|
145
|
+
|
146
|
+
if preload_scope && !preload_scope.empty_scope?
|
147
|
+
scope.merge!(preload_scope)
|
148
|
+
end
|
149
|
+
|
150
|
+
if preload_scope && preload_scope.strict_loading_value
|
151
|
+
scope.strict_loading
|
152
|
+
else
|
153
|
+
scope
|
154
|
+
end
|
127
155
|
end
|
128
156
|
end
|
129
157
|
end
|