activerecord 4.1.16 → 4.2.0.beta1

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.

Files changed (167) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +634 -2185
  3. data/README.rdoc +15 -10
  4. data/lib/active_record.rb +2 -1
  5. data/lib/active_record/aggregations.rb +12 -8
  6. data/lib/active_record/associations.rb +58 -33
  7. data/lib/active_record/associations/association.rb +1 -1
  8. data/lib/active_record/associations/association_scope.rb +53 -21
  9. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  10. data/lib/active_record/associations/builder/association.rb +16 -5
  11. data/lib/active_record/associations/builder/belongs_to.rb +7 -29
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -11
  13. data/lib/active_record/associations/builder/has_one.rb +2 -2
  14. data/lib/active_record/associations/builder/singular_association.rb +8 -1
  15. data/lib/active_record/associations/collection_association.rb +32 -44
  16. data/lib/active_record/associations/collection_proxy.rb +1 -10
  17. data/lib/active_record/associations/has_many_association.rb +60 -14
  18. data/lib/active_record/associations/has_many_through_association.rb +34 -23
  19. data/lib/active_record/associations/has_one_association.rb +0 -1
  20. data/lib/active_record/associations/join_dependency.rb +7 -9
  21. data/lib/active_record/associations/join_dependency/join_association.rb +18 -14
  22. data/lib/active_record/associations/preloader.rb +2 -2
  23. data/lib/active_record/associations/preloader/association.rb +9 -5
  24. data/lib/active_record/associations/preloader/through_association.rb +3 -3
  25. data/lib/active_record/associations/singular_association.rb +16 -1
  26. data/lib/active_record/associations/through_association.rb +6 -22
  27. data/lib/active_record/attribute.rb +131 -0
  28. data/lib/active_record/attribute_assignment.rb +19 -11
  29. data/lib/active_record/attribute_decorators.rb +66 -0
  30. data/lib/active_record/attribute_methods.rb +53 -90
  31. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
  32. data/lib/active_record/attribute_methods/dirty.rb +85 -42
  33. data/lib/active_record/attribute_methods/primary_key.rb +6 -8
  34. data/lib/active_record/attribute_methods/read.rb +14 -57
  35. data/lib/active_record/attribute_methods/serialization.rb +12 -146
  36. data/lib/active_record/attribute_methods/time_zone_conversion.rb +32 -40
  37. data/lib/active_record/attribute_methods/write.rb +8 -23
  38. data/lib/active_record/attribute_set.rb +77 -0
  39. data/lib/active_record/attribute_set/builder.rb +32 -0
  40. data/lib/active_record/attributes.rb +122 -0
  41. data/lib/active_record/autosave_association.rb +11 -21
  42. data/lib/active_record/base.rb +9 -19
  43. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +69 -45
  44. data/lib/active_record/connection_adapters/abstract/database_statements.rb +22 -42
  45. data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -60
  46. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +37 -2
  47. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +102 -21
  48. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +9 -33
  49. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +178 -55
  50. data/lib/active_record/connection_adapters/abstract/transaction.rb +120 -115
  51. data/lib/active_record/connection_adapters/abstract_adapter.rb +143 -57
  52. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +156 -107
  53. data/lib/active_record/connection_adapters/column.rb +13 -244
  54. data/lib/active_record/connection_adapters/connection_specification.rb +6 -20
  55. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -15
  56. data/lib/active_record/connection_adapters/mysql_adapter.rb +55 -143
  57. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
  58. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  59. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +39 -20
  60. data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
  61. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +96 -0
  62. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  63. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  64. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
  65. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  66. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  67. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
  68. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  69. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
  70. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +76 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +85 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  86. data/lib/active_record/connection_adapters/postgresql/quoting.rb +42 -122
  87. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
  88. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +154 -0
  89. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +86 -34
  90. data/lib/active_record/connection_adapters/postgresql/utils.rb +66 -0
  91. data/lib/active_record/connection_adapters/postgresql_adapter.rb +188 -452
  92. data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
  93. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +54 -47
  94. data/lib/active_record/connection_handling.rb +1 -1
  95. data/lib/active_record/core.rb +119 -22
  96. data/lib/active_record/counter_cache.rb +60 -6
  97. data/lib/active_record/enum.rb +9 -10
  98. data/lib/active_record/errors.rb +27 -26
  99. data/lib/active_record/explain.rb +1 -1
  100. data/lib/active_record/fixtures.rb +52 -45
  101. data/lib/active_record/gem_version.rb +3 -3
  102. data/lib/active_record/inheritance.rb +33 -8
  103. data/lib/active_record/integration.rb +4 -4
  104. data/lib/active_record/locking/optimistic.rb +34 -16
  105. data/lib/active_record/migration.rb +22 -32
  106. data/lib/active_record/migration/command_recorder.rb +19 -2
  107. data/lib/active_record/migration/join_table.rb +1 -1
  108. data/lib/active_record/model_schema.rb +39 -48
  109. data/lib/active_record/nested_attributes.rb +8 -18
  110. data/lib/active_record/persistence.rb +39 -22
  111. data/lib/active_record/query_cache.rb +3 -3
  112. data/lib/active_record/querying.rb +1 -8
  113. data/lib/active_record/railtie.rb +17 -10
  114. data/lib/active_record/railties/databases.rake +47 -42
  115. data/lib/active_record/readonly_attributes.rb +0 -1
  116. data/lib/active_record/reflection.rb +225 -92
  117. data/lib/active_record/relation.rb +35 -11
  118. data/lib/active_record/relation/batches.rb +0 -2
  119. data/lib/active_record/relation/calculations.rb +28 -32
  120. data/lib/active_record/relation/delegation.rb +1 -1
  121. data/lib/active_record/relation/finder_methods.rb +42 -20
  122. data/lib/active_record/relation/merger.rb +0 -1
  123. data/lib/active_record/relation/predicate_builder.rb +1 -22
  124. data/lib/active_record/relation/predicate_builder/array_handler.rb +16 -11
  125. data/lib/active_record/relation/predicate_builder/relation_handler.rb +0 -4
  126. data/lib/active_record/relation/query_methods.rb +98 -62
  127. data/lib/active_record/relation/spawn_methods.rb +6 -7
  128. data/lib/active_record/result.rb +16 -9
  129. data/lib/active_record/sanitization.rb +8 -1
  130. data/lib/active_record/schema.rb +0 -1
  131. data/lib/active_record/schema_dumper.rb +51 -9
  132. data/lib/active_record/schema_migration.rb +4 -0
  133. data/lib/active_record/scoping/default.rb +5 -4
  134. data/lib/active_record/serializers/xml_serializer.rb +3 -7
  135. data/lib/active_record/statement_cache.rb +79 -5
  136. data/lib/active_record/store.rb +5 -5
  137. data/lib/active_record/tasks/database_tasks.rb +37 -5
  138. data/lib/active_record/tasks/mysql_database_tasks.rb +10 -16
  139. data/lib/active_record/tasks/postgresql_database_tasks.rb +2 -2
  140. data/lib/active_record/timestamp.rb +9 -7
  141. data/lib/active_record/transactions.rb +35 -21
  142. data/lib/active_record/type.rb +20 -0
  143. data/lib/active_record/type/binary.rb +40 -0
  144. data/lib/active_record/type/boolean.rb +19 -0
  145. data/lib/active_record/type/date.rb +46 -0
  146. data/lib/active_record/type/date_time.rb +43 -0
  147. data/lib/active_record/type/decimal.rb +40 -0
  148. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  149. data/lib/active_record/type/float.rb +19 -0
  150. data/lib/active_record/type/hash_lookup_type_map.rb +19 -0
  151. data/lib/active_record/type/integer.rb +23 -0
  152. data/lib/active_record/type/mutable.rb +16 -0
  153. data/lib/active_record/type/numeric.rb +36 -0
  154. data/lib/active_record/type/serialized.rb +51 -0
  155. data/lib/active_record/type/string.rb +36 -0
  156. data/lib/active_record/type/text.rb +11 -0
  157. data/lib/active_record/type/time.rb +26 -0
  158. data/lib/active_record/type/time_value.rb +38 -0
  159. data/lib/active_record/type/type_map.rb +48 -0
  160. data/lib/active_record/type/value.rb +101 -0
  161. data/lib/active_record/validations.rb +21 -16
  162. data/lib/active_record/validations/uniqueness.rb +9 -23
  163. data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
  164. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +1 -1
  165. data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
  166. metadata +71 -14
  167. data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -2,28 +2,33 @@ module ActiveRecord
2
2
  class PredicateBuilder
3
3
  class ArrayHandler # :nodoc:
4
4
  def call(attribute, value)
5
+ return attribute.in([]) if value.empty?
6
+
5
7
  values = value.map { |x| x.is_a?(Base) ? x.id : x }
6
8
  ranges, values = values.partition { |v| v.is_a?(Range) }
9
+ nils, values = values.partition(&:nil?)
7
10
 
8
- values_predicate = if values.include?(nil)
9
- values = values.compact
10
-
11
+ values_predicate =
11
12
  case values.length
12
- when 0
13
- attribute.eq(nil)
14
- when 1
15
- attribute.eq(values.first).or(attribute.eq(nil))
16
- else
17
- attribute.in(values).or(attribute.eq(nil))
13
+ when 0 then NullPredicate
14
+ when 1 then attribute.eq(values.first)
15
+ else attribute.in(values)
18
16
  end
19
- else
20
- attribute.in(values)
17
+
18
+ unless nils.empty?
19
+ values_predicate = values_predicate.or(attribute.eq(nil))
21
20
  end
22
21
 
23
22
  array_predicates = ranges.map { |range| attribute.in(range) }
24
23
  array_predicates << values_predicate
25
24
  array_predicates.inject { |composite, predicate| composite.or(predicate) }
26
25
  end
26
+
27
+ module NullPredicate
28
+ def self.or(other)
29
+ other
30
+ end
31
+ end
27
32
  end
28
33
  end
29
34
  end
@@ -6,10 +6,6 @@ module ActiveRecord
6
6
  value = value.select(value.klass.arel_table[value.klass.primary_key])
7
7
  end
8
8
 
9
- value = value.dup
10
- value.where_values = value.where_values.map do |node|
11
- node.dup rescue node
12
- end
13
9
  attribute.in(value.arel.ast)
14
10
  end
15
11
  end
@@ -67,6 +67,7 @@ module ActiveRecord
67
67
  #
68
68
  def #{name}_values=(values) # def select_values=(values)
69
69
  raise ImmutableRelation if @loaded # raise ImmutableRelation if @loaded
70
+ check_cached_relation
70
71
  @values[:#{name}] = values # @values[:select] = values
71
72
  end # end
72
73
  CODE
@@ -84,11 +85,22 @@ module ActiveRecord
84
85
  class_eval <<-CODE, __FILE__, __LINE__ + 1
85
86
  def #{name}_value=(value) # def readonly_value=(value)
86
87
  raise ImmutableRelation if @loaded # raise ImmutableRelation if @loaded
88
+ check_cached_relation
87
89
  @values[:#{name}] = value # @values[:readonly] = value
88
90
  end # end
89
91
  CODE
90
92
  end
91
93
 
94
+ def check_cached_relation # :nodoc:
95
+ if defined?(@arel) && @arel
96
+ @arel = nil
97
+ ActiveSupport::Deprecation.warn <<-WARNING
98
+ Modifying already cached Relation. The cache will be reset.
99
+ Use a cloned Relation to prevent this warning.
100
+ WARNING
101
+ end
102
+ end
103
+
92
104
  def create_with_value # :nodoc:
93
105
  @values[:create_with] || {}
94
106
  end
@@ -173,7 +185,7 @@ module ActiveRecord
173
185
 
174
186
  # Use to indicate that the given +table_names+ are referenced by an SQL string,
175
187
  # and should therefore be JOINed in any query rather than loaded separately.
176
- # This method only works in conjuction with +includes+.
188
+ # This method only works in conjunction with +includes+.
177
189
  # See #includes for more details.
178
190
  #
179
191
  # User.includes(:posts).where("posts.name = 'foo'")
@@ -207,7 +219,7 @@ module ActiveRecord
207
219
  # fields are retrieved:
208
220
  #
209
221
  # Model.select(:field)
210
- # # => [#<Model field:value>]
222
+ # # => [#<Model id: nil, field: "value">]
211
223
  #
212
224
  # Although in the above example it looks as though this method returns an
213
225
  # array, it actually returns a relation object and can have other query
@@ -216,12 +228,12 @@ module ActiveRecord
216
228
  # The argument to the method can also be an array of fields.
217
229
  #
218
230
  # Model.select(:field, :other_field, :and_one_more)
219
- # # => [#<Model field: "value", other_field: "value", and_one_more: "value">]
231
+ # # => [#<Model id: nil, field: "value", other_field: "value", and_one_more: "value">]
220
232
  #
221
233
  # You can also use one or more strings, which will be used unchanged as SELECT fields.
222
234
  #
223
235
  # Model.select('field AS field_one', 'other_field AS field_two')
224
- # # => [#<Model field: "value", other_field: "value">]
236
+ # # => [#<Model id: nil, field: "value", other_field: "value">]
225
237
  #
226
238
  # If an alias was specified, it will be accessible from the resulting objects:
227
239
  #
@@ -229,7 +241,7 @@ module ActiveRecord
229
241
  # # => "value"
230
242
  #
231
243
  # Accessing attributes of an object that do not have fields retrieved by a select
232
- # will throw <tt>ActiveModel::MissingAttributeError</tt>:
244
+ # except +id+ will throw <tt>ActiveModel::MissingAttributeError</tt>:
233
245
  #
234
246
  # Model.select(:field).first.other_field
235
247
  # # => ActiveModel::MissingAttributeError: missing attribute: other_field
@@ -266,6 +278,10 @@ module ActiveRecord
266
278
  #
267
279
  # User.group('name AS grouped_name, age')
268
280
  # => [#<User id: 3, name: "Foo", age: 21, ...>, #<User id: 2, name: "Oscar", age: 21, ...>, #<User id: 5, name: "Foo", age: 23, ...>]
281
+ #
282
+ # Passing in an array of attributes to group by is also supported.
283
+ # User.select([:id, :first_name]).group(:id, :first_name).first(3)
284
+ # => [#<User id: 1, first_name: "Bill">, #<User id: 2, first_name: "Earl">, #<User id: 3, first_name: "Beto">]
269
285
  def group(*args)
270
286
  check_if_method_has_arguments!(:group, args)
271
287
  spawn.group!(*args)
@@ -280,15 +296,6 @@ module ActiveRecord
280
296
 
281
297
  # Allows to specify an order attribute:
282
298
  #
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
299
  # User.order(:name)
293
300
  # => SELECT "users".* FROM "users" ORDER BY "users"."name" ASC
294
301
  #
@@ -297,6 +304,15 @@ module ActiveRecord
297
304
  #
298
305
  # User.order(:name, email: :desc)
299
306
  # => SELECT "users".* FROM "users" ORDER BY "users"."name" ASC, "users"."email" DESC
307
+ #
308
+ # User.order('name')
309
+ # => SELECT "users".* FROM "users" ORDER BY name
310
+ #
311
+ # User.order('name DESC')
312
+ # => SELECT "users".* FROM "users" ORDER BY name DESC
313
+ #
314
+ # User.order('name DESC, email')
315
+ # => SELECT "users".* FROM "users" ORDER BY name DESC, email
300
316
  def order(*args)
301
317
  check_if_method_has_arguments!(:order, args)
302
318
  spawn.order!(*args)
@@ -560,18 +576,14 @@ module ActiveRecord
560
576
  end
561
577
  end
562
578
 
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
579
+ def where!(opts, *rest) # :nodoc:
580
+ if Hash === opts
581
+ opts = sanitize_forbidden_attributes(opts)
582
+ references!(PredicateBuilder.references(opts))
574
583
  end
584
+
585
+ self.where_values += build_where(opts, rest)
586
+ self
575
587
  end
576
588
 
577
589
  # Allows you to change a previously set where condition for a given attribute, instead of appending to that condition.
@@ -677,11 +689,11 @@ module ActiveRecord
677
689
  # end
678
690
  #
679
691
  def none
680
- where("1=0").extending!(NullRelation)
692
+ extending(NullRelation)
681
693
  end
682
694
 
683
695
  def none! # :nodoc:
684
- where!("1=0").extending!(NullRelation)
696
+ extending!(NullRelation)
685
697
  end
686
698
 
687
699
  # Sets readonly attributes for the returned relation. If value is
@@ -833,7 +845,9 @@ module ActiveRecord
833
845
  end
834
846
 
835
847
  def reverse_order! # :nodoc:
836
- self.reverse_order_value = !reverse_order_value
848
+ orders = order_values.uniq
849
+ orders.reject!(&:blank?)
850
+ self.order_values = reverse_sql_order(orders)
837
851
  self
838
852
  end
839
853
 
@@ -849,25 +863,26 @@ module ActiveRecord
849
863
 
850
864
  build_joins(arel, joins_values.flatten) unless joins_values.empty?
851
865
 
852
- collapse_wheres(arel, (where_values - ['']).uniq)
866
+ collapse_wheres(arel, (where_values - [''])) #TODO: Add uniq with real value comparison / ignore uniqs that have binds
853
867
 
854
868
  arel.having(*having_values.uniq.reject(&:blank?)) unless having_values.empty?
855
869
 
856
870
  arel.take(connection.sanitize_limit(limit_value)) if limit_value
857
871
  arel.skip(offset_value.to_i) if offset_value
858
- arel.group(*arel_columns(group_values.uniq.reject(&:blank?))) unless group_values.empty?
872
+
873
+ arel.group(*group_values.uniq.reject(&:blank?)) unless group_values.empty?
859
874
 
860
875
  build_order(arel)
861
876
 
862
- build_select(arel)
877
+ build_select(arel, select_values.uniq)
863
878
 
864
879
  arel.distinct(distinct_value)
865
880
  arel.from(build_from) if from_value
866
881
  arel.lock(lock_value) if lock_value
867
882
 
868
883
  # Reorder bind indexes if joins produced bind values
869
- bvs = arel.bind_values + bind_values
870
- if bvs.any?
884
+ if arel.bind_values.any?
885
+ bvs = arel.bind_values + bind_values
871
886
  arel.ast.grep(Arel::Nodes::BindParam).each_with_index do |bp, i|
872
887
  column = bvs[i].first
873
888
  bp.replace connection.substitute_at(column, i)
@@ -887,8 +902,9 @@ module ActiveRecord
887
902
 
888
903
  case scope
889
904
  when :order
890
- self.reverse_order_value = false
891
905
  result = []
906
+ when :where
907
+ self.bind_values = []
892
908
  else
893
909
  result = [] unless single_val_method
894
910
  end
@@ -939,18 +955,15 @@ module ActiveRecord
939
955
  def build_where(opts, other = [])
940
956
  case opts
941
957
  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
958
  [@klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))]
950
959
  when Hash
951
960
  opts = PredicateBuilder.resolve_column_aliases(klass, opts)
952
- attributes = @klass.send(:expand_hash_conditions_for_aggregates, opts)
953
961
 
962
+ bv_len = bind_values.length
963
+ tmp_opts, bind_values = create_binds(opts, bv_len)
964
+ self.bind_values += bind_values
965
+
966
+ attributes = @klass.send(:expand_hash_conditions_for_aggregates, tmp_opts)
954
967
  add_relations_to_bind_values(attributes)
955
968
 
956
969
  PredicateBuilder.build_from_hash(klass, attributes, table)
@@ -959,6 +972,29 @@ module ActiveRecord
959
972
  end
960
973
  end
961
974
 
975
+ def create_binds(opts, idx)
976
+ bindable, non_binds = opts.partition do |column, value|
977
+ case value
978
+ when String, Integer, ActiveRecord::StatementCache::Substitute
979
+ @klass.columns_hash.include? column.to_s
980
+ else
981
+ false
982
+ end
983
+ end
984
+
985
+ new_opts = {}
986
+ binds = []
987
+
988
+ bindable.each_with_index do |(column,value), index|
989
+ binds.push [@klass.columns_hash[column.to_s], value]
990
+ new_opts[column] = connection.substitute_at(column, index + idx)
991
+ end
992
+
993
+ non_binds.each { |column,value| new_opts[column] = value }
994
+
995
+ [new_opts, binds]
996
+ end
997
+
962
998
  def build_from
963
999
  opts, name = from_value
964
1000
  case opts
@@ -1000,33 +1036,29 @@ module ActiveRecord
1000
1036
  join_list
1001
1037
  )
1002
1038
 
1003
- joins = join_dependency.join_constraints stashed_association_joins
1039
+ join_infos = join_dependency.join_constraints stashed_association_joins
1004
1040
 
1005
- joins.each { |join| manager.from(join) }
1041
+ join_infos.each do |info|
1042
+ info.joins.each { |join| manager.from(join) }
1043
+ manager.bind_values.concat info.binds
1044
+ end
1006
1045
 
1007
1046
  manager.join_sources.concat(join_list)
1008
1047
 
1009
1048
  manager
1010
1049
  end
1011
1050
 
1012
- def build_select(arel)
1013
- if select_values.any?
1014
- arel.project(*arel_columns(select_values.uniq))
1051
+ def build_select(arel, selects)
1052
+ if !selects.empty?
1053
+ expanded_select = selects.map do |field|
1054
+ columns_hash.key?(field.to_s) ? arel_table[field] : field
1055
+ end
1056
+ arel.project(*expanded_select)
1015
1057
  else
1016
1058
  arel.project(@klass.arel_table[Arel.star])
1017
1059
  end
1018
1060
  end
1019
1061
 
1020
- def arel_columns(columns)
1021
- columns.map do |field|
1022
- if columns_hash.key?(field.to_s)
1023
- arel_table[field]
1024
- else
1025
- field
1026
- end
1027
- end
1028
- end
1029
-
1030
1062
  def reverse_sql_order(order_query)
1031
1063
  order_query = ["#{quoted_table_name}.#{quoted_primary_key} ASC"] if order_query.empty?
1032
1064
 
@@ -1052,15 +1084,19 @@ module ActiveRecord
1052
1084
  def build_order(arel)
1053
1085
  orders = order_values.uniq
1054
1086
  orders.reject!(&:blank?)
1055
- orders = reverse_sql_order(orders) if reverse_order_value
1056
1087
 
1057
1088
  arel.order(*orders) unless orders.empty?
1058
1089
  end
1059
1090
 
1091
+ VALID_DIRECTIONS = [:asc, :desc, :ASC, :DESC,
1092
+ 'asc', 'desc', 'ASC', 'DESC'] # :nodoc:
1093
+
1060
1094
  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'
1095
+ args.each do |arg|
1096
+ next unless arg.is_a?(Hash)
1097
+ arg.each do |_key, value|
1098
+ raise ArgumentError, "Direction \"#{value}\" is invalid. Valid " \
1099
+ "directions are: #{VALID_DIRECTIONS.inspect}" unless VALID_DIRECTIONS.include?(value)
1064
1100
  end
1065
1101
  end
1066
1102
  end
@@ -1082,7 +1118,7 @@ module ActiveRecord
1082
1118
  when Hash
1083
1119
  arg.map { |field, dir|
1084
1120
  field = klass.attribute_alias(field) if klass.attribute_alias?(field)
1085
- table[field].send(dir)
1121
+ table[field].send(dir.downcase)
1086
1122
  }
1087
1123
  else
1088
1124
  arg
@@ -12,7 +12,6 @@ module ActiveRecord
12
12
 
13
13
  # Merges in the conditions from <tt>other</tt>, if <tt>other</tt> is an <tt>ActiveRecord::Relation</tt>.
14
14
  # Returns an array representing the intersection of the resulting records with <tt>other</tt>, if <tt>other</tt> is an array.
15
- #
16
15
  # Post.where(published: true).joins(:comments).merge( Comment.where(spam: false) )
17
16
  # # Performs a single join query with both where conditions.
18
17
  #
@@ -38,14 +37,11 @@ module ActiveRecord
38
37
  end
39
38
 
40
39
  def merge!(other) # :nodoc:
41
- if other.is_a?(Hash)
42
- Relation::HashMerger.new(self, other).merge
43
- elsif other.is_a?(Relation)
44
- Relation::Merger.new(self, other).merge
45
- elsif other.respond_to?(:to_proc)
40
+ if !other.is_a?(Relation) && other.respond_to?(:to_proc)
46
41
  instance_exec(&other)
47
42
  else
48
- raise ArgumentError, "#{other.inspect} is not an ActiveRecord::Relation"
43
+ klass = other.is_a?(Hash) ? Relation::HashMerger : Relation::Merger
44
+ klass.new(self, other).merge
49
45
  end
50
46
  end
51
47
 
@@ -62,6 +58,9 @@ module ActiveRecord
62
58
  # Post.order('id asc').only(:where) # discards the order condition
63
59
  # Post.order('id asc').only(:where, :order) # uses the specified order
64
60
  def only(*onlies)
61
+ if onlies.any? { |o| o == :where }
62
+ onlies << :bind
63
+ end
65
64
  relation_with values.slice(*onlies)
66
65
  end
67
66
 
@@ -31,7 +31,7 @@ module ActiveRecord
31
31
  class Result
32
32
  include Enumerable
33
33
 
34
- IDENTITY_TYPE = Class.new { def type_cast(v); v; end }.new # :nodoc:
34
+ IDENTITY_TYPE = Type::Value.new # :nodoc:
35
35
 
36
36
  attr_reader :columns, :rows, :column_types
37
37
 
@@ -42,14 +42,6 @@ module ActiveRecord
42
42
  @column_types = column_types
43
43
  end
44
44
 
45
- def identity_type # :nodoc:
46
- IDENTITY_TYPE
47
- end
48
-
49
- def column_type(name)
50
- @column_types[name] || identity_type
51
- end
52
-
53
45
  def each
54
46
  if block_given?
55
47
  hash_rows.each { |row| yield row }
@@ -82,6 +74,15 @@ module ActiveRecord
82
74
  hash_rows.last
83
75
  end
84
76
 
77
+ def cast_values(type_overrides = {}) # :nodoc:
78
+ types = columns.map { |name| column_type(name, type_overrides) }
79
+ result = rows.map do |values|
80
+ types.zip(values).map { |type, value| type.type_cast_from_database(value) }
81
+ end
82
+
83
+ columns.one? ? result.map!(&:first) : result
84
+ end
85
+
85
86
  def initialize_copy(other)
86
87
  @columns = columns.dup
87
88
  @rows = rows.dup
@@ -91,6 +92,12 @@ module ActiveRecord
91
92
 
92
93
  private
93
94
 
95
+ def column_type(name, type_overrides = {})
96
+ type_overrides.fetch(name) do
97
+ column_types.fetch(name, IDENTITY_TYPE)
98
+ end
99
+ end
100
+
94
101
  def hash_rows
95
102
  @hash_rows ||=
96
103
  begin