activerecord 5.2.3 → 6.0.0.rc2
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 +605 -554
- data/MIT-LICENSE +3 -1
- data/README.rdoc +4 -2
- data/examples/performance.rb +1 -1
- data/lib/active_record.rb +9 -2
- data/lib/active_record/aggregations.rb +4 -2
- data/lib/active_record/associations.rb +19 -14
- data/lib/active_record/associations/association.rb +52 -19
- data/lib/active_record/associations/association_scope.rb +4 -6
- data/lib/active_record/associations/belongs_to_association.rb +36 -42
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
- data/lib/active_record/associations/builder/association.rb +14 -18
- data/lib/active_record/associations/builder/belongs_to.rb +19 -52
- data/lib/active_record/associations/builder/collection_association.rb +5 -15
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
- data/lib/active_record/associations/builder/has_many.rb +2 -0
- data/lib/active_record/associations/builder/has_one.rb +35 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -0
- data/lib/active_record/associations/collection_association.rb +6 -21
- data/lib/active_record/associations/collection_proxy.rb +12 -15
- data/lib/active_record/associations/foreign_association.rb +7 -0
- data/lib/active_record/associations/has_many_association.rb +2 -10
- data/lib/active_record/associations/has_many_through_association.rb +18 -25
- data/lib/active_record/associations/has_one_association.rb +28 -30
- data/lib/active_record/associations/has_one_through_association.rb +5 -5
- data/lib/active_record/associations/join_dependency.rb +24 -28
- data/lib/active_record/associations/join_dependency/join_association.rb +27 -7
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
- data/lib/active_record/associations/preloader.rb +39 -31
- data/lib/active_record/associations/preloader/association.rb +38 -36
- data/lib/active_record/associations/preloader/through_association.rb +48 -39
- data/lib/active_record/associations/singular_association.rb +2 -16
- data/lib/active_record/attribute_assignment.rb +7 -10
- data/lib/active_record/attribute_methods.rb +28 -100
- data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
- data/lib/active_record/attribute_methods/dirty.rb +111 -40
- data/lib/active_record/attribute_methods/primary_key.rb +15 -22
- data/lib/active_record/attribute_methods/query.rb +2 -3
- data/lib/active_record/attribute_methods/read.rb +15 -53
- data/lib/active_record/attribute_methods/serialization.rb +1 -1
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
- data/lib/active_record/attribute_methods/write.rb +17 -24
- data/lib/active_record/attributes.rb +13 -0
- data/lib/active_record/autosave_association.rb +15 -5
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/callbacks.rb +5 -19
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +111 -19
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +95 -123
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +20 -11
- data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +19 -12
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +76 -48
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +132 -53
- data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -56
- data/lib/active_record/connection_adapters/abstract_adapter.rb +172 -41
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +128 -194
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +52 -42
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +6 -10
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +73 -13
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +129 -13
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +24 -7
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +20 -1
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +12 -1
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +55 -53
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +160 -74
- data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -6
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +42 -11
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +127 -143
- data/lib/active_record/connection_handling.rb +149 -27
- data/lib/active_record/core.rb +100 -60
- data/lib/active_record/counter_cache.rb +4 -29
- data/lib/active_record/database_configurations.rb +204 -0
- data/lib/active_record/database_configurations/database_config.rb +37 -0
- data/lib/active_record/database_configurations/hash_config.rb +50 -0
- data/lib/active_record/database_configurations/url_config.rb +79 -0
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/enum.rb +28 -7
- data/lib/active_record/errors.rb +15 -7
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixture_set/model_metadata.rb +33 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +153 -0
- data/lib/active_record/fixture_set/table_rows.rb +47 -0
- data/lib/active_record/fixtures.rb +140 -472
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +13 -3
- data/lib/active_record/insert_all.rb +180 -0
- data/lib/active_record/integration.rb +68 -16
- data/lib/active_record/internal_metadata.rb +10 -2
- data/lib/active_record/locking/optimistic.rb +5 -6
- data/lib/active_record/locking/pessimistic.rb +3 -3
- data/lib/active_record/log_subscriber.rb +7 -26
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +90 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/migration.rb +87 -76
- data/lib/active_record/migration/command_recorder.rb +50 -6
- data/lib/active_record/migration/compatibility.rb +76 -49
- data/lib/active_record/model_schema.rb +30 -9
- data/lib/active_record/nested_attributes.rb +2 -2
- data/lib/active_record/no_touching.rb +7 -0
- data/lib/active_record/persistence.rb +228 -24
- data/lib/active_record/query_cache.rb +11 -4
- data/lib/active_record/querying.rb +32 -20
- data/lib/active_record/railtie.rb +80 -43
- data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
- data/lib/active_record/railties/controller_runtime.rb +30 -35
- data/lib/active_record/railties/databases.rake +193 -46
- data/lib/active_record/reflection.rb +42 -44
- data/lib/active_record/relation.rb +310 -80
- data/lib/active_record/relation/batches.rb +13 -10
- data/lib/active_record/relation/calculations.rb +58 -49
- data/lib/active_record/relation/delegation.rb +26 -43
- data/lib/active_record/relation/finder_methods.rb +14 -27
- data/lib/active_record/relation/merger.rb +11 -20
- data/lib/active_record/relation/predicate_builder.rb +4 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
- data/lib/active_record/relation/query_attribute.rb +13 -8
- data/lib/active_record/relation/query_methods.rb +182 -68
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/relation/where_clause.rb +14 -10
- data/lib/active_record/relation/where_clause_factory.rb +1 -2
- data/lib/active_record/result.rb +30 -11
- data/lib/active_record/sanitization.rb +32 -40
- data/lib/active_record/schema.rb +2 -11
- data/lib/active_record/schema_dumper.rb +22 -7
- data/lib/active_record/schema_migration.rb +5 -1
- data/lib/active_record/scoping.rb +8 -8
- data/lib/active_record/scoping/default.rb +6 -7
- data/lib/active_record/scoping/named.rb +19 -15
- data/lib/active_record/statement_cache.rb +32 -5
- data/lib/active_record/store.rb +87 -8
- data/lib/active_record/table_metadata.rb +10 -17
- data/lib/active_record/tasks/database_tasks.rb +159 -25
- data/lib/active_record/tasks/mysql_database_tasks.rb +5 -5
- data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
- data/lib/active_record/test_databases.rb +38 -0
- data/lib/active_record/test_fixtures.rb +224 -0
- data/lib/active_record/timestamp.rb +39 -25
- data/lib/active_record/touch_later.rb +4 -2
- data/lib/active_record/transactions.rb +56 -65
- data/lib/active_record/translation.rb +1 -1
- data/lib/active_record/type.rb +3 -4
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type_caster/connection.rb +15 -14
- data/lib/active_record/type_caster/map.rb +1 -4
- data/lib/active_record/validations/uniqueness.rb +15 -27
- data/lib/arel.rb +51 -0
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes.rb +22 -0
- data/lib/arel/attributes/attribute.rb +37 -0
- data/lib/arel/collectors/bind.rb +24 -0
- data/lib/arel/collectors/composite.rb +31 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +20 -0
- data/lib/arel/collectors/substitute_binds.rb +28 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes.rb +68 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +52 -0
- data/lib/arel/nodes/bind_param.rb +36 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +50 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +18 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +8 -0
- data/lib/arel/nodes/in.rb +8 -0
- data/lib/arel/nodes/infix_operation.rb +80 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +50 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +16 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +27 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +45 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +257 -0
- data/lib/arel/select_manager.rb +271 -0
- data/lib/arel/table.rb +110 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors.rb +20 -0
- data/lib/arel/visitors/depth_first.rb +204 -0
- data/lib/arel/visitors/dot.rb +297 -0
- data/lib/arel/visitors/ibm_db.rb +34 -0
- data/lib/arel/visitors/informix.rb +62 -0
- data/lib/arel/visitors/mssql.rb +157 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +159 -0
- data/lib/arel/visitors/oracle12.rb +66 -0
- data/lib/arel/visitors/postgresql.rb +110 -0
- data/lib/arel/visitors/sqlite.rb +39 -0
- data/lib/arel/visitors/to_sql.rb +889 -0
- data/lib/arel/visitors/visitor.rb +46 -0
- data/lib/arel/visitors/where_sql.rb +23 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/rails/generators/active_record/migration.rb +14 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +109 -27
- data/lib/active_record/collection_cache_key.rb +0 -53
@@ -4,33 +4,46 @@ module ActiveRecord
|
|
4
4
|
module Associations
|
5
5
|
class Preloader
|
6
6
|
class Association #:nodoc:
|
7
|
-
attr_reader :preloaded_records
|
8
|
-
|
9
7
|
def initialize(klass, owners, reflection, preload_scope)
|
10
8
|
@klass = klass
|
11
9
|
@owners = owners
|
12
10
|
@reflection = reflection
|
13
11
|
@preload_scope = preload_scope
|
14
12
|
@model = owners.first && owners.first.class
|
15
|
-
@preloaded_records = []
|
16
13
|
end
|
17
14
|
|
18
|
-
def run
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
15
|
+
def run
|
16
|
+
if !preload_scope || preload_scope.empty_scope?
|
17
|
+
owners.each do |owner|
|
18
|
+
associate_records_to_owner(owner, records_by_owner[owner] || [])
|
19
|
+
end
|
20
|
+
else
|
21
|
+
# Custom preload scope is used and
|
22
|
+
# the association can not be marked as loaded
|
23
|
+
# Loading into a Hash instead
|
24
|
+
records_by_owner
|
23
25
|
end
|
26
|
+
self
|
27
|
+
end
|
24
28
|
|
25
|
-
|
26
|
-
|
29
|
+
def records_by_owner
|
30
|
+
# owners can be duplicated when a relation has a collection association join
|
31
|
+
# #compare_by_identity makes such owners different hash keys
|
32
|
+
@records_by_owner ||= preloaded_records.each_with_object({}.compare_by_identity) do |record, result|
|
33
|
+
owners_by_key[convert_key(record[association_key_name])].each do |owner|
|
34
|
+
(result[owner] ||= []) << record
|
35
|
+
end
|
27
36
|
end
|
28
37
|
end
|
29
38
|
|
30
|
-
|
31
|
-
|
39
|
+
def preloaded_records
|
40
|
+
return @preloaded_records if defined?(@preloaded_records)
|
41
|
+
@preloaded_records = owner_keys.empty? ? [] : records_for(owner_keys)
|
42
|
+
end
|
32
43
|
|
33
44
|
private
|
45
|
+
attr_reader :owners, :reflection, :preload_scope, :model, :klass
|
46
|
+
|
34
47
|
# The name of the key on the associated records
|
35
48
|
def association_key_name
|
36
49
|
reflection.join_primary_key(klass)
|
@@ -43,11 +56,10 @@ module ActiveRecord
|
|
43
56
|
|
44
57
|
def associate_records_to_owner(owner, records)
|
45
58
|
association = owner.association(reflection.name)
|
46
|
-
association.loaded!
|
47
59
|
if reflection.collection?
|
48
|
-
association.target
|
60
|
+
association.target = records
|
49
61
|
else
|
50
|
-
association.target = records.first
|
62
|
+
association.target = records.first
|
51
63
|
end
|
52
64
|
end
|
53
65
|
|
@@ -56,13 +68,10 @@ module ActiveRecord
|
|
56
68
|
end
|
57
69
|
|
58
70
|
def owners_by_key
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
h[key] = owner if key
|
63
|
-
end
|
71
|
+
@owners_by_key ||= owners.each_with_object({}) do |owner, result|
|
72
|
+
key = convert_key(owner[owner_key_name])
|
73
|
+
(result[key] ||= []) << owner if key
|
64
74
|
end
|
65
|
-
@owners_by_key
|
66
75
|
end
|
67
76
|
|
68
77
|
def key_conversion_required?
|
@@ -89,21 +98,14 @@ module ActiveRecord
|
|
89
98
|
@model.type_for_attribute(owner_key_name).type
|
90
99
|
end
|
91
100
|
|
92
|
-
def
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
101
|
+
def records_for(ids)
|
102
|
+
scope.where(association_key_name => ids).load do |record|
|
103
|
+
# Processing only the first owner
|
104
|
+
# because the record is modified but not an owner
|
105
|
+
owner = owners_by_key[convert_key(record[association_key_name])].first
|
106
|
+
association = owner.association(reflection.name)
|
107
|
+
association.set_inverse_instance(record)
|
99
108
|
end
|
100
|
-
@preloaded_records.group_by do |record|
|
101
|
-
convert_key(record[association_key_name])
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
def records_for(ids, &block)
|
106
|
-
scope.where(association_key_name => ids).load(&block)
|
107
109
|
end
|
108
110
|
|
109
111
|
def scope
|
@@ -117,7 +119,7 @@ module ActiveRecord
|
|
117
119
|
def build_scope
|
118
120
|
scope = klass.scope_for_association
|
119
121
|
|
120
|
-
if reflection.type
|
122
|
+
if reflection.type && !reflection.through_reflection?
|
121
123
|
scope.where!(reflection.type => model.polymorphic_name)
|
122
124
|
end
|
123
125
|
|
@@ -4,42 +4,57 @@ module ActiveRecord
|
|
4
4
|
module Associations
|
5
5
|
class Preloader
|
6
6
|
class ThroughAssociation < Association # :nodoc:
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
7
|
+
PRELOADER = ActiveRecord::Associations::Preloader.new
|
8
|
+
|
9
|
+
def initialize(*)
|
10
|
+
super
|
11
|
+
@already_loaded = owners.first.association(through_reflection.name).loaded?
|
12
|
+
end
|
13
|
+
|
14
|
+
def preloaded_records
|
15
|
+
@preloaded_records ||= source_preloaders.flat_map(&:preloaded_records)
|
16
|
+
end
|
17
|
+
|
18
|
+
def records_by_owner
|
19
|
+
return @records_by_owner if defined?(@records_by_owner)
|
20
|
+
source_records_by_owner = source_preloaders.map(&:records_by_owner).reduce(:merge)
|
21
|
+
through_records_by_owner = through_preloaders.map(&:records_by_owner).reduce(:merge)
|
22
|
+
|
23
|
+
@records_by_owner = owners.each_with_object({}) do |owner, result|
|
24
|
+
through_records = through_records_by_owner[owner] || []
|
25
|
+
|
26
|
+
if @already_loaded
|
19
27
|
if source_type = reflection.options[:source_type]
|
20
28
|
through_records = through_records.select do |record|
|
21
29
|
record[reflection.foreign_type] == source_type
|
22
30
|
end
|
23
31
|
end
|
24
|
-
else
|
25
|
-
owner.association(through_reflection.name).reset if through_scope
|
26
|
-
end
|
27
|
-
result = through_records.flat_map do |record|
|
28
|
-
association = record.association(source_reflection.name)
|
29
|
-
target = association.target
|
30
|
-
association.reset if preload_scope
|
31
|
-
target
|
32
32
|
end
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
result.uniq! if reflection_scope.distinct_value
|
33
|
+
|
34
|
+
records = through_records.flat_map do |record|
|
35
|
+
source_records_by_owner[record]
|
37
36
|
end
|
38
|
-
|
37
|
+
|
38
|
+
records.compact!
|
39
|
+
records.sort_by! { |rhs| preload_index[rhs] } if scope.order_values.any?
|
40
|
+
records.uniq! if scope.distinct_value
|
41
|
+
result[owner] = records
|
39
42
|
end
|
40
43
|
end
|
41
44
|
|
42
45
|
private
|
46
|
+
def source_preloaders
|
47
|
+
@source_preloaders ||= PRELOADER.preload(middle_records, source_reflection.name, scope)
|
48
|
+
end
|
49
|
+
|
50
|
+
def middle_records
|
51
|
+
through_preloaders.flat_map(&:preloaded_records)
|
52
|
+
end
|
53
|
+
|
54
|
+
def through_preloaders
|
55
|
+
@through_preloaders ||= PRELOADER.preload(owners, through_reflection.name, through_scope)
|
56
|
+
end
|
57
|
+
|
43
58
|
def through_reflection
|
44
59
|
reflection.through_reflection
|
45
60
|
end
|
@@ -49,8 +64,8 @@ module ActiveRecord
|
|
49
64
|
end
|
50
65
|
|
51
66
|
def preload_index
|
52
|
-
@preload_index ||=
|
53
|
-
result[
|
67
|
+
@preload_index ||= preloaded_records.each_with_object({}).with_index do |(record, result), index|
|
68
|
+
result[record] = index
|
54
69
|
end
|
55
70
|
end
|
56
71
|
|
@@ -58,11 +73,15 @@ module ActiveRecord
|
|
58
73
|
scope = through_reflection.klass.unscoped
|
59
74
|
options = reflection.options
|
60
75
|
|
76
|
+
values = reflection_scope.values
|
77
|
+
if annotations = values[:annotate]
|
78
|
+
scope.annotate!(*annotations)
|
79
|
+
end
|
80
|
+
|
61
81
|
if options[:source_type]
|
62
82
|
scope.where! reflection.foreign_type => options[:source_type]
|
63
83
|
elsif !reflection_scope.where_clause.empty?
|
64
84
|
scope.where_clause = reflection_scope.where_clause
|
65
|
-
values = reflection_scope.values
|
66
85
|
|
67
86
|
if includes = values[:includes]
|
68
87
|
scope.includes!(source_reflection.name => includes)
|
@@ -89,17 +108,7 @@ module ActiveRecord
|
|
89
108
|
end
|
90
109
|
end
|
91
110
|
|
92
|
-
scope
|
93
|
-
end
|
94
|
-
|
95
|
-
def target_reflection_scope
|
96
|
-
if preload_scope
|
97
|
-
reflection_scope.merge(preload_scope)
|
98
|
-
elsif reflection.scope
|
99
|
-
reflection_scope
|
100
|
-
else
|
101
|
-
nil
|
102
|
-
end
|
111
|
+
scope
|
103
112
|
end
|
104
113
|
end
|
105
114
|
end
|
@@ -26,7 +26,7 @@ module ActiveRecord
|
|
26
26
|
# Implements the reload reader method, e.g. foo.reload_bar for
|
27
27
|
# Foo.has_one :bar
|
28
28
|
def force_reload_reader
|
29
|
-
|
29
|
+
reload(true)
|
30
30
|
target
|
31
31
|
end
|
32
32
|
|
@@ -36,21 +36,7 @@ module ActiveRecord
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def find_target
|
39
|
-
|
40
|
-
return scope.take if skip_statement_cache?(scope)
|
41
|
-
|
42
|
-
conn = klass.connection
|
43
|
-
sc = reflection.association_scope_cache(conn, owner) do |params|
|
44
|
-
as = AssociationScope.create { params.bind }
|
45
|
-
target_scope.merge!(as.scope(self)).limit(1)
|
46
|
-
end
|
47
|
-
|
48
|
-
binds = AssociationScope.get_bind_values(owner, reflection.chain)
|
49
|
-
sc.execute(binds, conn) do |record|
|
50
|
-
set_inverse_instance record
|
51
|
-
end.first
|
52
|
-
rescue ::RangeError
|
53
|
-
nil
|
39
|
+
super.first
|
54
40
|
end
|
55
41
|
|
56
42
|
def replace(record)
|
@@ -4,7 +4,6 @@ require "active_model/forbidden_attributes_protection"
|
|
4
4
|
|
5
5
|
module ActiveRecord
|
6
6
|
module AttributeAssignment
|
7
|
-
extend ActiveSupport::Concern
|
8
7
|
include ActiveModel::AttributeAssignment
|
9
8
|
|
10
9
|
private
|
@@ -46,16 +45,14 @@ module ActiveRecord
|
|
46
45
|
def execute_callstack_for_multiparameter_attributes(callstack)
|
47
46
|
errors = []
|
48
47
|
callstack.each do |name, values_with_empty_parameters|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
values = values_with_empty_parameters
|
54
|
-
end
|
55
|
-
send("#{name}=", values)
|
56
|
-
rescue => ex
|
57
|
-
errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name} (#{ex.message})", ex, name)
|
48
|
+
if values_with_empty_parameters.each_value.all?(&:nil?)
|
49
|
+
values = nil
|
50
|
+
else
|
51
|
+
values = values_with_empty_parameters
|
58
52
|
end
|
53
|
+
send("#{name}=", values)
|
54
|
+
rescue => ex
|
55
|
+
errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name} (#{ex.message})", ex, name)
|
59
56
|
end
|
60
57
|
unless errors.empty?
|
61
58
|
error_descriptions = errors.map(&:message).join(",")
|
@@ -22,16 +22,7 @@ module ActiveRecord
|
|
22
22
|
delegate :column_for_attribute, to: :class
|
23
23
|
end
|
24
24
|
|
25
|
-
|
26
|
-
def self.set_name_cache(name, value)
|
27
|
-
const_name = "ATTR_#{name}"
|
28
|
-
unless const_defined? const_name
|
29
|
-
const_set const_name, value.dup.freeze
|
30
|
-
end
|
31
|
-
end
|
32
|
-
}
|
33
|
-
|
34
|
-
BLACKLISTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass)
|
25
|
+
RESTRICTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass)
|
35
26
|
|
36
27
|
class GeneratedAttributeMethods < Module #:nodoc:
|
37
28
|
include Mutex_m
|
@@ -44,7 +35,8 @@ module ActiveRecord
|
|
44
35
|
end
|
45
36
|
|
46
37
|
def initialize_generated_modules # :nodoc:
|
47
|
-
@generated_attribute_methods = GeneratedAttributeMethods.new
|
38
|
+
@generated_attribute_methods = const_set(:GeneratedAttributeMethods, GeneratedAttributeMethods.new)
|
39
|
+
private_constant :GeneratedAttributeMethods
|
48
40
|
@attribute_methods_generated = false
|
49
41
|
include @generated_attribute_methods
|
50
42
|
|
@@ -59,7 +51,7 @@ module ActiveRecord
|
|
59
51
|
# attribute methods.
|
60
52
|
generated_attribute_methods.synchronize do
|
61
53
|
return false if @attribute_methods_generated
|
62
|
-
superclass.define_attribute_methods unless
|
54
|
+
superclass.define_attribute_methods unless base_class?
|
63
55
|
super(attribute_names)
|
64
56
|
@attribute_methods_generated = true
|
65
57
|
end
|
@@ -123,7 +115,7 @@ module ActiveRecord
|
|
123
115
|
# A class method is 'dangerous' if it is already (re)defined by Active Record, but
|
124
116
|
# not by any ancestors. (So 'puts' is not dangerous but 'new' is.)
|
125
117
|
def dangerous_class_method?(method_name)
|
126
|
-
|
118
|
+
RESTRICTED_CLASS_METHODS.include?(method_name.to_s) || class_method_defined_within?(method_name, Base)
|
127
119
|
end
|
128
120
|
|
129
121
|
def class_method_defined_within?(name, klass, superklass = klass.superclass) # :nodoc:
|
@@ -167,57 +159,6 @@ module ActiveRecord
|
|
167
159
|
end
|
168
160
|
end
|
169
161
|
|
170
|
-
# Regexp whitelist. Matches the following:
|
171
|
-
# "#{table_name}.#{column_name}"
|
172
|
-
# "#{column_name}"
|
173
|
-
COLUMN_NAME_WHITELIST = /\A(?:\w+\.)?\w+\z/i
|
174
|
-
|
175
|
-
# Regexp whitelist. Matches the following:
|
176
|
-
# "#{table_name}.#{column_name}"
|
177
|
-
# "#{table_name}.#{column_name} #{direction}"
|
178
|
-
# "#{table_name}.#{column_name} #{direction} NULLS FIRST"
|
179
|
-
# "#{table_name}.#{column_name} NULLS LAST"
|
180
|
-
# "#{column_name}"
|
181
|
-
# "#{column_name} #{direction}"
|
182
|
-
# "#{column_name} #{direction} NULLS FIRST"
|
183
|
-
# "#{column_name} NULLS LAST"
|
184
|
-
COLUMN_NAME_ORDER_WHITELIST = /
|
185
|
-
\A
|
186
|
-
(?:\w+\.)?
|
187
|
-
\w+
|
188
|
-
(?:\s+asc|\s+desc)?
|
189
|
-
(?:\s+nulls\s+(?:first|last))?
|
190
|
-
\z
|
191
|
-
/ix
|
192
|
-
|
193
|
-
def enforce_raw_sql_whitelist(args, whitelist: COLUMN_NAME_WHITELIST) # :nodoc:
|
194
|
-
unexpected = args.reject do |arg|
|
195
|
-
arg.kind_of?(Arel::Node) ||
|
196
|
-
arg.is_a?(Arel::Nodes::SqlLiteral) ||
|
197
|
-
arg.is_a?(Arel::Attributes::Attribute) ||
|
198
|
-
arg.to_s.split(/\s*,\s*/).all? { |part| whitelist.match?(part) }
|
199
|
-
end
|
200
|
-
|
201
|
-
return if unexpected.none?
|
202
|
-
|
203
|
-
if allow_unsafe_raw_sql == :deprecated
|
204
|
-
ActiveSupport::Deprecation.warn(
|
205
|
-
"Dangerous query method (method whose arguments are used as raw " \
|
206
|
-
"SQL) called with non-attribute argument(s): " \
|
207
|
-
"#{unexpected.map(&:inspect).join(", ")}. Non-attribute " \
|
208
|
-
"arguments will be disallowed in Rails 6.0. This method should " \
|
209
|
-
"not be called with user-provided values, such as request " \
|
210
|
-
"parameters or model attributes. Known-safe values can be passed " \
|
211
|
-
"by wrapping them in Arel.sql()."
|
212
|
-
)
|
213
|
-
else
|
214
|
-
raise(ActiveRecord::UnknownAttributeReference,
|
215
|
-
"Query method called with non-attribute argument(s): " +
|
216
|
-
unexpected.map(&:inspect).join(", ")
|
217
|
-
)
|
218
|
-
end
|
219
|
-
end
|
220
|
-
|
221
162
|
# Returns true if the given attribute exists, otherwise false.
|
222
163
|
#
|
223
164
|
# class Person < ActiveRecord::Base
|
@@ -270,21 +211,14 @@ module ActiveRecord
|
|
270
211
|
def respond_to?(name, include_private = false)
|
271
212
|
return false unless super
|
272
213
|
|
273
|
-
case name
|
274
|
-
when :to_partial_path
|
275
|
-
name = "to_partial_path".freeze
|
276
|
-
when :to_model
|
277
|
-
name = "to_model".freeze
|
278
|
-
else
|
279
|
-
name = name.to_s
|
280
|
-
end
|
281
|
-
|
282
214
|
# If the result is true then check for the select case.
|
283
215
|
# For queries selecting a subset of columns, return false for unselected columns.
|
284
216
|
# We check defined?(@attributes) not to issue warnings if called on objects that
|
285
217
|
# have been allocated but not yet initialized.
|
286
|
-
if defined?(@attributes)
|
287
|
-
|
218
|
+
if defined?(@attributes)
|
219
|
+
if name = self.class.symbol_column_to_string(name.to_sym)
|
220
|
+
return has_attribute?(name)
|
221
|
+
end
|
288
222
|
end
|
289
223
|
|
290
224
|
true
|
@@ -344,15 +278,8 @@ module ActiveRecord
|
|
344
278
|
# person.attribute_for_inspect(:tag_ids)
|
345
279
|
# # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]"
|
346
280
|
def attribute_for_inspect(attr_name)
|
347
|
-
value =
|
348
|
-
|
349
|
-
if value.is_a?(String) && value.length > 50
|
350
|
-
"#{value[0, 50]}...".inspect
|
351
|
-
elsif value.is_a?(Date) || value.is_a?(Time)
|
352
|
-
%("#{value.to_s(:db)}")
|
353
|
-
else
|
354
|
-
value.inspect
|
355
|
-
end
|
281
|
+
value = _read_attribute(attr_name)
|
282
|
+
format_for_inspect(value)
|
356
283
|
end
|
357
284
|
|
358
285
|
# Returns +true+ if the specified +attribute+ has been set by the user or by a
|
@@ -443,23 +370,12 @@ module ActiveRecord
|
|
443
370
|
@attributes.accessed
|
444
371
|
end
|
445
372
|
|
446
|
-
|
447
|
-
|
448
|
-
def attribute_method?(attr_name) # :nodoc:
|
373
|
+
private
|
374
|
+
def attribute_method?(attr_name)
|
449
375
|
# We check defined? because Syck calls respond_to? before actually calling initialize.
|
450
376
|
defined?(@attributes) && @attributes.key?(attr_name)
|
451
377
|
end
|
452
378
|
|
453
|
-
private
|
454
|
-
|
455
|
-
def attributes_with_values_for_create(attribute_names)
|
456
|
-
attributes_with_values(attributes_for_create(attribute_names))
|
457
|
-
end
|
458
|
-
|
459
|
-
def attributes_with_values_for_update(attribute_names)
|
460
|
-
attributes_with_values(attributes_for_update(attribute_names))
|
461
|
-
end
|
462
|
-
|
463
379
|
def attributes_with_values(attribute_names)
|
464
380
|
attribute_names.each_with_object({}) do |name, attrs|
|
465
381
|
attrs[name] = _read_attribute(name)
|
@@ -468,7 +384,8 @@ module ActiveRecord
|
|
468
384
|
|
469
385
|
# Filters the primary keys and readonly attributes from the attribute names.
|
470
386
|
def attributes_for_update(attribute_names)
|
471
|
-
attribute_names
|
387
|
+
attribute_names &= self.class.column_names
|
388
|
+
attribute_names.delete_if do |name|
|
472
389
|
readonly_attribute?(name)
|
473
390
|
end
|
474
391
|
end
|
@@ -476,17 +393,28 @@ module ActiveRecord
|
|
476
393
|
# Filters out the primary keys, from the attribute names, when the primary
|
477
394
|
# key is to be generated (e.g. the id attribute has no value).
|
478
395
|
def attributes_for_create(attribute_names)
|
479
|
-
attribute_names
|
396
|
+
attribute_names &= self.class.column_names
|
397
|
+
attribute_names.delete_if do |name|
|
480
398
|
pk_attribute?(name) && id.nil?
|
481
399
|
end
|
482
400
|
end
|
483
401
|
|
402
|
+
def format_for_inspect(value)
|
403
|
+
if value.is_a?(String) && value.length > 50
|
404
|
+
"#{value[0, 50]}...".inspect
|
405
|
+
elsif value.is_a?(Date) || value.is_a?(Time)
|
406
|
+
%("#{value.to_s(:db)}")
|
407
|
+
else
|
408
|
+
value.inspect
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
484
412
|
def readonly_attribute?(name)
|
485
413
|
self.class.readonly_attributes.include?(name)
|
486
414
|
end
|
487
415
|
|
488
416
|
def pk_attribute?(name)
|
489
|
-
name ==
|
417
|
+
name == @primary_key
|
490
418
|
end
|
491
419
|
end
|
492
420
|
end
|