activerecord 6.0.1 → 6.1.0
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 +843 -626
- data/MIT-LICENSE +1 -1
- data/README.rdoc +3 -3
- data/lib/active_record/aggregations.rb +1 -2
- data/lib/active_record/association_relation.rb +18 -17
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +49 -37
- data/lib/active_record/associations/association_scope.rb +17 -15
- data/lib/active_record/associations/belongs_to_association.rb +15 -5
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
- data/lib/active_record/associations/builder/association.rb +9 -3
- 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 -3
- 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 +25 -8
- data/lib/active_record/associations/collection_proxy.rb +14 -7
- data/lib/active_record/associations/foreign_association.rb +13 -0
- data/lib/active_record/associations/has_many_association.rb +24 -3
- 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/join_association.rb +36 -14
- data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
- data/lib/active_record/associations/join_dependency.rb +73 -42
- 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/preloader.rb +12 -7
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +115 -12
- data/lib/active_record/attribute_assignment.rb +10 -9
- data/lib/active_record/attribute_methods/before_type_cast.rb +13 -10
- data/lib/active_record/attribute_methods/dirty.rb +3 -13
- data/lib/active_record/attribute_methods/primary_key.rb +6 -4
- data/lib/active_record/attribute_methods/query.rb +3 -6
- data/lib/active_record/attribute_methods/read.rb +8 -12
- data/lib/active_record/attribute_methods/serialization.rb +11 -6
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
- data/lib/active_record/attribute_methods/write.rb +12 -21
- data/lib/active_record/attribute_methods.rb +64 -54
- data/lib/active_record/attributes.rb +32 -8
- data/lib/active_record/autosave_association.rb +56 -41
- data/lib/active_record/base.rb +2 -14
- data/lib/active_record/callbacks.rb +153 -24
- data/lib/active_record/coders/yaml_column.rb +1 -2
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +190 -136
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +82 -37
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -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 +152 -116
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -52
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +263 -107
- data/lib/active_record/connection_adapters/abstract/transaction.rb +82 -35
- data/lib/active_record/connection_adapters/abstract_adapter.rb +60 -73
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +136 -111
- 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/column.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +28 -36
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
- data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -7
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +17 -13
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -13
- data/lib/active_record/connection_adapters/pool_config.rb +63 -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 +19 -56
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -6
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +7 -3
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +72 -54
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +77 -57
- 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 +36 -12
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -5
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +57 -57
- data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
- data/lib/active_record/connection_adapters.rb +50 -0
- data/lib/active_record/connection_handling.rb +210 -87
- data/lib/active_record/core.rb +229 -65
- data/lib/active_record/counter_cache.rb +4 -1
- 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 -41
- data/lib/active_record/database_configurations.rb +124 -85
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/dynamic_matchers.rb +2 -3
- data/lib/active_record/enum.rb +40 -16
- data/lib/active_record/errors.rb +47 -12
- data/lib/active_record/explain.rb +9 -5
- 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 -3
- data/lib/active_record/fixture_set/table_rows.rb +0 -1
- data/lib/active_record/fixtures.rb +54 -11
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +40 -21
- data/lib/active_record/insert_all.rb +38 -9
- 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 +22 -17
- data/lib/active_record/locking/pessimistic.rb +6 -2
- data/lib/active_record/log_subscriber.rb +27 -9
- data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +6 -2
- data/lib/active_record/middleware/database_selector.rb +4 -2
- data/lib/active_record/migration/command_recorder.rb +53 -45
- data/lib/active_record/migration/compatibility.rb +70 -20
- data/lib/active_record/migration/join_table.rb +0 -1
- data/lib/active_record/migration.rb +114 -84
- data/lib/active_record/model_schema.rb +117 -15
- data/lib/active_record/nested_attributes.rb +2 -5
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/null_relation.rb +0 -1
- data/lib/active_record/persistence.rb +50 -46
- data/lib/active_record/query_cache.rb +15 -5
- data/lib/active_record/querying.rb +12 -7
- data/lib/active_record/railtie.rb +65 -45
- data/lib/active_record/railties/databases.rake +267 -93
- data/lib/active_record/readonly_attributes.rb +4 -0
- data/lib/active_record/reflection.rb +77 -63
- data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
- data/lib/active_record/relation/batches.rb +38 -32
- data/lib/active_record/relation/calculations.rb +102 -45
- data/lib/active_record/relation/delegation.rb +9 -7
- data/lib/active_record/relation/finder_methods.rb +45 -16
- data/lib/active_record/relation/from_clause.rb +5 -1
- data/lib/active_record/relation/merger.rb +27 -26
- 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 +3 -3
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +55 -35
- data/lib/active_record/relation/query_methods.rb +335 -187
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +8 -8
- data/lib/active_record/relation/where_clause.rb +104 -58
- data/lib/active_record/relation.rb +108 -68
- data/lib/active_record/result.rb +41 -34
- 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/default.rb +0 -1
- data/lib/active_record/scoping/named.rb +7 -18
- data/lib/active_record/scoping.rb +0 -1
- 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 +3 -3
- data/lib/active_record/suppressor.rb +2 -2
- data/lib/active_record/table_metadata.rb +39 -36
- data/lib/active_record/tasks/database_tasks.rb +139 -113
- data/lib/active_record/tasks/mysql_database_tasks.rb +34 -36
- data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -27
- data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -10
- data/lib/active_record/test_databases.rb +5 -4
- data/lib/active_record/test_fixtures.rb +38 -16
- data/lib/active_record/timestamp.rb +4 -7
- data/lib/active_record/touch_later.rb +20 -21
- data/lib/active_record/transactions.rb +21 -70
- data/lib/active_record/type/adapter_specific_registry.rb +2 -5
- data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
- data/lib/active_record/type/serialized.rb +6 -3
- data/lib/active_record/type/time.rb +10 -0
- data/lib/active_record/type/type_map.rb +0 -1
- data/lib/active_record/type/unsigned_integer.rb +0 -1
- data/lib/active_record/type.rb +8 -2
- 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/associated.rb +1 -2
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +24 -4
- data/lib/active_record/validations.rb +3 -3
- data/lib/active_record.rb +7 -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/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/nodes.rb +3 -1
- data/lib/arel/predications.rb +17 -24
- data/lib/arel/select_manager.rb +1 -2
- data/lib/arel/table.rb +13 -5
- data/lib/arel/visitors/dot.rb +14 -3
- data/lib/arel/visitors/mysql.rb +11 -1
- data/lib/arel/visitors/postgresql.rb +15 -5
- data/lib/arel/visitors/sqlite.rb +0 -1
- data/lib/arel/visitors/to_sql.rb +89 -79
- data/lib/arel/visitors/visitor.rb +0 -1
- data/lib/arel/visitors.rb +0 -7
- data/lib/arel.rb +5 -9
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -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 +4 -4
- data/lib/rails/generators/active_record/migration.rb +6 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +38 -2
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- metadata +26 -26
- data/lib/active_record/attribute_decorators.rb +0 -90
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
- 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 -204
- data/lib/arel/visitors/ibm_db.rb +0 -34
- data/lib/arel/visitors/informix.rb +0 -62
- data/lib/arel/visitors/mssql.rb +0 -157
- data/lib/arel/visitors/oracle.rb +0 -159
- data/lib/arel/visitors/oracle12.rb +0 -66
- data/lib/arel/visitors/where_sql.rb +0 -23
@@ -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
|
@@ -378,7 +391,9 @@ module ActiveRecord
|
|
378
391
|
end
|
379
392
|
|
380
393
|
def remove_records(existing_records, records, method)
|
381
|
-
|
394
|
+
catch(:abort) do
|
395
|
+
records.each { |record| callback(:before_remove, record) }
|
396
|
+
end || return
|
382
397
|
|
383
398
|
delete_records(existing_records, method) if existing_records.any?
|
384
399
|
@target -= records
|
@@ -434,7 +449,9 @@ module ActiveRecord
|
|
434
449
|
end
|
435
450
|
|
436
451
|
def replace_on_target(record, index, skip_callbacks)
|
437
|
-
|
452
|
+
catch(:abort) do
|
453
|
+
callback(:before_add, record)
|
454
|
+
end || return unless skip_callbacks
|
438
455
|
|
439
456
|
set_inverse_instance(record)
|
440
457
|
|
@@ -27,7 +27,7 @@ module ActiveRecord
|
|
27
27
|
# is computed directly through SQL and does not trigger by itself the
|
28
28
|
# instantiation of the actual post records.
|
29
29
|
class CollectionProxy < Relation
|
30
|
-
def initialize(klass, association) #:nodoc:
|
30
|
+
def initialize(klass, association, **) #:nodoc:
|
31
31
|
@association = association
|
32
32
|
super klass
|
33
33
|
|
@@ -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,22 +1087,28 @@ 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
|
|
1103
1111
|
private
|
1104
|
-
|
1105
1112
|
def find_nth_with_limit(index, limit)
|
1106
1113
|
load_target if find_from_target?
|
1107
1114
|
super
|
@@ -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: association_class.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
|
@@ -37,7 +59,6 @@ module ActiveRecord
|
|
37
59
|
end
|
38
60
|
|
39
61
|
private
|
40
|
-
|
41
62
|
# Returns the number of records in this collection.
|
42
63
|
#
|
43
64
|
# If the association has a counter cache it gets that value. Otherwise
|
@@ -53,7 +74,7 @@ module ActiveRecord
|
|
53
74
|
# the loaded flag is set to true as well.
|
54
75
|
def count_records
|
55
76
|
count = if reflection.has_cached_counter?
|
56
|
-
owner.
|
77
|
+
owner.read_attribute(reflection.counter_cache_column).to_i
|
57
78
|
else
|
58
79
|
scope.count(:all)
|
59
80
|
end
|
@@ -76,7 +97,7 @@ module ActiveRecord
|
|
76
97
|
if reflection.counter_must_be_updated_by_has_many?
|
77
98
|
counter = reflection.counter_cache_column
|
78
99
|
owner.increment(counter, difference)
|
79
|
-
owner.send(:
|
100
|
+
owner.send(:"clear_#{counter}_change")
|
80
101
|
end
|
81
102
|
end
|
82
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)
|
@@ -14,7 +14,6 @@ module ActiveRecord
|
|
14
14
|
super(reflection.klass, children)
|
15
15
|
|
16
16
|
@reflection = reflection
|
17
|
-
@tables = nil
|
18
17
|
end
|
19
18
|
|
20
19
|
def match?(other)
|
@@ -24,25 +23,46 @@ module ActiveRecord
|
|
24
23
|
|
25
24
|
def join_constraints(foreign_table, foreign_klass, join_type, alias_tracker)
|
26
25
|
joins = []
|
26
|
+
chain = []
|
27
|
+
|
28
|
+
reflection.chain.each do |reflection|
|
29
|
+
table, terminated = yield reflection
|
30
|
+
@table ||= table
|
31
|
+
|
32
|
+
if terminated
|
33
|
+
foreign_table, foreign_klass = table, reflection.klass
|
34
|
+
break
|
35
|
+
end
|
36
|
+
|
37
|
+
chain << [reflection, table]
|
38
|
+
end
|
27
39
|
|
28
40
|
# The chain starts with the target table, but we want to end with it here (makes
|
29
41
|
# more sense in this context), so we reverse
|
30
|
-
|
31
|
-
table = tables[-i]
|
42
|
+
chain.reverse_each do |reflection, table|
|
32
43
|
klass = reflection.klass
|
33
44
|
|
34
45
|
join_scope = reflection.join_scope(table, foreign_table, foreign_klass)
|
35
46
|
|
47
|
+
unless join_scope.references_values.empty?
|
48
|
+
join_dependency = join_scope.construct_join_dependency(
|
49
|
+
join_scope.eager_load_values | join_scope.includes_values, Arel::Nodes::OuterJoin
|
50
|
+
)
|
51
|
+
join_scope.joins!(join_dependency)
|
52
|
+
end
|
53
|
+
|
36
54
|
arel = join_scope.arel(alias_tracker.aliases)
|
37
55
|
nodes = arel.constraints.first
|
38
56
|
|
39
|
-
|
40
|
-
|
57
|
+
if nodes.is_a?(Arel::Nodes::And)
|
58
|
+
others = nodes.children.extract! do |node|
|
59
|
+
!Arel.fetch_attribute(node) { |attr| attr.relation.name == table.name }
|
60
|
+
end
|
41
61
|
end
|
42
62
|
|
43
|
-
joins <<
|
63
|
+
joins << join_type.new(table, Arel::Nodes::On.new(nodes))
|
44
64
|
|
45
|
-
|
65
|
+
if others && !others.empty?
|
46
66
|
joins.concat arel.join_sources
|
47
67
|
append_constraints(joins.last, others)
|
48
68
|
end
|
@@ -54,24 +74,26 @@ module ActiveRecord
|
|
54
74
|
joins
|
55
75
|
end
|
56
76
|
|
57
|
-
def tables=(tables)
|
58
|
-
@tables = tables
|
59
|
-
@table = tables.first
|
60
|
-
end
|
61
|
-
|
62
77
|
def readonly?
|
63
78
|
return @readonly if defined?(@readonly)
|
64
79
|
|
65
80
|
@readonly = reflection.scope && reflection.scope_for(base_klass.unscoped).readonly_value
|
66
81
|
end
|
67
82
|
|
83
|
+
def strict_loading?
|
84
|
+
return @strict_loading if defined?(@strict_loading)
|
85
|
+
|
86
|
+
@strict_loading = reflection.scope && reflection.scope_for(base_klass.unscoped).strict_loading_value
|
87
|
+
end
|
88
|
+
|
68
89
|
private
|
69
90
|
def append_constraints(join, constraints)
|
70
91
|
if join.is_a?(Arel::Nodes::StringJoin)
|
71
|
-
join_string =
|
92
|
+
join_string = Arel::Nodes::And.new(constraints.unshift join.left)
|
72
93
|
join.left = Arel.sql(base_klass.connection.visitor.compile(join_string))
|
73
94
|
else
|
74
|
-
join.right
|
95
|
+
right = join.right
|
96
|
+
right.expr = Arel::Nodes::And.new(constraints.unshift right.expr)
|
75
97
|
end
|
76
98
|
end
|
77
99
|
end
|
@@ -17,7 +17,7 @@ module ActiveRecord
|
|
17
17
|
# association.
|
18
18
|
attr_reader :base_klass, :children
|
19
19
|
|
20
|
-
delegate :table_name, :column_names, :primary_key, to: :base_klass
|
20
|
+
delegate :table_name, :column_names, :primary_key, :attribute_types, to: :base_klass
|
21
21
|
|
22
22
|
def initialize(base_klass, children)
|
23
23
|
@base_klass = base_klass
|
@@ -62,8 +62,8 @@ module ActiveRecord
|
|
62
62
|
hash
|
63
63
|
end
|
64
64
|
|
65
|
-
def instantiate(row, aliases, &block)
|
66
|
-
base_klass.instantiate(extract_record(row, aliases), &block)
|
65
|
+
def instantiate(row, aliases, column_types = {}, &block)
|
66
|
+
base_klass.instantiate(extract_record(row, aliases), column_types, &block)
|
67
67
|
end
|
68
68
|
end
|
69
69
|
end
|
@@ -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,18 +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
|
+
|
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
|
109
130
|
|
110
131
|
message_bus = ActiveSupport::Notifications.instrumenter
|
111
132
|
|
@@ -117,8 +138,8 @@ module ActiveRecord
|
|
117
138
|
message_bus.instrument("instantiation.active_record", payload) do
|
118
139
|
result_set.each { |row_hash|
|
119
140
|
parent_key = primary_key ? row_hash[primary_key] : row_hash
|
120
|
-
parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases, &block)
|
121
|
-
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)
|
122
143
|
}
|
123
144
|
end
|
124
145
|
|
@@ -126,30 +147,36 @@ module ActiveRecord
|
|
126
147
|
end
|
127
148
|
|
128
149
|
def apply_column_aliases(relation)
|
150
|
+
@join_root_alias = relation.select_values.empty?
|
129
151
|
relation._select!(-> { aliases.columns })
|
130
152
|
end
|
131
153
|
|
154
|
+
def each(&block)
|
155
|
+
join_root.each(&block)
|
156
|
+
end
|
157
|
+
|
132
158
|
protected
|
133
159
|
attr_reader :join_root, :join_type
|
134
160
|
|
135
161
|
private
|
136
|
-
attr_reader :alias_tracker
|
162
|
+
attr_reader :alias_tracker, :join_root_alias
|
137
163
|
|
138
164
|
def aliases
|
139
165
|
@aliases ||= Aliases.new join_root.each_with_index.map { |join_part, i|
|
140
|
-
|
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|
|
141
174
|
Aliases::Column.new column_name, "t#{i}_r#{j}"
|
142
175
|
}
|
143
176
|
Aliases::Table.new(join_part, columns)
|
144
177
|
}
|
145
178
|
end
|
146
179
|
|
147
|
-
def construct_tables!(join_root)
|
148
|
-
join_root.each_children do |parent, child|
|
149
|
-
child.tables = table_aliases_for(parent, child)
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
180
|
def make_join_constraints(join_root, join_type)
|
154
181
|
join_root.children.flat_map do |child|
|
155
182
|
make_constraints(join_root, child, join_type)
|
@@ -159,23 +186,25 @@ module ActiveRecord
|
|
159
186
|
def make_constraints(parent, child, join_type)
|
160
187
|
foreign_table = parent.table
|
161
188
|
foreign_klass = parent.base_klass
|
162
|
-
|
163
|
-
|
164
|
-
|
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
|
165
192
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
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]
|
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
|
175
204
|
|
176
|
-
|
177
|
-
|
178
|
-
|
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) }
|
179
208
|
end
|
180
209
|
|
181
210
|
def walk(left, right, join_type)
|
@@ -206,7 +235,7 @@ module ActiveRecord
|
|
206
235
|
end
|
207
236
|
end
|
208
237
|
|
209
|
-
def construct(ar_parent, parent, row, seen, model_cache)
|
238
|
+
def construct(ar_parent, parent, row, seen, model_cache, strict_loading_value)
|
210
239
|
return if ar_parent.nil?
|
211
240
|
|
212
241
|
parent.children.each do |node|
|
@@ -215,7 +244,7 @@ module ActiveRecord
|
|
215
244
|
other.loaded!
|
216
245
|
elsif ar_parent.association_cached?(node.reflection.name)
|
217
246
|
model = ar_parent.association(node.reflection.name).target
|
218
|
-
construct(model, node, row, seen, model_cache)
|
247
|
+
construct(model, node, row, seen, model_cache, strict_loading_value)
|
219
248
|
next
|
220
249
|
end
|
221
250
|
|
@@ -227,24 +256,25 @@ module ActiveRecord
|
|
227
256
|
next
|
228
257
|
end
|
229
258
|
|
230
|
-
model = seen[ar_parent
|
259
|
+
model = seen[ar_parent][node][id]
|
231
260
|
|
232
261
|
if model
|
233
|
-
construct(model, node, row, seen, model_cache)
|
262
|
+
construct(model, node, row, seen, model_cache, strict_loading_value)
|
234
263
|
else
|
235
|
-
model = construct_model(ar_parent, node, row, model_cache, id)
|
264
|
+
model = construct_model(ar_parent, node, row, model_cache, id, strict_loading_value)
|
236
265
|
|
237
|
-
seen[ar_parent
|
238
|
-
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)
|
239
268
|
end
|
240
269
|
end
|
241
270
|
end
|
242
271
|
|
243
|
-
def construct_model(record, node, row, model_cache, id)
|
272
|
+
def construct_model(record, node, row, model_cache, id, strict_loading_value)
|
244
273
|
other = record.association(node.reflection.name)
|
245
274
|
|
246
275
|
model = model_cache[node][id] ||=
|
247
276
|
node.instantiate(row, aliases.column_aliases(node)) do |m|
|
277
|
+
m.strict_loading! if strict_loading_value
|
248
278
|
other.set_inverse_instance(m)
|
249
279
|
end
|
250
280
|
|
@@ -255,6 +285,7 @@ module ActiveRecord
|
|
255
285
|
end
|
256
286
|
|
257
287
|
model.readonly! if node.readonly?
|
288
|
+
model.strict_loading! if node.strict_loading?
|
258
289
|
model
|
259
290
|
end
|
260
291
|
end
|