activerecord 5.2.4.1 → 6.0.1
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 +676 -572
- data/MIT-LICENSE +3 -1
- data/README.rdoc +4 -2
- data/examples/performance.rb +1 -1
- data/lib/active_record.rb +9 -2
- data/lib/active_record/aggregations.rb +4 -2
- data/lib/active_record/association_relation.rb +15 -6
- data/lib/active_record/associations.rb +20 -15
- data/lib/active_record/associations/association.rb +61 -20
- data/lib/active_record/associations/association_scope.rb +4 -6
- data/lib/active_record/associations/belongs_to_association.rb +36 -42
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
- data/lib/active_record/associations/builder/association.rb +14 -18
- data/lib/active_record/associations/builder/belongs_to.rb +19 -52
- data/lib/active_record/associations/builder/collection_association.rb +3 -13
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
- data/lib/active_record/associations/builder/has_many.rb +2 -0
- data/lib/active_record/associations/builder/has_one.rb +35 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -0
- data/lib/active_record/associations/collection_association.rb +6 -21
- data/lib/active_record/associations/collection_proxy.rb +12 -15
- data/lib/active_record/associations/foreign_association.rb +7 -0
- data/lib/active_record/associations/has_many_association.rb +2 -10
- data/lib/active_record/associations/has_many_through_association.rb +14 -14
- data/lib/active_record/associations/has_one_association.rb +28 -30
- data/lib/active_record/associations/has_one_through_association.rb +5 -5
- data/lib/active_record/associations/join_dependency.rb +28 -28
- data/lib/active_record/associations/join_dependency/join_association.rb +9 -10
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
- data/lib/active_record/associations/preloader.rb +40 -32
- data/lib/active_record/associations/preloader/association.rb +38 -36
- data/lib/active_record/associations/preloader/through_association.rb +48 -39
- data/lib/active_record/associations/singular_association.rb +2 -16
- data/lib/active_record/attribute_assignment.rb +7 -10
- data/lib/active_record/attribute_methods.rb +28 -100
- data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
- data/lib/active_record/attribute_methods/dirty.rb +111 -40
- data/lib/active_record/attribute_methods/primary_key.rb +15 -22
- data/lib/active_record/attribute_methods/query.rb +2 -3
- data/lib/active_record/attribute_methods/read.rb +15 -53
- data/lib/active_record/attribute_methods/serialization.rb +1 -1
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
- data/lib/active_record/attribute_methods/write.rb +17 -24
- data/lib/active_record/attributes.rb +13 -0
- data/lib/active_record/autosave_association.rb +2 -2
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/callbacks.rb +5 -19
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +104 -16
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +99 -123
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +18 -8
- data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +19 -12
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +76 -48
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +132 -53
- data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -56
- data/lib/active_record/connection_adapters/abstract_adapter.rb +187 -43
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +138 -195
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +53 -43
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +6 -10
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +75 -13
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +129 -13
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +22 -1
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/range.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 +6 -3
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +12 -1
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +55 -53
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +164 -74
- data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -6
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +42 -11
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +129 -141
- data/lib/active_record/connection_handling.rb +155 -26
- data/lib/active_record/core.rb +103 -59
- data/lib/active_record/counter_cache.rb +4 -29
- data/lib/active_record/database_configurations.rb +233 -0
- data/lib/active_record/database_configurations/database_config.rb +37 -0
- data/lib/active_record/database_configurations/hash_config.rb +50 -0
- data/lib/active_record/database_configurations/url_config.rb +79 -0
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/enum.rb +37 -7
- data/lib/active_record/errors.rb +15 -7
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixture_set/model_metadata.rb +33 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +153 -0
- data/lib/active_record/fixture_set/table_rows.rb +47 -0
- data/lib/active_record/fixtures.rb +145 -472
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +13 -3
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +68 -16
- data/lib/active_record/internal_metadata.rb +10 -2
- data/lib/active_record/locking/optimistic.rb +5 -6
- data/lib/active_record/locking/pessimistic.rb +3 -3
- data/lib/active_record/log_subscriber.rb +7 -26
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/migration.rb +100 -81
- data/lib/active_record/migration/command_recorder.rb +50 -6
- data/lib/active_record/migration/compatibility.rb +76 -49
- data/lib/active_record/model_schema.rb +33 -9
- data/lib/active_record/nested_attributes.rb +2 -2
- data/lib/active_record/no_touching.rb +7 -0
- data/lib/active_record/persistence.rb +228 -24
- data/lib/active_record/query_cache.rb +11 -4
- data/lib/active_record/querying.rb +32 -20
- data/lib/active_record/railtie.rb +80 -43
- data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
- data/lib/active_record/railties/controller_runtime.rb +30 -35
- data/lib/active_record/railties/databases.rake +196 -46
- data/lib/active_record/reflection.rb +32 -30
- data/lib/active_record/relation.rb +311 -80
- data/lib/active_record/relation/batches.rb +13 -10
- data/lib/active_record/relation/calculations.rb +53 -47
- data/lib/active_record/relation/delegation.rb +26 -43
- data/lib/active_record/relation/finder_methods.rb +23 -27
- data/lib/active_record/relation/merger.rb +11 -20
- data/lib/active_record/relation/predicate_builder.rb +4 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
- data/lib/active_record/relation/query_attribute.rb +13 -8
- data/lib/active_record/relation/query_methods.rb +202 -64
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/relation/where_clause.rb +14 -10
- data/lib/active_record/relation/where_clause_factory.rb +1 -2
- data/lib/active_record/result.rb +30 -11
- data/lib/active_record/sanitization.rb +32 -40
- data/lib/active_record/schema.rb +2 -11
- data/lib/active_record/schema_dumper.rb +22 -7
- data/lib/active_record/schema_migration.rb +5 -1
- data/lib/active_record/scoping.rb +8 -8
- data/lib/active_record/scoping/default.rb +4 -5
- data/lib/active_record/scoping/named.rb +19 -15
- data/lib/active_record/statement_cache.rb +30 -3
- data/lib/active_record/store.rb +87 -8
- data/lib/active_record/table_metadata.rb +10 -17
- data/lib/active_record/tasks/database_tasks.rb +194 -25
- data/lib/active_record/tasks/mysql_database_tasks.rb +5 -5
- data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
- data/lib/active_record/test_databases.rb +23 -0
- data/lib/active_record/test_fixtures.rb +224 -0
- data/lib/active_record/timestamp.rb +39 -25
- data/lib/active_record/touch_later.rb +4 -2
- data/lib/active_record/transactions.rb +56 -65
- data/lib/active_record/translation.rb +1 -1
- data/lib/active_record/type.rb +3 -4
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type_caster/connection.rb +15 -14
- data/lib/active_record/type_caster/map.rb +1 -4
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record/validations/uniqueness.rb +15 -27
- data/lib/arel.rb +58 -0
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes.rb +22 -0
- data/lib/arel/attributes/attribute.rb +37 -0
- data/lib/arel/collectors/bind.rb +24 -0
- data/lib/arel/collectors/composite.rb +31 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +20 -0
- data/lib/arel/collectors/substitute_binds.rb +28 -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 +68 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +52 -0
- data/lib/arel/nodes/bind_param.rb +36 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +50 -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 +18 -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 +8 -0
- data/lib/arel/nodes/in.rb +8 -0
- data/lib/arel/nodes/infix_operation.rb +80 -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 +50 -0
- data/lib/arel/nodes/node_expression.rb +13 -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 +16 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +27 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +45 -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 +257 -0
- data/lib/arel/select_manager.rb +271 -0
- data/lib/arel/table.rb +110 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors.rb +20 -0
- data/lib/arel/visitors/depth_first.rb +204 -0
- data/lib/arel/visitors/dot.rb +297 -0
- data/lib/arel/visitors/ibm_db.rb +34 -0
- data/lib/arel/visitors/informix.rb +62 -0
- data/lib/arel/visitors/mssql.rb +157 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +159 -0
- data/lib/arel/visitors/oracle12.rb +66 -0
- data/lib/arel/visitors/postgresql.rb +110 -0
- data/lib/arel/visitors/sqlite.rb +39 -0
- data/lib/arel/visitors/to_sql.rb +889 -0
- data/lib/arel/visitors/visitor.rb +46 -0
- data/lib/arel/visitors/where_sql.rb +23 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/rails/generators/active_record/migration.rb +14 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +110 -25
- data/lib/active_record/collection_cache_key.rb +0 -53
@@ -36,14 +36,6 @@ module ActiveRecord
|
|
36
36
|
super
|
37
37
|
end
|
38
38
|
|
39
|
-
def empty?
|
40
|
-
if reflection.has_cached_counter?
|
41
|
-
size.zero?
|
42
|
-
else
|
43
|
-
super
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
39
|
private
|
48
40
|
|
49
41
|
# Returns the number of records in this collection.
|
@@ -69,7 +61,7 @@ module ActiveRecord
|
|
69
61
|
# If there's nothing in the database and @target has no new records
|
70
62
|
# we are certain the current target is an empty array. This is a
|
71
63
|
# documented side-effect of the method that may avoid an extra SELECT.
|
72
|
-
|
64
|
+
loaded! if count == 0
|
73
65
|
|
74
66
|
[association_scope.limit_value, count].compact.min
|
75
67
|
end
|
@@ -92,7 +84,7 @@ module ActiveRecord
|
|
92
84
|
if method == :delete_all
|
93
85
|
scope.delete_all
|
94
86
|
else
|
95
|
-
scope.update_all(
|
87
|
+
scope.update_all(nullified_owner_attributes)
|
96
88
|
end
|
97
89
|
end
|
98
90
|
|
@@ -21,20 +21,6 @@ module ActiveRecord
|
|
21
21
|
super
|
22
22
|
end
|
23
23
|
|
24
|
-
def concat_records(records)
|
25
|
-
ensure_not_nested
|
26
|
-
|
27
|
-
records = super(records, true)
|
28
|
-
|
29
|
-
if owner.new_record? && records
|
30
|
-
records.flatten.each do |record|
|
31
|
-
build_through_record(record)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
records
|
36
|
-
end
|
37
|
-
|
38
24
|
def insert_record(record, validate = true, raise = false)
|
39
25
|
ensure_not_nested
|
40
26
|
|
@@ -48,6 +34,20 @@ module ActiveRecord
|
|
48
34
|
end
|
49
35
|
|
50
36
|
private
|
37
|
+
def concat_records(records)
|
38
|
+
ensure_not_nested
|
39
|
+
|
40
|
+
records = super(records, true)
|
41
|
+
|
42
|
+
if owner.new_record? && records
|
43
|
+
records.flatten.each do |record|
|
44
|
+
build_through_record(record)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
records
|
49
|
+
end
|
50
|
+
|
51
51
|
# The through record (built with build_record) is temporarily cached
|
52
52
|
# so that it may be reused if insert_record is subsequently called.
|
53
53
|
#
|
@@ -23,35 +23,6 @@ module ActiveRecord
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
def replace(record, save = true)
|
27
|
-
raise_on_type_mismatch!(record) if record
|
28
|
-
load_target
|
29
|
-
|
30
|
-
return target unless target || record
|
31
|
-
|
32
|
-
assigning_another_record = target != record
|
33
|
-
if assigning_another_record || record.has_changes_to_save?
|
34
|
-
save &&= owner.persisted?
|
35
|
-
|
36
|
-
transaction_if(save) do
|
37
|
-
remove_target!(options[:dependent]) if target && !target.destroyed? && assigning_another_record
|
38
|
-
|
39
|
-
if record
|
40
|
-
set_owner_attributes(record)
|
41
|
-
set_inverse_instance(record)
|
42
|
-
|
43
|
-
if save && !record.save
|
44
|
-
nullify_owner_attributes(record)
|
45
|
-
set_owner_attributes(target) if target
|
46
|
-
raise RecordNotSaved, "Failed to save the new associated #{reflection.name}."
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
self.target = record
|
53
|
-
end
|
54
|
-
|
55
26
|
def delete(method = options[:dependent])
|
56
27
|
if load_target
|
57
28
|
case method
|
@@ -62,12 +33,39 @@ module ActiveRecord
|
|
62
33
|
target.destroy
|
63
34
|
throw(:abort) unless target.destroyed?
|
64
35
|
when :nullify
|
65
|
-
target.update_columns(
|
36
|
+
target.update_columns(nullified_owner_attributes) if target.persisted?
|
66
37
|
end
|
67
38
|
end
|
68
39
|
end
|
69
40
|
|
70
41
|
private
|
42
|
+
def replace(record, save = true)
|
43
|
+
raise_on_type_mismatch!(record) if record
|
44
|
+
|
45
|
+
return target unless load_target || record
|
46
|
+
|
47
|
+
assigning_another_record = target != record
|
48
|
+
if assigning_another_record || record.has_changes_to_save?
|
49
|
+
save &&= owner.persisted?
|
50
|
+
|
51
|
+
transaction_if(save) do
|
52
|
+
remove_target!(options[:dependent]) if target && !target.destroyed? && assigning_another_record
|
53
|
+
|
54
|
+
if record
|
55
|
+
set_owner_attributes(record)
|
56
|
+
set_inverse_instance(record)
|
57
|
+
|
58
|
+
if save && !record.save
|
59
|
+
nullify_owner_attributes(record)
|
60
|
+
set_owner_attributes(target) if target
|
61
|
+
raise RecordNotSaved, "Failed to save the new associated #{reflection.name}."
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
self.target = record
|
68
|
+
end
|
71
69
|
|
72
70
|
# The reason that the save param for replace is false, if for create (not just build),
|
73
71
|
# is because the setting of the foreign keys is actually handled by the scoping when
|
@@ -6,12 +6,12 @@ module ActiveRecord
|
|
6
6
|
class HasOneThroughAssociation < HasOneAssociation #:nodoc:
|
7
7
|
include ThroughAssociation
|
8
8
|
|
9
|
-
def replace(record, save = true)
|
10
|
-
create_through_record(record, save)
|
11
|
-
self.target = record
|
12
|
-
end
|
13
|
-
|
14
9
|
private
|
10
|
+
def replace(record, save = true)
|
11
|
+
create_through_record(record, save)
|
12
|
+
self.target = record
|
13
|
+
end
|
14
|
+
|
15
15
|
def create_through_record(record, save)
|
16
16
|
ensure_not_nested
|
17
17
|
|
@@ -14,10 +14,8 @@ module ActiveRecord
|
|
14
14
|
i[column.name] = column.alias
|
15
15
|
}
|
16
16
|
}
|
17
|
-
@
|
18
|
-
h[table.node] = table.columns
|
19
|
-
[column.name, column.alias]
|
20
|
-
}
|
17
|
+
@columns_cache = tables.each_with_object({}) { |table, h|
|
18
|
+
h[table.node] = table.columns
|
21
19
|
}
|
22
20
|
end
|
23
21
|
|
@@ -25,9 +23,8 @@ module ActiveRecord
|
|
25
23
|
@tables.flat_map(&:column_aliases)
|
26
24
|
end
|
27
25
|
|
28
|
-
# An array of [column_name, alias] pairs for the table
|
29
26
|
def column_aliases(node)
|
30
|
-
@
|
27
|
+
@columns_cache[node]
|
31
28
|
end
|
32
29
|
|
33
30
|
def column_alias(node, column)
|
@@ -67,16 +64,21 @@ module ActiveRecord
|
|
67
64
|
end
|
68
65
|
end
|
69
66
|
|
70
|
-
def initialize(base, table, associations)
|
67
|
+
def initialize(base, table, associations, join_type)
|
71
68
|
tree = self.class.make_tree associations
|
72
69
|
@join_root = JoinBase.new(base, table, build(tree, base))
|
70
|
+
@join_type = join_type
|
71
|
+
end
|
72
|
+
|
73
|
+
def base_klass
|
74
|
+
join_root.base_klass
|
73
75
|
end
|
74
76
|
|
75
77
|
def reflections
|
76
78
|
join_root.drop(1).map!(&:reflection)
|
77
79
|
end
|
78
80
|
|
79
|
-
def join_constraints(joins_to_add,
|
81
|
+
def join_constraints(joins_to_add, alias_tracker)
|
80
82
|
@alias_tracker = alias_tracker
|
81
83
|
|
82
84
|
construct_tables!(join_root)
|
@@ -85,9 +87,9 @@ module ActiveRecord
|
|
85
87
|
joins.concat joins_to_add.flat_map { |oj|
|
86
88
|
construct_tables!(oj.join_root)
|
87
89
|
if join_root.match? oj.join_root
|
88
|
-
walk join_root, oj.
|
90
|
+
walk(join_root, oj.join_root, oj.join_type)
|
89
91
|
else
|
90
|
-
make_join_constraints(oj.join_root, join_type)
|
92
|
+
make_join_constraints(oj.join_root, oj.join_type)
|
91
93
|
end
|
92
94
|
}
|
93
95
|
end
|
@@ -116,7 +118,7 @@ module ActiveRecord
|
|
116
118
|
result_set.each { |row_hash|
|
117
119
|
parent_key = primary_key ? row_hash[primary_key] : row_hash
|
118
120
|
parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases, &block)
|
119
|
-
construct(parent, join_root, row_hash,
|
121
|
+
construct(parent, join_root, row_hash, seen, model_cache)
|
120
122
|
}
|
121
123
|
end
|
122
124
|
|
@@ -128,9 +130,11 @@ module ActiveRecord
|
|
128
130
|
end
|
129
131
|
|
130
132
|
protected
|
131
|
-
attr_reader :
|
133
|
+
attr_reader :join_root, :join_type
|
132
134
|
|
133
135
|
private
|
136
|
+
attr_reader :alias_tracker
|
137
|
+
|
134
138
|
def aliases
|
135
139
|
@aliases ||= Aliases.new join_root.each_with_index.map { |join_part, i|
|
136
140
|
columns = join_part.column_names.each_with_index.map { |column_name, j|
|
@@ -152,7 +156,7 @@ module ActiveRecord
|
|
152
156
|
end
|
153
157
|
end
|
154
158
|
|
155
|
-
def make_constraints(parent, child, join_type
|
159
|
+
def make_constraints(parent, child, join_type)
|
156
160
|
foreign_table = parent.table
|
157
161
|
foreign_klass = parent.base_klass
|
158
162
|
joins = child.join_constraints(foreign_table, foreign_klass, join_type, alias_tracker)
|
@@ -170,17 +174,17 @@ module ActiveRecord
|
|
170
174
|
end
|
171
175
|
|
172
176
|
def table_alias_for(reflection, parent, join)
|
173
|
-
name =
|
177
|
+
name = reflection.alias_candidate(parent.table_name)
|
174
178
|
join ? "#{name}_join" : name
|
175
179
|
end
|
176
180
|
|
177
|
-
def walk(left, right)
|
181
|
+
def walk(left, right, join_type)
|
178
182
|
intersection, missing = right.children.map { |node1|
|
179
183
|
[left.children.find { |node2| node1.match? node2 }, node1]
|
180
184
|
}.partition(&:first)
|
181
185
|
|
182
|
-
joins = intersection.flat_map { |l, r| r.table = l.table; walk(l, r) }
|
183
|
-
joins.concat missing.flat_map { |_, n| make_constraints(left, n) }
|
186
|
+
joins = intersection.flat_map { |l, r| r.table = l.table; walk(l, r, join_type) }
|
187
|
+
joins.concat missing.flat_map { |_, n| make_constraints(left, n, join_type) }
|
184
188
|
end
|
185
189
|
|
186
190
|
def find_reflection(klass, name)
|
@@ -202,7 +206,7 @@ module ActiveRecord
|
|
202
206
|
end
|
203
207
|
end
|
204
208
|
|
205
|
-
def construct(ar_parent, parent, row,
|
209
|
+
def construct(ar_parent, parent, row, seen, model_cache)
|
206
210
|
return if ar_parent.nil?
|
207
211
|
|
208
212
|
parent.children.each do |node|
|
@@ -211,7 +215,7 @@ module ActiveRecord
|
|
211
215
|
other.loaded!
|
212
216
|
elsif ar_parent.association_cached?(node.reflection.name)
|
213
217
|
model = ar_parent.association(node.reflection.name).target
|
214
|
-
construct(model, node, row,
|
218
|
+
construct(model, node, row, seen, model_cache)
|
215
219
|
next
|
216
220
|
end
|
217
221
|
|
@@ -226,22 +230,17 @@ module ActiveRecord
|
|
226
230
|
model = seen[ar_parent.object_id][node][id]
|
227
231
|
|
228
232
|
if model
|
229
|
-
construct(model, node, row,
|
233
|
+
construct(model, node, row, seen, model_cache)
|
230
234
|
else
|
231
|
-
model = construct_model(ar_parent, node, row, model_cache, id
|
232
|
-
|
233
|
-
if node.reflection.scope &&
|
234
|
-
node.reflection.scope_for(node.base_klass.unscoped).readonly_value
|
235
|
-
model.readonly!
|
236
|
-
end
|
235
|
+
model = construct_model(ar_parent, node, row, model_cache, id)
|
237
236
|
|
238
237
|
seen[ar_parent.object_id][node][id] = model
|
239
|
-
construct(model, node, row,
|
238
|
+
construct(model, node, row, seen, model_cache)
|
240
239
|
end
|
241
240
|
end
|
242
241
|
end
|
243
242
|
|
244
|
-
def construct_model(record, node, row, model_cache, id
|
243
|
+
def construct_model(record, node, row, model_cache, id)
|
245
244
|
other = record.association(node.reflection.name)
|
246
245
|
|
247
246
|
model = model_cache[node][id] ||=
|
@@ -255,6 +254,7 @@ module ActiveRecord
|
|
255
254
|
other.target = model
|
256
255
|
end
|
257
256
|
|
257
|
+
model.readonly! if node.readonly?
|
258
258
|
model
|
259
259
|
end
|
260
260
|
end
|
@@ -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
|
@@ -35,10 +36,9 @@ module ActiveRecord
|
|
35
36
|
arel = join_scope.arel(alias_tracker.aliases)
|
36
37
|
nodes = arel.constraints.first
|
37
38
|
|
38
|
-
others
|
39
|
-
!
|
39
|
+
others = nodes.children.extract! do |node|
|
40
|
+
!Arel.fetch_attribute(node) { |attr| attr.relation.name == table.name }
|
40
41
|
end
|
41
|
-
nodes = table.create_and(children)
|
42
42
|
|
43
43
|
joins << table.create_join(table, table.create_on(nodes), join_type)
|
44
44
|
|
@@ -59,14 +59,13 @@ module ActiveRecord
|
|
59
59
|
@table = tables.first
|
60
60
|
end
|
61
61
|
|
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
|
62
|
+
def readonly?
|
63
|
+
return @readonly if defined?(@readonly)
|
69
64
|
|
65
|
+
@readonly = reflection.scope && reflection.scope_for(base_klass.unscoped).readonly_value
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
70
69
|
def append_constraints(join, constraints)
|
71
70
|
if join.is_a?(Arel::Nodes::StringJoin)
|
72
71
|
join_string = table.create_and(constraints.unshift(join.left))
|
@@ -54,8 +54,8 @@ 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
|
|
@@ -88,7 +88,6 @@ module ActiveRecord
|
|
88
88
|
if records.empty?
|
89
89
|
[]
|
90
90
|
else
|
91
|
-
records.uniq!
|
92
91
|
Array.wrap(associations).flat_map { |association|
|
93
92
|
preloaders_on association, records, preload_scope
|
94
93
|
}
|
@@ -98,34 +97,34 @@ module ActiveRecord
|
|
98
97
|
private
|
99
98
|
|
100
99
|
# Loads all the given data into +records+ for the +association+.
|
101
|
-
def preloaders_on(association, records, scope)
|
100
|
+
def preloaders_on(association, records, scope, polymorphic_parent = false)
|
102
101
|
case association
|
103
102
|
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)
|
103
|
+
preloaders_for_hash(association, records, scope, polymorphic_parent)
|
104
|
+
when Symbol, String
|
105
|
+
preloaders_for_one(association, records, scope, polymorphic_parent)
|
109
106
|
else
|
110
107
|
raise ArgumentError, "#{association.inspect} was not recognized for preload"
|
111
108
|
end
|
112
109
|
end
|
113
110
|
|
114
|
-
def preloaders_for_hash(association, records, scope)
|
111
|
+
def preloaders_for_hash(association, records, scope, polymorphic_parent)
|
115
112
|
association.flat_map { |parent, child|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
113
|
+
grouped_records(parent, records, polymorphic_parent).flat_map do |reflection, reflection_records|
|
114
|
+
loaders = preloaders_for_reflection(reflection, reflection_records, scope)
|
115
|
+
recs = loaders.flat_map(&:preloaded_records).uniq
|
116
|
+
child_polymorphic_parent = reflection && reflection.options[:polymorphic]
|
117
|
+
loaders.concat Array.wrap(child).flat_map { |assoc|
|
118
|
+
preloaders_on assoc, recs, scope, child_polymorphic_parent
|
119
|
+
}
|
120
|
+
loaders
|
121
|
+
end
|
123
122
|
}
|
124
123
|
end
|
125
124
|
|
126
125
|
# Loads all the given data into +records+ for a singular +association+.
|
127
126
|
#
|
128
|
-
# Functions by instantiating a preloader class such as Preloader::
|
127
|
+
# Functions by instantiating a preloader class such as Preloader::Association and
|
129
128
|
# call the +run+ method for each passed in class in the +records+ argument.
|
130
129
|
#
|
131
130
|
# Not all records have the same class, so group then preload group on the reflection
|
@@ -135,24 +134,25 @@ module ActiveRecord
|
|
135
134
|
# Additionally, polymorphic belongs_to associations can have multiple associated
|
136
135
|
# classes, depending on the polymorphic_type field. So we group by the classes as
|
137
136
|
# well.
|
138
|
-
def preloaders_for_one(association, records, scope)
|
139
|
-
grouped_records(association, records
|
140
|
-
|
141
|
-
|
142
|
-
loader.run self
|
143
|
-
loader
|
137
|
+
def preloaders_for_one(association, records, scope, polymorphic_parent)
|
138
|
+
grouped_records(association, records, polymorphic_parent)
|
139
|
+
.flat_map do |reflection, reflection_records|
|
140
|
+
preloaders_for_reflection reflection, reflection_records, scope
|
144
141
|
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def preloaders_for_reflection(reflection, records, scope)
|
145
|
+
records.group_by { |record| record.association(reflection.name).klass }.map do |rhs_klass, rs|
|
146
|
+
preloader_for(reflection, rs).new(rhs_klass, rs, reflection, scope).run
|
145
147
|
end
|
146
148
|
end
|
147
149
|
|
148
|
-
def grouped_records(association, records)
|
150
|
+
def grouped_records(association, records, polymorphic_parent)
|
149
151
|
h = {}
|
150
152
|
records.each do |record|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
klasses = h[assoc.reflection] ||= {}
|
155
|
-
(klasses[assoc.klass] ||= []) << record
|
153
|
+
reflection = record.class._reflect_on_association(association)
|
154
|
+
next if polymorphic_parent && !reflection || !record.association(association).klass
|
155
|
+
(h[reflection] ||= []) << record
|
156
156
|
end
|
157
157
|
h
|
158
158
|
end
|
@@ -163,13 +163,21 @@ module ActiveRecord
|
|
163
163
|
@reflection = reflection
|
164
164
|
end
|
165
165
|
|
166
|
-
def run
|
166
|
+
def run
|
167
|
+
self
|
168
|
+
end
|
167
169
|
|
168
170
|
def preloaded_records
|
169
|
-
|
171
|
+
@preloaded_records ||= records_by_owner.flat_map(&:last)
|
172
|
+
end
|
173
|
+
|
174
|
+
def records_by_owner
|
175
|
+
@records_by_owner ||= owners.each_with_object({}) do |owner, result|
|
176
|
+
result[owner] = Array(owner.association(reflection.name).target)
|
177
|
+
end
|
170
178
|
end
|
171
179
|
|
172
|
-
|
180
|
+
private
|
173
181
|
attr_reader :owners, :reflection
|
174
182
|
end
|
175
183
|
|
@@ -177,7 +185,7 @@ module ActiveRecord
|
|
177
185
|
# and attach it to a relation. The class returned implements a `run` method
|
178
186
|
# that accepts a preloader.
|
179
187
|
def preloader_for(reflection, owners)
|
180
|
-
if owners.
|
188
|
+
if owners.first.association(reflection.name).loaded?
|
181
189
|
return AlreadyLoaded
|
182
190
|
end
|
183
191
|
reflection.check_preloadable!
|