activerecord 4.2.11.1 → 5.0.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 +5 -5
- data/CHANGELOG.md +1282 -1195
- data/MIT-LICENSE +2 -2
- data/README.rdoc +7 -8
- data/examples/performance.rb +2 -3
- data/examples/simple.rb +0 -1
- data/lib/active_record.rb +8 -4
- data/lib/active_record/aggregations.rb +35 -24
- data/lib/active_record/association_relation.rb +3 -3
- data/lib/active_record/associations.rb +317 -209
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +11 -9
- data/lib/active_record/associations/association_scope.rb +73 -102
- data/lib/active_record/associations/belongs_to_association.rb +21 -32
- data/lib/active_record/associations/builder/association.rb +28 -34
- data/lib/active_record/associations/builder/belongs_to.rb +43 -18
- data/lib/active_record/associations/builder/collection_association.rb +7 -19
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +14 -11
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +11 -6
- data/lib/active_record/associations/builder/singular_association.rb +3 -10
- data/lib/active_record/associations/collection_association.rb +49 -41
- data/lib/active_record/associations/collection_proxy.rb +67 -27
- data/lib/active_record/associations/foreign_association.rb +1 -1
- data/lib/active_record/associations/has_many_association.rb +20 -71
- data/lib/active_record/associations/has_many_through_association.rb +8 -47
- data/lib/active_record/associations/has_one_association.rb +12 -5
- data/lib/active_record/associations/join_dependency.rb +29 -19
- data/lib/active_record/associations/join_dependency/join_association.rb +16 -10
- data/lib/active_record/associations/preloader.rb +14 -4
- data/lib/active_record/associations/preloader/association.rb +46 -52
- data/lib/active_record/associations/preloader/collection_association.rb +0 -6
- data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
- data/lib/active_record/associations/preloader/has_one.rb +0 -8
- data/lib/active_record/associations/preloader/through_association.rb +27 -14
- data/lib/active_record/associations/singular_association.rb +7 -1
- data/lib/active_record/associations/through_association.rb +11 -3
- data/lib/active_record/attribute.rb +68 -18
- data/lib/active_record/attribute/user_provided_default.rb +28 -0
- data/lib/active_record/attribute_assignment.rb +19 -140
- data/lib/active_record/attribute_decorators.rb +6 -5
- data/lib/active_record/attribute_methods.rb +76 -47
- data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
- data/lib/active_record/attribute_methods/dirty.rb +46 -86
- data/lib/active_record/attribute_methods/primary_key.rb +2 -2
- data/lib/active_record/attribute_methods/query.rb +2 -2
- data/lib/active_record/attribute_methods/read.rb +31 -59
- data/lib/active_record/attribute_methods/serialization.rb +13 -16
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -14
- data/lib/active_record/attribute_methods/write.rb +13 -37
- data/lib/active_record/attribute_mutation_tracker.rb +70 -0
- data/lib/active_record/attribute_set.rb +30 -3
- data/lib/active_record/attribute_set/builder.rb +6 -4
- data/lib/active_record/attributes.rb +199 -81
- data/lib/active_record/autosave_association.rb +49 -16
- data/lib/active_record/base.rb +32 -23
- data/lib/active_record/callbacks.rb +39 -43
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +20 -8
- data/lib/active_record/collection_cache_key.rb +40 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +452 -182
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -61
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -10
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -185
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +380 -141
- data/lib/active_record/connection_adapters/abstract/transaction.rb +51 -34
- data/lib/active_record/connection_adapters/abstract_adapter.rb +141 -59
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +401 -370
- data/lib/active_record/connection_adapters/column.rb +28 -43
- data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +29 -166
- data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +10 -72
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +27 -57
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -18
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +234 -148
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +248 -160
- data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +149 -192
- data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
- data/lib/active_record/connection_handling.rb +37 -14
- data/lib/active_record/core.rb +89 -107
- data/lib/active_record/counter_cache.rb +13 -24
- data/lib/active_record/dynamic_matchers.rb +1 -20
- data/lib/active_record/enum.rb +113 -76
- data/lib/active_record/errors.rb +87 -48
- data/lib/active_record/explain_registry.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +26 -5
- data/lib/active_record/fixtures.rb +76 -40
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +32 -40
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/internal_metadata.rb +56 -0
- data/lib/active_record/legacy_yaml_adapter.rb +18 -2
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +15 -15
- data/lib/active_record/locking/pessimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +43 -21
- data/lib/active_record/migration.rb +363 -133
- data/lib/active_record/migration/command_recorder.rb +59 -18
- data/lib/active_record/migration/compatibility.rb +126 -0
- data/lib/active_record/model_schema.rb +129 -41
- data/lib/active_record/nested_attributes.rb +58 -29
- data/lib/active_record/null_relation.rb +16 -8
- data/lib/active_record/persistence.rb +121 -80
- data/lib/active_record/query_cache.rb +15 -18
- data/lib/active_record/querying.rb +10 -9
- data/lib/active_record/railtie.rb +23 -16
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +69 -46
- data/lib/active_record/readonly_attributes.rb +1 -1
- data/lib/active_record/reflection.rb +282 -115
- data/lib/active_record/relation.rb +176 -116
- data/lib/active_record/relation/batches.rb +139 -34
- data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
- data/lib/active_record/relation/calculations.rb +79 -108
- data/lib/active_record/relation/delegation.rb +7 -20
- data/lib/active_record/relation/finder_methods.rb +163 -81
- data/lib/active_record/relation/from_clause.rb +32 -0
- data/lib/active_record/relation/merger.rb +16 -42
- data/lib/active_record/relation/predicate_builder.rb +120 -107
- data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/query_attribute.rb +19 -0
- data/lib/active_record/relation/query_methods.rb +308 -244
- data/lib/active_record/relation/record_fetch_warning.rb +49 -0
- data/lib/active_record/relation/spawn_methods.rb +4 -7
- data/lib/active_record/relation/where_clause.rb +174 -0
- data/lib/active_record/relation/where_clause_factory.rb +38 -0
- data/lib/active_record/result.rb +4 -3
- data/lib/active_record/runtime_registry.rb +1 -1
- data/lib/active_record/sanitization.rb +95 -66
- data/lib/active_record/schema.rb +26 -22
- data/lib/active_record/schema_dumper.rb +62 -38
- data/lib/active_record/schema_migration.rb +11 -14
- data/lib/active_record/scoping.rb +32 -15
- data/lib/active_record/scoping/default.rb +23 -9
- data/lib/active_record/scoping/named.rb +49 -28
- data/lib/active_record/secure_token.rb +38 -0
- data/lib/active_record/serialization.rb +2 -4
- data/lib/active_record/statement_cache.rb +16 -14
- data/lib/active_record/store.rb +8 -3
- data/lib/active_record/suppressor.rb +58 -0
- data/lib/active_record/table_metadata.rb +68 -0
- data/lib/active_record/tasks/database_tasks.rb +57 -43
- data/lib/active_record/tasks/mysql_database_tasks.rb +6 -14
- data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -2
- data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
- data/lib/active_record/timestamp.rb +20 -9
- data/lib/active_record/touch_later.rb +58 -0
- data/lib/active_record/transactions.rb +138 -56
- data/lib/active_record/type.rb +66 -17
- data/lib/active_record/type/adapter_specific_registry.rb +130 -0
- data/lib/active_record/type/date.rb +2 -45
- data/lib/active_record/type/date_time.rb +2 -49
- data/lib/active_record/type/internal/abstract_json.rb +29 -0
- data/lib/active_record/type/internal/timezone.rb +15 -0
- data/lib/active_record/type/serialized.rb +15 -14
- data/lib/active_record/type/time.rb +10 -16
- data/lib/active_record/type/type_map.rb +4 -4
- data/lib/active_record/type_caster.rb +7 -0
- data/lib/active_record/type_caster/connection.rb +29 -0
- data/lib/active_record/type_caster/map.rb +19 -0
- data/lib/active_record/validations.rb +33 -32
- data/lib/active_record/validations/absence.rb +23 -0
- data/lib/active_record/validations/associated.rb +10 -3
- data/lib/active_record/validations/length.rb +24 -0
- data/lib/active_record/validations/presence.rb +11 -12
- data/lib/active_record/validations/uniqueness.rb +30 -29
- data/lib/rails/generators/active_record/migration.rb +7 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
- data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +32 -15
- data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
- metadata +59 -34
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
- data/lib/active_record/serializers/xml_serializer.rb +0 -193
- data/lib/active_record/type/big_integer.rb +0 -13
- data/lib/active_record/type/binary.rb +0 -50
- data/lib/active_record/type/boolean.rb +0 -31
- data/lib/active_record/type/decimal.rb +0 -64
- data/lib/active_record/type/decimal_without_scale.rb +0 -11
- data/lib/active_record/type/decorator.rb +0 -14
- data/lib/active_record/type/float.rb +0 -19
- data/lib/active_record/type/integer.rb +0 -59
- data/lib/active_record/type/mutable.rb +0 -16
- data/lib/active_record/type/numeric.rb +0 -36
- data/lib/active_record/type/string.rb +0 -40
- data/lib/active_record/type/text.rb +0 -11
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/unsigned_integer.rb +0 -15
- data/lib/active_record/type/value.rb +0 -110
@@ -1,5 +1,5 @@
|
|
1
1
|
module ActiveRecord
|
2
|
-
# = Active Record
|
2
|
+
# = Active Record Has One Association
|
3
3
|
module Associations
|
4
4
|
class HasOneAssociation < SingularAssociation #:nodoc:
|
5
5
|
include ForeignAssociation
|
@@ -11,9 +11,16 @@ module ActiveRecord
|
|
11
11
|
|
12
12
|
when :restrict_with_error
|
13
13
|
if load_target
|
14
|
-
record =
|
15
|
-
owner.errors.
|
16
|
-
|
14
|
+
record = owner.class.human_attribute_name(reflection.name).downcase
|
15
|
+
message = owner.errors.generate_message(:base, :'restrict_dependent_destroy.one', record: record, raise: true) rescue nil
|
16
|
+
if message
|
17
|
+
ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
|
18
|
+
The error key `:'restrict_dependent_destroy.one'` has been deprecated and will be removed in Rails 5.1.
|
19
|
+
Please use `:'restrict_dependent_destroy.has_one'` instead.
|
20
|
+
MESSAGE
|
21
|
+
end
|
22
|
+
owner.errors.add(:base, message || :'restrict_dependent_destroy.has_one', record: record)
|
23
|
+
throw(:abort)
|
17
24
|
end
|
18
25
|
|
19
26
|
else
|
@@ -58,7 +65,7 @@ module ActiveRecord
|
|
58
65
|
when :destroy
|
59
66
|
target.destroy
|
60
67
|
when :nullify
|
61
|
-
target.update_columns(reflection.foreign_key => nil)
|
68
|
+
target.update_columns(reflection.foreign_key => nil) if target.persisted?
|
62
69
|
end
|
63
70
|
end
|
64
71
|
end
|
@@ -20,7 +20,7 @@ module ActiveRecord
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def columns
|
23
|
-
@tables.flat_map
|
23
|
+
@tables.flat_map(&:column_aliases)
|
24
24
|
end
|
25
25
|
|
26
26
|
# An array of [column_name, alias] pairs for the table
|
@@ -32,7 +32,7 @@ module ActiveRecord
|
|
32
32
|
@alias_cache[node][column]
|
33
33
|
end
|
34
34
|
|
35
|
-
class Table < Struct.new(:node, :columns)
|
35
|
+
class Table < Struct.new(:node, :columns) # :nodoc:
|
36
36
|
def table
|
37
37
|
Arel::Nodes::TableAlias.new node.table, node.aliased_table_name
|
38
38
|
end
|
@@ -93,8 +93,7 @@ module ActiveRecord
|
|
93
93
|
# joins # => []
|
94
94
|
#
|
95
95
|
def initialize(base, associations, joins)
|
96
|
-
@alias_tracker = AliasTracker.
|
97
|
-
@alias_tracker.aliased_table_for(base.table_name, base.table_name) # Updates the count for base.table_name to 1
|
96
|
+
@alias_tracker = AliasTracker.create_with_joins(base.connection, base.table_name, joins, base.type_caster)
|
98
97
|
tree = self.class.make_tree associations
|
99
98
|
@join_root = JoinBase.new base, build(tree, base)
|
100
99
|
@join_root.children.each { |child| construct_tables! @join_root, child }
|
@@ -104,9 +103,14 @@ module ActiveRecord
|
|
104
103
|
join_root.drop(1).map!(&:reflection)
|
105
104
|
end
|
106
105
|
|
107
|
-
def join_constraints(outer_joins)
|
106
|
+
def join_constraints(outer_joins, join_type)
|
108
107
|
joins = join_root.children.flat_map { |child|
|
109
|
-
|
108
|
+
|
109
|
+
if join_type == Arel::Nodes::OuterJoin
|
110
|
+
make_left_outer_joins join_root, child
|
111
|
+
else
|
112
|
+
make_inner_joins join_root, child
|
113
|
+
end
|
110
114
|
}
|
111
115
|
|
112
116
|
joins.concat outer_joins.flat_map { |oj|
|
@@ -132,9 +136,9 @@ module ActiveRecord
|
|
132
136
|
def instantiate(result_set, aliases)
|
133
137
|
primary_key = aliases.column_alias(join_root, join_root.primary_key)
|
134
138
|
|
135
|
-
seen = Hash.new { |
|
136
|
-
|
137
|
-
|
139
|
+
seen = Hash.new { |i, object_id|
|
140
|
+
i[object_id] = Hash.new { |j, child_class|
|
141
|
+
j[child_class] = {}
|
138
142
|
}
|
139
143
|
}
|
140
144
|
|
@@ -177,6 +181,14 @@ module ActiveRecord
|
|
177
181
|
[info] + child.children.flat_map { |c| make_outer_joins(child, c) }
|
178
182
|
end
|
179
183
|
|
184
|
+
def make_left_outer_joins(parent, child)
|
185
|
+
tables = child.tables
|
186
|
+
join_type = Arel::Nodes::OuterJoin
|
187
|
+
info = make_constraints parent, child, tables, join_type
|
188
|
+
|
189
|
+
[info] + child.children.flat_map { |c| make_left_outer_joins(child, c) }
|
190
|
+
end
|
191
|
+
|
180
192
|
def make_inner_joins(parent, child)
|
181
193
|
tables = child.tables
|
182
194
|
join_type = Arel::Nodes::InnerJoin
|
@@ -216,7 +228,7 @@ module ActiveRecord
|
|
216
228
|
|
217
229
|
def find_reflection(klass, name)
|
218
230
|
klass._reflect_on_association(name) or
|
219
|
-
raise ConfigurationError, "
|
231
|
+
raise ConfigurationError, "Can't join '#{ klass.name }' to association named '#{ name }'; perhaps you misspelled it?"
|
220
232
|
end
|
221
233
|
|
222
234
|
def build(associations, base_klass)
|
@@ -235,18 +247,15 @@ module ActiveRecord
|
|
235
247
|
|
236
248
|
def construct(ar_parent, parent, row, rs, seen, model_cache, aliases)
|
237
249
|
return if ar_parent.nil?
|
238
|
-
primary_id = ar_parent.id
|
239
250
|
|
240
251
|
parent.children.each do |node|
|
241
252
|
if node.reflection.collection?
|
242
253
|
other = ar_parent.association(node.reflection.name)
|
243
254
|
other.loaded!
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
next
|
249
|
-
end
|
255
|
+
elsif ar_parent.association_cached?(node.reflection.name)
|
256
|
+
model = ar_parent.association(node.reflection.name).target
|
257
|
+
construct(model, node, row, rs, seen, model_cache, aliases)
|
258
|
+
next
|
250
259
|
end
|
251
260
|
|
252
261
|
key = aliases.column_alias(node, node.primary_key)
|
@@ -257,13 +266,14 @@ module ActiveRecord
|
|
257
266
|
next
|
258
267
|
end
|
259
268
|
|
260
|
-
model = seen[
|
269
|
+
model = seen[ar_parent.object_id][node.base_klass][id]
|
261
270
|
|
262
271
|
if model
|
263
272
|
construct(model, node, row, rs, seen, model_cache, aliases)
|
264
273
|
else
|
265
274
|
model = construct_model(ar_parent, node, row, model_cache, id, aliases)
|
266
|
-
|
275
|
+
model.readonly!
|
276
|
+
seen[ar_parent.object_id][node.base_klass][id] = model
|
267
277
|
construct(model, node, row, rs, seen, model_cache, aliases)
|
268
278
|
end
|
269
279
|
end
|
@@ -25,7 +25,7 @@ module ActiveRecord
|
|
25
25
|
|
26
26
|
def join_constraints(foreign_table, foreign_klass, node, join_type, tables, scope_chain, chain)
|
27
27
|
joins = []
|
28
|
-
|
28
|
+
binds = []
|
29
29
|
tables = tables.reverse
|
30
30
|
|
31
31
|
scope_chain_index = 0
|
@@ -43,20 +43,27 @@ module ActiveRecord
|
|
43
43
|
|
44
44
|
constraint = build_constraint(klass, table, key, foreign_table, foreign_key)
|
45
45
|
|
46
|
+
predicate_builder = PredicateBuilder.new(TableMetadata.new(klass, table))
|
46
47
|
scope_chain_items = scope_chain[scope_chain_index].map do |item|
|
47
48
|
if item.is_a?(Relation)
|
48
49
|
item
|
49
50
|
else
|
50
|
-
ActiveRecord::Relation.create(klass, table
|
51
|
+
ActiveRecord::Relation.create(klass, table, predicate_builder)
|
52
|
+
.instance_exec(node, &item)
|
51
53
|
end
|
52
54
|
end
|
53
55
|
scope_chain_index += 1
|
54
56
|
|
55
57
|
klass_scope =
|
56
|
-
if klass.current_scope
|
57
|
-
klass.
|
58
|
+
if klass.current_scope
|
59
|
+
klass.current_scope.clone
|
58
60
|
else
|
59
|
-
|
61
|
+
relation = ActiveRecord::Relation.create(
|
62
|
+
klass,
|
63
|
+
table,
|
64
|
+
predicate_builder,
|
65
|
+
)
|
66
|
+
klass.send(:build_default_scope, relation)
|
60
67
|
end
|
61
68
|
scope_chain_items.concat [klass_scope].compact
|
62
69
|
|
@@ -65,7 +72,7 @@ module ActiveRecord
|
|
65
72
|
end
|
66
73
|
|
67
74
|
if rel && !rel.arel.constraints.empty?
|
68
|
-
|
75
|
+
binds += rel.bound_attributes
|
69
76
|
constraint = constraint.and rel.arel.constraints
|
70
77
|
end
|
71
78
|
|
@@ -73,9 +80,8 @@ module ActiveRecord
|
|
73
80
|
value = foreign_klass.base_class.name
|
74
81
|
column = klass.columns_hash[reflection.type.to_s]
|
75
82
|
|
76
|
-
|
77
|
-
|
78
|
-
constraint = constraint.and table[reflection.type].eq substitute
|
83
|
+
binds << Relation::QueryAttribute.new(column.name, value, klass.type_for_attribute(column.name))
|
84
|
+
constraint = constraint.and klass.arel_attribute(reflection.type, table).eq(Arel::Nodes::BindParam.new)
|
79
85
|
end
|
80
86
|
|
81
87
|
joins << table.create_join(table, table.create_on(constraint), join_type)
|
@@ -84,7 +90,7 @@ module ActiveRecord
|
|
84
90
|
foreign_table, foreign_klass = table, klass
|
85
91
|
end
|
86
92
|
|
87
|
-
JoinInformation.new joins,
|
93
|
+
JoinInformation.new joins, binds
|
88
94
|
end
|
89
95
|
|
90
96
|
# Builds equality condition.
|
@@ -54,6 +54,8 @@ module ActiveRecord
|
|
54
54
|
autoload :BelongsTo, 'active_record/associations/preloader/belongs_to'
|
55
55
|
end
|
56
56
|
|
57
|
+
NULL_RELATION = Struct.new(:values, :where_clause, :joins_values).new({}, Relation::WhereClause.empty, [])
|
58
|
+
|
57
59
|
# Eager loads the named associations for the given Active Record record(s).
|
58
60
|
#
|
59
61
|
# In this description, 'association name' shall refer to the name passed
|
@@ -88,9 +90,6 @@ module ActiveRecord
|
|
88
90
|
# [ :books, :author ]
|
89
91
|
# { author: :avatar }
|
90
92
|
# [ :books, { author: :avatar } ]
|
91
|
-
|
92
|
-
NULL_RELATION = Struct.new(:values, :bind_values).new({}, [])
|
93
|
-
|
94
93
|
def preload(records, associations, preload_scope = nil)
|
95
94
|
records = Array.wrap(records).compact.uniq
|
96
95
|
associations = Array.wrap(associations)
|
@@ -107,6 +106,7 @@ module ActiveRecord
|
|
107
106
|
|
108
107
|
private
|
109
108
|
|
109
|
+
# Loads all the given data into +records+ for the +association+.
|
110
110
|
def preloaders_on(association, records, scope)
|
111
111
|
case association
|
112
112
|
when Hash
|
@@ -116,7 +116,7 @@ module ActiveRecord
|
|
116
116
|
when String
|
117
117
|
preloaders_for_one(association.to_sym, records, scope)
|
118
118
|
else
|
119
|
-
raise ArgumentError, "#{association.inspect} was not
|
119
|
+
raise ArgumentError, "#{association.inspect} was not recognized for preload"
|
120
120
|
end
|
121
121
|
end
|
122
122
|
|
@@ -132,6 +132,11 @@ module ActiveRecord
|
|
132
132
|
}
|
133
133
|
end
|
134
134
|
|
135
|
+
# Loads all the given data into +records+ for a singular +association+.
|
136
|
+
#
|
137
|
+
# Functions by instantiating a preloader class such as Preloader::HasManyThrough and
|
138
|
+
# call the +run+ method for each passed in class in the +records+ argument.
|
139
|
+
#
|
135
140
|
# Not all records have the same class, so group then preload group on the reflection
|
136
141
|
# itself so that if various subclass share the same association then we do not split
|
137
142
|
# them unnecessarily
|
@@ -179,8 +184,13 @@ module ActiveRecord
|
|
179
184
|
def self.new(klass, owners, reflection, preload_scope); self; end
|
180
185
|
def self.run(preloader); end
|
181
186
|
def self.preloaded_records; []; end
|
187
|
+
def self.owners; []; end
|
182
188
|
end
|
183
189
|
|
190
|
+
# Returns a class containing the logic needed to load preload the data
|
191
|
+
# and attach it to a relation. For example +Preloader::Association+ or
|
192
|
+
# +Preloader::HasManyThrough+. The class returned implements a `run` method
|
193
|
+
# that accepts a preloader.
|
184
194
|
def preloader_for(reflection, owners, rhs_klass)
|
185
195
|
return NullPreloader unless rhs_klass
|
186
196
|
|
@@ -12,7 +12,6 @@ module ActiveRecord
|
|
12
12
|
@preload_scope = preload_scope
|
13
13
|
@model = owners.first && owners.first.class
|
14
14
|
@scope = nil
|
15
|
-
@owners_by_key = nil
|
16
15
|
@preloaded_records = []
|
17
16
|
end
|
18
17
|
|
@@ -33,7 +32,7 @@ module ActiveRecord
|
|
33
32
|
end
|
34
33
|
|
35
34
|
def query_scope(ids)
|
36
|
-
scope.where(
|
35
|
+
scope.where(association_key_name => ids)
|
37
36
|
end
|
38
37
|
|
39
38
|
def table
|
@@ -48,7 +47,7 @@ module ActiveRecord
|
|
48
47
|
# This is overridden by HABTM as the condition should be on the foreign_key column in
|
49
48
|
# the join table
|
50
49
|
def association_key
|
51
|
-
table
|
50
|
+
klass.arel_attribute(association_key_name, table)
|
52
51
|
end
|
53
52
|
|
54
53
|
# The name of the key on the model which declares the association
|
@@ -56,18 +55,6 @@ module ActiveRecord
|
|
56
55
|
raise NotImplementedError
|
57
56
|
end
|
58
57
|
|
59
|
-
def owners_by_key
|
60
|
-
@owners_by_key ||= if key_conversion_required?
|
61
|
-
owners.group_by do |owner|
|
62
|
-
owner[owner_key_name].to_s
|
63
|
-
end
|
64
|
-
else
|
65
|
-
owners.group_by do |owner|
|
66
|
-
owner[owner_key_name]
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
58
|
def options
|
72
59
|
reflection.options
|
73
60
|
end
|
@@ -75,32 +62,33 @@ module ActiveRecord
|
|
75
62
|
private
|
76
63
|
|
77
64
|
def associated_records_by_owner(preloader)
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
# Each record may have multiple owners, and vice-versa
|
82
|
-
records_by_owner = owners.each_with_object({}) do |owner,h|
|
83
|
-
h[owner] = []
|
65
|
+
records = load_records
|
66
|
+
owners.each_with_object({}) do |owner, result|
|
67
|
+
result[owner] = records[convert_key(owner[owner_key_name])] || []
|
84
68
|
end
|
69
|
+
end
|
85
70
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
records = load_slices sliced
|
92
|
-
records.each do |record, owner_key|
|
93
|
-
owners_map[owner_key].each do |owner|
|
94
|
-
records_by_owner[owner] << record
|
95
|
-
end
|
71
|
+
def owner_keys
|
72
|
+
unless defined?(@owner_keys)
|
73
|
+
@owner_keys = owners.map do |owner|
|
74
|
+
owner[owner_key_name]
|
96
75
|
end
|
76
|
+
@owner_keys.uniq!
|
77
|
+
@owner_keys.compact!
|
97
78
|
end
|
98
|
-
|
99
|
-
records_by_owner
|
79
|
+
@owner_keys
|
100
80
|
end
|
101
81
|
|
102
82
|
def key_conversion_required?
|
103
|
-
association_key_type != owner_key_type
|
83
|
+
@key_conversion_required ||= association_key_type != owner_key_type
|
84
|
+
end
|
85
|
+
|
86
|
+
def convert_key(key)
|
87
|
+
if key_conversion_required?
|
88
|
+
key.to_s
|
89
|
+
else
|
90
|
+
key
|
91
|
+
end
|
104
92
|
end
|
105
93
|
|
106
94
|
def association_key_type
|
@@ -111,17 +99,17 @@ module ActiveRecord
|
|
111
99
|
@model.type_for_attribute(owner_key_name.to_s).type
|
112
100
|
end
|
113
101
|
|
114
|
-
def
|
115
|
-
|
102
|
+
def load_records
|
103
|
+
return {} if owner_keys.empty?
|
104
|
+
# Some databases impose a limit on the number of ids in a list (in Oracle it's 1000)
|
105
|
+
# Make several smaller queries if necessary or make one query if the adapter supports it
|
106
|
+
slices = owner_keys.each_slice(klass.connection.in_clause_length || owner_keys.size)
|
107
|
+
@preloaded_records = slices.flat_map do |slice|
|
116
108
|
records_for(slice)
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
key = key.to_s if key_conversion_required?
|
122
|
-
|
123
|
-
[record, key]
|
124
|
-
}
|
109
|
+
end
|
110
|
+
@preloaded_records.group_by do |record|
|
111
|
+
convert_key(record[association_key_name])
|
112
|
+
end
|
125
113
|
end
|
126
114
|
|
127
115
|
def reflection_scope
|
@@ -131,19 +119,25 @@ module ActiveRecord
|
|
131
119
|
def build_scope
|
132
120
|
scope = klass.unscoped
|
133
121
|
|
134
|
-
values
|
135
|
-
reflection_binds = reflection_scope.bind_values
|
122
|
+
values = reflection_scope.values
|
136
123
|
preload_values = preload_scope.values
|
137
|
-
preload_binds = preload_scope.bind_values
|
138
124
|
|
139
|
-
scope.
|
125
|
+
scope.where_clause = reflection_scope.where_clause + preload_scope.where_clause
|
140
126
|
scope.references_values = Array(values[:references]) + Array(preload_values[:references])
|
141
|
-
scope.bind_values = (reflection_binds + preload_binds)
|
142
127
|
|
143
|
-
|
128
|
+
if preload_values[:select] || values[:select]
|
129
|
+
scope._select!(preload_values[:select] || values[:select])
|
130
|
+
end
|
144
131
|
scope.includes! preload_values[:includes] || values[:includes]
|
145
|
-
|
146
|
-
|
132
|
+
if preload_scope.joins_values.any?
|
133
|
+
scope.joins!(preload_scope.joins_values)
|
134
|
+
else
|
135
|
+
scope.joins!(reflection_scope.joins_values)
|
136
|
+
end
|
137
|
+
|
138
|
+
if order_values = preload_values[:order] || values[:order]
|
139
|
+
scope.order!(order_values)
|
140
|
+
end
|
147
141
|
|
148
142
|
if preload_values[:reordering] || values[:reordering]
|
149
143
|
scope.reordering_value = true
|
@@ -2,13 +2,8 @@ module ActiveRecord
|
|
2
2
|
module Associations
|
3
3
|
class Preloader
|
4
4
|
class CollectionAssociation < Association #:nodoc:
|
5
|
-
|
6
5
|
private
|
7
6
|
|
8
|
-
def build_scope
|
9
|
-
super.order(preload_scope.values[:order] || reflection_scope.values[:order])
|
10
|
-
end
|
11
|
-
|
12
7
|
def preload(preloader)
|
13
8
|
associated_records_by_owner(preloader).each do |owner, records|
|
14
9
|
association = owner.association(reflection.name)
|
@@ -17,7 +12,6 @@ module ActiveRecord
|
|
17
12
|
records.each { |record| association.set_inverse_instance(record) }
|
18
13
|
end
|
19
14
|
end
|
20
|
-
|
21
15
|
end
|
22
16
|
end
|
23
17
|
end
|