activerecord 4.1.0 → 4.2.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 +776 -1330
- data/README.rdoc +15 -10
- data/lib/active_record/aggregations.rb +12 -8
- data/lib/active_record/association_relation.rb +4 -0
- data/lib/active_record/associations/alias_tracker.rb +14 -13
- data/lib/active_record/associations/association.rb +2 -2
- data/lib/active_record/associations/association_scope.rb +83 -43
- data/lib/active_record/associations/belongs_to_association.rb +15 -5
- 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/has_and_belongs_to_many.rb +9 -6
- 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 +66 -29
- data/lib/active_record/associations/collection_proxy.rb +22 -26
- data/lib/active_record/associations/has_many_association.rb +65 -18
- data/lib/active_record/associations/has_many_through_association.rb +55 -27
- data/lib/active_record/associations/has_one_association.rb +0 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +19 -15
- data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
- data/lib/active_record/associations/join_dependency.rb +20 -12
- data/lib/active_record/associations/preloader/association.rb +34 -11
- data/lib/active_record/associations/preloader/through_association.rb +4 -3
- data/lib/active_record/associations/preloader.rb +49 -59
- data/lib/active_record/associations/singular_association.rb +25 -4
- data/lib/active_record/associations/through_association.rb +23 -14
- data/lib/active_record/associations.rb +171 -42
- data/lib/active_record/attribute.rb +149 -0
- data/lib/active_record/attribute_assignment.rb +18 -10
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
- data/lib/active_record/attribute_methods/dirty.rb +98 -44
- data/lib/active_record/attribute_methods/primary_key.rb +14 -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 +37 -147
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +34 -28
- data/lib/active_record/attribute_methods/write.rb +14 -21
- data/lib/active_record/attribute_methods.rb +67 -94
- data/lib/active_record/attribute_set/builder.rb +86 -0
- data/lib/active_record/attribute_set.rb +77 -0
- data/lib/active_record/attributes.rb +139 -0
- data/lib/active_record/autosave_association.rb +45 -38
- data/lib/active_record/base.rb +10 -20
- data/lib/active_record/callbacks.rb +7 -7
- data/lib/active_record/coders/json.rb +13 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +78 -52
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +38 -59
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -55
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +46 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +126 -54
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +198 -64
- data/lib/active_record/connection_adapters/abstract/transaction.rb +126 -114
- data/lib/active_record/connection_adapters/abstract_adapter.rb +154 -55
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +240 -135
- data/lib/active_record/connection_adapters/column.rb +28 -239
- data/lib/active_record/connection_adapters/connection_specification.rb +16 -25
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -22
- data/lib/active_record/connection_adapters/mysql_adapter.rb +65 -149
- 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 +39 -27
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -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 +14 -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 +27 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -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 +15 -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 +97 -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 -374
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +55 -135
- 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 +127 -38
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +220 -466
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +66 -61
- data/lib/active_record/connection_handling.rb +3 -3
- data/lib/active_record/core.rb +143 -32
- data/lib/active_record/counter_cache.rb +60 -7
- data/lib/active_record/enum.rb +10 -11
- data/lib/active_record/errors.rb +49 -27
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixtures.rb +56 -70
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +35 -10
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/locking/optimistic.rb +35 -17
- data/lib/active_record/log_subscriber.rb +1 -1
- 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 +52 -49
- data/lib/active_record/model_schema.rb +49 -57
- data/lib/active_record/nested_attributes.rb +7 -7
- data/lib/active_record/null_relation.rb +19 -5
- data/lib/active_record/persistence.rb +50 -31
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +10 -7
- data/lib/active_record/railtie.rb +14 -11
- data/lib/active_record/railties/databases.rake +56 -54
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +286 -102
- data/lib/active_record/relation/batches.rb +0 -1
- data/lib/active_record/relation/calculations.rb +39 -31
- data/lib/active_record/relation/delegation.rb +2 -2
- data/lib/active_record/relation/finder_methods.rb +80 -36
- data/lib/active_record/relation/merger.rb +25 -30
- data/lib/active_record/relation/predicate_builder/array_handler.rb +31 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +11 -10
- data/lib/active_record/relation/query_methods.rb +141 -55
- data/lib/active_record/relation/spawn_methods.rb +3 -0
- data/lib/active_record/relation.rb +69 -30
- 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 +58 -26
- data/lib/active_record/schema_migration.rb +11 -0
- data/lib/active_record/scoping/default.rb +8 -7
- 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 +19 -10
- data/lib/active_record/tasks/database_tasks.rb +73 -7
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -2
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
- data/lib/active_record/timestamp.rb +11 -9
- data/lib/active_record/transactions.rb +37 -21
- 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 +30 -0
- data/lib/active_record/type/date.rb +46 -0
- data/lib/active_record/type/date_time.rb +43 -0
- data/lib/active_record/type/decimal.rb +40 -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 +17 -0
- data/lib/active_record/type/integer.rb +55 -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 +56 -0
- data/lib/active_record/type/string.rb +36 -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 +101 -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 +6 -4
- data/lib/active_record/validations/uniqueness.rb +11 -17
- data/lib/active_record/validations.rb +25 -19
- data/lib/active_record.rb +3 -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 +4 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb +6 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
- metadata +65 -10
- data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -26,7 +26,7 @@ module ActiveRecord
|
|
26
26
|
queries << '1=0'
|
27
27
|
else
|
28
28
|
table = Arel::Table.new(column, default_table.engine)
|
29
|
-
association = klass.
|
29
|
+
association = klass._reflect_on_association(column)
|
30
30
|
|
31
31
|
value.each do |k, v|
|
32
32
|
queries.concat expand(association && association.klass, table, k, v)
|
@@ -55,7 +55,7 @@ module ActiveRecord
|
|
55
55
|
#
|
56
56
|
# For polymorphic relationships, find the foreign key and type:
|
57
57
|
# PriceEstimate.where(estimate_of: treasure)
|
58
|
-
if klass && reflection = klass.
|
58
|
+
if klass && reflection = klass._reflect_on_association(column)
|
59
59
|
if reflection.polymorphic? && base_class = polymorphic_base_class_from_value(value)
|
60
60
|
queries << build(table[reflection.foreign_type], base_class)
|
61
61
|
end
|
@@ -109,17 +109,18 @@ module ActiveRecord
|
|
109
109
|
# FIXME: I think we need to deprecate this behavior
|
110
110
|
register_handler(Class, ->(attribute, value) { attribute.eq(value.name) })
|
111
111
|
register_handler(Base, ->(attribute, value) { attribute.eq(value.id) })
|
112
|
-
register_handler(Range, ->(attribute, value) { attribute.
|
112
|
+
register_handler(Range, ->(attribute, value) { attribute.between(value) })
|
113
113
|
register_handler(Relation, RelationHandler.new)
|
114
114
|
register_handler(Array, ArrayHandler.new)
|
115
115
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
116
|
+
def self.build(attribute, value)
|
117
|
+
handler_for(value).call(attribute, value)
|
118
|
+
end
|
119
|
+
private_class_method :build
|
120
120
|
|
121
|
-
|
122
|
-
|
123
|
-
|
121
|
+
def self.handler_for(object)
|
122
|
+
@handlers.detect { |klass, _| klass === object }.last
|
123
|
+
end
|
124
|
+
private_class_method :handler_for
|
124
125
|
end
|
125
126
|
end
|
@@ -1,9 +1,13 @@
|
|
1
1
|
require 'active_support/core_ext/array/wrap'
|
2
|
+
require 'active_support/core_ext/string/filters'
|
3
|
+
require 'active_model/forbidden_attributes_protection'
|
2
4
|
|
3
5
|
module ActiveRecord
|
4
6
|
module QueryMethods
|
5
7
|
extend ActiveSupport::Concern
|
6
8
|
|
9
|
+
include ActiveModel::ForbiddenAttributesProtection
|
10
|
+
|
7
11
|
# WhereChain objects act as placeholder for queries in which #where does not have any parameter.
|
8
12
|
# In this case, #where must be chained with #not to return a new relation.
|
9
13
|
class WhereChain
|
@@ -64,6 +68,7 @@ module ActiveRecord
|
|
64
68
|
#
|
65
69
|
def #{name}_values=(values) # def select_values=(values)
|
66
70
|
raise ImmutableRelation if @loaded # raise ImmutableRelation if @loaded
|
71
|
+
check_cached_relation
|
67
72
|
@values[:#{name}] = values # @values[:select] = values
|
68
73
|
end # end
|
69
74
|
CODE
|
@@ -81,11 +86,22 @@ module ActiveRecord
|
|
81
86
|
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
82
87
|
def #{name}_value=(value) # def readonly_value=(value)
|
83
88
|
raise ImmutableRelation if @loaded # raise ImmutableRelation if @loaded
|
89
|
+
check_cached_relation
|
84
90
|
@values[:#{name}] = value # @values[:readonly] = value
|
85
91
|
end # end
|
86
92
|
CODE
|
87
93
|
end
|
88
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
|
+
|
89
105
|
def create_with_value # :nodoc:
|
90
106
|
@values[:create_with] || {}
|
91
107
|
end
|
@@ -170,7 +186,7 @@ module ActiveRecord
|
|
170
186
|
|
171
187
|
# Use to indicate that the given +table_names+ are referenced by an SQL string,
|
172
188
|
# and should therefore be JOINed in any query rather than loaded separately.
|
173
|
-
# This method only works in
|
189
|
+
# This method only works in conjunction with +includes+.
|
174
190
|
# See #includes for more details.
|
175
191
|
#
|
176
192
|
# User.includes(:posts).where("posts.name = 'foo'")
|
@@ -204,7 +220,7 @@ module ActiveRecord
|
|
204
220
|
# fields are retrieved:
|
205
221
|
#
|
206
222
|
# Model.select(:field)
|
207
|
-
# # => [#<Model field:value>]
|
223
|
+
# # => [#<Model id: nil, field: "value">]
|
208
224
|
#
|
209
225
|
# Although in the above example it looks as though this method returns an
|
210
226
|
# array, it actually returns a relation object and can have other query
|
@@ -213,12 +229,12 @@ module ActiveRecord
|
|
213
229
|
# The argument to the method can also be an array of fields.
|
214
230
|
#
|
215
231
|
# Model.select(:field, :other_field, :and_one_more)
|
216
|
-
# # => [#<Model field: "value", other_field: "value", and_one_more: "value">]
|
232
|
+
# # => [#<Model id: nil, field: "value", other_field: "value", and_one_more: "value">]
|
217
233
|
#
|
218
234
|
# You can also use one or more strings, which will be used unchanged as SELECT fields.
|
219
235
|
#
|
220
236
|
# Model.select('field AS field_one', 'other_field AS field_two')
|
221
|
-
# # => [#<Model field: "value", other_field: "value">]
|
237
|
+
# # => [#<Model id: nil, field: "value", other_field: "value">]
|
222
238
|
#
|
223
239
|
# If an alias was specified, it will be accessible from the resulting objects:
|
224
240
|
#
|
@@ -226,7 +242,7 @@ module ActiveRecord
|
|
226
242
|
# # => "value"
|
227
243
|
#
|
228
244
|
# Accessing attributes of an object that do not have fields retrieved by a select
|
229
|
-
# will throw <tt>ActiveModel::MissingAttributeError</tt>:
|
245
|
+
# except +id+ will throw <tt>ActiveModel::MissingAttributeError</tt>:
|
230
246
|
#
|
231
247
|
# Model.select(:field).first.other_field
|
232
248
|
# # => ActiveModel::MissingAttributeError: missing attribute: other_field
|
@@ -235,11 +251,11 @@ module ActiveRecord
|
|
235
251
|
to_a.select { |*block_args| yield(*block_args) }
|
236
252
|
else
|
237
253
|
raise ArgumentError, 'Call this with at least one field' if fields.empty?
|
238
|
-
spawn.
|
254
|
+
spawn._select!(*fields)
|
239
255
|
end
|
240
256
|
end
|
241
257
|
|
242
|
-
def
|
258
|
+
def _select!(*fields) # :nodoc:
|
243
259
|
fields.flatten!
|
244
260
|
fields.map! do |field|
|
245
261
|
klass.attribute_alias?(field) ? klass.attribute_alias(field) : field
|
@@ -263,6 +279,10 @@ module ActiveRecord
|
|
263
279
|
#
|
264
280
|
# User.group('name AS grouped_name, age')
|
265
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">]
|
266
286
|
def group(*args)
|
267
287
|
check_if_method_has_arguments!(:group, args)
|
268
288
|
spawn.group!(*args)
|
@@ -277,15 +297,6 @@ module ActiveRecord
|
|
277
297
|
|
278
298
|
# Allows to specify an order attribute:
|
279
299
|
#
|
280
|
-
# User.order('name')
|
281
|
-
# => SELECT "users".* FROM "users" ORDER BY name
|
282
|
-
#
|
283
|
-
# User.order('name DESC')
|
284
|
-
# => SELECT "users".* FROM "users" ORDER BY name DESC
|
285
|
-
#
|
286
|
-
# User.order('name DESC, email')
|
287
|
-
# => SELECT "users".* FROM "users" ORDER BY name DESC, email
|
288
|
-
#
|
289
300
|
# User.order(:name)
|
290
301
|
# => SELECT "users".* FROM "users" ORDER BY "users"."name" ASC
|
291
302
|
#
|
@@ -294,6 +305,15 @@ module ActiveRecord
|
|
294
305
|
#
|
295
306
|
# User.order(:name, email: :desc)
|
296
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
|
297
317
|
def order(*args)
|
298
318
|
check_if_method_has_arguments!(:order, args)
|
299
319
|
spawn.order!(*args)
|
@@ -407,19 +427,17 @@ module ActiveRecord
|
|
407
427
|
# => SELECT "users".* FROM "users" LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id
|
408
428
|
def joins(*args)
|
409
429
|
check_if_method_has_arguments!(:joins, args)
|
410
|
-
|
411
|
-
args.compact!
|
412
|
-
args.flatten!
|
413
|
-
|
414
430
|
spawn.joins!(*args)
|
415
431
|
end
|
416
432
|
|
417
433
|
def joins!(*args) # :nodoc:
|
434
|
+
args.compact!
|
435
|
+
args.flatten!
|
418
436
|
self.joins_values += args
|
419
437
|
self
|
420
438
|
end
|
421
439
|
|
422
|
-
def bind(value)
|
440
|
+
def bind(value) # :nodoc:
|
423
441
|
spawn.bind!(value)
|
424
442
|
end
|
425
443
|
|
@@ -557,15 +575,14 @@ module ActiveRecord
|
|
557
575
|
end
|
558
576
|
end
|
559
577
|
|
560
|
-
def where!(opts
|
561
|
-
if
|
562
|
-
|
563
|
-
|
564
|
-
references!(PredicateBuilder.references(opts)) if Hash === opts
|
565
|
-
|
566
|
-
self.where_values += build_where(opts, rest)
|
567
|
-
self
|
578
|
+
def where!(opts, *rest) # :nodoc:
|
579
|
+
if Hash === opts
|
580
|
+
opts = sanitize_forbidden_attributes(opts)
|
581
|
+
references!(PredicateBuilder.references(opts))
|
568
582
|
end
|
583
|
+
|
584
|
+
self.where_values += build_where(opts, rest)
|
585
|
+
self
|
569
586
|
end
|
570
587
|
|
571
588
|
# Allows you to change a previously set where condition for a given attribute, instead of appending to that condition.
|
@@ -671,11 +688,11 @@ module ActiveRecord
|
|
671
688
|
# end
|
672
689
|
#
|
673
690
|
def none
|
674
|
-
extending(NullRelation)
|
691
|
+
where("1=0").extending!(NullRelation)
|
675
692
|
end
|
676
693
|
|
677
694
|
def none! # :nodoc:
|
678
|
-
extending!(NullRelation)
|
695
|
+
where!("1=0").extending!(NullRelation)
|
679
696
|
end
|
680
697
|
|
681
698
|
# Sets readonly attributes for the returned relation. If value is
|
@@ -711,7 +728,13 @@ module ActiveRecord
|
|
711
728
|
end
|
712
729
|
|
713
730
|
def create_with!(value) # :nodoc:
|
714
|
-
|
731
|
+
if value
|
732
|
+
value = sanitize_forbidden_attributes(value)
|
733
|
+
self.create_with_value = create_with_value.merge(value)
|
734
|
+
else
|
735
|
+
self.create_with_value = {}
|
736
|
+
end
|
737
|
+
|
715
738
|
self
|
716
739
|
end
|
717
740
|
|
@@ -821,7 +844,9 @@ module ActiveRecord
|
|
821
844
|
end
|
822
845
|
|
823
846
|
def reverse_order! # :nodoc:
|
824
|
-
|
847
|
+
orders = order_values.uniq
|
848
|
+
orders.reject!(&:blank?)
|
849
|
+
self.order_values = reverse_sql_order(orders)
|
825
850
|
self
|
826
851
|
end
|
827
852
|
|
@@ -837,7 +862,7 @@ module ActiveRecord
|
|
837
862
|
|
838
863
|
build_joins(arel, joins_values.flatten) unless joins_values.empty?
|
839
864
|
|
840
|
-
collapse_wheres(arel, (where_values - [''])
|
865
|
+
collapse_wheres(arel, (where_values - [''])) #TODO: Add uniq with real value comparison / ignore uniqs that have binds
|
841
866
|
|
842
867
|
arel.having(*having_values.uniq.reject(&:blank?)) unless having_values.empty?
|
843
868
|
|
@@ -867,8 +892,9 @@ module ActiveRecord
|
|
867
892
|
|
868
893
|
case scope
|
869
894
|
when :order
|
870
|
-
self.reverse_order_value = false
|
871
895
|
result = []
|
896
|
+
when :where
|
897
|
+
self.bind_values = []
|
872
898
|
else
|
873
899
|
result = [] unless single_val_method
|
874
900
|
end
|
@@ -881,7 +907,7 @@ module ActiveRecord
|
|
881
907
|
|
882
908
|
where_values.reject! do |rel|
|
883
909
|
case rel
|
884
|
-
when Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual
|
910
|
+
when Arel::Nodes::Between, Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual, Arel::Nodes::LessThanOrEqual, Arel::Nodes::GreaterThanOrEqual
|
885
911
|
subrelation = (rel.left.kind_of?(Arel::Attributes::Attribute) ? rel.left : rel.right)
|
886
912
|
subrelation.name == target_value
|
887
913
|
end
|
@@ -919,21 +945,15 @@ module ActiveRecord
|
|
919
945
|
def build_where(opts, other = [])
|
920
946
|
case opts
|
921
947
|
when String, Array
|
922
|
-
#TODO: Remove duplication with: /activerecord/lib/active_record/sanitization.rb:113
|
923
|
-
values = Hash === other.first ? other.first.values : other
|
924
|
-
|
925
|
-
values.grep(ActiveRecord::Relation) do |rel|
|
926
|
-
self.bind_values += rel.bind_values
|
927
|
-
end
|
928
|
-
|
929
948
|
[@klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))]
|
930
949
|
when Hash
|
931
950
|
opts = PredicateBuilder.resolve_column_aliases(klass, opts)
|
932
|
-
attributes = @klass.send(:expand_hash_conditions_for_aggregates, opts)
|
933
951
|
|
934
|
-
|
935
|
-
|
936
|
-
|
952
|
+
tmp_opts, bind_values = create_binds(opts)
|
953
|
+
self.bind_values += bind_values
|
954
|
+
|
955
|
+
attributes = @klass.send(:expand_hash_conditions_for_aggregates, tmp_opts)
|
956
|
+
add_relations_to_bind_values(attributes)
|
937
957
|
|
938
958
|
PredicateBuilder.build_from_hash(klass, attributes, table)
|
939
959
|
else
|
@@ -941,6 +961,46 @@ module ActiveRecord
|
|
941
961
|
end
|
942
962
|
end
|
943
963
|
|
964
|
+
def create_binds(opts)
|
965
|
+
bindable, non_binds = opts.partition do |column, value|
|
966
|
+
case value
|
967
|
+
when String, Integer, ActiveRecord::StatementCache::Substitute
|
968
|
+
@klass.columns_hash.include? column.to_s
|
969
|
+
else
|
970
|
+
false
|
971
|
+
end
|
972
|
+
end
|
973
|
+
|
974
|
+
association_binds, non_binds = non_binds.partition do |column, value|
|
975
|
+
value.is_a?(Hash) && association_for_table(column)
|
976
|
+
end
|
977
|
+
|
978
|
+
new_opts = {}
|
979
|
+
binds = []
|
980
|
+
|
981
|
+
bindable.each do |(column,value)|
|
982
|
+
binds.push [@klass.columns_hash[column.to_s], value]
|
983
|
+
new_opts[column] = connection.substitute_at(column)
|
984
|
+
end
|
985
|
+
|
986
|
+
association_binds.each do |(column, value)|
|
987
|
+
association_relation = association_for_table(column).klass.send(:relation)
|
988
|
+
association_new_opts, association_bind = association_relation.send(:create_binds, value)
|
989
|
+
new_opts[column] = association_new_opts
|
990
|
+
binds += association_bind
|
991
|
+
end
|
992
|
+
|
993
|
+
non_binds.each { |column,value| new_opts[column] = value }
|
994
|
+
|
995
|
+
[new_opts, binds]
|
996
|
+
end
|
997
|
+
|
998
|
+
def association_for_table(table_name)
|
999
|
+
table_name = table_name.to_s
|
1000
|
+
@klass._reflect_on_association(table_name) ||
|
1001
|
+
@klass._reflect_on_association(table_name.singularize)
|
1002
|
+
end
|
1003
|
+
|
944
1004
|
def build_from
|
945
1005
|
opts, name = from_value
|
946
1006
|
case opts
|
@@ -982,9 +1042,12 @@ module ActiveRecord
|
|
982
1042
|
join_list
|
983
1043
|
)
|
984
1044
|
|
985
|
-
|
1045
|
+
join_infos = join_dependency.join_constraints stashed_association_joins
|
986
1046
|
|
987
|
-
|
1047
|
+
join_infos.each do |info|
|
1048
|
+
info.joins.each { |join| manager.from(join) }
|
1049
|
+
manager.bind_values.concat info.binds
|
1050
|
+
end
|
988
1051
|
|
989
1052
|
manager.join_sources.concat(join_list)
|
990
1053
|
|
@@ -994,8 +1057,13 @@ module ActiveRecord
|
|
994
1057
|
def build_select(arel, selects)
|
995
1058
|
if !selects.empty?
|
996
1059
|
expanded_select = selects.map do |field|
|
997
|
-
columns_hash.key?(field.to_s)
|
1060
|
+
if (Symbol === field || String === field) && columns_hash.key?(field.to_s)
|
1061
|
+
arel_table[field]
|
1062
|
+
else
|
1063
|
+
field
|
1064
|
+
end
|
998
1065
|
end
|
1066
|
+
|
999
1067
|
arel.project(*expanded_select)
|
1000
1068
|
else
|
1001
1069
|
arel.project(@klass.arel_table[Arel.star])
|
@@ -1027,15 +1095,19 @@ module ActiveRecord
|
|
1027
1095
|
def build_order(arel)
|
1028
1096
|
orders = order_values.uniq
|
1029
1097
|
orders.reject!(&:blank?)
|
1030
|
-
orders = reverse_sql_order(orders) if reverse_order_value
|
1031
1098
|
|
1032
1099
|
arel.order(*orders) unless orders.empty?
|
1033
1100
|
end
|
1034
1101
|
|
1102
|
+
VALID_DIRECTIONS = [:asc, :desc, :ASC, :DESC,
|
1103
|
+
'asc', 'desc', 'ASC', 'DESC'] # :nodoc:
|
1104
|
+
|
1035
1105
|
def validate_order_args(args)
|
1036
|
-
args.
|
1037
|
-
unless (
|
1038
|
-
|
1106
|
+
args.each do |arg|
|
1107
|
+
next unless arg.is_a?(Hash)
|
1108
|
+
arg.each do |_key, value|
|
1109
|
+
raise ArgumentError, "Direction \"#{value}\" is invalid. Valid " \
|
1110
|
+
"directions are: #{VALID_DIRECTIONS.inspect}" unless VALID_DIRECTIONS.include?(value)
|
1039
1111
|
end
|
1040
1112
|
end
|
1041
1113
|
end
|
@@ -1057,7 +1129,7 @@ module ActiveRecord
|
|
1057
1129
|
when Hash
|
1058
1130
|
arg.map { |field, dir|
|
1059
1131
|
field = klass.attribute_alias(field) if klass.attribute_alias?(field)
|
1060
|
-
table[field].send(dir)
|
1132
|
+
table[field].send(dir.downcase)
|
1061
1133
|
}
|
1062
1134
|
else
|
1063
1135
|
arg
|
@@ -1086,5 +1158,19 @@ module ActiveRecord
|
|
1086
1158
|
raise ArgumentError, "The method .#{method_name}() must contain arguments."
|
1087
1159
|
end
|
1088
1160
|
end
|
1161
|
+
|
1162
|
+
# This function is recursive just for better readablity.
|
1163
|
+
# #where argument doesn't support more than one level nested hash in real world.
|
1164
|
+
def add_relations_to_bind_values(attributes)
|
1165
|
+
if attributes.is_a?(Hash)
|
1166
|
+
attributes.each_value do |value|
|
1167
|
+
if value.is_a?(ActiveRecord::Relation)
|
1168
|
+
self.bind_values += value.bind_values
|
1169
|
+
else
|
1170
|
+
add_relations_to_bind_values(value)
|
1171
|
+
end
|
1172
|
+
end
|
1173
|
+
end
|
1174
|
+
end
|
1089
1175
|
end
|
1090
1176
|
end
|
@@ -58,6 +58,9 @@ module ActiveRecord
|
|
58
58
|
# Post.order('id asc').only(:where) # discards the order condition
|
59
59
|
# Post.order('id asc').only(:where, :order) # uses the specified order
|
60
60
|
def only(*onlies)
|
61
|
+
if onlies.any? { |o| o == :where }
|
62
|
+
onlies << :bind
|
63
|
+
end
|
61
64
|
relation_with values.slice(*onlies)
|
62
65
|
end
|
63
66
|
|
@@ -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
|
|
@@ -70,24 +70,35 @@ module ActiveRecord
|
|
70
70
|
binds)
|
71
71
|
end
|
72
72
|
|
73
|
-
def
|
73
|
+
def _update_record(values, id, id_was) # :nodoc:
|
74
74
|
substitutes, binds = substitute_values values
|
75
|
-
|
75
|
+
|
76
|
+
scope = @klass.unscoped
|
77
|
+
|
78
|
+
if @klass.finder_needs_type_condition?
|
79
|
+
scope.unscope!(where: @klass.inheritance_column)
|
80
|
+
end
|
81
|
+
|
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)
|
76
87
|
|
77
88
|
@klass.connection.update(
|
78
89
|
um,
|
79
90
|
'SQL',
|
80
|
-
|
91
|
+
bvs,
|
92
|
+
)
|
81
93
|
end
|
82
94
|
|
83
95
|
def substitute_values(values) # :nodoc:
|
84
|
-
|
85
|
-
binds = substitutes.map do |arel_attr, value|
|
96
|
+
binds = values.map do |arel_attr, value|
|
86
97
|
[@klass.columns_hash[arel_attr.name], value]
|
87
98
|
end
|
88
99
|
|
89
|
-
substitutes.each_with_index do |
|
90
|
-
|
100
|
+
substitutes = values.each_with_index.map do |(arel_attr, _), i|
|
101
|
+
[arel_attr, @klass.connection.substitute_at(binds[i][0])]
|
91
102
|
end
|
92
103
|
|
93
104
|
[substitutes, binds]
|
@@ -223,6 +234,7 @@ module ActiveRecord
|
|
223
234
|
# Please see further details in the
|
224
235
|
# {Active Record Query Interface guide}[http://guides.rubyonrails.org/active_record_querying.html#running-explain].
|
225
236
|
def explain
|
237
|
+
#TODO: Fix for binds.
|
226
238
|
exec_explain(collecting_queries_for_explain { exec_queries })
|
227
239
|
end
|
228
240
|
|
@@ -232,13 +244,18 @@ module ActiveRecord
|
|
232
244
|
@records
|
233
245
|
end
|
234
246
|
|
247
|
+
# Serializes the relation objects Array.
|
248
|
+
def encode_with(coder)
|
249
|
+
coder.represent_seq(nil, to_a)
|
250
|
+
end
|
251
|
+
|
235
252
|
def as_json(options = nil) #:nodoc:
|
236
253
|
to_a.as_json(options)
|
237
254
|
end
|
238
255
|
|
239
256
|
# Returns size of the records.
|
240
257
|
def size
|
241
|
-
loaded? ? @records.length : count
|
258
|
+
loaded? ? @records.length : count(:all)
|
242
259
|
end
|
243
260
|
|
244
261
|
# Returns true if there are no records.
|
@@ -248,8 +265,7 @@ module ActiveRecord
|
|
248
265
|
if limit_value == 0
|
249
266
|
true
|
250
267
|
else
|
251
|
-
|
252
|
-
c = count
|
268
|
+
c = count(:all)
|
253
269
|
c.respond_to?(:zero?) ? c.zero? : c.empty?
|
254
270
|
end
|
255
271
|
end
|
@@ -288,10 +304,11 @@ module ActiveRecord
|
|
288
304
|
klass.current_scope = previous
|
289
305
|
end
|
290
306
|
|
291
|
-
# Updates all records with details given
|
292
|
-
#
|
293
|
-
#
|
294
|
-
#
|
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.
|
295
312
|
#
|
296
313
|
# ==== Parameters
|
297
314
|
#
|
@@ -324,7 +341,8 @@ module ActiveRecord
|
|
324
341
|
stmt.wheres = arel.constraints
|
325
342
|
end
|
326
343
|
|
327
|
-
|
344
|
+
bvs = arel.bind_values + bind_values
|
345
|
+
@klass.connection.update stmt, 'SQL', bvs
|
328
346
|
end
|
329
347
|
|
330
348
|
# Updates an object (or multiple objects) and saves it to the database, if validations pass.
|
@@ -428,12 +446,21 @@ module ActiveRecord
|
|
428
446
|
# If you need to destroy dependent associations or call your <tt>before_*</tt> or
|
429
447
|
# +after_destroy+ callbacks, use the +destroy_all+ method instead.
|
430
448
|
#
|
431
|
-
# If
|
449
|
+
# If an invalid method is supplied, +delete_all+ raises an ActiveRecord error:
|
432
450
|
#
|
433
451
|
# Post.limit(100).delete_all
|
434
|
-
# # => ActiveRecord::ActiveRecordError: delete_all doesn't support limit
|
452
|
+
# # => ActiveRecord::ActiveRecordError: delete_all doesn't support limit
|
435
453
|
def delete_all(conditions = nil)
|
436
|
-
|
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
|
437
464
|
|
438
465
|
if conditions
|
439
466
|
where(conditions).delete_all
|
@@ -517,11 +544,11 @@ module ActiveRecord
|
|
517
544
|
find_with_associations { |rel| relation = rel }
|
518
545
|
end
|
519
546
|
|
520
|
-
|
521
|
-
binds = relation.bind_values.dup
|
522
|
-
|
523
|
-
|
524
|
-
|
547
|
+
arel = relation.arel
|
548
|
+
binds = (arel.bind_values + relation.bind_values).dup
|
549
|
+
binds.map! { |bv| connection.quote(*bv.reverse) }
|
550
|
+
collect = visitor.accept(arel.ast, Arel::Collectors::Bind.new)
|
551
|
+
collect.substitute_binds(binds).join
|
525
552
|
end
|
526
553
|
end
|
527
554
|
|
@@ -529,16 +556,22 @@ module ActiveRecord
|
|
529
556
|
#
|
530
557
|
# User.where(name: 'Oscar').where_values_hash
|
531
558
|
# # => {name: "Oscar"}
|
532
|
-
def where_values_hash
|
559
|
+
def where_values_hash(relation_table_name = table_name)
|
533
560
|
equalities = where_values.grep(Arel::Nodes::Equality).find_all { |node|
|
534
|
-
node.left.relation.name ==
|
561
|
+
node.left.relation.name == relation_table_name
|
535
562
|
}
|
536
563
|
|
537
564
|
binds = Hash[bind_values.find_all(&:first).map { |column, v| [column.name, v] }]
|
538
565
|
|
539
566
|
Hash[equalities.map { |where|
|
540
567
|
name = where.left.name
|
541
|
-
[name, binds.fetch(name.to_s) {
|
568
|
+
[name, binds.fetch(name.to_s) {
|
569
|
+
case where.right
|
570
|
+
when Array then where.right.map(&:val)
|
571
|
+
else
|
572
|
+
where.right.val
|
573
|
+
end
|
574
|
+
}]
|
542
575
|
}]
|
543
576
|
end
|
544
577
|
|
@@ -570,6 +603,8 @@ module ActiveRecord
|
|
570
603
|
# Compares two relations for equality.
|
571
604
|
def ==(other)
|
572
605
|
case other
|
606
|
+
when Associations::CollectionProxy, AssociationRelation
|
607
|
+
self == other.to_a
|
573
608
|
when Relation
|
574
609
|
other.to_sql == to_sql
|
575
610
|
when Array
|
@@ -600,11 +635,11 @@ module ActiveRecord
|
|
600
635
|
private
|
601
636
|
|
602
637
|
def exec_queries
|
603
|
-
@records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel, bind_values)
|
638
|
+
@records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel, arel.bind_values + bind_values)
|
604
639
|
|
605
640
|
preload = preload_values
|
606
641
|
preload += includes_values unless eager_loading?
|
607
|
-
preloader =
|
642
|
+
preloader = build_preloader
|
608
643
|
preload.each do |associations|
|
609
644
|
preloader.preload @records, associations
|
610
645
|
end
|
@@ -615,6 +650,10 @@ module ActiveRecord
|
|
615
650
|
@records
|
616
651
|
end
|
617
652
|
|
653
|
+
def build_preloader
|
654
|
+
ActiveRecord::Associations::Preloader.new
|
655
|
+
end
|
656
|
+
|
618
657
|
def references_eager_loaded_tables?
|
619
658
|
joined_tables = arel.join_sources.map do |join|
|
620
659
|
if join.is_a?(Arel::Nodes::StringJoin)
|