activerecord 4.1.16 → 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 -1801
- data/README.rdoc +15 -10
- data/lib/active_record/aggregations.rb +15 -8
- data/lib/active_record/association_relation.rb +13 -0
- 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/join_association.rb +25 -15
- data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
- data/lib/active_record/associations/join_dependency.rb +26 -13
- 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/preloader.rb +36 -26
- data/lib/active_record/associations/singular_association.rb +17 -2
- data/lib/active_record/associations/through_association.rb +5 -12
- data/lib/active_record/associations.rb +158 -49
- 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/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_methods.rb +56 -94
- data/lib/active_record/attribute_set/builder.rb +106 -0
- data/lib/active_record/attribute_set.rb +81 -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/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/oid.rb +29 -388
- 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/command_recorder.rb +19 -2
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +71 -46
- 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/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/array_handler.rb +32 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -5
- data/lib/active_record/relation/predicate_builder.rb +16 -8
- data/lib/active_record/relation/query_methods.rb +114 -65
- data/lib/active_record/relation/spawn_methods.rb +3 -0
- data/lib/active_record/relation.rb +57 -25
- 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 +20 -11
- 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/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/type.rb +23 -0
- 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/active_record/validations.rb +25 -19
- data/lib/active_record.rb +4 -0
- 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,7 +6,9 @@ module ActiveRecord
|
|
6
6
|
autoload :ArrayHandler, 'active_record/relation/predicate_builder/array_handler'
|
7
7
|
|
8
8
|
def self.resolve_column_aliases(klass, hash)
|
9
|
-
|
9
|
+
# This method is a hot spot, so for now, use Hash[] to dup the hash.
|
10
|
+
# https://bugs.ruby-lang.org/issues/7166
|
11
|
+
hash = Hash[hash]
|
10
12
|
hash.keys.grep(Symbol) do |key|
|
11
13
|
if klass.attribute_alias? key
|
12
14
|
hash[klass.attribute_alias(key)] = hash.delete key
|
@@ -26,7 +28,7 @@ module ActiveRecord
|
|
26
28
|
queries << '1=0'
|
27
29
|
else
|
28
30
|
table = Arel::Table.new(column, default_table.engine)
|
29
|
-
association = klass._reflect_on_association(column
|
31
|
+
association = klass._reflect_on_association(column)
|
30
32
|
|
31
33
|
value.each do |k, v|
|
32
34
|
queries.concat expand(association && association.klass, table, k, v)
|
@@ -55,8 +57,9 @@ module ActiveRecord
|
|
55
57
|
#
|
56
58
|
# For polymorphic relationships, find the foreign key and type:
|
57
59
|
# PriceEstimate.where(estimate_of: treasure)
|
58
|
-
if klass && reflection = klass._reflect_on_association(column
|
60
|
+
if klass && reflection = klass._reflect_on_association(column)
|
59
61
|
base_class = polymorphic_base_class_from_value(value)
|
62
|
+
|
60
63
|
if reflection.polymorphic? && base_class
|
61
64
|
queries << build(table[reflection.foreign_type], base_class)
|
62
65
|
end
|
@@ -111,16 +114,15 @@ module ActiveRecord
|
|
111
114
|
@handlers.unshift([klass, handler])
|
112
115
|
end
|
113
116
|
|
114
|
-
|
117
|
+
BASIC_OBJECT_HANDLER = ->(attribute, value) { attribute.eq(value) } # :nodoc:
|
118
|
+
register_handler(BasicObject, BASIC_OBJECT_HANDLER)
|
115
119
|
# FIXME: I think we need to deprecate this behavior
|
116
120
|
register_handler(Class, ->(attribute, value) { attribute.eq(value.name) })
|
117
121
|
register_handler(Base, ->(attribute, value) { attribute.eq(value.id) })
|
118
|
-
register_handler(Range, ->(attribute, value) { attribute.
|
122
|
+
register_handler(Range, ->(attribute, value) { attribute.between(value) })
|
119
123
|
register_handler(Relation, RelationHandler.new)
|
120
124
|
register_handler(Array, ArrayHandler.new)
|
121
125
|
|
122
|
-
private
|
123
|
-
|
124
126
|
def self.build(attribute, value)
|
125
127
|
handler_for(value).call(attribute, value)
|
126
128
|
end
|
@@ -138,10 +140,16 @@ module ActiveRecord
|
|
138
140
|
when Array
|
139
141
|
value.map { |v| convert_value_to_association_ids(v, primary_key) }
|
140
142
|
when Base
|
141
|
-
value.
|
143
|
+
value._read_attribute(primary_key)
|
142
144
|
else
|
143
145
|
value
|
144
146
|
end
|
145
147
|
end
|
148
|
+
|
149
|
+
def self.can_be_bound?(value) # :nodoc:
|
150
|
+
!value.nil? &&
|
151
|
+
!value.is_a?(Hash) &&
|
152
|
+
handler_for(value) == BASIC_OBJECT_HANDLER
|
153
|
+
end
|
146
154
|
end
|
147
155
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'active_support/core_ext/array/wrap'
|
2
|
+
require 'active_support/core_ext/string/filters'
|
2
3
|
require 'active_model/forbidden_attributes_protection'
|
3
4
|
|
4
5
|
module ActiveRecord
|
@@ -67,6 +68,7 @@ module ActiveRecord
|
|
67
68
|
#
|
68
69
|
def #{name}_values=(values) # def select_values=(values)
|
69
70
|
raise ImmutableRelation if @loaded # raise ImmutableRelation if @loaded
|
71
|
+
check_cached_relation
|
70
72
|
@values[:#{name}] = values # @values[:select] = values
|
71
73
|
end # end
|
72
74
|
CODE
|
@@ -84,11 +86,22 @@ module ActiveRecord
|
|
84
86
|
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
85
87
|
def #{name}_value=(value) # def readonly_value=(value)
|
86
88
|
raise ImmutableRelation if @loaded # raise ImmutableRelation if @loaded
|
89
|
+
check_cached_relation
|
87
90
|
@values[:#{name}] = value # @values[:readonly] = value
|
88
91
|
end # end
|
89
92
|
CODE
|
90
93
|
end
|
91
94
|
|
95
|
+
def check_cached_relation # :nodoc:
|
96
|
+
if defined?(@arel) && @arel
|
97
|
+
@arel = nil
|
98
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
99
|
+
Modifying already cached Relation. The cache will be reset. Use a
|
100
|
+
cloned Relation to prevent this warning.
|
101
|
+
MSG
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
92
105
|
def create_with_value # :nodoc:
|
93
106
|
@values[:create_with] || {}
|
94
107
|
end
|
@@ -173,7 +186,7 @@ module ActiveRecord
|
|
173
186
|
|
174
187
|
# Use to indicate that the given +table_names+ are referenced by an SQL string,
|
175
188
|
# and should therefore be JOINed in any query rather than loaded separately.
|
176
|
-
# This method only works in
|
189
|
+
# This method only works in conjunction with +includes+.
|
177
190
|
# See #includes for more details.
|
178
191
|
#
|
179
192
|
# User.includes(:posts).where("posts.name = 'foo'")
|
@@ -207,7 +220,7 @@ module ActiveRecord
|
|
207
220
|
# fields are retrieved:
|
208
221
|
#
|
209
222
|
# Model.select(:field)
|
210
|
-
# # => [#<Model field:value>]
|
223
|
+
# # => [#<Model id: nil, field: "value">]
|
211
224
|
#
|
212
225
|
# Although in the above example it looks as though this method returns an
|
213
226
|
# array, it actually returns a relation object and can have other query
|
@@ -216,12 +229,12 @@ module ActiveRecord
|
|
216
229
|
# The argument to the method can also be an array of fields.
|
217
230
|
#
|
218
231
|
# Model.select(:field, :other_field, :and_one_more)
|
219
|
-
# # => [#<Model field: "value", other_field: "value", and_one_more: "value">]
|
232
|
+
# # => [#<Model id: nil, field: "value", other_field: "value", and_one_more: "value">]
|
220
233
|
#
|
221
234
|
# You can also use one or more strings, which will be used unchanged as SELECT fields.
|
222
235
|
#
|
223
236
|
# Model.select('field AS field_one', 'other_field AS field_two')
|
224
|
-
# # => [#<Model field: "value", other_field: "value">]
|
237
|
+
# # => [#<Model id: nil, field: "value", other_field: "value">]
|
225
238
|
#
|
226
239
|
# If an alias was specified, it will be accessible from the resulting objects:
|
227
240
|
#
|
@@ -229,7 +242,7 @@ module ActiveRecord
|
|
229
242
|
# # => "value"
|
230
243
|
#
|
231
244
|
# Accessing attributes of an object that do not have fields retrieved by a select
|
232
|
-
# will throw <tt>ActiveModel::MissingAttributeError</tt>:
|
245
|
+
# except +id+ will throw <tt>ActiveModel::MissingAttributeError</tt>:
|
233
246
|
#
|
234
247
|
# Model.select(:field).first.other_field
|
235
248
|
# # => ActiveModel::MissingAttributeError: missing attribute: other_field
|
@@ -245,7 +258,7 @@ module ActiveRecord
|
|
245
258
|
def _select!(*fields) # :nodoc:
|
246
259
|
fields.flatten!
|
247
260
|
fields.map! do |field|
|
248
|
-
klass.attribute_alias?(field) ? klass.attribute_alias(field) : field
|
261
|
+
klass.attribute_alias?(field) ? klass.attribute_alias(field).to_sym : field
|
249
262
|
end
|
250
263
|
self.select_values += fields
|
251
264
|
self
|
@@ -266,6 +279,10 @@ module ActiveRecord
|
|
266
279
|
#
|
267
280
|
# User.group('name AS grouped_name, age')
|
268
281
|
# => [#<User id: 3, name: "Foo", age: 21, ...>, #<User id: 2, name: "Oscar", age: 21, ...>, #<User id: 5, name: "Foo", age: 23, ...>]
|
282
|
+
#
|
283
|
+
# Passing in an array of attributes to group by is also supported.
|
284
|
+
# User.select([:id, :first_name]).group(:id, :first_name).first(3)
|
285
|
+
# => [#<User id: 1, first_name: "Bill">, #<User id: 2, first_name: "Earl">, #<User id: 3, first_name: "Beto">]
|
269
286
|
def group(*args)
|
270
287
|
check_if_method_has_arguments!(:group, args)
|
271
288
|
spawn.group!(*args)
|
@@ -280,15 +297,6 @@ module ActiveRecord
|
|
280
297
|
|
281
298
|
# Allows to specify an order attribute:
|
282
299
|
#
|
283
|
-
# User.order('name')
|
284
|
-
# => SELECT "users".* FROM "users" ORDER BY name
|
285
|
-
#
|
286
|
-
# User.order('name DESC')
|
287
|
-
# => SELECT "users".* FROM "users" ORDER BY name DESC
|
288
|
-
#
|
289
|
-
# User.order('name DESC, email')
|
290
|
-
# => SELECT "users".* FROM "users" ORDER BY name DESC, email
|
291
|
-
#
|
292
300
|
# User.order(:name)
|
293
301
|
# => SELECT "users".* FROM "users" ORDER BY "users"."name" ASC
|
294
302
|
#
|
@@ -297,6 +305,15 @@ module ActiveRecord
|
|
297
305
|
#
|
298
306
|
# User.order(:name, email: :desc)
|
299
307
|
# => SELECT "users".* FROM "users" ORDER BY "users"."name" ASC, "users"."email" DESC
|
308
|
+
#
|
309
|
+
# User.order('name')
|
310
|
+
# => SELECT "users".* FROM "users" ORDER BY name
|
311
|
+
#
|
312
|
+
# User.order('name DESC')
|
313
|
+
# => SELECT "users".* FROM "users" ORDER BY name DESC
|
314
|
+
#
|
315
|
+
# User.order('name DESC, email')
|
316
|
+
# => SELECT "users".* FROM "users" ORDER BY name DESC, email
|
300
317
|
def order(*args)
|
301
318
|
check_if_method_has_arguments!(:order, args)
|
302
319
|
spawn.order!(*args)
|
@@ -410,19 +427,17 @@ module ActiveRecord
|
|
410
427
|
# => SELECT "users".* FROM "users" LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id
|
411
428
|
def joins(*args)
|
412
429
|
check_if_method_has_arguments!(:joins, args)
|
413
|
-
|
414
|
-
args.compact!
|
415
|
-
args.flatten!
|
416
|
-
|
417
430
|
spawn.joins!(*args)
|
418
431
|
end
|
419
432
|
|
420
433
|
def joins!(*args) # :nodoc:
|
434
|
+
args.compact!
|
435
|
+
args.flatten!
|
421
436
|
self.joins_values += args
|
422
437
|
self
|
423
438
|
end
|
424
439
|
|
425
|
-
def bind(value)
|
440
|
+
def bind(value) # :nodoc:
|
426
441
|
spawn.bind!(value)
|
427
442
|
end
|
428
443
|
|
@@ -560,18 +575,14 @@ module ActiveRecord
|
|
560
575
|
end
|
561
576
|
end
|
562
577
|
|
563
|
-
def where!(opts
|
564
|
-
if
|
565
|
-
|
566
|
-
|
567
|
-
if Hash === opts
|
568
|
-
opts = sanitize_forbidden_attributes(opts)
|
569
|
-
references!(PredicateBuilder.references(opts))
|
570
|
-
end
|
571
|
-
|
572
|
-
self.where_values += build_where(opts, rest)
|
573
|
-
self
|
578
|
+
def where!(opts, *rest) # :nodoc:
|
579
|
+
if Hash === opts
|
580
|
+
opts = sanitize_forbidden_attributes(opts)
|
581
|
+
references!(PredicateBuilder.references(opts))
|
574
582
|
end
|
583
|
+
|
584
|
+
self.where_values += build_where(opts, rest)
|
585
|
+
self
|
575
586
|
end
|
576
587
|
|
577
588
|
# Allows you to change a previously set where condition for a given attribute, instead of appending to that condition.
|
@@ -746,6 +757,9 @@ module ActiveRecord
|
|
746
757
|
|
747
758
|
def from!(value, subquery_name = nil) # :nodoc:
|
748
759
|
self.from_value = [value, subquery_name]
|
760
|
+
if value.is_a? Relation
|
761
|
+
self.bind_values = value.arel.bind_values + value.bind_values + bind_values
|
762
|
+
end
|
749
763
|
self
|
750
764
|
end
|
751
765
|
|
@@ -833,7 +847,9 @@ module ActiveRecord
|
|
833
847
|
end
|
834
848
|
|
835
849
|
def reverse_order! # :nodoc:
|
836
|
-
|
850
|
+
orders = order_values.uniq
|
851
|
+
orders.reject!(&:blank?)
|
852
|
+
self.order_values = reverse_sql_order(orders)
|
837
853
|
self
|
838
854
|
end
|
839
855
|
|
@@ -849,7 +865,7 @@ module ActiveRecord
|
|
849
865
|
|
850
866
|
build_joins(arel, joins_values.flatten) unless joins_values.empty?
|
851
867
|
|
852
|
-
collapse_wheres(arel, (where_values - [''])
|
868
|
+
collapse_wheres(arel, (where_values - [''])) #TODO: Add uniq with real value comparison / ignore uniqs that have binds
|
853
869
|
|
854
870
|
arel.having(*having_values.uniq.reject(&:blank?)) unless having_values.empty?
|
855
871
|
|
@@ -865,15 +881,6 @@ module ActiveRecord
|
|
865
881
|
arel.from(build_from) if from_value
|
866
882
|
arel.lock(lock_value) if lock_value
|
867
883
|
|
868
|
-
# Reorder bind indexes if joins produced bind values
|
869
|
-
bvs = arel.bind_values + bind_values
|
870
|
-
if bvs.any?
|
871
|
-
arel.ast.grep(Arel::Nodes::BindParam).each_with_index do |bp, i|
|
872
|
-
column = bvs[i].first
|
873
|
-
bp.replace connection.substitute_at(column, i)
|
874
|
-
end
|
875
|
-
end
|
876
|
-
|
877
884
|
arel
|
878
885
|
end
|
879
886
|
|
@@ -887,8 +894,9 @@ module ActiveRecord
|
|
887
894
|
|
888
895
|
case scope
|
889
896
|
when :order
|
890
|
-
self.reverse_order_value = false
|
891
897
|
result = []
|
898
|
+
when :where
|
899
|
+
self.bind_values = []
|
892
900
|
else
|
893
901
|
result = [] unless single_val_method
|
894
902
|
end
|
@@ -899,9 +907,9 @@ module ActiveRecord
|
|
899
907
|
def where_unscoping(target_value)
|
900
908
|
target_value = target_value.to_s
|
901
909
|
|
902
|
-
where_values.reject
|
910
|
+
self.where_values = where_values.reject do |rel|
|
903
911
|
case rel
|
904
|
-
when Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual
|
912
|
+
when Arel::Nodes::Between, Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual, Arel::Nodes::LessThan, Arel::Nodes::LessThanOrEqual, Arel::Nodes::GreaterThan, Arel::Nodes::GreaterThanOrEqual
|
905
913
|
subrelation = (rel.left.kind_of?(Arel::Attributes::Attribute) ? rel.left : rel.right)
|
906
914
|
subrelation.name == target_value
|
907
915
|
end
|
@@ -939,18 +947,14 @@ module ActiveRecord
|
|
939
947
|
def build_where(opts, other = [])
|
940
948
|
case opts
|
941
949
|
when String, Array
|
942
|
-
#TODO: Remove duplication with: /activerecord/lib/active_record/sanitization.rb:113
|
943
|
-
values = Hash === other.first ? other.first.values : other
|
944
|
-
|
945
|
-
values.grep(ActiveRecord::Relation) do |rel|
|
946
|
-
self.bind_values += rel.bind_values
|
947
|
-
end
|
948
|
-
|
949
950
|
[@klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))]
|
950
951
|
when Hash
|
951
952
|
opts = PredicateBuilder.resolve_column_aliases(klass, opts)
|
952
|
-
attributes = @klass.send(:expand_hash_conditions_for_aggregates, opts)
|
953
953
|
|
954
|
+
tmp_opts, bind_values = create_binds(opts)
|
955
|
+
self.bind_values += bind_values
|
956
|
+
|
957
|
+
attributes = @klass.send(:expand_hash_conditions_for_aggregates, tmp_opts)
|
954
958
|
add_relations_to_bind_values(attributes)
|
955
959
|
|
956
960
|
PredicateBuilder.build_from_hash(klass, attributes, table)
|
@@ -959,12 +963,50 @@ module ActiveRecord
|
|
959
963
|
end
|
960
964
|
end
|
961
965
|
|
966
|
+
def create_binds(opts)
|
967
|
+
bindable, non_binds = opts.partition do |column, value|
|
968
|
+
PredicateBuilder.can_be_bound?(value) &&
|
969
|
+
@klass.columns_hash.include?(column.to_s) &&
|
970
|
+
!@klass.reflect_on_aggregation(column)
|
971
|
+
end
|
972
|
+
|
973
|
+
association_binds, non_binds = non_binds.partition do |column, value|
|
974
|
+
value.is_a?(Hash) && association_for_table(column)
|
975
|
+
end
|
976
|
+
|
977
|
+
new_opts = {}
|
978
|
+
binds = []
|
979
|
+
|
980
|
+
connection = self.connection
|
981
|
+
|
982
|
+
bindable.each do |(column,value)|
|
983
|
+
binds.push [@klass.columns_hash[column.to_s], value]
|
984
|
+
new_opts[column] = connection.substitute_at(column)
|
985
|
+
end
|
986
|
+
|
987
|
+
association_binds.each do |(column, value)|
|
988
|
+
association_relation = association_for_table(column).klass.send(:relation)
|
989
|
+
association_new_opts, association_bind = association_relation.send(:create_binds, value)
|
990
|
+
new_opts[column] = association_new_opts
|
991
|
+
binds += association_bind
|
992
|
+
end
|
993
|
+
|
994
|
+
non_binds.each { |column,value| new_opts[column] = value }
|
995
|
+
|
996
|
+
[new_opts, binds]
|
997
|
+
end
|
998
|
+
|
999
|
+
def association_for_table(table_name)
|
1000
|
+
table_name = table_name.to_s
|
1001
|
+
@klass._reflect_on_association(table_name) ||
|
1002
|
+
@klass._reflect_on_association(table_name.singularize)
|
1003
|
+
end
|
1004
|
+
|
962
1005
|
def build_from
|
963
1006
|
opts, name = from_value
|
964
1007
|
case opts
|
965
1008
|
when Relation
|
966
1009
|
name ||= 'subquery'
|
967
|
-
self.bind_values = opts.bind_values + self.bind_values
|
968
1010
|
opts.arel.as(name.to_s)
|
969
1011
|
else
|
970
1012
|
opts
|
@@ -1000,9 +1042,12 @@ module ActiveRecord
|
|
1000
1042
|
join_list
|
1001
1043
|
)
|
1002
1044
|
|
1003
|
-
|
1045
|
+
join_infos = join_dependency.join_constraints stashed_association_joins
|
1004
1046
|
|
1005
|
-
|
1047
|
+
join_infos.each do |info|
|
1048
|
+
info.joins.each { |join| manager.from(join) }
|
1049
|
+
manager.bind_values.concat info.binds
|
1050
|
+
end
|
1006
1051
|
|
1007
1052
|
manager.join_sources.concat(join_list)
|
1008
1053
|
|
@@ -1019,8 +1064,10 @@ module ActiveRecord
|
|
1019
1064
|
|
1020
1065
|
def arel_columns(columns)
|
1021
1066
|
columns.map do |field|
|
1022
|
-
if columns_hash.key?(field.to_s)
|
1067
|
+
if (Symbol === field || String === field) && columns_hash.key?(field.to_s) && !from_value
|
1023
1068
|
arel_table[field]
|
1069
|
+
elsif Symbol === field
|
1070
|
+
connection.quote_table_name(field.to_s)
|
1024
1071
|
else
|
1025
1072
|
field
|
1026
1073
|
end
|
@@ -1052,15 +1099,19 @@ module ActiveRecord
|
|
1052
1099
|
def build_order(arel)
|
1053
1100
|
orders = order_values.uniq
|
1054
1101
|
orders.reject!(&:blank?)
|
1055
|
-
orders = reverse_sql_order(orders) if reverse_order_value
|
1056
1102
|
|
1057
1103
|
arel.order(*orders) unless orders.empty?
|
1058
1104
|
end
|
1059
1105
|
|
1106
|
+
VALID_DIRECTIONS = [:asc, :desc, :ASC, :DESC,
|
1107
|
+
'asc', 'desc', 'ASC', 'DESC'] # :nodoc:
|
1108
|
+
|
1060
1109
|
def validate_order_args(args)
|
1061
|
-
args.
|
1062
|
-
unless (
|
1063
|
-
|
1110
|
+
args.each do |arg|
|
1111
|
+
next unless arg.is_a?(Hash)
|
1112
|
+
arg.each do |_key, value|
|
1113
|
+
raise ArgumentError, "Direction \"#{value}\" is invalid. Valid " \
|
1114
|
+
"directions are: #{VALID_DIRECTIONS.inspect}" unless VALID_DIRECTIONS.include?(value)
|
1064
1115
|
end
|
1065
1116
|
end
|
1066
1117
|
end
|
@@ -1082,7 +1133,7 @@ module ActiveRecord
|
|
1082
1133
|
when Hash
|
1083
1134
|
arg.map { |field, dir|
|
1084
1135
|
field = klass.attribute_alias(field) if klass.attribute_alias?(field)
|
1085
|
-
table[field].send(dir)
|
1136
|
+
table[field].send(dir.downcase)
|
1086
1137
|
}
|
1087
1138
|
else
|
1088
1139
|
arg
|
@@ -1112,13 +1163,11 @@ module ActiveRecord
|
|
1112
1163
|
end
|
1113
1164
|
end
|
1114
1165
|
|
1115
|
-
# This function is recursive just for better readablity.
|
1116
|
-
# #where argument doesn't support more than one level nested hash in real world.
|
1117
1166
|
def add_relations_to_bind_values(attributes)
|
1118
1167
|
if attributes.is_a?(Hash)
|
1119
1168
|
attributes.each_value do |value|
|
1120
1169
|
if value.is_a?(ActiveRecord::Relation)
|
1121
|
-
self.bind_values += value.bind_values
|
1170
|
+
self.bind_values += value.arel.bind_values + value.bind_values
|
1122
1171
|
else
|
1123
1172
|
add_relations_to_bind_values(value)
|
1124
1173
|
end
|
@@ -62,6 +62,9 @@ module ActiveRecord
|
|
62
62
|
# Post.order('id asc').only(:where) # discards the order condition
|
63
63
|
# Post.order('id asc').only(:where, :order) # uses the specified order
|
64
64
|
def only(*onlies)
|
65
|
+
if onlies.any? { |o| o == :where }
|
66
|
+
onlies << :bind
|
67
|
+
end
|
65
68
|
relation_with values.slice(*onlies)
|
66
69
|
end
|
67
70
|
|
@@ -1,16 +1,16 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
|
+
require 'arel/collectors/bind'
|
2
3
|
|
3
4
|
module ActiveRecord
|
4
5
|
# = Active Record Relation
|
5
6
|
class Relation
|
6
|
-
JoinOperation = Struct.new(:relation, :join_class, :on)
|
7
|
-
|
8
7
|
MULTI_VALUE_METHODS = [:includes, :eager_load, :preload, :select, :group,
|
9
8
|
:order, :joins, :where, :having, :bind, :references,
|
10
9
|
:extending, :unscope]
|
11
10
|
|
12
11
|
SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :from, :reordering,
|
13
12
|
:reverse_order, :distinct, :create_with, :uniq]
|
13
|
+
INVALID_METHODS_FOR_DELETE_ALL = [:limit, :distinct, :offset, :group, :having]
|
14
14
|
|
15
15
|
VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS
|
16
16
|
|
@@ -79,22 +79,26 @@ module ActiveRecord
|
|
79
79
|
scope.unscope!(where: @klass.inheritance_column)
|
80
80
|
end
|
81
81
|
|
82
|
-
|
82
|
+
relation = scope.where(@klass.primary_key => (id_was || id))
|
83
|
+
bvs = binds + relation.bind_values
|
84
|
+
um = relation
|
85
|
+
.arel
|
86
|
+
.compile_update(substitutes, @klass.primary_key)
|
83
87
|
|
84
88
|
@klass.connection.update(
|
85
89
|
um,
|
86
90
|
'SQL',
|
87
|
-
|
91
|
+
bvs,
|
92
|
+
)
|
88
93
|
end
|
89
94
|
|
90
95
|
def substitute_values(values) # :nodoc:
|
91
|
-
|
92
|
-
binds = substitutes.map do |arel_attr, value|
|
96
|
+
binds = values.map do |arel_attr, value|
|
93
97
|
[@klass.columns_hash[arel_attr.name], value]
|
94
98
|
end
|
95
99
|
|
96
|
-
substitutes.each_with_index do |
|
97
|
-
|
100
|
+
substitutes = values.each_with_index.map do |(arel_attr, _), i|
|
101
|
+
[arel_attr, @klass.connection.substitute_at(binds[i][0])]
|
98
102
|
end
|
99
103
|
|
100
104
|
[substitutes, binds]
|
@@ -230,6 +234,7 @@ module ActiveRecord
|
|
230
234
|
# Please see further details in the
|
231
235
|
# {Active Record Query Interface guide}[http://guides.rubyonrails.org/active_record_querying.html#running-explain].
|
232
236
|
def explain
|
237
|
+
#TODO: Fix for binds.
|
233
238
|
exec_explain(collecting_queries_for_explain { exec_queries })
|
234
239
|
end
|
235
240
|
|
@@ -239,6 +244,11 @@ module ActiveRecord
|
|
239
244
|
@records
|
240
245
|
end
|
241
246
|
|
247
|
+
# Serializes the relation objects Array.
|
248
|
+
def encode_with(coder)
|
249
|
+
coder.represent_seq(nil, to_a)
|
250
|
+
end
|
251
|
+
|
242
252
|
def as_json(options = nil) #:nodoc:
|
243
253
|
to_a.as_json(options)
|
244
254
|
end
|
@@ -294,10 +304,11 @@ module ActiveRecord
|
|
294
304
|
klass.current_scope = previous
|
295
305
|
end
|
296
306
|
|
297
|
-
# Updates all records with details given
|
298
|
-
#
|
299
|
-
#
|
300
|
-
#
|
307
|
+
# Updates all records in the current relation with details given. This method constructs a single SQL UPDATE
|
308
|
+
# statement and sends it straight to the database. It does not instantiate the involved models and it does not
|
309
|
+
# trigger Active Record callbacks or validations. Values passed to `update_all` will not go through
|
310
|
+
# ActiveRecord's type-casting behavior. It should receive only values that can be passed as-is to the SQL
|
311
|
+
# database.
|
301
312
|
#
|
302
313
|
# ==== Parameters
|
303
314
|
#
|
@@ -330,7 +341,8 @@ module ActiveRecord
|
|
330
341
|
stmt.wheres = arel.constraints
|
331
342
|
end
|
332
343
|
|
333
|
-
|
344
|
+
bvs = arel.bind_values + bind_values
|
345
|
+
@klass.connection.update stmt, 'SQL', bvs
|
334
346
|
end
|
335
347
|
|
336
348
|
# Updates an object (or multiple objects) and saves it to the database, if validations pass.
|
@@ -434,12 +446,21 @@ module ActiveRecord
|
|
434
446
|
# If you need to destroy dependent associations or call your <tt>before_*</tt> or
|
435
447
|
# +after_destroy+ callbacks, use the +destroy_all+ method instead.
|
436
448
|
#
|
437
|
-
# If
|
449
|
+
# If an invalid method is supplied, +delete_all+ raises an ActiveRecord error:
|
438
450
|
#
|
439
451
|
# Post.limit(100).delete_all
|
440
|
-
# # => ActiveRecord::ActiveRecordError: delete_all doesn't support limit
|
452
|
+
# # => ActiveRecord::ActiveRecordError: delete_all doesn't support limit
|
441
453
|
def delete_all(conditions = nil)
|
442
|
-
|
454
|
+
invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select { |method|
|
455
|
+
if MULTI_VALUE_METHODS.include?(method)
|
456
|
+
send("#{method}_values").any?
|
457
|
+
else
|
458
|
+
send("#{method}_value")
|
459
|
+
end
|
460
|
+
}
|
461
|
+
if invalid_methods.any?
|
462
|
+
raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
|
463
|
+
end
|
443
464
|
|
444
465
|
if conditions
|
445
466
|
where(conditions).delete_all
|
@@ -453,7 +474,8 @@ module ActiveRecord
|
|
453
474
|
stmt.wheres = arel.constraints
|
454
475
|
end
|
455
476
|
|
456
|
-
|
477
|
+
bvs = arel.bind_values + bind_values
|
478
|
+
affected = @klass.connection.delete(stmt, 'SQL', bvs)
|
457
479
|
|
458
480
|
reset
|
459
481
|
affected
|
@@ -523,11 +545,11 @@ module ActiveRecord
|
|
523
545
|
find_with_associations { |rel| relation = rel }
|
524
546
|
end
|
525
547
|
|
526
|
-
|
527
|
-
binds = relation.bind_values.dup
|
528
|
-
|
529
|
-
|
530
|
-
|
548
|
+
arel = relation.arel
|
549
|
+
binds = (arel.bind_values + relation.bind_values).dup
|
550
|
+
binds.map! { |bv| connection.quote(*bv.reverse) }
|
551
|
+
collect = visitor.accept(arel.ast, Arel::Collectors::Bind.new)
|
552
|
+
collect.substitute_binds(binds).join
|
531
553
|
end
|
532
554
|
end
|
533
555
|
|
@@ -544,7 +566,13 @@ module ActiveRecord
|
|
544
566
|
|
545
567
|
Hash[equalities.map { |where|
|
546
568
|
name = where.left.name
|
547
|
-
[name, binds.fetch(name.to_s) {
|
569
|
+
[name, binds.fetch(name.to_s) {
|
570
|
+
case where.right
|
571
|
+
when Array then where.right.map(&:val)
|
572
|
+
when Arel::Nodes::Casted
|
573
|
+
where.right.val
|
574
|
+
end
|
575
|
+
}]
|
548
576
|
}]
|
549
577
|
end
|
550
578
|
|
@@ -608,11 +636,11 @@ module ActiveRecord
|
|
608
636
|
private
|
609
637
|
|
610
638
|
def exec_queries
|
611
|
-
@records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel, bind_values)
|
639
|
+
@records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel, arel.bind_values + bind_values)
|
612
640
|
|
613
641
|
preload = preload_values
|
614
642
|
preload += includes_values unless eager_loading?
|
615
|
-
preloader =
|
643
|
+
preloader = build_preloader
|
616
644
|
preload.each do |associations|
|
617
645
|
preloader.preload @records, associations
|
618
646
|
end
|
@@ -623,6 +651,10 @@ module ActiveRecord
|
|
623
651
|
@records
|
624
652
|
end
|
625
653
|
|
654
|
+
def build_preloader
|
655
|
+
ActiveRecord::Associations::Preloader.new
|
656
|
+
end
|
657
|
+
|
626
658
|
def references_eager_loaded_tables?
|
627
659
|
joined_tables = arel.join_sources.map do |join|
|
628
660
|
if join.is_a?(Arel::Nodes::StringJoin)
|