activerecord 4.1.15 → 4.2.11.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +1162 -1792
- data/README.rdoc +15 -10
- data/lib/active_record.rb +4 -0
- data/lib/active_record/aggregations.rb +15 -8
- data/lib/active_record/association_relation.rb +13 -0
- data/lib/active_record/associations.rb +158 -49
- data/lib/active_record/associations/alias_tracker.rb +3 -12
- data/lib/active_record/associations/association.rb +16 -4
- data/lib/active_record/associations/association_scope.rb +83 -38
- data/lib/active_record/associations/belongs_to_association.rb +28 -10
- data/lib/active_record/associations/builder/association.rb +15 -4
- data/lib/active_record/associations/builder/belongs_to.rb +7 -29
- data/lib/active_record/associations/builder/collection_association.rb +5 -1
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +8 -13
- data/lib/active_record/associations/builder/has_many.rb +1 -1
- data/lib/active_record/associations/builder/has_one.rb +2 -2
- data/lib/active_record/associations/builder/singular_association.rb +8 -1
- data/lib/active_record/associations/collection_association.rb +63 -27
- data/lib/active_record/associations/collection_proxy.rb +29 -35
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +83 -22
- data/lib/active_record/associations/has_many_through_association.rb +49 -26
- data/lib/active_record/associations/has_one_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +26 -13
- data/lib/active_record/associations/join_dependency/join_association.rb +25 -15
- data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
- data/lib/active_record/associations/preloader.rb +36 -26
- data/lib/active_record/associations/preloader/association.rb +14 -11
- data/lib/active_record/associations/preloader/through_association.rb +4 -3
- data/lib/active_record/associations/singular_association.rb +17 -2
- data/lib/active_record/associations/through_association.rb +5 -12
- data/lib/active_record/attribute.rb +163 -0
- data/lib/active_record/attribute_assignment.rb +19 -11
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods.rb +56 -94
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
- data/lib/active_record/attribute_methods/dirty.rb +107 -43
- data/lib/active_record/attribute_methods/primary_key.rb +7 -8
- data/lib/active_record/attribute_methods/query.rb +1 -1
- data/lib/active_record/attribute_methods/read.rb +22 -59
- data/lib/active_record/attribute_methods/serialization.rb +16 -150
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -40
- data/lib/active_record/attribute_methods/write.rb +9 -24
- data/lib/active_record/attribute_set.rb +81 -0
- data/lib/active_record/attribute_set/builder.rb +106 -0
- data/lib/active_record/attributes.rb +147 -0
- data/lib/active_record/autosave_association.rb +19 -12
- data/lib/active_record/base.rb +13 -24
- data/lib/active_record/callbacks.rb +6 -6
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +84 -52
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +52 -50
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +60 -60
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +39 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +138 -56
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +268 -71
- data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -118
- data/lib/active_record/connection_adapters/abstract_adapter.rb +171 -59
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +293 -139
- data/lib/active_record/connection_adapters/column.rb +29 -240
- data/lib/active_record/connection_adapters/connection_specification.rb +15 -24
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
- data/lib/active_record/connection_adapters/mysql_adapter.rb +67 -144
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
- data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +40 -25
- data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +46 -136
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +131 -43
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -477
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -75
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +163 -39
- data/lib/active_record/counter_cache.rb +60 -6
- data/lib/active_record/enum.rb +9 -11
- data/lib/active_record/errors.rb +53 -30
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixtures.rb +55 -69
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +35 -10
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/legacy_yaml_adapter.rb +30 -0
- data/lib/active_record/locking/optimistic.rb +46 -26
- data/lib/active_record/migration.rb +71 -46
- data/lib/active_record/migration/command_recorder.rb +19 -2
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/model_schema.rb +52 -58
- data/lib/active_record/nested_attributes.rb +5 -5
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/persistence.rb +46 -26
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +10 -7
- data/lib/active_record/railtie.rb +18 -11
- data/lib/active_record/railties/databases.rake +50 -51
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +273 -114
- data/lib/active_record/relation.rb +57 -25
- data/lib/active_record/relation/batches.rb +0 -2
- data/lib/active_record/relation/calculations.rb +41 -37
- data/lib/active_record/relation/finder_methods.rb +70 -47
- data/lib/active_record/relation/merger.rb +39 -29
- data/lib/active_record/relation/predicate_builder.rb +16 -8
- data/lib/active_record/relation/predicate_builder/array_handler.rb +32 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -5
- data/lib/active_record/relation/query_methods.rb +114 -65
- data/lib/active_record/relation/spawn_methods.rb +3 -0
- data/lib/active_record/result.rb +18 -7
- data/lib/active_record/sanitization.rb +12 -2
- data/lib/active_record/schema.rb +0 -1
- data/lib/active_record/schema_dumper.rb +59 -28
- data/lib/active_record/schema_migration.rb +5 -4
- data/lib/active_record/scoping/default.rb +6 -4
- data/lib/active_record/scoping/named.rb +4 -0
- data/lib/active_record/serializers/xml_serializer.rb +3 -7
- data/lib/active_record/statement_cache.rb +95 -10
- data/lib/active_record/store.rb +5 -5
- data/lib/active_record/tasks/database_tasks.rb +61 -6
- data/lib/active_record/tasks/mysql_database_tasks.rb +32 -17
- data/lib/active_record/tasks/postgresql_database_tasks.rb +20 -9
- data/lib/active_record/timestamp.rb +9 -7
- data/lib/active_record/transactions.rb +53 -27
- data/lib/active_record/type.rb +23 -0
- data/lib/active_record/type/big_integer.rb +13 -0
- data/lib/active_record/type/binary.rb +50 -0
- data/lib/active_record/type/boolean.rb +31 -0
- data/lib/active_record/type/date.rb +50 -0
- data/lib/active_record/type/date_time.rb +54 -0
- data/lib/active_record/type/decimal.rb +64 -0
- data/lib/active_record/type/decimal_without_scale.rb +11 -0
- data/lib/active_record/type/decorator.rb +14 -0
- data/lib/active_record/type/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
- data/lib/active_record/type/integer.rb +59 -0
- data/lib/active_record/type/mutable.rb +16 -0
- data/lib/active_record/type/numeric.rb +36 -0
- data/lib/active_record/type/serialized.rb +62 -0
- data/lib/active_record/type/string.rb +40 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +26 -0
- data/lib/active_record/type/time_value.rb +38 -0
- data/lib/active_record/type/type_map.rb +64 -0
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type/value.rb +110 -0
- data/lib/active_record/validations.rb +25 -19
- data/lib/active_record/validations/associated.rb +5 -3
- data/lib/active_record/validations/presence.rb +5 -3
- data/lib/active_record/validations/uniqueness.rb +25 -29
- data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
- metadata +66 -11
- data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -6,6 +6,7 @@ module ActiveRecord
|
|
6
6
|
# If the association has a <tt>:through</tt> option further specialization
|
7
7
|
# is provided by its child HasManyThroughAssociation.
|
8
8
|
class HasManyAssociation < CollectionAssociation #:nodoc:
|
9
|
+
include ForeignAssociation
|
9
10
|
|
10
11
|
def handle_dependency
|
11
12
|
case options[:dependent]
|
@@ -41,6 +42,14 @@ module ActiveRecord
|
|
41
42
|
end
|
42
43
|
end
|
43
44
|
|
45
|
+
def empty?
|
46
|
+
if has_cached_counter?
|
47
|
+
size.zero?
|
48
|
+
else
|
49
|
+
super
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
44
53
|
private
|
45
54
|
|
46
55
|
# Returns the number of records in this collection.
|
@@ -58,7 +67,7 @@ module ActiveRecord
|
|
58
67
|
# the loaded flag is set to true as well.
|
59
68
|
def count_records
|
60
69
|
count = if has_cached_counter?
|
61
|
-
owner.
|
70
|
+
owner._read_attribute cached_counter_attribute_name
|
62
71
|
else
|
63
72
|
scope.count
|
64
73
|
end
|
@@ -71,20 +80,42 @@ module ActiveRecord
|
|
71
80
|
[association_scope.limit_value, count].compact.min
|
72
81
|
end
|
73
82
|
|
83
|
+
|
84
|
+
# Returns whether a counter cache should be used for this association.
|
85
|
+
#
|
86
|
+
# The counter_cache option must be given on either the owner or inverse
|
87
|
+
# association, and the column must be present on the owner.
|
74
88
|
def has_cached_counter?(reflection = reflection())
|
75
|
-
|
89
|
+
if reflection.options[:counter_cache] || (inverse = inverse_which_updates_counter_cache(reflection)) && inverse.options[:counter_cache]
|
90
|
+
owner.attribute_present?(cached_counter_attribute_name(reflection))
|
91
|
+
end
|
76
92
|
end
|
77
93
|
|
78
94
|
def cached_counter_attribute_name(reflection = reflection())
|
79
|
-
options[:counter_cache]
|
95
|
+
if reflection.options[:counter_cache]
|
96
|
+
reflection.options[:counter_cache].to_s
|
97
|
+
else
|
98
|
+
"#{reflection.name}_count"
|
99
|
+
end
|
80
100
|
end
|
81
101
|
|
82
102
|
def update_counter(difference, reflection = reflection())
|
103
|
+
update_counter_in_database(difference, reflection)
|
104
|
+
update_counter_in_memory(difference, reflection)
|
105
|
+
end
|
106
|
+
|
107
|
+
def update_counter_in_database(difference, reflection = reflection())
|
83
108
|
if has_cached_counter?(reflection)
|
84
109
|
counter = cached_counter_attribute_name(reflection)
|
85
110
|
owner.class.update_counters(owner.id, counter => difference)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def update_counter_in_memory(difference, reflection = reflection())
|
115
|
+
if counter_must_be_updated_by_has_many?(reflection)
|
116
|
+
counter = cached_counter_attribute_name(reflection)
|
86
117
|
owner[counter] += difference
|
87
|
-
owner.
|
118
|
+
owner.send(:clear_attribute_changes, counter) # eww
|
88
119
|
end
|
89
120
|
end
|
90
121
|
|
@@ -98,13 +129,41 @@ module ActiveRecord
|
|
98
129
|
# it will be decremented twice.
|
99
130
|
#
|
100
131
|
# Hence this method.
|
101
|
-
def
|
132
|
+
def inverse_which_updates_counter_cache(reflection = reflection())
|
102
133
|
counter_name = cached_counter_attribute_name(reflection)
|
103
|
-
reflection
|
104
|
-
|
134
|
+
inverse_which_updates_counter_named(counter_name, reflection)
|
135
|
+
end
|
136
|
+
alias inverse_updates_counter_cache? inverse_which_updates_counter_cache
|
137
|
+
|
138
|
+
def inverse_which_updates_counter_named(counter_name, reflection)
|
139
|
+
reflection.klass._reflections.values.find { |inverse_reflection|
|
140
|
+
inverse_reflection.belongs_to? &&
|
105
141
|
inverse_reflection.counter_cache_column == counter_name
|
106
142
|
}
|
107
143
|
end
|
144
|
+
alias inverse_updates_counter_named? inverse_which_updates_counter_named
|
145
|
+
|
146
|
+
def inverse_updates_counter_in_memory?(reflection)
|
147
|
+
inverse = inverse_which_updates_counter_cache(reflection)
|
148
|
+
inverse && inverse == reflection.inverse_of
|
149
|
+
end
|
150
|
+
|
151
|
+
def counter_must_be_updated_by_has_many?(reflection)
|
152
|
+
!inverse_updates_counter_in_memory?(reflection) && has_cached_counter?(reflection)
|
153
|
+
end
|
154
|
+
|
155
|
+
def delete_count(method, scope)
|
156
|
+
if method == :delete_all
|
157
|
+
scope.delete_all
|
158
|
+
else
|
159
|
+
scope.update_all(reflection.foreign_key => nil)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def delete_or_nullify_all_records(method)
|
164
|
+
count = delete_count(method, self.scope)
|
165
|
+
update_counter(-count)
|
166
|
+
end
|
108
167
|
|
109
168
|
# Deletes the records according to the <tt>:dependent</tt> option.
|
110
169
|
def delete_records(records, method)
|
@@ -112,26 +171,28 @@ module ActiveRecord
|
|
112
171
|
records.each(&:destroy!)
|
113
172
|
update_counter(-records.length) unless inverse_updates_counter_cache?
|
114
173
|
else
|
115
|
-
|
116
|
-
|
117
|
-
else
|
118
|
-
scope = self.scope.where(reflection.klass.primary_key => records)
|
119
|
-
end
|
120
|
-
|
121
|
-
if method == :delete_all
|
122
|
-
update_counter(-scope.delete_all)
|
123
|
-
else
|
124
|
-
update_counter(-scope.update_all(reflection.foreign_key => nil))
|
125
|
-
end
|
174
|
+
scope = self.scope.where(reflection.klass.primary_key => records)
|
175
|
+
update_counter(-delete_count(method, scope))
|
126
176
|
end
|
127
177
|
end
|
128
178
|
|
129
|
-
def
|
130
|
-
|
131
|
-
|
179
|
+
def concat_records(records, *)
|
180
|
+
update_counter_if_success(super, records.length)
|
181
|
+
end
|
182
|
+
|
183
|
+
def _create_record(attributes, *)
|
184
|
+
if attributes.is_a?(Array)
|
185
|
+
super
|
132
186
|
else
|
133
|
-
|
187
|
+
update_counter_if_success(super, 1)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def update_counter_if_success(saved_successfully, difference)
|
192
|
+
if saved_successfully
|
193
|
+
update_counter_in_memory(difference)
|
134
194
|
end
|
195
|
+
saved_successfully
|
135
196
|
end
|
136
197
|
end
|
137
198
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'active_support/core_ext/string/filters'
|
1
2
|
|
2
3
|
module ActiveRecord
|
3
4
|
# = Active Record Has Many Through Association
|
@@ -12,13 +13,14 @@ module ActiveRecord
|
|
12
13
|
@through_association = nil
|
13
14
|
end
|
14
15
|
|
15
|
-
# Returns the size of the collection by executing a SELECT COUNT(*) query
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
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.
|
19
21
|
def size
|
20
22
|
if has_cached_counter?
|
21
|
-
owner.
|
23
|
+
owner._read_attribute cached_counter_attribute_name(reflection)
|
22
24
|
elsif loaded?
|
23
25
|
target.size
|
24
26
|
else
|
@@ -62,7 +64,16 @@ module ActiveRecord
|
|
62
64
|
end
|
63
65
|
|
64
66
|
save_through_record(record)
|
65
|
-
|
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
|
+
|
75
|
+
update_counter_in_database(1)
|
76
|
+
end
|
66
77
|
record
|
67
78
|
end
|
68
79
|
|
@@ -72,19 +83,22 @@ module ActiveRecord
|
|
72
83
|
@through_association ||= owner.association(through_reflection.name)
|
73
84
|
end
|
74
85
|
|
75
|
-
#
|
76
|
-
#
|
77
|
-
# want to use the exact same object.
|
86
|
+
# The through record (built with build_record) is temporarily cached
|
87
|
+
# so that it may be reused if insert_record is subsequently called.
|
78
88
|
#
|
79
|
-
# However, after insert_record has been called,
|
80
|
-
#
|
81
|
-
# association
|
89
|
+
# However, after insert_record has been called, the cache is cleared in
|
90
|
+
# order to allow multiple instances of the same record in an association.
|
82
91
|
def build_through_record(record)
|
83
92
|
@through_records[record.object_id] ||= begin
|
84
93
|
ensure_mutable
|
85
94
|
|
86
95
|
through_record = through_association.build(*options_for_through_record)
|
87
96
|
through_record.send("#{source_reflection.name}=", record)
|
97
|
+
|
98
|
+
if options[:source_type]
|
99
|
+
through_record.send("#{source_reflection.foreign_type}=", options[:source_type])
|
100
|
+
end
|
101
|
+
|
88
102
|
through_record
|
89
103
|
end
|
90
104
|
end
|
@@ -112,9 +126,9 @@ module ActiveRecord
|
|
112
126
|
|
113
127
|
inverse = source_reflection.inverse_of
|
114
128
|
if inverse
|
115
|
-
if inverse.
|
129
|
+
if inverse.collection?
|
116
130
|
record.send(inverse.name) << build_through_record(record)
|
117
|
-
elsif inverse.
|
131
|
+
elsif inverse.has_one?
|
118
132
|
record.send("#{inverse.name}=", build_through_record(record))
|
119
133
|
end
|
120
134
|
end
|
@@ -123,7 +137,7 @@ module ActiveRecord
|
|
123
137
|
end
|
124
138
|
|
125
139
|
def target_reflection_has_associated_record?
|
126
|
-
!(through_reflection.
|
140
|
+
!(through_reflection.belongs_to? && owner[through_reflection.foreign_key].blank?)
|
127
141
|
end
|
128
142
|
|
129
143
|
def update_through_counter?(method)
|
@@ -137,13 +151,13 @@ module ActiveRecord
|
|
137
151
|
end
|
138
152
|
end
|
139
153
|
|
154
|
+
def delete_or_nullify_all_records(method)
|
155
|
+
delete_records(load_target, method)
|
156
|
+
end
|
157
|
+
|
140
158
|
def delete_records(records, method)
|
141
159
|
ensure_not_nested
|
142
160
|
|
143
|
-
# This is unoptimised; it will load all the target records
|
144
|
-
# even when we just want to delete everything.
|
145
|
-
records = load_target if records == :all
|
146
|
-
|
147
161
|
scope = through_association.scope
|
148
162
|
scope.where! construct_join_attributes(*records)
|
149
163
|
|
@@ -152,8 +166,8 @@ module ActiveRecord
|
|
152
166
|
if scope.klass.primary_key
|
153
167
|
count = scope.destroy_all.length
|
154
168
|
else
|
155
|
-
scope.
|
156
|
-
record.
|
169
|
+
scope.each do |record|
|
170
|
+
record._run_destroy_callbacks
|
157
171
|
end
|
158
172
|
|
159
173
|
arel = scope.arel
|
@@ -177,11 +191,11 @@ module ActiveRecord
|
|
177
191
|
klass.decrement_counter counter, records.map(&:id)
|
178
192
|
end
|
179
193
|
|
180
|
-
if through_reflection.
|
194
|
+
if through_reflection.collection? && update_through_counter?(method)
|
181
195
|
update_counter(-count, through_reflection)
|
196
|
+
else
|
197
|
+
update_counter(-count)
|
182
198
|
end
|
183
|
-
|
184
|
-
update_counter(-count)
|
185
199
|
end
|
186
200
|
|
187
201
|
def through_records_for(record)
|
@@ -198,7 +212,7 @@ module ActiveRecord
|
|
198
212
|
records.each do |record|
|
199
213
|
through_records = through_records_for(record)
|
200
214
|
|
201
|
-
if through_reflection.
|
215
|
+
if through_reflection.collection?
|
202
216
|
through_records.each { |r| through_association.target.delete(r) }
|
203
217
|
else
|
204
218
|
if through_records.include?(through_association.target)
|
@@ -212,13 +226,22 @@ module ActiveRecord
|
|
212
226
|
|
213
227
|
def find_target
|
214
228
|
return [] unless target_reflection_has_associated_record?
|
215
|
-
|
229
|
+
get_records
|
216
230
|
end
|
217
231
|
|
218
232
|
# NOTE - not sure that we can actually cope with inverses here
|
219
233
|
def invertible_for?(record)
|
220
234
|
false
|
221
235
|
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
|
222
245
|
end
|
223
246
|
end
|
224
247
|
end
|
@@ -94,7 +94,7 @@ module ActiveRecord
|
|
94
94
|
#
|
95
95
|
def initialize(base, associations, joins)
|
96
96
|
@alias_tracker = AliasTracker.create(base.connection, joins)
|
97
|
-
@alias_tracker.
|
97
|
+
@alias_tracker.aliased_table_for(base.table_name, base.table_name) # Updates the count for base.table_name to 1
|
98
98
|
tree = self.class.make_tree associations
|
99
99
|
@join_root = JoinBase.new base, build(tree, base)
|
100
100
|
@join_root.children.each { |child| construct_tables! @join_root, child }
|
@@ -131,7 +131,6 @@ module ActiveRecord
|
|
131
131
|
|
132
132
|
def instantiate(result_set, aliases)
|
133
133
|
primary_key = aliases.column_alias(join_root, join_root.primary_key)
|
134
|
-
type_caster = result_set.column_type primary_key
|
135
134
|
|
136
135
|
seen = Hash.new { |h,parent_klass|
|
137
136
|
h[parent_klass] = Hash.new { |i,parent_id|
|
@@ -143,13 +142,21 @@ module ActiveRecord
|
|
143
142
|
parents = model_cache[join_root]
|
144
143
|
column_aliases = aliases.column_aliases join_root
|
145
144
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
145
|
+
message_bus = ActiveSupport::Notifications.instrumenter
|
146
|
+
|
147
|
+
payload = {
|
148
|
+
record_count: result_set.length,
|
149
|
+
class_name: join_root.base_klass.name
|
151
150
|
}
|
152
151
|
|
152
|
+
message_bus.instrument('instantiation.active_record', payload) do
|
153
|
+
result_set.each { |row_hash|
|
154
|
+
parent_key = primary_key ? row_hash[primary_key] : row_hash
|
155
|
+
parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases)
|
156
|
+
construct(parent, join_root, row_hash, result_set, seen, model_cache, aliases)
|
157
|
+
}
|
158
|
+
end
|
159
|
+
|
153
160
|
parents.values
|
154
161
|
end
|
155
162
|
|
@@ -165,17 +172,17 @@ module ActiveRecord
|
|
165
172
|
def make_outer_joins(parent, child)
|
166
173
|
tables = table_aliases_for(parent, child)
|
167
174
|
join_type = Arel::Nodes::OuterJoin
|
168
|
-
|
175
|
+
info = make_constraints parent, child, tables, join_type
|
169
176
|
|
170
|
-
|
177
|
+
[info] + child.children.flat_map { |c| make_outer_joins(child, c) }
|
171
178
|
end
|
172
179
|
|
173
180
|
def make_inner_joins(parent, child)
|
174
181
|
tables = child.tables
|
175
182
|
join_type = Arel::Nodes::InnerJoin
|
176
|
-
|
183
|
+
info = make_constraints parent, child, tables, join_type
|
177
184
|
|
178
|
-
|
185
|
+
[info] + child.children.flat_map { |c| make_inner_joins(child, c) }
|
179
186
|
end
|
180
187
|
|
181
188
|
def table_aliases_for(parent, node)
|
@@ -216,8 +223,9 @@ module ActiveRecord
|
|
216
223
|
associations.map do |name, right|
|
217
224
|
reflection = find_reflection base_klass, name
|
218
225
|
reflection.check_validity!
|
226
|
+
reflection.check_eager_loadable!
|
219
227
|
|
220
|
-
if reflection.
|
228
|
+
if reflection.polymorphic?
|
221
229
|
raise EagerLoadPolymorphicError.new(reflection)
|
222
230
|
end
|
223
231
|
|
@@ -226,6 +234,7 @@ module ActiveRecord
|
|
226
234
|
end
|
227
235
|
|
228
236
|
def construct(ar_parent, parent, row, rs, seen, model_cache, aliases)
|
237
|
+
return if ar_parent.nil?
|
229
238
|
primary_id = ar_parent.id
|
230
239
|
|
231
240
|
parent.children.each do |node|
|
@@ -242,7 +251,11 @@ module ActiveRecord
|
|
242
251
|
|
243
252
|
key = aliases.column_alias(node, node.primary_key)
|
244
253
|
id = row[key]
|
245
|
-
|
254
|
+
if id.nil?
|
255
|
+
nil_association = ar_parent.association(node.reflection.name)
|
256
|
+
nil_association.loaded!
|
257
|
+
next
|
258
|
+
end
|
246
259
|
|
247
260
|
model = seen[parent.base_klass][primary_id][node.base_klass][id]
|
248
261
|
|
@@ -21,8 +21,11 @@ module ActiveRecord
|
|
21
21
|
super && reflection == other.reflection
|
22
22
|
end
|
23
23
|
|
24
|
+
JoinInformation = Struct.new :joins, :binds
|
25
|
+
|
24
26
|
def join_constraints(foreign_table, foreign_klass, node, join_type, tables, scope_chain, chain)
|
25
27
|
joins = []
|
28
|
+
bind_values = []
|
26
29
|
tables = tables.reverse
|
27
30
|
|
28
31
|
scope_chain_index = 0
|
@@ -34,14 +37,9 @@ module ActiveRecord
|
|
34
37
|
table = tables.shift
|
35
38
|
klass = reflection.klass
|
36
39
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
foreign_key = reflection.foreign_key
|
41
|
-
else
|
42
|
-
key = reflection.foreign_key
|
43
|
-
foreign_key = reflection.active_record_primary_key
|
44
|
-
end
|
40
|
+
join_keys = reflection.join_keys(klass)
|
41
|
+
key = join_keys.key
|
42
|
+
foreign_key = join_keys.foreign_key
|
45
43
|
|
46
44
|
constraint = build_constraint(klass, table, key, foreign_table, foreign_key)
|
47
45
|
|
@@ -54,27 +52,39 @@ module ActiveRecord
|
|
54
52
|
end
|
55
53
|
scope_chain_index += 1
|
56
54
|
|
57
|
-
|
55
|
+
klass_scope =
|
56
|
+
if klass.current_scope && klass.current_scope.values.blank?
|
57
|
+
klass.unscoped
|
58
|
+
else
|
59
|
+
klass.send(:build_default_scope, ActiveRecord::Relation.create(klass, table))
|
60
|
+
end
|
61
|
+
scope_chain_items.concat [klass_scope].compact
|
58
62
|
|
59
63
|
rel = scope_chain_items.inject(scope_chain_items.shift) do |left, right|
|
60
64
|
left.merge right
|
61
65
|
end
|
62
66
|
|
63
|
-
if reflection.type
|
64
|
-
constraint = constraint.and table[reflection.type].eq foreign_klass.base_class.name
|
65
|
-
end
|
66
|
-
|
67
67
|
if rel && !rel.arel.constraints.empty?
|
68
|
+
bind_values.concat rel.bind_values
|
68
69
|
constraint = constraint.and rel.arel.constraints
|
69
70
|
end
|
70
71
|
|
72
|
+
if reflection.type
|
73
|
+
value = foreign_klass.base_class.name
|
74
|
+
column = klass.columns_hash[reflection.type.to_s]
|
75
|
+
|
76
|
+
substitute = klass.connection.substitute_at(column)
|
77
|
+
bind_values.push [column, value]
|
78
|
+
constraint = constraint.and table[reflection.type].eq substitute
|
79
|
+
end
|
80
|
+
|
71
81
|
joins << table.create_join(table, table.create_on(constraint), join_type)
|
72
82
|
|
73
83
|
# The current table in this iteration becomes the foreign table in the next
|
74
84
|
foreign_table, foreign_klass = table, klass
|
75
85
|
end
|
76
86
|
|
77
|
-
joins
|
87
|
+
JoinInformation.new joins, bind_values
|
78
88
|
end
|
79
89
|
|
80
90
|
# Builds equality condition.
|
@@ -86,7 +96,7 @@ module ActiveRecord
|
|
86
96
|
# end
|
87
97
|
#
|
88
98
|
# If I execute `Physician.joins(:appointments).to_a` then
|
89
|
-
#
|
99
|
+
# klass # => Physician
|
90
100
|
# table # => #<Arel::Table @name="appointments" ...>
|
91
101
|
# key # => physician_id
|
92
102
|
# foreign_table # => #<Arel::Table @name="physicians" ...>
|