activerecord 4.2.6 → 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 +4 -4
- data/CHANGELOG.md +1307 -1105
- 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/aggregations.rb +37 -23
- data/lib/active_record/association_relation.rb +3 -3
- 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 +50 -31
- data/lib/active_record/associations/collection_proxy.rb +69 -29
- 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/join_association.rb +20 -8
- data/lib/active_record/associations/join_dependency.rb +29 -19
- 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/preloader.rb +14 -4
- data/lib/active_record/associations/singular_association.rb +7 -1
- data/lib/active_record/associations/through_association.rb +11 -3
- data/lib/active_record/associations.rb +317 -209
- data/lib/active_record/attribute/user_provided_default.rb +28 -0
- data/lib/active_record/attribute.rb +68 -18
- data/lib/active_record/attribute_assignment.rb +20 -141
- data/lib/active_record/attribute_decorators.rb +6 -5
- 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 +14 -38
- data/lib/active_record/attribute_methods.rb +70 -45
- data/lib/active_record/attribute_mutation_tracker.rb +70 -0
- data/lib/active_record/attribute_set/builder.rb +6 -4
- data/lib/active_record/attribute_set.rb +30 -3
- data/lib/active_record/attributes.rb +199 -80
- 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 -9
- 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 +378 -140
- data/lib/active_record/connection_adapters/abstract/transaction.rb +51 -34
- data/lib/active_record/connection_adapters/abstract_adapter.rb +153 -59
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +405 -362
- 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 +25 -176
- 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/array.rb +27 -56
- 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/oid.rb +1 -6
- 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 +148 -203
- 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 +3 -3
- 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/command_recorder.rb +59 -18
- data/lib/active_record/migration/compatibility.rb +126 -0
- data/lib/active_record/migration.rb +364 -109
- data/lib/active_record/model_schema.rb +128 -38
- 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 +27 -18
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +58 -45
- data/lib/active_record/readonly_attributes.rb +1 -1
- data/lib/active_record/reflection.rb +282 -115
- data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
- data/lib/active_record/relation/batches.rb +139 -34
- data/lib/active_record/relation/calculations.rb +80 -102
- 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/array_handler.rb +11 -15
- 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/predicate_builder.rb +120 -107
- 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/relation.rb +176 -116
- 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 -17
- data/lib/active_record/scoping/default.rb +23 -9
- data/lib/active_record/scoping/named.rb +49 -28
- data/lib/active_record/scoping.rb +32 -15
- 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 +58 -41
- data/lib/active_record/tasks/mysql_database_tasks.rb +16 -20
- 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/adapter_specific_registry.rb +130 -0
- data/lib/active_record/type/date.rb +2 -41
- 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.rb +66 -17
- data/lib/active_record/type_caster/connection.rb +29 -0
- data/lib/active_record/type_caster/map.rb +19 -0
- data/lib/active_record/type_caster.rb +7 -0
- 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/active_record/validations.rb +33 -32
- data/lib/active_record.rb +7 -2
- 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/migration.rb +7 -0
- 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 +58 -34
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
- 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 -50
- 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 -105
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'active_support/core_ext/string/filters'
|
2
|
-
|
3
1
|
module ActiveRecord
|
4
2
|
# = Active Record Has Many Through Association
|
5
3
|
module Associations
|
@@ -13,21 +11,6 @@ module ActiveRecord
|
|
13
11
|
@through_association = nil
|
14
12
|
end
|
15
13
|
|
16
|
-
# Returns the size of the collection by executing a SELECT COUNT(*) query
|
17
|
-
# if the collection hasn't been loaded, and by calling collection.size if
|
18
|
-
# it has. If the collection will likely have a size greater than zero,
|
19
|
-
# and if fetching the collection will be needed afterwards, one less
|
20
|
-
# SELECT query will be generated by using #length instead.
|
21
|
-
def size
|
22
|
-
if has_cached_counter?
|
23
|
-
owner._read_attribute cached_counter_attribute_name(reflection)
|
24
|
-
elsif loaded?
|
25
|
-
target.size
|
26
|
-
else
|
27
|
-
super
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
14
|
def concat(*records)
|
32
15
|
unless owner.new_record?
|
33
16
|
records.flatten.each do |record|
|
@@ -55,25 +38,14 @@ module ActiveRecord
|
|
55
38
|
def insert_record(record, validate = true, raise = false)
|
56
39
|
ensure_not_nested
|
57
40
|
|
58
|
-
if
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
return unless record.save(:validate => validate)
|
63
|
-
end
|
41
|
+
if raise
|
42
|
+
record.save!(:validate => validate)
|
43
|
+
else
|
44
|
+
return unless record.save(:validate => validate)
|
64
45
|
end
|
65
46
|
|
66
47
|
save_through_record(record)
|
67
|
-
if has_cached_counter? && !through_reflection_updates_counter_cache?
|
68
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
69
|
-
Automatic updating of counter caches on through associations has been
|
70
|
-
deprecated, and will be removed in Rails 5. Instead, please set the
|
71
|
-
appropriate `counter_cache` options on the `has_many` and `belongs_to`
|
72
|
-
for your associations to #{through_reflection.name}.
|
73
|
-
MSG
|
74
48
|
|
75
|
-
update_counter_in_database(1)
|
76
|
-
end
|
77
49
|
record
|
78
50
|
end
|
79
51
|
|
@@ -143,7 +115,7 @@ module ActiveRecord
|
|
143
115
|
def update_through_counter?(method)
|
144
116
|
case method
|
145
117
|
when :destroy
|
146
|
-
!inverse_updates_counter_cache?
|
118
|
+
!through_reflection.inverse_updates_counter_cache?
|
147
119
|
when :nullify
|
148
120
|
false
|
149
121
|
else
|
@@ -166,17 +138,15 @@ module ActiveRecord
|
|
166
138
|
if scope.klass.primary_key
|
167
139
|
count = scope.destroy_all.length
|
168
140
|
else
|
169
|
-
scope.each
|
170
|
-
record._run_destroy_callbacks
|
171
|
-
end
|
141
|
+
scope.each(&:_run_destroy_callbacks)
|
172
142
|
|
173
143
|
arel = scope.arel
|
174
144
|
|
175
|
-
stmt = Arel::DeleteManager.new
|
145
|
+
stmt = Arel::DeleteManager.new
|
176
146
|
stmt.from scope.klass.arel_table
|
177
147
|
stmt.wheres = arel.constraints
|
178
148
|
|
179
|
-
count = scope.klass.connection.delete(stmt, 'SQL', scope.
|
149
|
+
count = scope.klass.connection.delete(stmt, 'SQL', scope.bound_attributes)
|
180
150
|
end
|
181
151
|
when :nullify
|
182
152
|
count = scope.update_all(source_reflection.foreign_key => nil)
|
@@ -233,15 +203,6 @@ module ActiveRecord
|
|
233
203
|
def invertible_for?(record)
|
234
204
|
false
|
235
205
|
end
|
236
|
-
|
237
|
-
def has_cached_counter?(reflection = reflection())
|
238
|
-
owner.attribute_present?(cached_counter_attribute_name(reflection))
|
239
|
-
end
|
240
|
-
|
241
|
-
def through_reflection_updates_counter_cache?
|
242
|
-
counter_name = cached_counter_attribute_name
|
243
|
-
inverse_updates_counter_named?(counter_name, through_reflection)
|
244
|
-
end
|
245
206
|
end
|
246
207
|
end
|
247
208
|
end
|
@@ -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
|
@@ -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,23 +43,36 @@ 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 =
|
58
|
+
if klass.current_scope
|
59
|
+
klass.current_scope.clone
|
60
|
+
else
|
61
|
+
relation = ActiveRecord::Relation.create(
|
62
|
+
klass,
|
63
|
+
table,
|
64
|
+
predicate_builder,
|
65
|
+
)
|
66
|
+
klass.send(:build_default_scope, relation)
|
67
|
+
end
|
68
|
+
scope_chain_items.concat [klass_scope].compact
|
56
69
|
|
57
70
|
rel = scope_chain_items.inject(scope_chain_items.shift) do |left, right|
|
58
71
|
left.merge right
|
59
72
|
end
|
60
73
|
|
61
74
|
if rel && !rel.arel.constraints.empty?
|
62
|
-
|
75
|
+
binds += rel.bound_attributes
|
63
76
|
constraint = constraint.and rel.arel.constraints
|
64
77
|
end
|
65
78
|
|
@@ -67,9 +80,8 @@ module ActiveRecord
|
|
67
80
|
value = foreign_klass.base_class.name
|
68
81
|
column = klass.columns_hash[reflection.type.to_s]
|
69
82
|
|
70
|
-
|
71
|
-
|
72
|
-
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)
|
73
85
|
end
|
74
86
|
|
75
87
|
joins << table.create_join(table, table.create_on(constraint), join_type)
|
@@ -78,7 +90,7 @@ module ActiveRecord
|
|
78
90
|
foreign_table, foreign_klass = table, klass
|
79
91
|
end
|
80
92
|
|
81
|
-
JoinInformation.new joins,
|
93
|
+
JoinInformation.new joins, binds
|
82
94
|
end
|
83
95
|
|
84
96
|
# Builds equality condition.
|
@@ -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
|
@@ -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
|
@@ -2,7 +2,6 @@ module ActiveRecord
|
|
2
2
|
module Associations
|
3
3
|
class Preloader
|
4
4
|
class HasOne < SingularAssociation #:nodoc:
|
5
|
-
|
6
5
|
def association_key_name
|
7
6
|
reflection.foreign_key
|
8
7
|
end
|
@@ -10,13 +9,6 @@ module ActiveRecord
|
|
10
9
|
def owner_key_name
|
11
10
|
reflection.active_record_primary_key
|
12
11
|
end
|
13
|
-
|
14
|
-
private
|
15
|
-
|
16
|
-
def build_scope
|
17
|
-
super.order(preload_scope.values[:order] || reflection_scope.values[:order])
|
18
|
-
end
|
19
|
-
|
20
12
|
end
|
21
13
|
end
|
22
14
|
end
|
@@ -18,7 +18,8 @@ module ActiveRecord
|
|
18
18
|
through_records = owners.map do |owner|
|
19
19
|
association = owner.association through_reflection.name
|
20
20
|
|
21
|
-
|
21
|
+
center = target_records_from_association(association)
|
22
|
+
[owner, Array(center)]
|
22
23
|
end
|
23
24
|
|
24
25
|
reset_association owners, through_reflection.name
|
@@ -37,28 +38,35 @@ module ActiveRecord
|
|
37
38
|
}
|
38
39
|
end
|
39
40
|
|
40
|
-
|
41
|
-
@preloaded_records.each_with_index do |record,i|
|
42
|
-
record_offset[record] = i
|
43
|
-
end
|
44
|
-
|
45
|
-
through_records.each_with_object({}) { |(lhs,center),records_by_owner|
|
41
|
+
through_records.each_with_object({}) do |(lhs,center), records_by_owner|
|
46
42
|
pl_to_middle = center.group_by { |record| middle_to_pl[record] }
|
47
43
|
|
48
44
|
records_by_owner[lhs] = pl_to_middle.flat_map do |pl, middles|
|
49
45
|
rhs_records = middles.flat_map { |r|
|
50
46
|
association = r.association source_reflection.name
|
51
47
|
|
52
|
-
association
|
48
|
+
target_records_from_association(association)
|
53
49
|
}.compact
|
54
50
|
|
55
|
-
|
51
|
+
# Respect the order on `reflection_scope` if it exists, else use the natural order.
|
52
|
+
if reflection_scope.values[:order].present?
|
53
|
+
@id_map ||= id_to_index_map @preloaded_records
|
54
|
+
rhs_records.sort_by { |rhs| @id_map[rhs] }
|
55
|
+
else
|
56
|
+
rhs_records
|
57
|
+
end
|
56
58
|
end
|
57
|
-
|
59
|
+
end
|
58
60
|
end
|
59
61
|
|
60
62
|
private
|
61
63
|
|
64
|
+
def id_to_index_map(ids)
|
65
|
+
id_map = {}
|
66
|
+
ids.each_with_index { |id, index| id_map[id] = index }
|
67
|
+
id_map
|
68
|
+
end
|
69
|
+
|
62
70
|
def reset_association(owners, association_name)
|
63
71
|
should_reset = (through_scope != through_reflection.klass.unscoped) ||
|
64
72
|
(reflection.options[:source_type] && through_reflection.collection?)
|
@@ -78,18 +86,23 @@ module ActiveRecord
|
|
78
86
|
if options[:source_type]
|
79
87
|
scope.where! reflection.foreign_type => options[:source_type]
|
80
88
|
else
|
81
|
-
unless reflection_scope.
|
89
|
+
unless reflection_scope.where_clause.empty?
|
82
90
|
scope.includes_values = Array(reflection_scope.values[:includes] || options[:source])
|
83
|
-
scope.
|
84
|
-
scope.bind_values = reflection_scope.bind_values
|
91
|
+
scope.where_clause = reflection_scope.where_clause
|
85
92
|
end
|
86
93
|
|
87
94
|
scope.references! reflection_scope.values[:references]
|
88
|
-
scope =
|
95
|
+
if scope.eager_loading? && order_values = reflection_scope.values[:order]
|
96
|
+
scope = scope.order(order_values)
|
97
|
+
end
|
89
98
|
end
|
90
99
|
|
91
100
|
scope
|
92
101
|
end
|
102
|
+
|
103
|
+
def target_records_from_association(association)
|
104
|
+
association.loaded? ? association.target : association.reader
|
105
|
+
end
|
93
106
|
end
|
94
107
|
end
|
95
108
|
end
|