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.

Files changed (185) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1162 -1801
  3. data/README.rdoc +15 -10
  4. data/lib/active_record/aggregations.rb +15 -8
  5. data/lib/active_record/association_relation.rb +13 -0
  6. data/lib/active_record/associations/alias_tracker.rb +3 -12
  7. data/lib/active_record/associations/association.rb +16 -4
  8. data/lib/active_record/associations/association_scope.rb +83 -38
  9. data/lib/active_record/associations/belongs_to_association.rb +28 -10
  10. data/lib/active_record/associations/builder/association.rb +15 -4
  11. data/lib/active_record/associations/builder/belongs_to.rb +7 -29
  12. data/lib/active_record/associations/builder/collection_association.rb +5 -1
  13. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +8 -13
  14. data/lib/active_record/associations/builder/has_many.rb +1 -1
  15. data/lib/active_record/associations/builder/has_one.rb +2 -2
  16. data/lib/active_record/associations/builder/singular_association.rb +8 -1
  17. data/lib/active_record/associations/collection_association.rb +63 -27
  18. data/lib/active_record/associations/collection_proxy.rb +29 -35
  19. data/lib/active_record/associations/foreign_association.rb +11 -0
  20. data/lib/active_record/associations/has_many_association.rb +83 -22
  21. data/lib/active_record/associations/has_many_through_association.rb +49 -26
  22. data/lib/active_record/associations/has_one_association.rb +1 -1
  23. data/lib/active_record/associations/join_dependency/join_association.rb +25 -15
  24. data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
  25. data/lib/active_record/associations/join_dependency.rb +26 -13
  26. data/lib/active_record/associations/preloader/association.rb +14 -11
  27. data/lib/active_record/associations/preloader/through_association.rb +4 -3
  28. data/lib/active_record/associations/preloader.rb +36 -26
  29. data/lib/active_record/associations/singular_association.rb +17 -2
  30. data/lib/active_record/associations/through_association.rb +5 -12
  31. data/lib/active_record/associations.rb +158 -49
  32. data/lib/active_record/attribute.rb +163 -0
  33. data/lib/active_record/attribute_assignment.rb +19 -11
  34. data/lib/active_record/attribute_decorators.rb +66 -0
  35. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  36. data/lib/active_record/attribute_methods/dirty.rb +107 -43
  37. data/lib/active_record/attribute_methods/primary_key.rb +7 -8
  38. data/lib/active_record/attribute_methods/query.rb +1 -1
  39. data/lib/active_record/attribute_methods/read.rb +22 -59
  40. data/lib/active_record/attribute_methods/serialization.rb +16 -150
  41. data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -40
  42. data/lib/active_record/attribute_methods/write.rb +9 -24
  43. data/lib/active_record/attribute_methods.rb +56 -94
  44. data/lib/active_record/attribute_set/builder.rb +106 -0
  45. data/lib/active_record/attribute_set.rb +81 -0
  46. data/lib/active_record/attributes.rb +147 -0
  47. data/lib/active_record/autosave_association.rb +19 -12
  48. data/lib/active_record/base.rb +13 -24
  49. data/lib/active_record/callbacks.rb +6 -6
  50. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +84 -52
  51. data/lib/active_record/connection_adapters/abstract/database_statements.rb +52 -50
  52. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
  53. data/lib/active_record/connection_adapters/abstract/quoting.rb +60 -60
  54. data/lib/active_record/connection_adapters/abstract/savepoints.rb +1 -1
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +39 -4
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +138 -56
  57. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
  58. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +268 -71
  59. data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -118
  60. data/lib/active_record/connection_adapters/abstract_adapter.rb +171 -59
  61. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +293 -139
  62. data/lib/active_record/connection_adapters/column.rb +29 -240
  63. data/lib/active_record/connection_adapters/connection_specification.rb +15 -24
  64. data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
  65. data/lib/active_record/connection_adapters/mysql_adapter.rb +67 -144
  66. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
  67. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  68. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +40 -25
  69. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
  70. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
  95. data/lib/active_record/connection_adapters/postgresql/quoting.rb +46 -136
  96. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
  97. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  98. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +131 -43
  99. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  100. data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -477
  101. data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
  102. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -75
  103. data/lib/active_record/connection_handling.rb +1 -1
  104. data/lib/active_record/core.rb +163 -39
  105. data/lib/active_record/counter_cache.rb +60 -6
  106. data/lib/active_record/enum.rb +9 -11
  107. data/lib/active_record/errors.rb +53 -30
  108. data/lib/active_record/explain.rb +1 -1
  109. data/lib/active_record/explain_subscriber.rb +1 -1
  110. data/lib/active_record/fixtures.rb +55 -69
  111. data/lib/active_record/gem_version.rb +4 -4
  112. data/lib/active_record/inheritance.rb +35 -10
  113. data/lib/active_record/integration.rb +4 -4
  114. data/lib/active_record/legacy_yaml_adapter.rb +30 -0
  115. data/lib/active_record/locking/optimistic.rb +46 -26
  116. data/lib/active_record/migration/command_recorder.rb +19 -2
  117. data/lib/active_record/migration/join_table.rb +1 -1
  118. data/lib/active_record/migration.rb +71 -46
  119. data/lib/active_record/model_schema.rb +52 -58
  120. data/lib/active_record/nested_attributes.rb +5 -5
  121. data/lib/active_record/no_touching.rb +1 -1
  122. data/lib/active_record/persistence.rb +46 -26
  123. data/lib/active_record/query_cache.rb +3 -3
  124. data/lib/active_record/querying.rb +10 -7
  125. data/lib/active_record/railtie.rb +18 -11
  126. data/lib/active_record/railties/databases.rake +50 -51
  127. data/lib/active_record/readonly_attributes.rb +0 -1
  128. data/lib/active_record/reflection.rb +273 -114
  129. data/lib/active_record/relation/batches.rb +0 -2
  130. data/lib/active_record/relation/calculations.rb +41 -37
  131. data/lib/active_record/relation/finder_methods.rb +70 -47
  132. data/lib/active_record/relation/merger.rb +39 -29
  133. data/lib/active_record/relation/predicate_builder/array_handler.rb +32 -13
  134. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -5
  135. data/lib/active_record/relation/predicate_builder.rb +16 -8
  136. data/lib/active_record/relation/query_methods.rb +114 -65
  137. data/lib/active_record/relation/spawn_methods.rb +3 -0
  138. data/lib/active_record/relation.rb +57 -25
  139. data/lib/active_record/result.rb +18 -7
  140. data/lib/active_record/sanitization.rb +12 -2
  141. data/lib/active_record/schema.rb +0 -1
  142. data/lib/active_record/schema_dumper.rb +59 -28
  143. data/lib/active_record/schema_migration.rb +5 -4
  144. data/lib/active_record/scoping/default.rb +6 -4
  145. data/lib/active_record/scoping/named.rb +4 -0
  146. data/lib/active_record/serializers/xml_serializer.rb +3 -7
  147. data/lib/active_record/statement_cache.rb +95 -10
  148. data/lib/active_record/store.rb +5 -5
  149. data/lib/active_record/tasks/database_tasks.rb +61 -6
  150. data/lib/active_record/tasks/mysql_database_tasks.rb +20 -11
  151. data/lib/active_record/tasks/postgresql_database_tasks.rb +20 -9
  152. data/lib/active_record/timestamp.rb +9 -7
  153. data/lib/active_record/transactions.rb +53 -27
  154. data/lib/active_record/type/big_integer.rb +13 -0
  155. data/lib/active_record/type/binary.rb +50 -0
  156. data/lib/active_record/type/boolean.rb +31 -0
  157. data/lib/active_record/type/date.rb +50 -0
  158. data/lib/active_record/type/date_time.rb +54 -0
  159. data/lib/active_record/type/decimal.rb +64 -0
  160. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  161. data/lib/active_record/type/decorator.rb +14 -0
  162. data/lib/active_record/type/float.rb +19 -0
  163. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  164. data/lib/active_record/type/integer.rb +59 -0
  165. data/lib/active_record/type/mutable.rb +16 -0
  166. data/lib/active_record/type/numeric.rb +36 -0
  167. data/lib/active_record/type/serialized.rb +62 -0
  168. data/lib/active_record/type/string.rb +40 -0
  169. data/lib/active_record/type/text.rb +11 -0
  170. data/lib/active_record/type/time.rb +26 -0
  171. data/lib/active_record/type/time_value.rb +38 -0
  172. data/lib/active_record/type/type_map.rb +64 -0
  173. data/lib/active_record/type/unsigned_integer.rb +15 -0
  174. data/lib/active_record/type/value.rb +110 -0
  175. data/lib/active_record/type.rb +23 -0
  176. data/lib/active_record/validations/associated.rb +5 -3
  177. data/lib/active_record/validations/presence.rb +5 -3
  178. data/lib/active_record/validations/uniqueness.rb +25 -29
  179. data/lib/active_record/validations.rb +25 -19
  180. data/lib/active_record.rb +4 -0
  181. data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
  182. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +1 -1
  183. data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
  184. metadata +66 -11
  185. 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
- hash = hash.dup
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.to_sym)
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.to_sym)
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
- register_handler(BasicObject, ->(attribute, value) { attribute.eq(value) })
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.in(value) })
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.read_attribute(primary_key)
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 conjuction with +includes+.
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 = :chain, *rest) # :nodoc:
564
- if opts == :chain
565
- WhereChain.new(self)
566
- else
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
- self.reverse_order_value = !reverse_order_value
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 - ['']).uniq)
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! do |rel|
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
- joins = join_dependency.join_constraints stashed_association_joins
1045
+ join_infos = join_dependency.join_constraints stashed_association_joins
1004
1046
 
1005
- joins.each { |join| manager.from(join) }
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.grep(Hash) do |h|
1062
- unless (h.values - [:asc, :desc]).empty?
1063
- raise ArgumentError, 'Direction should be :asc or :desc'
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
- um = scope.where(@klass.arel_table[@klass.primary_key].eq(id_was || id)).arel.compile_update(substitutes, @klass.primary_key)
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
- binds)
91
+ bvs,
92
+ )
88
93
  end
89
94
 
90
95
  def substitute_values(values) # :nodoc:
91
- substitutes = values.sort_by { |arel_attr,_| arel_attr.name }
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 |tuple, i|
97
- tuple[1] = @klass.connection.substitute_at(binds[i][0], i)
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 if they match a set of conditions supplied, limits and order can
298
- # also be supplied. This method constructs a single SQL UPDATE statement and sends it straight to the
299
- # database. It does not instantiate the involved models and it does not trigger Active Record callbacks
300
- # or validations.
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
- @klass.connection.update stmt, 'SQL', bind_values
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 a limit scope is supplied, +delete_all+ raises an ActiveRecord error:
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 scope
452
+ # # => ActiveRecord::ActiveRecordError: delete_all doesn't support limit
441
453
  def delete_all(conditions = nil)
442
- raise ActiveRecordError.new("delete_all doesn't support limit scope") if self.limit_value
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
- affected = @klass.connection.delete(stmt, 'SQL', bind_values)
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
- ast = relation.arel.ast
527
- binds = relation.bind_values.dup
528
- visitor.accept(ast) do
529
- connection.quote(*binds.shift.reverse)
530
- end
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) { where.right }]
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 = ActiveRecord::Associations::Preloader.new
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)