activerecord 5.0.7 → 5.1.7
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 +657 -2080
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/examples/performance.rb +28 -28
- data/examples/simple.rb +3 -3
- data/lib/active_record/aggregations.rb +244 -244
- data/lib/active_record/association_relation.rb +5 -5
- data/lib/active_record/associations/alias_tracker.rb +10 -11
- data/lib/active_record/associations/association.rb +23 -5
- data/lib/active_record/associations/association_scope.rb +95 -81
- data/lib/active_record/associations/belongs_to_association.rb +7 -4
- data/lib/active_record/associations/builder/belongs_to.rb +30 -16
- data/lib/active_record/associations/builder/collection_association.rb +1 -2
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +27 -27
- data/lib/active_record/associations/collection_association.rb +36 -205
- data/lib/active_record/associations/collection_proxy.rb +132 -63
- data/lib/active_record/associations/has_many_association.rb +10 -19
- data/lib/active_record/associations/has_many_through_association.rb +12 -4
- data/lib/active_record/associations/has_one_association.rb +24 -28
- data/lib/active_record/associations/has_one_through_association.rb +5 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +4 -28
- data/lib/active_record/associations/join_dependency/join_base.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +121 -118
- data/lib/active_record/associations/preloader/association.rb +64 -64
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -2
- data/lib/active_record/associations/preloader/collection_association.rb +6 -6
- data/lib/active_record/associations/preloader/has_many.rb +0 -2
- data/lib/active_record/associations/preloader/singular_association.rb +6 -8
- data/lib/active_record/associations/preloader/through_association.rb +41 -41
- data/lib/active_record/associations/preloader.rb +94 -94
- data/lib/active_record/associations/singular_association.rb +8 -25
- data/lib/active_record/associations/through_association.rb +2 -5
- data/lib/active_record/associations.rb +1591 -1562
- data/lib/active_record/attribute/user_provided_default.rb +4 -2
- data/lib/active_record/attribute.rb +98 -71
- data/lib/active_record/attribute_assignment.rb +61 -61
- data/lib/active_record/attribute_decorators.rb +35 -13
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -7
- data/lib/active_record/attribute_methods/dirty.rb +229 -46
- data/lib/active_record/attribute_methods/primary_key.rb +74 -73
- data/lib/active_record/attribute_methods/read.rb +39 -35
- data/lib/active_record/attribute_methods/serialization.rb +7 -7
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +35 -58
- data/lib/active_record/attribute_methods/write.rb +30 -33
- data/lib/active_record/attribute_methods.rb +56 -65
- data/lib/active_record/attribute_mutation_tracker.rb +63 -11
- data/lib/active_record/attribute_set/builder.rb +27 -33
- data/lib/active_record/attribute_set/yaml_encoder.rb +41 -0
- data/lib/active_record/attribute_set.rb +9 -6
- data/lib/active_record/attributes.rb +22 -22
- data/lib/active_record/autosave_association.rb +18 -13
- data/lib/active_record/base.rb +24 -22
- data/lib/active_record/callbacks.rb +56 -14
- data/lib/active_record/coders/yaml_column.rb +9 -11
- data/lib/active_record/collection_cache_key.rb +3 -4
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +330 -284
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +1 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +39 -37
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +32 -27
- data/lib/active_record/connection_adapters/abstract/quoting.rb +62 -51
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +10 -20
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +74 -79
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +53 -41
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +120 -100
- data/lib/active_record/connection_adapters/abstract/transaction.rb +49 -43
- data/lib/active_record/connection_adapters/abstract_adapter.rb +165 -135
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +404 -424
- data/lib/active_record/connection_adapters/column.rb +26 -4
- data/lib/active_record/connection_adapters/connection_specification.rb +128 -118
- data/lib/active_record/connection_adapters/mysql/column.rb +6 -31
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +36 -49
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +22 -22
- data/lib/active_record/connection_adapters/mysql/quoting.rb +6 -12
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +49 -45
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +16 -19
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -28
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +43 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +7 -6
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +23 -27
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +32 -53
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +16 -16
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +0 -10
- data/lib/active_record/connection_adapters/postgresql/oid/{rails_5_1_point.rb → legacy_point.rb} +9 -16
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +28 -8
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +32 -30
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +51 -51
- data/lib/active_record/connection_adapters/postgresql/oid.rb +22 -21
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +40 -35
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +37 -24
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +19 -23
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +182 -222
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +6 -4
- data/lib/active_record/connection_adapters/postgresql/utils.rb +7 -5
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +198 -167
- data/lib/active_record/connection_adapters/schema_cache.rb +16 -7
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +3 -3
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +1 -1
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +16 -19
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +1 -8
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +28 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +17 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +32 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +184 -167
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -7
- data/lib/active_record/connection_handling.rb +14 -26
- data/lib/active_record/core.rb +109 -93
- data/lib/active_record/counter_cache.rb +60 -13
- data/lib/active_record/define_callbacks.rb +20 -0
- data/lib/active_record/dynamic_matchers.rb +80 -79
- data/lib/active_record/enum.rb +8 -6
- data/lib/active_record/errors.rb +64 -15
- data/lib/active_record/explain.rb +1 -2
- data/lib/active_record/explain_registry.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +7 -4
- data/lib/active_record/fixture_set/file.rb +11 -8
- data/lib/active_record/fixtures.rb +66 -53
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/inheritance.rb +93 -79
- data/lib/active_record/integration.rb +7 -7
- data/lib/active_record/internal_metadata.rb +3 -16
- data/lib/active_record/legacy_yaml_adapter.rb +1 -1
- data/lib/active_record/locking/optimistic.rb +69 -74
- data/lib/active_record/locking/pessimistic.rb +10 -1
- data/lib/active_record/log_subscriber.rb +23 -28
- data/lib/active_record/migration/command_recorder.rb +94 -94
- data/lib/active_record/migration/compatibility.rb +100 -47
- data/lib/active_record/migration/join_table.rb +6 -6
- data/lib/active_record/migration.rb +153 -155
- data/lib/active_record/model_schema.rb +94 -107
- data/lib/active_record/nested_attributes.rb +200 -199
- data/lib/active_record/null_relation.rb +11 -34
- data/lib/active_record/persistence.rb +65 -50
- data/lib/active_record/query_cache.rb +2 -6
- data/lib/active_record/querying.rb +3 -4
- data/lib/active_record/railtie.rb +16 -17
- data/lib/active_record/railties/controller_runtime.rb +6 -2
- data/lib/active_record/railties/databases.rake +105 -133
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +2 -2
- data/lib/active_record/reflection.rb +154 -108
- data/lib/active_record/relation/batches/batch_enumerator.rb +1 -1
- data/lib/active_record/relation/batches.rb +80 -51
- data/lib/active_record/relation/calculations.rb +169 -162
- data/lib/active_record/relation/delegation.rb +32 -31
- data/lib/active_record/relation/finder_methods.rb +197 -231
- data/lib/active_record/relation/merger.rb +58 -62
- data/lib/active_record/relation/predicate_builder/array_handler.rb +7 -5
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +23 -23
- data/lib/active_record/relation/predicate_builder/base_handler.rb +3 -1
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +0 -8
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +12 -10
- data/lib/active_record/relation/predicate_builder/range_handler.rb +0 -8
- data/lib/active_record/relation/predicate_builder.rb +92 -89
- data/lib/active_record/relation/query_attribute.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +255 -293
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +4 -5
- data/lib/active_record/relation/where_clause.rb +80 -65
- data/lib/active_record/relation/where_clause_factory.rb +47 -8
- data/lib/active_record/relation.rb +93 -119
- data/lib/active_record/result.rb +41 -32
- data/lib/active_record/runtime_registry.rb +3 -3
- data/lib/active_record/sanitization.rb +176 -192
- data/lib/active_record/schema.rb +3 -3
- data/lib/active_record/schema_dumper.rb +15 -38
- data/lib/active_record/schema_migration.rb +8 -4
- data/lib/active_record/scoping/default.rb +90 -90
- data/lib/active_record/scoping/named.rb +11 -11
- data/lib/active_record/scoping.rb +6 -6
- data/lib/active_record/secure_token.rb +2 -2
- data/lib/active_record/statement_cache.rb +13 -15
- data/lib/active_record/store.rb +31 -32
- data/lib/active_record/suppressor.rb +2 -1
- data/lib/active_record/table_metadata.rb +9 -5
- data/lib/active_record/tasks/database_tasks.rb +65 -55
- data/lib/active_record/tasks/mysql_database_tasks.rb +76 -73
- data/lib/active_record/tasks/postgresql_database_tasks.rb +72 -47
- data/lib/active_record/tasks/sqlite_database_tasks.rb +18 -16
- data/lib/active_record/timestamp.rb +46 -25
- data/lib/active_record/touch_later.rb +1 -2
- data/lib/active_record/transactions.rb +97 -109
- data/lib/active_record/type/adapter_specific_registry.rb +46 -42
- data/lib/active_record/type/decimal_without_scale.rb +13 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +3 -3
- data/lib/active_record/type/internal/abstract_json.rb +4 -0
- data/lib/active_record/type/serialized.rb +14 -8
- data/lib/active_record/type/text.rb +9 -0
- data/lib/active_record/type/time.rb +0 -1
- data/lib/active_record/type/type_map.rb +11 -15
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type.rb +17 -13
- data/lib/active_record/type_caster/connection.rb +8 -6
- data/lib/active_record/type_caster/map.rb +3 -1
- data/lib/active_record/type_caster.rb +2 -2
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record/validations/presence.rb +2 -2
- data/lib/active_record/validations/uniqueness.rb +8 -39
- data/lib/active_record/validations.rb +4 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +20 -20
- data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -34
- data/lib/rails/generators/active_record/migration.rb +1 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +9 -9
- data/lib/rails/generators/active_record.rb +4 -4
- metadata +24 -13
- data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
@@ -16,25 +16,16 @@ module ActiveRecord
|
|
16
16
|
when :restrict_with_error
|
17
17
|
unless empty?
|
18
18
|
record = owner.class.human_attribute_name(reflection.name).downcase
|
19
|
-
|
20
|
-
if message
|
21
|
-
ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
|
22
|
-
The error key `:'restrict_dependent_destroy.many'` has been deprecated and will be removed in Rails 5.1.
|
23
|
-
Please use `:'restrict_dependent_destroy.has_many'` instead.
|
24
|
-
MESSAGE
|
25
|
-
end
|
26
|
-
owner.errors.add(:base, message || :'restrict_dependent_destroy.has_many', record: record)
|
19
|
+
owner.errors.add(:base, :'restrict_dependent_destroy.has_many', record: record)
|
27
20
|
throw(:abort)
|
28
21
|
end
|
29
22
|
|
23
|
+
when :destroy
|
24
|
+
# No point in executing the counter update since we're going to destroy the parent anyway
|
25
|
+
load_target.each { |t| t.destroyed_by_association = reflection }
|
26
|
+
destroy_all
|
30
27
|
else
|
31
|
-
|
32
|
-
# No point in executing the counter update since we're going to destroy the parent anyway
|
33
|
-
load_target.each { |t| t.destroyed_by_association = reflection }
|
34
|
-
destroy_all
|
35
|
-
else
|
36
|
-
delete_all
|
37
|
-
end
|
28
|
+
delete_all
|
38
29
|
end
|
39
30
|
end
|
40
31
|
|
@@ -68,15 +59,15 @@ module ActiveRecord
|
|
68
59
|
# the loaded flag is set to true as well.
|
69
60
|
def count_records
|
70
61
|
count = if reflection.has_cached_counter?
|
71
|
-
owner._read_attribute
|
62
|
+
owner._read_attribute(reflection.counter_cache_column).to_i
|
72
63
|
else
|
73
|
-
scope.count
|
64
|
+
scope.count(:all)
|
74
65
|
end
|
75
66
|
|
76
67
|
# If there's nothing in the database and @target has no new records
|
77
68
|
# we are certain the current target is an empty array. This is a
|
78
69
|
# documented side-effect of the method that may avoid an extra SELECT.
|
79
|
-
@target ||= []
|
70
|
+
(@target ||= []) && loaded! if count == 0
|
80
71
|
|
81
72
|
[association_scope.limit_value, count].compact.min
|
82
73
|
end
|
@@ -104,7 +95,7 @@ module ActiveRecord
|
|
104
95
|
end
|
105
96
|
|
106
97
|
def delete_or_nullify_all_records(method)
|
107
|
-
count = delete_count(method,
|
98
|
+
count = delete_count(method, scope)
|
108
99
|
update_counter(-count)
|
109
100
|
end
|
110
101
|
|
@@ -38,7 +38,7 @@ module ActiveRecord
|
|
38
38
|
def insert_record(record, validate = true, raise = false)
|
39
39
|
ensure_not_nested
|
40
40
|
|
41
|
-
if record.new_record? || record.
|
41
|
+
if record.new_record? || record.has_changes_to_save?
|
42
42
|
return unless super
|
43
43
|
end
|
44
44
|
|
@@ -84,7 +84,10 @@ module ActiveRecord
|
|
84
84
|
end
|
85
85
|
|
86
86
|
def save_through_record(record)
|
87
|
-
build_through_record(record)
|
87
|
+
association = build_through_record(record)
|
88
|
+
if association.changed?
|
89
|
+
association.save!
|
90
|
+
end
|
88
91
|
ensure
|
89
92
|
@through_records.delete(record.object_id)
|
90
93
|
end
|
@@ -106,6 +109,11 @@ module ActiveRecord
|
|
106
109
|
record
|
107
110
|
end
|
108
111
|
|
112
|
+
def remove_records(existing_records, records, method)
|
113
|
+
super
|
114
|
+
delete_through_records(records)
|
115
|
+
end
|
116
|
+
|
109
117
|
def target_reflection_has_associated_record?
|
110
118
|
!(through_reflection.belongs_to? && owner[through_reflection.foreign_key].blank?)
|
111
119
|
end
|
@@ -144,7 +152,7 @@ module ActiveRecord
|
|
144
152
|
stmt.from scope.klass.arel_table
|
145
153
|
stmt.wheres = arel.constraints
|
146
154
|
|
147
|
-
count = scope.klass.connection.delete(stmt,
|
155
|
+
count = scope.klass.connection.delete(stmt, "SQL", scope.bound_attributes)
|
148
156
|
end
|
149
157
|
when :nullify
|
150
158
|
count = scope.update_all(source_reflection.foreign_key => nil)
|
@@ -194,7 +202,7 @@ module ActiveRecord
|
|
194
202
|
|
195
203
|
def find_target
|
196
204
|
return [] unless target_reflection_has_associated_record?
|
197
|
-
|
205
|
+
super
|
198
206
|
end
|
199
207
|
|
200
208
|
# NOTE - not sure that we can actually cope with inverses here
|
@@ -12,14 +12,7 @@ module ActiveRecord
|
|
12
12
|
when :restrict_with_error
|
13
13
|
if load_target
|
14
14
|
record = owner.class.human_attribute_name(reflection.name).downcase
|
15
|
-
|
16
|
-
if message
|
17
|
-
ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
|
18
|
-
The error key `:'restrict_dependent_destroy.one'` has been deprecated and will be removed in Rails 5.1.
|
19
|
-
Please use `:'restrict_dependent_destroy.has_one'` instead.
|
20
|
-
MESSAGE
|
21
|
-
end
|
22
|
-
owner.errors.add(:base, message || :'restrict_dependent_destroy.has_one', record: record)
|
15
|
+
owner.errors.add(:base, :'restrict_dependent_destroy.has_one', record: record)
|
23
16
|
throw(:abort)
|
24
17
|
end
|
25
18
|
|
@@ -32,10 +25,10 @@ module ActiveRecord
|
|
32
25
|
raise_on_type_mismatch!(record) if record
|
33
26
|
load_target
|
34
27
|
|
35
|
-
return
|
28
|
+
return target unless target || record
|
36
29
|
|
37
30
|
assigning_another_record = target != record
|
38
|
-
if assigning_another_record || record.
|
31
|
+
if assigning_another_record || record.has_changes_to_save?
|
39
32
|
save &&= owner.persisted?
|
40
33
|
|
41
34
|
transaction_if(save) do
|
@@ -60,12 +53,13 @@ module ActiveRecord
|
|
60
53
|
def delete(method = options[:dependent])
|
61
54
|
if load_target
|
62
55
|
case method
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
56
|
+
when :delete
|
57
|
+
target.delete
|
58
|
+
when :destroy
|
59
|
+
target.destroyed_by_association = reflection
|
60
|
+
target.destroy
|
61
|
+
when :nullify
|
62
|
+
target.update_columns(reflection.foreign_key => nil) if target.persisted?
|
69
63
|
end
|
70
64
|
end
|
71
65
|
end
|
@@ -82,18 +76,20 @@ module ActiveRecord
|
|
82
76
|
|
83
77
|
def remove_target!(method)
|
84
78
|
case method
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
79
|
+
when :delete
|
80
|
+
target.delete
|
81
|
+
when :destroy
|
82
|
+
target.destroyed_by_association = reflection
|
83
|
+
target.destroy
|
84
|
+
else
|
85
|
+
nullify_owner_attributes(target)
|
86
|
+
remove_inverse_instance(target)
|
87
|
+
|
88
|
+
if target.persisted? && owner.persisted? && !target.save
|
89
|
+
set_owner_attributes(target)
|
90
|
+
raise RecordNotSaved, "Failed to remove the existing associated #{reflection.name}. " \
|
91
|
+
"The record failed to save after its foreign key was set to nil."
|
92
|
+
end
|
97
93
|
end
|
98
94
|
end
|
99
95
|
|
@@ -15,13 +15,17 @@ module ActiveRecord
|
|
15
15
|
ensure_not_nested
|
16
16
|
|
17
17
|
through_proxy = owner.association(through_reflection.name)
|
18
|
-
through_record = through_proxy.
|
18
|
+
through_record = through_proxy.load_target
|
19
19
|
|
20
20
|
if through_record && !record
|
21
21
|
through_record.destroy
|
22
22
|
elsif record
|
23
23
|
attributes = construct_join_attributes(record)
|
24
24
|
|
25
|
+
if through_record && through_record.destroyed?
|
26
|
+
through_record = through_proxy.tap(&:reload).target
|
27
|
+
end
|
28
|
+
|
25
29
|
if through_record
|
26
30
|
through_record.update(attributes)
|
27
31
|
elsif owner.new_record?
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "active_record/associations/join_dependency/join_part"
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Associations
|
@@ -23,48 +23,24 @@ module ActiveRecord
|
|
23
23
|
|
24
24
|
JoinInformation = Struct.new :joins, :binds
|
25
25
|
|
26
|
-
def join_constraints(foreign_table, foreign_klass,
|
26
|
+
def join_constraints(foreign_table, foreign_klass, join_type, tables, chain)
|
27
27
|
joins = []
|
28
28
|
binds = []
|
29
29
|
tables = tables.reverse
|
30
30
|
|
31
|
-
scope_chain_index = 0
|
32
|
-
scope_chain = scope_chain.reverse
|
33
|
-
|
34
31
|
# The chain starts with the target table, but we want to end with it here (makes
|
35
32
|
# more sense in this context), so we reverse
|
36
33
|
chain.reverse_each do |reflection|
|
37
34
|
table = tables.shift
|
38
35
|
klass = reflection.klass
|
39
36
|
|
40
|
-
join_keys = reflection.join_keys
|
37
|
+
join_keys = reflection.join_keys
|
41
38
|
key = join_keys.key
|
42
39
|
foreign_key = join_keys.foreign_key
|
43
40
|
|
44
41
|
constraint = build_constraint(klass, table, key, foreign_table, foreign_key)
|
45
42
|
|
46
|
-
|
47
|
-
scope_chain_items = scope_chain[scope_chain_index].map do |item|
|
48
|
-
if item.is_a?(Relation)
|
49
|
-
item
|
50
|
-
else
|
51
|
-
ActiveRecord::Relation.create(klass, table, predicate_builder)
|
52
|
-
.instance_exec(node, &item)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
scope_chain_index += 1
|
56
|
-
|
57
|
-
relation = ActiveRecord::Relation.create(klass, table, predicate_builder)
|
58
|
-
current_scope = klass.current_scope
|
59
|
-
|
60
|
-
klass_scope =
|
61
|
-
if current_scope && current_scope.empty_scope?
|
62
|
-
relation
|
63
|
-
else
|
64
|
-
klass.send(:build_default_scope, relation)
|
65
|
-
end
|
66
|
-
|
67
|
-
rel = scope_chain_items.inject(klass_scope || scope_chain_items.shift, &:merge!)
|
43
|
+
rel = reflection.join_scope(table)
|
68
44
|
|
69
45
|
if rel && !rel.arel.constraints.empty?
|
70
46
|
binds += rel.bound_attributes
|
@@ -15,7 +15,7 @@ module ActiveRecord
|
|
15
15
|
# association.
|
16
16
|
attr_reader :base_klass, :children
|
17
17
|
|
18
|
-
delegate :table_name, :column_names, :primary_key, :
|
18
|
+
delegate :table_name, :column_names, :primary_key, to: :base_klass
|
19
19
|
|
20
20
|
def initialize(base_klass, children)
|
21
21
|
@base_klass = base_klass
|
@@ -1,18 +1,18 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module Associations
|
3
3
|
class JoinDependency # :nodoc:
|
4
|
-
autoload :JoinBase,
|
5
|
-
autoload :JoinAssociation,
|
4
|
+
autoload :JoinBase, "active_record/associations/join_dependency/join_base"
|
5
|
+
autoload :JoinAssociation, "active_record/associations/join_dependency/join_association"
|
6
6
|
|
7
7
|
class Aliases # :nodoc:
|
8
8
|
def initialize(tables)
|
9
9
|
@tables = tables
|
10
|
-
@alias_cache = tables.each_with_object({}) { |table,h|
|
11
|
-
h[table.node] = table.columns.each_with_object({}) { |column,i|
|
10
|
+
@alias_cache = tables.each_with_object({}) { |table, h|
|
11
|
+
h[table.node] = table.columns.each_with_object({}) { |column, i|
|
12
12
|
i[column.name] = column.alias
|
13
13
|
}
|
14
14
|
}
|
15
|
-
@name_and_alias_cache = tables.each_with_object({}) { |table,h|
|
15
|
+
@name_and_alias_cache = tables.each_with_object({}) { |table, h|
|
16
16
|
h[table.node] = table.columns.map { |column|
|
17
17
|
[column.name, column.alias]
|
18
18
|
}
|
@@ -32,7 +32,7 @@ module ActiveRecord
|
|
32
32
|
@alias_cache[node][column]
|
33
33
|
end
|
34
34
|
|
35
|
-
|
35
|
+
Table = Struct.new(:node, :columns) do # :nodoc:
|
36
36
|
def table
|
37
37
|
Arel::Nodes::TableAlias.new node.table, node.aliased_table_name
|
38
38
|
end
|
@@ -62,7 +62,7 @@ module ActiveRecord
|
|
62
62
|
walk_tree assoc, hash
|
63
63
|
end
|
64
64
|
when Hash
|
65
|
-
associations.each do |k,v|
|
65
|
+
associations.each do |k, v|
|
66
66
|
cache = hash[k] ||= {}
|
67
67
|
walk_tree v, cache
|
68
68
|
end
|
@@ -92,8 +92,9 @@ module ActiveRecord
|
|
92
92
|
# associations # => [:appointments]
|
93
93
|
# joins # => []
|
94
94
|
#
|
95
|
-
def initialize(base, associations, joins)
|
96
|
-
@alias_tracker = AliasTracker.create_with_joins(base.connection, base.table_name, joins
|
95
|
+
def initialize(base, associations, joins, eager_loading: true)
|
96
|
+
@alias_tracker = AliasTracker.create_with_joins(base.connection, base.table_name, joins)
|
97
|
+
@eager_loading = eager_loading
|
97
98
|
tree = self.class.make_tree associations
|
98
99
|
@join_root = JoinBase.new base, build(tree, base)
|
99
100
|
@join_root.children.each { |child| construct_tables! @join_root, child }
|
@@ -125,15 +126,15 @@ module ActiveRecord
|
|
125
126
|
end
|
126
127
|
|
127
128
|
def aliases
|
128
|
-
Aliases.new join_root.each_with_index.map { |join_part,i|
|
129
|
-
columns = join_part.column_names.each_with_index.map { |column_name,j|
|
129
|
+
@aliases ||= Aliases.new join_root.each_with_index.map { |join_part, i|
|
130
|
+
columns = join_part.column_names.each_with_index.map { |column_name, j|
|
130
131
|
Aliases::Column.new column_name, "t#{i}_r#{j}"
|
131
132
|
}
|
132
133
|
Aliases::Table.new(join_part, columns)
|
133
134
|
}
|
134
135
|
end
|
135
136
|
|
136
|
-
def instantiate(result_set,
|
137
|
+
def instantiate(result_set, &block)
|
137
138
|
primary_key = aliases.column_alias(join_root, join_root.primary_key)
|
138
139
|
|
139
140
|
seen = Hash.new { |i, object_id|
|
@@ -142,7 +143,7 @@ module ActiveRecord
|
|
142
143
|
}
|
143
144
|
}
|
144
145
|
|
145
|
-
model_cache = Hash.new { |h,klass| h[klass] = {} }
|
146
|
+
model_cache = Hash.new { |h, klass| h[klass] = {} }
|
146
147
|
parents = model_cache[join_root]
|
147
148
|
column_aliases = aliases.column_aliases join_root
|
148
149
|
|
@@ -153,10 +154,10 @@ module ActiveRecord
|
|
153
154
|
class_name: join_root.base_klass.name
|
154
155
|
}
|
155
156
|
|
156
|
-
message_bus.instrument(
|
157
|
+
message_bus.instrument("instantiation.active_record", payload) do
|
157
158
|
result_set.each { |row_hash|
|
158
159
|
parent_key = primary_key ? row_hash[primary_key] : row_hash
|
159
|
-
parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases)
|
160
|
+
parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases, &block)
|
160
161
|
construct(parent, join_root, row_hash, result_set, seen, model_cache, aliases)
|
161
162
|
}
|
162
163
|
end
|
@@ -166,139 +167,141 @@ module ActiveRecord
|
|
166
167
|
|
167
168
|
private
|
168
169
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
170
|
+
def make_constraints(parent, child, tables, join_type)
|
171
|
+
chain = child.reflection.chain
|
172
|
+
foreign_table = parent.table
|
173
|
+
foreign_klass = parent.base_klass
|
174
|
+
child.join_constraints(foreign_table, foreign_klass, join_type, tables, chain)
|
175
|
+
end
|
175
176
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
177
|
+
def make_outer_joins(parent, child)
|
178
|
+
tables = table_aliases_for(parent, child)
|
179
|
+
join_type = Arel::Nodes::OuterJoin
|
180
|
+
info = make_constraints parent, child, tables, join_type
|
180
181
|
|
181
|
-
|
182
|
-
|
182
|
+
[info] + child.children.flat_map { |c| make_outer_joins(child, c) }
|
183
|
+
end
|
183
184
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
185
|
+
def make_left_outer_joins(parent, child)
|
186
|
+
tables = child.tables
|
187
|
+
join_type = Arel::Nodes::OuterJoin
|
188
|
+
info = make_constraints parent, child, tables, join_type
|
188
189
|
|
189
|
-
|
190
|
-
|
190
|
+
[info] + child.children.flat_map { |c| make_left_outer_joins(child, c) }
|
191
|
+
end
|
191
192
|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
193
|
+
def make_inner_joins(parent, child)
|
194
|
+
tables = child.tables
|
195
|
+
join_type = Arel::Nodes::InnerJoin
|
196
|
+
info = make_constraints parent, child, tables, join_type
|
196
197
|
|
197
|
-
|
198
|
-
|
198
|
+
[info] + child.children.flat_map { |c| make_inner_joins(child, c) }
|
199
|
+
end
|
199
200
|
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
201
|
+
def table_aliases_for(parent, node)
|
202
|
+
node.reflection.chain.map { |reflection|
|
203
|
+
alias_tracker.aliased_table_for(
|
204
|
+
reflection.table_name,
|
205
|
+
table_alias_for(reflection, parent, reflection != node.reflection),
|
206
|
+
reflection.klass.type_caster
|
207
|
+
)
|
208
|
+
}
|
209
|
+
end
|
208
210
|
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
211
|
+
def construct_tables!(parent, node)
|
212
|
+
node.tables = table_aliases_for(parent, node)
|
213
|
+
node.children.each { |child| construct_tables! node, child }
|
214
|
+
end
|
213
215
|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
216
|
+
def table_alias_for(reflection, parent, join)
|
217
|
+
name = "#{reflection.plural_name}_#{parent.table_name}"
|
218
|
+
name << "_join" if join
|
219
|
+
name
|
220
|
+
end
|
219
221
|
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
222
|
+
def walk(left, right)
|
223
|
+
intersection, missing = right.children.map { |node1|
|
224
|
+
[left.children.find { |node2| node1.match? node2 }, node1]
|
225
|
+
}.partition(&:first)
|
224
226
|
|
225
|
-
|
226
|
-
|
227
|
-
|
227
|
+
ojs = missing.flat_map { |_, n| make_outer_joins left, n }
|
228
|
+
intersection.flat_map { |l, r| walk l, r }.concat ojs
|
229
|
+
end
|
228
230
|
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
231
|
+
def find_reflection(klass, name)
|
232
|
+
klass._reflect_on_association(name) ||
|
233
|
+
raise(ConfigurationError, "Can't join '#{klass.name}' to association named '#{name}'; perhaps you misspelled it?")
|
234
|
+
end
|
233
235
|
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
236
|
+
def build(associations, base_klass)
|
237
|
+
associations.map do |name, right|
|
238
|
+
reflection = find_reflection base_klass, name
|
239
|
+
reflection.check_validity!
|
240
|
+
reflection.check_eager_loadable!
|
239
241
|
|
240
|
-
|
241
|
-
|
242
|
-
|
242
|
+
if reflection.polymorphic?
|
243
|
+
next unless @eager_loading
|
244
|
+
raise EagerLoadPolymorphicError.new(reflection)
|
245
|
+
end
|
243
246
|
|
244
|
-
|
247
|
+
JoinAssociation.new reflection, build(right, reflection.klass)
|
248
|
+
end.compact
|
245
249
|
end
|
246
|
-
end
|
247
250
|
|
248
|
-
|
249
|
-
|
251
|
+
def construct(ar_parent, parent, row, rs, seen, model_cache, aliases)
|
252
|
+
return if ar_parent.nil?
|
253
|
+
|
254
|
+
parent.children.each do |node|
|
255
|
+
if node.reflection.collection?
|
256
|
+
other = ar_parent.association(node.reflection.name)
|
257
|
+
other.loaded!
|
258
|
+
elsif ar_parent.association_cached?(node.reflection.name)
|
259
|
+
model = ar_parent.association(node.reflection.name).target
|
260
|
+
construct(model, node, row, rs, seen, model_cache, aliases)
|
261
|
+
next
|
262
|
+
end
|
250
263
|
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
next
|
259
|
-
end
|
264
|
+
key = aliases.column_alias(node, node.primary_key)
|
265
|
+
id = row[key]
|
266
|
+
if id.nil?
|
267
|
+
nil_association = ar_parent.association(node.reflection.name)
|
268
|
+
nil_association.loaded!
|
269
|
+
next
|
270
|
+
end
|
260
271
|
|
261
|
-
|
262
|
-
id = row[key]
|
263
|
-
if id.nil?
|
264
|
-
nil_association = ar_parent.association(node.reflection.name)
|
265
|
-
nil_association.loaded!
|
266
|
-
next
|
267
|
-
end
|
272
|
+
model = seen[ar_parent.object_id][node.base_klass][id]
|
268
273
|
|
269
|
-
|
274
|
+
if model
|
275
|
+
construct(model, node, row, rs, seen, model_cache, aliases)
|
276
|
+
else
|
277
|
+
model = construct_model(ar_parent, node, row, model_cache, id, aliases)
|
270
278
|
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
model = construct_model(ar_parent, node, row, model_cache, id, aliases)
|
279
|
+
if node.reflection.scope_for(node.base_klass).readonly_value
|
280
|
+
model.readonly!
|
281
|
+
end
|
275
282
|
|
276
|
-
|
277
|
-
model
|
283
|
+
seen[ar_parent.object_id][node.base_klass][id] = model
|
284
|
+
construct(model, node, row, rs, seen, model_cache, aliases)
|
278
285
|
end
|
279
|
-
|
280
|
-
seen[ar_parent.object_id][node.base_klass][id] = model
|
281
|
-
construct(model, node, row, rs, seen, model_cache, aliases)
|
282
286
|
end
|
283
287
|
end
|
284
|
-
end
|
285
288
|
|
286
|
-
|
287
|
-
|
289
|
+
def construct_model(record, node, row, model_cache, id, aliases)
|
290
|
+
other = record.association(node.reflection.name)
|
288
291
|
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
+
model = model_cache[node][id] ||=
|
293
|
+
node.instantiate(row, aliases.column_aliases(node)) do |m|
|
294
|
+
other.set_inverse_instance(m)
|
295
|
+
end
|
296
|
+
|
297
|
+
if node.reflection.collection?
|
298
|
+
other.target.push(model)
|
299
|
+
else
|
300
|
+
other.target = model
|
292
301
|
end
|
293
302
|
|
294
|
-
|
295
|
-
other.target.push(model)
|
296
|
-
else
|
297
|
-
other.target = model
|
303
|
+
model
|
298
304
|
end
|
299
|
-
|
300
|
-
model
|
301
|
-
end
|
302
305
|
end
|
303
306
|
end
|
304
307
|
end
|