activerecord 6.0.3 → 6.1.3
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 +968 -682
- data/MIT-LICENSE +1 -1
- data/README.rdoc +3 -3
- data/lib/active_record.rb +7 -14
- data/lib/active_record/aggregations.rb +5 -5
- data/lib/active_record/association_relation.rb +30 -12
- data/lib/active_record/associations.rb +118 -11
- data/lib/active_record/associations/alias_tracker.rb +19 -15
- data/lib/active_record/associations/association.rb +44 -28
- data/lib/active_record/associations/association_scope.rb +19 -15
- data/lib/active_record/associations/belongs_to_association.rb +22 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -3
- data/lib/active_record/associations/builder/association.rb +32 -5
- data/lib/active_record/associations/builder/belongs_to.rb +10 -7
- data/lib/active_record/associations/builder/collection_association.rb +5 -4
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -1
- data/lib/active_record/associations/builder/has_many.rb +6 -2
- data/lib/active_record/associations/builder/has_one.rb +11 -14
- data/lib/active_record/associations/builder/singular_association.rb +1 -1
- data/lib/active_record/associations/collection_association.rb +19 -6
- data/lib/active_record/associations/collection_proxy.rb +13 -5
- data/lib/active_record/associations/foreign_association.rb +13 -0
- data/lib/active_record/associations/has_many_association.rb +24 -2
- data/lib/active_record/associations/has_many_through_association.rb +10 -4
- data/lib/active_record/associations/has_one_association.rb +15 -1
- data/lib/active_record/associations/join_dependency.rb +72 -50
- data/lib/active_record/associations/join_dependency/join_association.rb +39 -16
- data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
- data/lib/active_record/associations/preloader.rb +11 -5
- data/lib/active_record/associations/preloader/association.rb +51 -25
- data/lib/active_record/associations/preloader/through_association.rb +2 -2
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/attribute_assignment.rb +10 -8
- data/lib/active_record/attribute_methods.rb +64 -54
- data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
- data/lib/active_record/attribute_methods/dirty.rb +1 -11
- data/lib/active_record/attribute_methods/primary_key.rb +6 -2
- data/lib/active_record/attribute_methods/query.rb +3 -6
- data/lib/active_record/attribute_methods/read.rb +8 -11
- data/lib/active_record/attribute_methods/serialization.rb +11 -5
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -13
- data/lib/active_record/attribute_methods/write.rb +12 -20
- data/lib/active_record/attributes.rb +33 -8
- data/lib/active_record/autosave_association.rb +57 -40
- data/lib/active_record/base.rb +2 -14
- data/lib/active_record/callbacks.rb +152 -22
- data/lib/active_record/coders/yaml_column.rb +1 -1
- data/lib/active_record/connection_adapters.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +191 -134
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +66 -23
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -8
- data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +116 -27
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +228 -83
- data/lib/active_record/connection_adapters/abstract/transaction.rb +80 -32
- data/lib/active_record/connection_adapters/abstract_adapter.rb +54 -72
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +129 -88
- data/lib/active_record/connection_adapters/column.rb +15 -1
- 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/database_statements.rb +23 -25
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +18 -3
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -6
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +5 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +11 -7
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -12
- 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 +24 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +13 -54
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- 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/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -5
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -29
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +74 -63
- data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +31 -6
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +37 -4
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +49 -50
- data/lib/active_record/connection_handling.rb +218 -71
- data/lib/active_record/core.rb +245 -61
- data/lib/active_record/database_configurations.rb +124 -85
- data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
- data/lib/active_record/database_configurations/database_config.rb +52 -9
- data/lib/active_record/database_configurations/hash_config.rb +54 -8
- data/lib/active_record/database_configurations/url_config.rb +15 -40
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/enum.rb +82 -38
- data/lib/active_record/errors.rb +47 -12
- data/lib/active_record/explain.rb +9 -4
- 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 +1 -2
- data/lib/active_record/fixture_set/render_context.rb +1 -1
- data/lib/active_record/fixture_set/table_row.rb +2 -2
- data/lib/active_record/fixtures.rb +58 -9
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/inheritance.rb +40 -18
- data/lib/active_record/insert_all.rb +35 -6
- data/lib/active_record/integration.rb +3 -5
- data/lib/active_record/internal_metadata.rb +16 -7
- data/lib/active_record/legacy_yaml_adapter.rb +7 -3
- data/lib/active_record/locking/optimistic.rb +33 -17
- data/lib/active_record/locking/pessimistic.rb +6 -2
- data/lib/active_record/log_subscriber.rb +27 -8
- data/lib/active_record/middleware/database_selector.rb +4 -1
- data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
- data/lib/active_record/migration.rb +114 -84
- data/lib/active_record/migration/command_recorder.rb +47 -27
- data/lib/active_record/migration/compatibility.rb +68 -17
- data/lib/active_record/model_schema.rb +117 -13
- data/lib/active_record/nested_attributes.rb +2 -3
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/persistence.rb +50 -45
- data/lib/active_record/query_cache.rb +15 -5
- data/lib/active_record/querying.rb +11 -6
- data/lib/active_record/railtie.rb +64 -44
- data/lib/active_record/railties/console_sandbox.rb +2 -4
- data/lib/active_record/railties/databases.rake +276 -99
- data/lib/active_record/readonly_attributes.rb +4 -0
- data/lib/active_record/reflection.rb +71 -57
- data/lib/active_record/relation.rb +95 -67
- data/lib/active_record/relation/batches.rb +38 -31
- data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
- data/lib/active_record/relation/calculations.rb +101 -44
- data/lib/active_record/relation/delegation.rb +2 -1
- data/lib/active_record/relation/finder_methods.rb +45 -15
- data/lib/active_record/relation/from_clause.rb +1 -1
- data/lib/active_record/relation/merger.rb +27 -25
- data/lib/active_record/relation/predicate_builder.rb +61 -38
- data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -6
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +333 -195
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +8 -7
- data/lib/active_record/relation/where_clause.rb +107 -60
- data/lib/active_record/result.rb +41 -33
- data/lib/active_record/runtime_registry.rb +2 -2
- data/lib/active_record/sanitization.rb +6 -17
- data/lib/active_record/schema_dumper.rb +34 -4
- data/lib/active_record/schema_migration.rb +2 -8
- data/lib/active_record/scoping/named.rb +6 -17
- 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 +20 -4
- data/lib/active_record/store.rb +2 -2
- data/lib/active_record/suppressor.rb +2 -2
- data/lib/active_record/table_metadata.rb +42 -51
- data/lib/active_record/tasks/database_tasks.rb +140 -113
- data/lib/active_record/tasks/mysql_database_tasks.rb +34 -35
- data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -26
- data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -9
- data/lib/active_record/test_databases.rb +5 -4
- data/lib/active_record/test_fixtures.rb +37 -16
- data/lib/active_record/timestamp.rb +4 -6
- data/lib/active_record/touch_later.rb +21 -21
- data/lib/active_record/transactions.rb +19 -66
- data/lib/active_record/type.rb +8 -1
- data/lib/active_record/type/serialized.rb +6 -2
- data/lib/active_record/type/time.rb +10 -0
- data/lib/active_record/type_caster/connection.rb +0 -1
- data/lib/active_record/type_caster/map.rb +8 -5
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +24 -4
- data/lib/arel.rb +5 -13
- data/lib/arel/attributes/attribute.rb +4 -0
- data/lib/arel/collectors/bind.rb +5 -0
- data/lib/arel/collectors/composite.rb +8 -0
- data/lib/arel/collectors/sql_string.rb +7 -0
- data/lib/arel/collectors/substitute_binds.rb +7 -0
- data/lib/arel/nodes.rb +3 -1
- data/lib/arel/nodes/binary.rb +82 -8
- data/lib/arel/nodes/bind_param.rb +8 -0
- data/lib/arel/nodes/casted.rb +21 -9
- data/lib/arel/nodes/equality.rb +6 -9
- data/lib/arel/nodes/grouping.rb +3 -0
- data/lib/arel/nodes/homogeneous_in.rb +72 -0
- data/lib/arel/nodes/in.rb +8 -1
- data/lib/arel/nodes/infix_operation.rb +13 -1
- data/lib/arel/nodes/join_source.rb +1 -1
- data/lib/arel/nodes/node.rb +7 -6
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/sql_literal.rb +3 -0
- data/lib/arel/nodes/table_alias.rb +7 -3
- data/lib/arel/nodes/unary.rb +0 -1
- data/lib/arel/predications.rb +12 -18
- data/lib/arel/select_manager.rb +1 -2
- data/lib/arel/table.rb +13 -5
- data/lib/arel/visitors.rb +0 -7
- data/lib/arel/visitors/dot.rb +14 -2
- data/lib/arel/visitors/mysql.rb +11 -1
- data/lib/arel/visitors/postgresql.rb +15 -4
- data/lib/arel/visitors/to_sql.rb +89 -78
- data/lib/rails/generators/active_record/migration.rb +6 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +3 -3
- 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
- metadata +28 -29
- data/lib/active_record/advisory_lock_base.rb +0 -18
- data/lib/active_record/attribute_decorators.rb +0 -88
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -296
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
- data/lib/active_record/relation/where_clause_factory.rb +0 -33
- data/lib/arel/attributes.rb +0 -22
- data/lib/arel/visitors/depth_first.rb +0 -203
- data/lib/arel/visitors/ibm_db.rb +0 -34
- data/lib/arel/visitors/informix.rb +0 -62
- data/lib/arel/visitors/mssql.rb +0 -156
- data/lib/arel/visitors/oracle.rb +0 -158
- data/lib/arel/visitors/oracle12.rb +0 -65
- data/lib/arel/visitors/where_sql.rb +0 -22
@@ -7,8 +7,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
7
7
|
CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove]
|
8
8
|
|
9
9
|
def self.valid_options(options)
|
10
|
-
super + [:
|
11
|
-
:after_add, :before_remove, :after_remove, :extend]
|
10
|
+
super + [:before_add, :after_add, :before_remove, :after_remove, :extend]
|
12
11
|
end
|
13
12
|
|
14
13
|
def self.define_callbacks(model, reflection)
|
@@ -31,8 +30,10 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
31
30
|
def self.define_callback(model, callback_name, name, options)
|
32
31
|
full_callback_name = "#{callback_name}_for_#{name}"
|
33
32
|
|
34
|
-
|
35
|
-
|
33
|
+
unless model.method_defined?(full_callback_name)
|
34
|
+
model.class_attribute(full_callback_name, instance_accessor: false, instance_predicate: false)
|
35
|
+
end
|
36
|
+
|
36
37
|
callbacks = Array(options[callback_name.to_sym]).map do |callback|
|
37
38
|
case callback
|
38
39
|
when Symbol
|
@@ -75,7 +75,6 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
75
75
|
def middle_options(join_model)
|
76
76
|
middle_options = {}
|
77
77
|
middle_options[:class_name] = "#{lhs_model.name}::#{join_model.name}"
|
78
|
-
middle_options[:source] = join_model.left_reflection.name
|
79
78
|
if options.key? :foreign_key
|
80
79
|
middle_options[:foreign_key] = options[:foreign_key]
|
81
80
|
end
|
@@ -7,11 +7,15 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def self.valid_options(options)
|
10
|
-
super + [:
|
10
|
+
valid = super + [:counter_cache, :join_table, :index_errors, :ensuring_owner_was]
|
11
|
+
valid += [:as, :foreign_type] if options[:as]
|
12
|
+
valid += [:through, :source, :source_type] if options[:through]
|
13
|
+
valid += [:ensuring_owner_was] if options[:dependent] == :destroy_async
|
14
|
+
valid
|
11
15
|
end
|
12
16
|
|
13
17
|
def self.valid_dependent_options
|
14
|
-
[:destroy, :delete_all, :nullify, :restrict_with_error, :restrict_with_exception]
|
18
|
+
[:destroy, :delete_all, :nullify, :restrict_with_error, :restrict_with_exception, :destroy_async]
|
15
19
|
end
|
16
20
|
|
17
21
|
private_class_method :macro, :valid_options, :valid_dependent_options
|
@@ -7,13 +7,15 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def self.valid_options(options)
|
10
|
-
valid = super
|
10
|
+
valid = super
|
11
|
+
valid += [:as, :foreign_type] if options[:as]
|
12
|
+
valid += [:ensuring_owner_was] if options[:dependent] == :destroy_async
|
11
13
|
valid += [:through, :source, :source_type] if options[:through]
|
12
14
|
valid
|
13
15
|
end
|
14
16
|
|
15
17
|
def self.valid_dependent_options
|
16
|
-
[:destroy, :delete, :nullify, :restrict_with_error, :restrict_with_exception]
|
18
|
+
[:destroy, :destroy_async, :delete, :nullify, :restrict_with_error, :restrict_with_exception]
|
17
19
|
end
|
18
20
|
|
19
21
|
def self.define_callbacks(model, reflection)
|
@@ -32,15 +34,12 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
32
34
|
end
|
33
35
|
end
|
34
36
|
|
35
|
-
def self.touch_record(
|
36
|
-
|
37
|
+
def self.touch_record(record, name, touch)
|
38
|
+
instance = record.send(name)
|
37
39
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
record.touch(touch)
|
42
|
-
else
|
43
|
-
record.touch
|
40
|
+
if instance&.persisted?
|
41
|
+
touch != true ?
|
42
|
+
instance.touch(touch) : instance.touch
|
44
43
|
end
|
45
44
|
end
|
46
45
|
|
@@ -48,11 +47,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
48
47
|
name = reflection.name
|
49
48
|
touch = reflection.options[:touch]
|
50
49
|
|
51
|
-
callback =
|
52
|
-
HasOne.touch_record(record, name, touch)
|
53
|
-
}
|
54
|
-
|
50
|
+
callback = -> (record) { HasOne.touch_record(record, name, touch) }
|
55
51
|
model.after_create callback, if: :saved_changes?
|
52
|
+
model.after_create_commit { association(name).reset_negative_cache }
|
56
53
|
model.after_update callback, if: :saved_changes?
|
57
54
|
model.after_destroy callback
|
58
55
|
model.after_touch callback
|
@@ -5,7 +5,7 @@
|
|
5
5
|
module ActiveRecord::Associations::Builder # :nodoc:
|
6
6
|
class SingularAssociation < Association #:nodoc:
|
7
7
|
def self.valid_options(options)
|
8
|
-
super + [:
|
8
|
+
super + [:required, :touch]
|
9
9
|
end
|
10
10
|
|
11
11
|
def self.define_accessors(model, reflection)
|
@@ -56,7 +56,7 @@ module ActiveRecord
|
|
56
56
|
def ids_writer(ids)
|
57
57
|
primary_key = reflection.association_primary_key
|
58
58
|
pk_type = klass.type_for_attribute(primary_key)
|
59
|
-
ids = Array(ids).
|
59
|
+
ids = Array(ids).compact_blank
|
60
60
|
ids.map! { |i| pk_type.cast(i) }
|
61
61
|
|
62
62
|
records = klass.where(primary_key => ids).index_by do |r|
|
@@ -101,11 +101,11 @@ module ActiveRecord
|
|
101
101
|
end
|
102
102
|
end
|
103
103
|
|
104
|
-
def build(attributes =
|
104
|
+
def build(attributes = nil, &block)
|
105
105
|
if attributes.is_a?(Array)
|
106
106
|
attributes.collect { |attr| build(attr, &block) }
|
107
107
|
else
|
108
|
-
add_to_target(build_record(attributes, &block))
|
108
|
+
add_to_target(build_record(attributes, &block), replace: true)
|
109
109
|
end
|
110
110
|
end
|
111
111
|
|
@@ -228,7 +228,7 @@ module ActiveRecord
|
|
228
228
|
# If the collection has been loaded
|
229
229
|
# it is equivalent to <tt>collection.size.zero?</tt>. If the
|
230
230
|
# collection has not been loaded, it is equivalent to
|
231
|
-
# <tt
|
231
|
+
# <tt>!collection.exists?</tt>. If the collection has not already been
|
232
232
|
# loaded and you are going to fetch the records anyway it is better to
|
233
233
|
# check <tt>collection.length.zero?</tt>.
|
234
234
|
def empty?
|
@@ -278,13 +278,24 @@ module ActiveRecord
|
|
278
278
|
target
|
279
279
|
end
|
280
280
|
|
281
|
-
def add_to_target(record, skip_callbacks
|
282
|
-
if association_scope.distinct_value
|
281
|
+
def add_to_target(record, skip_callbacks: false, replace: false, &block)
|
282
|
+
if replace || association_scope.distinct_value
|
283
283
|
index = @target.index(record)
|
284
284
|
end
|
285
285
|
replace_on_target(record, index, skip_callbacks, &block)
|
286
286
|
end
|
287
287
|
|
288
|
+
def target=(record)
|
289
|
+
return super unless ActiveRecord::Base.has_many_inversing
|
290
|
+
|
291
|
+
case record
|
292
|
+
when Array
|
293
|
+
super
|
294
|
+
else
|
295
|
+
add_to_target(record, skip_callbacks: true, replace: true)
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
288
299
|
def scope
|
289
300
|
scope = super
|
290
301
|
scope.none! if null_scope?
|
@@ -297,6 +308,8 @@ module ActiveRecord
|
|
297
308
|
|
298
309
|
def find_from_target?
|
299
310
|
loaded? ||
|
311
|
+
owner.strict_loading? ||
|
312
|
+
reflection.strict_loading? ||
|
300
313
|
owner.new_record? ||
|
301
314
|
target.any? { |record| record.new_record? || record.changed? }
|
302
315
|
end
|
@@ -51,6 +51,7 @@ module ActiveRecord
|
|
51
51
|
def loaded?
|
52
52
|
@association.loaded?
|
53
53
|
end
|
54
|
+
alias :loaded :loaded?
|
54
55
|
|
55
56
|
##
|
56
57
|
# :method: select
|
@@ -100,7 +101,7 @@ module ActiveRecord
|
|
100
101
|
# converting them into an array and iterating through them using
|
101
102
|
# Array#select.
|
102
103
|
#
|
103
|
-
# person.pets.select { |pet| pet.name
|
104
|
+
# person.pets.select { |pet| /oo/.match?(pet.name) }
|
104
105
|
# # => [
|
105
106
|
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
106
107
|
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
@@ -373,7 +374,7 @@ module ActiveRecord
|
|
373
374
|
# person.pets
|
374
375
|
# # => [#<Pet id: 1, name: "Gorby", group: "cats", person_id: 1>]
|
375
376
|
#
|
376
|
-
# other_pets = [Pet.new(name: 'Puff', group: 'celebrities']
|
377
|
+
# other_pets = [Pet.new(name: 'Puff', group: 'celebrities')]
|
377
378
|
#
|
378
379
|
# person.pets.replace(other_pets)
|
379
380
|
#
|
@@ -920,7 +921,7 @@ module ActiveRecord
|
|
920
921
|
!!@association.include?(record)
|
921
922
|
end
|
922
923
|
|
923
|
-
def proxy_association
|
924
|
+
def proxy_association # :nodoc:
|
924
925
|
@association
|
925
926
|
end
|
926
927
|
|
@@ -1086,17 +1087,24 @@ module ActiveRecord
|
|
1086
1087
|
end
|
1087
1088
|
|
1088
1089
|
def reset_scope # :nodoc:
|
1089
|
-
@offsets =
|
1090
|
+
@offsets = @take = nil
|
1090
1091
|
@scope = nil
|
1091
1092
|
self
|
1092
1093
|
end
|
1093
1094
|
|
1095
|
+
def inspect # :nodoc:
|
1096
|
+
load_target if find_from_target?
|
1097
|
+
super
|
1098
|
+
end
|
1099
|
+
|
1094
1100
|
delegate_methods = [
|
1095
1101
|
QueryMethods,
|
1096
1102
|
SpawnMethods,
|
1097
1103
|
].flat_map { |klass|
|
1098
1104
|
klass.public_instance_methods(false)
|
1099
|
-
} - self.public_instance_methods(false) - [:select] + [
|
1105
|
+
} - self.public_instance_methods(false) - [:select] + [
|
1106
|
+
:scoping, :values, :insert, :insert_all, :insert!, :insert_all!, :upsert, :upsert_all
|
1107
|
+
]
|
1100
1108
|
|
1101
1109
|
delegate(*delegate_methods, to: :scope)
|
1102
1110
|
|
@@ -16,5 +16,18 @@ module ActiveRecord::Associations
|
|
16
16
|
attrs[reflection.type] = nil if reflection.type.present?
|
17
17
|
end
|
18
18
|
end
|
19
|
+
|
20
|
+
private
|
21
|
+
# Sets the owner attributes on the given record
|
22
|
+
def set_owner_attributes(record)
|
23
|
+
return if options[:through]
|
24
|
+
|
25
|
+
key = owner._read_attribute(reflection.join_foreign_key)
|
26
|
+
record._write_attribute(reflection.join_primary_key, key)
|
27
|
+
|
28
|
+
if reflection.type
|
29
|
+
record._write_attribute(reflection.type, owner.class.polymorphic_name)
|
30
|
+
end
|
31
|
+
end
|
19
32
|
end
|
20
33
|
end
|
@@ -26,6 +26,28 @@ module ActiveRecord
|
|
26
26
|
# No point in executing the counter update since we're going to destroy the parent anyway
|
27
27
|
load_target.each { |t| t.destroyed_by_association = reflection }
|
28
28
|
destroy_all
|
29
|
+
when :destroy_async
|
30
|
+
load_target.each do |t|
|
31
|
+
t.destroyed_by_association = reflection
|
32
|
+
end
|
33
|
+
|
34
|
+
unless target.empty?
|
35
|
+
association_class = target.first.class
|
36
|
+
primary_key_column = association_class.primary_key.to_sym
|
37
|
+
|
38
|
+
ids = target.collect do |assoc|
|
39
|
+
assoc.public_send(primary_key_column)
|
40
|
+
end
|
41
|
+
|
42
|
+
enqueue_destroy_association(
|
43
|
+
owner_model_name: owner.class.to_s,
|
44
|
+
owner_id: owner.id,
|
45
|
+
association_class: reflection.klass.to_s,
|
46
|
+
association_ids: ids,
|
47
|
+
association_primary_key_column: primary_key_column,
|
48
|
+
ensuring_owner_was_method: options.fetch(:ensuring_owner_was, nil)
|
49
|
+
)
|
50
|
+
end
|
29
51
|
else
|
30
52
|
delete_all
|
31
53
|
end
|
@@ -52,7 +74,7 @@ module ActiveRecord
|
|
52
74
|
# the loaded flag is set to true as well.
|
53
75
|
def count_records
|
54
76
|
count = if reflection.has_cached_counter?
|
55
|
-
owner.
|
77
|
+
owner.read_attribute(reflection.counter_cache_column).to_i
|
56
78
|
else
|
57
79
|
scope.count(:all)
|
58
80
|
end
|
@@ -75,7 +97,7 @@ module ActiveRecord
|
|
75
97
|
if reflection.counter_must_be_updated_by_has_many?
|
76
98
|
counter = reflection.counter_cache_column
|
77
99
|
owner.increment(counter, difference)
|
78
|
-
owner.send(:
|
100
|
+
owner.send(:"clear_#{counter}_change")
|
79
101
|
end
|
80
102
|
end
|
81
103
|
|
@@ -8,7 +8,7 @@ module ActiveRecord
|
|
8
8
|
|
9
9
|
def initialize(owner, reflection)
|
10
10
|
super
|
11
|
-
@through_records = {}
|
11
|
+
@through_records = {}.compare_by_identity
|
12
12
|
end
|
13
13
|
|
14
14
|
def concat(*records)
|
@@ -54,7 +54,7 @@ module ActiveRecord
|
|
54
54
|
# However, after insert_record has been called, the cache is cleared in
|
55
55
|
# order to allow multiple instances of the same record in an association.
|
56
56
|
def build_through_record(record)
|
57
|
-
@through_records[record
|
57
|
+
@through_records[record] ||= begin
|
58
58
|
ensure_mutable
|
59
59
|
|
60
60
|
attributes = through_scope_attributes
|
@@ -65,7 +65,10 @@ module ActiveRecord
|
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
|
+
attr_reader :through_scope
|
69
|
+
|
68
70
|
def through_scope_attributes
|
71
|
+
scope = through_scope || self.scope
|
69
72
|
scope.where_values_hash(through_association.reflection.name.to_s).
|
70
73
|
except!(through_association.reflection.foreign_key,
|
71
74
|
through_association.reflection.klass.inheritance_column)
|
@@ -77,12 +80,13 @@ module ActiveRecord
|
|
77
80
|
association.save!
|
78
81
|
end
|
79
82
|
ensure
|
80
|
-
@through_records.delete(record
|
83
|
+
@through_records.delete(record)
|
81
84
|
end
|
82
85
|
|
83
86
|
def build_record(attributes)
|
84
87
|
ensure_not_nested
|
85
88
|
|
89
|
+
@through_scope = scope
|
86
90
|
record = super
|
87
91
|
|
88
92
|
inverse = source_reflection.inverse_of
|
@@ -95,6 +99,8 @@ module ActiveRecord
|
|
95
99
|
end
|
96
100
|
|
97
101
|
record
|
102
|
+
ensure
|
103
|
+
@through_scope = nil
|
98
104
|
end
|
99
105
|
|
100
106
|
def remove_records(existing_records, records, method)
|
@@ -202,7 +208,7 @@ module ActiveRecord
|
|
202
208
|
end
|
203
209
|
end
|
204
210
|
|
205
|
-
@through_records.delete(record
|
211
|
+
@through_records.delete(record)
|
206
212
|
end
|
207
213
|
end
|
208
214
|
|
@@ -32,6 +32,18 @@ module ActiveRecord
|
|
32
32
|
target.destroyed_by_association = reflection
|
33
33
|
target.destroy
|
34
34
|
throw(:abort) unless target.destroyed?
|
35
|
+
when :destroy_async
|
36
|
+
primary_key_column = target.class.primary_key.to_sym
|
37
|
+
id = target.public_send(primary_key_column)
|
38
|
+
|
39
|
+
enqueue_destroy_association(
|
40
|
+
owner_model_name: owner.class.to_s,
|
41
|
+
owner_id: owner.id,
|
42
|
+
association_class: reflection.klass.to_s,
|
43
|
+
association_ids: [id],
|
44
|
+
association_primary_key_column: primary_key_column,
|
45
|
+
ensuring_owner_was_method: options.fetch(:ensuring_owner_was, nil)
|
46
|
+
)
|
35
47
|
when :nullify
|
36
48
|
target.update_columns(nullified_owner_attributes) if target.persisted?
|
37
49
|
end
|
@@ -81,7 +93,9 @@ module ActiveRecord
|
|
81
93
|
target.delete
|
82
94
|
when :destroy
|
83
95
|
target.destroyed_by_association = reflection
|
84
|
-
target.
|
96
|
+
if target.persisted?
|
97
|
+
target.destroy
|
98
|
+
end
|
85
99
|
else
|
86
100
|
nullify_owner_attributes(target)
|
87
101
|
remove_inverse_instance(target)
|
@@ -34,7 +34,7 @@ module ActiveRecord
|
|
34
34
|
Table = Struct.new(:node, :columns) do # :nodoc:
|
35
35
|
def column_aliases
|
36
36
|
t = node.table
|
37
|
-
columns.map { |column| t[column.name].as
|
37
|
+
columns.map { |column| t[column.name].as(column.alias) }
|
38
38
|
end
|
39
39
|
end
|
40
40
|
Column = Struct.new(:name, :alias)
|
@@ -78,14 +78,18 @@ module ActiveRecord
|
|
78
78
|
join_root.drop(1).map!(&:reflection)
|
79
79
|
end
|
80
80
|
|
81
|
-
def join_constraints(joins_to_add, alias_tracker)
|
81
|
+
def join_constraints(joins_to_add, alias_tracker, references)
|
82
82
|
@alias_tracker = alias_tracker
|
83
|
+
@joined_tables = {}
|
84
|
+
@references = {}
|
85
|
+
|
86
|
+
references.each do |table_name|
|
87
|
+
@references[table_name.to_sym] = table_name if table_name.is_a?(Arel::Nodes::SqlLiteral)
|
88
|
+
end unless references.empty?
|
83
89
|
|
84
|
-
construct_tables!(join_root)
|
85
90
|
joins = make_join_constraints(join_root, join_type)
|
86
91
|
|
87
92
|
joins.concat joins_to_add.flat_map { |oj|
|
88
|
-
construct_tables!(oj.join_root)
|
89
93
|
if join_root.match? oj.join_root
|
90
94
|
walk(join_root, oj.join_root, oj.join_type)
|
91
95
|
else
|
@@ -94,20 +98,35 @@ module ActiveRecord
|
|
94
98
|
}
|
95
99
|
end
|
96
100
|
|
97
|
-
def instantiate(result_set, &block)
|
101
|
+
def instantiate(result_set, strict_loading_value, &block)
|
98
102
|
primary_key = aliases.column_alias(join_root, join_root.primary_key)
|
99
103
|
|
100
|
-
seen = Hash.new { |i,
|
101
|
-
i[
|
104
|
+
seen = Hash.new { |i, parent|
|
105
|
+
i[parent] = Hash.new { |j, child_class|
|
102
106
|
j[child_class] = {}
|
103
107
|
}
|
104
|
-
}
|
108
|
+
}.compare_by_identity
|
105
109
|
|
106
110
|
model_cache = Hash.new { |h, klass| h[klass] = {} }
|
107
111
|
parents = model_cache[join_root]
|
108
112
|
|
109
|
-
column_aliases = aliases.column_aliases
|
110
|
-
|
113
|
+
column_aliases = aliases.column_aliases(join_root)
|
114
|
+
column_names = []
|
115
|
+
|
116
|
+
result_set.columns.each do |name|
|
117
|
+
column_names << name unless /\At\d+_r\d+\z/.match?(name)
|
118
|
+
end
|
119
|
+
|
120
|
+
if column_names.empty?
|
121
|
+
column_types = {}
|
122
|
+
else
|
123
|
+
column_types = result_set.column_types
|
124
|
+
unless column_types.empty?
|
125
|
+
attribute_types = join_root.attribute_types
|
126
|
+
column_types = column_types.slice(*column_names).delete_if { |k, _| attribute_types.key?(k) }
|
127
|
+
end
|
128
|
+
column_aliases += column_names.map! { |name| Aliases::Column.new(name, name) }
|
129
|
+
end
|
111
130
|
|
112
131
|
message_bus = ActiveSupport::Notifications.instrumenter
|
113
132
|
|
@@ -119,8 +138,8 @@ module ActiveRecord
|
|
119
138
|
message_bus.instrument("instantiation.active_record", payload) do
|
120
139
|
result_set.each { |row_hash|
|
121
140
|
parent_key = primary_key ? row_hash[primary_key] : row_hash
|
122
|
-
parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases, &block)
|
123
|
-
construct(parent, join_root, row_hash, seen, model_cache)
|
141
|
+
parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases, column_types, &block)
|
142
|
+
construct(parent, join_root, row_hash, seen, model_cache, strict_loading_value)
|
124
143
|
}
|
125
144
|
end
|
126
145
|
|
@@ -128,37 +147,36 @@ module ActiveRecord
|
|
128
147
|
end
|
129
148
|
|
130
149
|
def apply_column_aliases(relation)
|
150
|
+
@join_root_alias = relation.select_values.empty?
|
131
151
|
relation._select!(-> { aliases.columns })
|
132
152
|
end
|
133
153
|
|
154
|
+
def each(&block)
|
155
|
+
join_root.each(&block)
|
156
|
+
end
|
157
|
+
|
134
158
|
protected
|
135
159
|
attr_reader :join_root, :join_type
|
136
160
|
|
137
161
|
private
|
138
|
-
attr_reader :alias_tracker
|
139
|
-
|
140
|
-
def explicit_selections(root_column_aliases, result_set)
|
141
|
-
root_names = root_column_aliases.map(&:name).to_set
|
142
|
-
result_set.columns
|
143
|
-
.reject { |n| root_names.include?(n) || n =~ /\At\d+_r\d+\z/ }
|
144
|
-
.map { |n| Aliases::Column.new(n, n) }
|
145
|
-
end
|
162
|
+
attr_reader :alias_tracker, :join_root_alias
|
146
163
|
|
147
164
|
def aliases
|
148
165
|
@aliases ||= Aliases.new join_root.each_with_index.map { |join_part, i|
|
149
|
-
|
166
|
+
column_names = if join_part == join_root && !join_root_alias
|
167
|
+
primary_key = join_root.primary_key
|
168
|
+
primary_key ? [primary_key] : []
|
169
|
+
else
|
170
|
+
join_part.column_names
|
171
|
+
end
|
172
|
+
|
173
|
+
columns = column_names.each_with_index.map { |column_name, j|
|
150
174
|
Aliases::Column.new column_name, "t#{i}_r#{j}"
|
151
175
|
}
|
152
176
|
Aliases::Table.new(join_part, columns)
|
153
177
|
}
|
154
178
|
end
|
155
179
|
|
156
|
-
def construct_tables!(join_root)
|
157
|
-
join_root.each_children do |parent, child|
|
158
|
-
child.tables = table_aliases_for(parent, child)
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
180
|
def make_join_constraints(join_root, join_type)
|
163
181
|
join_root.children.flat_map do |child|
|
164
182
|
make_constraints(join_root, child, join_type)
|
@@ -168,23 +186,25 @@ module ActiveRecord
|
|
168
186
|
def make_constraints(parent, child, join_type)
|
169
187
|
foreign_table = parent.table
|
170
188
|
foreign_klass = parent.base_klass
|
171
|
-
|
172
|
-
|
173
|
-
|
189
|
+
child.join_constraints(foreign_table, foreign_klass, join_type, alias_tracker) do |reflection|
|
190
|
+
table, terminated = @joined_tables[reflection]
|
191
|
+
root = reflection == child.reflection
|
174
192
|
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
193
|
+
if table && (!root || !terminated)
|
194
|
+
@joined_tables[reflection] = [table, root] if root
|
195
|
+
next table, true
|
196
|
+
end
|
197
|
+
|
198
|
+
table_name = @references[reflection.name.to_sym]&.to_s
|
199
|
+
|
200
|
+
table = alias_tracker.aliased_table_for(reflection.klass.arel_table, table_name) do
|
201
|
+
name = reflection.alias_candidate(parent.table_name)
|
202
|
+
root ? name : "#{name}_join"
|
203
|
+
end
|
184
204
|
|
185
|
-
|
186
|
-
|
187
|
-
|
205
|
+
@joined_tables[reflection] ||= [table, root] if join_type == Arel::Nodes::OuterJoin
|
206
|
+
table
|
207
|
+
end.concat child.children.flat_map { |c| make_constraints(child, c, join_type) }
|
188
208
|
end
|
189
209
|
|
190
210
|
def walk(left, right, join_type)
|
@@ -215,7 +235,7 @@ module ActiveRecord
|
|
215
235
|
end
|
216
236
|
end
|
217
237
|
|
218
|
-
def construct(ar_parent, parent, row, seen, model_cache)
|
238
|
+
def construct(ar_parent, parent, row, seen, model_cache, strict_loading_value)
|
219
239
|
return if ar_parent.nil?
|
220
240
|
|
221
241
|
parent.children.each do |node|
|
@@ -224,7 +244,7 @@ module ActiveRecord
|
|
224
244
|
other.loaded!
|
225
245
|
elsif ar_parent.association_cached?(node.reflection.name)
|
226
246
|
model = ar_parent.association(node.reflection.name).target
|
227
|
-
construct(model, node, row, seen, model_cache)
|
247
|
+
construct(model, node, row, seen, model_cache, strict_loading_value)
|
228
248
|
next
|
229
249
|
end
|
230
250
|
|
@@ -236,24 +256,25 @@ module ActiveRecord
|
|
236
256
|
next
|
237
257
|
end
|
238
258
|
|
239
|
-
model = seen[ar_parent
|
259
|
+
model = seen[ar_parent][node][id]
|
240
260
|
|
241
261
|
if model
|
242
|
-
construct(model, node, row, seen, model_cache)
|
262
|
+
construct(model, node, row, seen, model_cache, strict_loading_value)
|
243
263
|
else
|
244
|
-
model = construct_model(ar_parent, node, row, model_cache, id)
|
264
|
+
model = construct_model(ar_parent, node, row, model_cache, id, strict_loading_value)
|
245
265
|
|
246
|
-
seen[ar_parent
|
247
|
-
construct(model, node, row, seen, model_cache)
|
266
|
+
seen[ar_parent][node][id] = model
|
267
|
+
construct(model, node, row, seen, model_cache, strict_loading_value)
|
248
268
|
end
|
249
269
|
end
|
250
270
|
end
|
251
271
|
|
252
|
-
def construct_model(record, node, row, model_cache, id)
|
272
|
+
def construct_model(record, node, row, model_cache, id, strict_loading_value)
|
253
273
|
other = record.association(node.reflection.name)
|
254
274
|
|
255
275
|
model = model_cache[node][id] ||=
|
256
276
|
node.instantiate(row, aliases.column_aliases(node)) do |m|
|
277
|
+
m.strict_loading! if strict_loading_value
|
257
278
|
other.set_inverse_instance(m)
|
258
279
|
end
|
259
280
|
|
@@ -264,6 +285,7 @@ module ActiveRecord
|
|
264
285
|
end
|
265
286
|
|
266
287
|
model.readonly! if node.readonly?
|
288
|
+
model.strict_loading! if node.strict_loading?
|
267
289
|
model
|
268
290
|
end
|
269
291
|
end
|