activerecord 4.2.0 → 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 +1537 -789
- 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 +16 -3
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +23 -9
- data/lib/active_record/associations/association_scope.rb +74 -102
- data/lib/active_record/associations/belongs_to_association.rb +26 -29
- 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 +12 -20
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +22 -15
- 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 +61 -33
- data/lib/active_record/associations/collection_proxy.rb +81 -35
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +21 -57
- data/lib/active_record/associations/has_many_through_association.rb +15 -45
- data/lib/active_record/associations/has_one_association.rb +13 -5
- data/lib/active_record/associations/join_dependency/join_association.rb +20 -8
- data/lib/active_record/associations/join_dependency.rb +37 -21
- data/lib/active_record/associations/preloader/association.rb +51 -53
- 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 +18 -8
- data/lib/active_record/associations/singular_association.rb +8 -8
- data/lib/active_record/associations/through_association.rb +22 -9
- data/lib/active_record/associations.rb +321 -212
- data/lib/active_record/attribute/user_provided_default.rb +28 -0
- data/lib/active_record/attribute.rb +79 -15
- 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 +6 -1
- data/lib/active_record/attribute_methods/dirty.rb +51 -81
- 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 +65 -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 +37 -15
- data/lib/active_record/attribute_set.rb +34 -3
- data/lib/active_record/attributes.rb +199 -73
- data/lib/active_record/autosave_association.rb +73 -25
- data/lib/active_record/base.rb +35 -27
- 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 +457 -181
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +83 -59
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -9
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -4
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +246 -185
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +438 -136
- data/lib/active_record/connection_adapters/abstract/transaction.rb +53 -40
- data/lib/active_record/connection_adapters/abstract_adapter.rb +166 -66
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +429 -335
- 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 +26 -177
- data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +11 -73
- 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 +2 -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 -13
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -1
- 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/type_map_initializer.rb +17 -5
- 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 +248 -154
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +258 -170
- 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 +150 -209
- data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
- data/lib/active_record/connection_handling.rb +38 -15
- data/lib/active_record/core.rb +109 -114
- data/lib/active_record/counter_cache.rb +14 -25
- data/lib/active_record/dynamic_matchers.rb +1 -20
- data/lib/active_record/enum.rb +115 -79
- data/lib/active_record/errors.rb +88 -48
- data/lib/active_record/explain_registry.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +2 -2
- data/lib/active_record/fixture_set/file.rb +26 -5
- data/lib/active_record/fixtures.rb +84 -46
- data/lib/active_record/gem_version.rb +2 -2
- 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 +46 -0
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +27 -25
- 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 +372 -114
- data/lib/active_record/model_schema.rb +128 -38
- data/lib/active_record/nested_attributes.rb +71 -32
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/null_relation.rb +16 -8
- data/lib/active_record/persistence.rb +124 -80
- data/lib/active_record/query_cache.rb +15 -18
- data/lib/active_record/querying.rb +10 -9
- data/lib/active_record/railtie.rb +28 -19
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +67 -51
- data/lib/active_record/readonly_attributes.rb +1 -1
- data/lib/active_record/reflection.rb +318 -139
- 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 +167 -97
- data/lib/active_record/relation/from_clause.rb +32 -0
- data/lib/active_record/relation/merger.rb +38 -41
- data/lib/active_record/relation/predicate_builder/array_handler.rb +12 -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/predicate_builder.rb +124 -82
- data/lib/active_record/relation/query_attribute.rb +19 -0
- data/lib/active_record/relation/query_methods.rb +323 -257
- data/lib/active_record/relation/record_fetch_warning.rb +49 -0
- data/lib/active_record/relation/spawn_methods.rb +11 -10
- 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 -115
- 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 +24 -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 +59 -42
- data/lib/active_record/tasks/mysql_database_tasks.rb +32 -26
- data/lib/active_record/tasks/postgresql_database_tasks.rb +29 -9
- 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 +159 -67
- 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 -38
- data/lib/active_record/type/hash_lookup_type_map.rb +8 -2
- 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 +21 -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 +29 -18
- data/lib/active_record/validations.rb +33 -32
- data/lib/active_record.rb +9 -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 -6
- data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -7
- 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 +60 -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 -30
- data/lib/active_record/type/decimal.rb +0 -40
- 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 -55
- 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 -36
- 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 -101
@@ -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
|
|
@@ -94,6 +66,11 @@ module ActiveRecord
|
|
94
66
|
|
95
67
|
through_record = through_association.build(*options_for_through_record)
|
96
68
|
through_record.send("#{source_reflection.name}=", record)
|
69
|
+
|
70
|
+
if options[:source_type]
|
71
|
+
through_record.send("#{source_reflection.foreign_type}=", options[:source_type])
|
72
|
+
end
|
73
|
+
|
97
74
|
through_record
|
98
75
|
end
|
99
76
|
end
|
@@ -138,7 +115,7 @@ module ActiveRecord
|
|
138
115
|
def update_through_counter?(method)
|
139
116
|
case method
|
140
117
|
when :destroy
|
141
|
-
!inverse_updates_counter_cache?
|
118
|
+
!through_reflection.inverse_updates_counter_cache?
|
142
119
|
when :nullify
|
143
120
|
false
|
144
121
|
else
|
@@ -161,17 +138,15 @@ module ActiveRecord
|
|
161
138
|
if scope.klass.primary_key
|
162
139
|
count = scope.destroy_all.length
|
163
140
|
else
|
164
|
-
scope.each
|
165
|
-
record._run_destroy_callbacks
|
166
|
-
end
|
141
|
+
scope.each(&:_run_destroy_callbacks)
|
167
142
|
|
168
143
|
arel = scope.arel
|
169
144
|
|
170
|
-
stmt = Arel::DeleteManager.new
|
145
|
+
stmt = Arel::DeleteManager.new
|
171
146
|
stmt.from scope.klass.arel_table
|
172
147
|
stmt.wheres = arel.constraints
|
173
148
|
|
174
|
-
count = scope.klass.connection.delete(stmt, 'SQL', scope.
|
149
|
+
count = scope.klass.connection.delete(stmt, 'SQL', scope.bound_attributes)
|
175
150
|
end
|
176
151
|
when :nullify
|
177
152
|
count = scope.update_all(source_reflection.foreign_key => nil)
|
@@ -188,9 +163,9 @@ module ActiveRecord
|
|
188
163
|
|
189
164
|
if through_reflection.collection? && update_through_counter?(method)
|
190
165
|
update_counter(-count, through_reflection)
|
166
|
+
else
|
167
|
+
update_counter(-count)
|
191
168
|
end
|
192
|
-
|
193
|
-
update_counter(-count)
|
194
169
|
end
|
195
170
|
|
196
171
|
def through_records_for(record)
|
@@ -228,11 +203,6 @@ module ActiveRecord
|
|
228
203
|
def invertible_for?(record)
|
229
204
|
false
|
230
205
|
end
|
231
|
-
|
232
|
-
def through_reflection_updates_counter_cache?
|
233
|
-
counter_name = cached_counter_attribute_name
|
234
|
-
inverse_updates_counter_named?(counter_name, through_reflection)
|
235
|
-
end
|
236
206
|
end
|
237
207
|
end
|
238
208
|
end
|
@@ -1,7 +1,8 @@
|
|
1
1
|
module ActiveRecord
|
2
|
-
# = Active Record
|
2
|
+
# = Active Record Has One Association
|
3
3
|
module Associations
|
4
4
|
class HasOneAssociation < SingularAssociation #:nodoc:
|
5
|
+
include ForeignAssociation
|
5
6
|
|
6
7
|
def handle_dependency
|
7
8
|
case options[:dependent]
|
@@ -10,9 +11,16 @@ module ActiveRecord
|
|
10
11
|
|
11
12
|
when :restrict_with_error
|
12
13
|
if load_target
|
13
|
-
record =
|
14
|
-
owner.errors.
|
15
|
-
|
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)
|
16
24
|
end
|
17
25
|
|
18
26
|
else
|
@@ -57,7 +65,7 @@ module ActiveRecord
|
|
57
65
|
when :destroy
|
58
66
|
target.destroy
|
59
67
|
when :nullify
|
60
|
-
target.update_columns(reflection.foreign_key => nil)
|
68
|
+
target.update_columns(reflection.foreign_key => nil) if target.persisted?
|
61
69
|
end
|
62
70
|
end
|
63
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
|
|
@@ -151,7 +155,8 @@ module ActiveRecord
|
|
151
155
|
|
152
156
|
message_bus.instrument('instantiation.active_record', payload) do
|
153
157
|
result_set.each { |row_hash|
|
154
|
-
|
158
|
+
parent_key = primary_key ? row_hash[primary_key] : row_hash
|
159
|
+
parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases)
|
155
160
|
construct(parent, join_root, row_hash, result_set, seen, model_cache, aliases)
|
156
161
|
}
|
157
162
|
end
|
@@ -176,6 +181,14 @@ module ActiveRecord
|
|
176
181
|
[info] + child.children.flat_map { |c| make_outer_joins(child, c) }
|
177
182
|
end
|
178
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
|
+
|
179
192
|
def make_inner_joins(parent, child)
|
180
193
|
tables = child.tables
|
181
194
|
join_type = Arel::Nodes::InnerJoin
|
@@ -215,7 +228,7 @@ module ActiveRecord
|
|
215
228
|
|
216
229
|
def find_reflection(klass, name)
|
217
230
|
klass._reflect_on_association(name) or
|
218
|
-
raise ConfigurationError, "
|
231
|
+
raise ConfigurationError, "Can't join '#{ klass.name }' to association named '#{ name }'; perhaps you misspelled it?"
|
219
232
|
end
|
220
233
|
|
221
234
|
def build(associations, base_klass)
|
@@ -233,31 +246,34 @@ module ActiveRecord
|
|
233
246
|
end
|
234
247
|
|
235
248
|
def construct(ar_parent, parent, row, rs, seen, model_cache, aliases)
|
236
|
-
|
249
|
+
return if ar_parent.nil?
|
237
250
|
|
238
251
|
parent.children.each do |node|
|
239
252
|
if node.reflection.collection?
|
240
253
|
other = ar_parent.association(node.reflection.name)
|
241
254
|
other.loaded!
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
next
|
247
|
-
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
|
248
259
|
end
|
249
260
|
|
250
261
|
key = aliases.column_alias(node, node.primary_key)
|
251
262
|
id = row[key]
|
252
|
-
|
263
|
+
if id.nil?
|
264
|
+
nil_association = ar_parent.association(node.reflection.name)
|
265
|
+
nil_association.loaded!
|
266
|
+
next
|
267
|
+
end
|
253
268
|
|
254
|
-
model = seen[
|
269
|
+
model = seen[ar_parent.object_id][node.base_klass][id]
|
255
270
|
|
256
271
|
if model
|
257
272
|
construct(model, node, row, rs, seen, model_cache, aliases)
|
258
273
|
else
|
259
274
|
model = construct_model(ar_parent, node, row, model_cache, id, aliases)
|
260
|
-
|
275
|
+
model.readonly!
|
276
|
+
seen[ar_parent.object_id][node.base_klass][id] = model
|
261
277
|
construct(model, node, row, rs, seen, model_cache, aliases)
|
262
278
|
end
|
263
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,29 @@ 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
|
141
|
+
|
142
|
+
if preload_values[:reordering] || values[:reordering]
|
143
|
+
scope.reordering_value = true
|
144
|
+
end
|
147
145
|
|
148
146
|
if preload_values[:readonly] || values[:readonly]
|
149
147
|
scope.readonly!
|
@@ -153,7 +151,7 @@ module ActiveRecord
|
|
153
151
|
scope.where!(klass.table_name => { reflection.type => model.base_class.sti_name })
|
154
152
|
end
|
155
153
|
|
156
|
-
scope.unscope_values = Array(values[:unscope])
|
154
|
+
scope.unscope_values = Array(values[:unscope]) + Array(preload_values[:unscope])
|
157
155
|
klass.default_scoped.merge(scope)
|
158
156
|
end
|
159
157
|
end
|
@@ -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
|