activerecord 4.1.15 → 4.2.11.3

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 (185) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1162 -1792
  3. data/README.rdoc +15 -10
  4. data/lib/active_record.rb +4 -0
  5. data/lib/active_record/aggregations.rb +15 -8
  6. data/lib/active_record/association_relation.rb +13 -0
  7. data/lib/active_record/associations.rb +158 -49
  8. data/lib/active_record/associations/alias_tracker.rb +3 -12
  9. data/lib/active_record/associations/association.rb +16 -4
  10. data/lib/active_record/associations/association_scope.rb +83 -38
  11. data/lib/active_record/associations/belongs_to_association.rb +28 -10
  12. data/lib/active_record/associations/builder/association.rb +15 -4
  13. data/lib/active_record/associations/builder/belongs_to.rb +7 -29
  14. data/lib/active_record/associations/builder/collection_association.rb +5 -1
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +8 -13
  16. data/lib/active_record/associations/builder/has_many.rb +1 -1
  17. data/lib/active_record/associations/builder/has_one.rb +2 -2
  18. data/lib/active_record/associations/builder/singular_association.rb +8 -1
  19. data/lib/active_record/associations/collection_association.rb +63 -27
  20. data/lib/active_record/associations/collection_proxy.rb +29 -35
  21. data/lib/active_record/associations/foreign_association.rb +11 -0
  22. data/lib/active_record/associations/has_many_association.rb +83 -22
  23. data/lib/active_record/associations/has_many_through_association.rb +49 -26
  24. data/lib/active_record/associations/has_one_association.rb +1 -1
  25. data/lib/active_record/associations/join_dependency.rb +26 -13
  26. data/lib/active_record/associations/join_dependency/join_association.rb +25 -15
  27. data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
  28. data/lib/active_record/associations/preloader.rb +36 -26
  29. data/lib/active_record/associations/preloader/association.rb +14 -11
  30. data/lib/active_record/associations/preloader/through_association.rb +4 -3
  31. data/lib/active_record/associations/singular_association.rb +17 -2
  32. data/lib/active_record/associations/through_association.rb +5 -12
  33. data/lib/active_record/attribute.rb +163 -0
  34. data/lib/active_record/attribute_assignment.rb +19 -11
  35. data/lib/active_record/attribute_decorators.rb +66 -0
  36. data/lib/active_record/attribute_methods.rb +56 -94
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  38. data/lib/active_record/attribute_methods/dirty.rb +107 -43
  39. data/lib/active_record/attribute_methods/primary_key.rb +7 -8
  40. data/lib/active_record/attribute_methods/query.rb +1 -1
  41. data/lib/active_record/attribute_methods/read.rb +22 -59
  42. data/lib/active_record/attribute_methods/serialization.rb +16 -150
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -40
  44. data/lib/active_record/attribute_methods/write.rb +9 -24
  45. data/lib/active_record/attribute_set.rb +81 -0
  46. data/lib/active_record/attribute_set/builder.rb +106 -0
  47. data/lib/active_record/attributes.rb +147 -0
  48. data/lib/active_record/autosave_association.rb +19 -12
  49. data/lib/active_record/base.rb +13 -24
  50. data/lib/active_record/callbacks.rb +6 -6
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +84 -52
  52. data/lib/active_record/connection_adapters/abstract/database_statements.rb +52 -50
  53. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
  54. data/lib/active_record/connection_adapters/abstract/quoting.rb +60 -60
  55. data/lib/active_record/connection_adapters/abstract/savepoints.rb +1 -1
  56. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +39 -4
  57. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +138 -56
  58. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +268 -71
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -118
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +171 -59
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +293 -139
  63. data/lib/active_record/connection_adapters/column.rb +29 -240
  64. data/lib/active_record/connection_adapters/connection_specification.rb +15 -24
  65. data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
  66. data/lib/active_record/connection_adapters/mysql_adapter.rb +67 -144
  67. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
  68. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  69. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +40 -25
  70. data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
  71. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  96. data/lib/active_record/connection_adapters/postgresql/quoting.rb +46 -136
  97. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
  98. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  99. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +131 -43
  100. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  101. data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -477
  102. data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
  103. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -75
  104. data/lib/active_record/connection_handling.rb +1 -1
  105. data/lib/active_record/core.rb +163 -39
  106. data/lib/active_record/counter_cache.rb +60 -6
  107. data/lib/active_record/enum.rb +9 -11
  108. data/lib/active_record/errors.rb +53 -30
  109. data/lib/active_record/explain.rb +1 -1
  110. data/lib/active_record/explain_subscriber.rb +1 -1
  111. data/lib/active_record/fixtures.rb +55 -69
  112. data/lib/active_record/gem_version.rb +4 -4
  113. data/lib/active_record/inheritance.rb +35 -10
  114. data/lib/active_record/integration.rb +4 -4
  115. data/lib/active_record/legacy_yaml_adapter.rb +30 -0
  116. data/lib/active_record/locking/optimistic.rb +46 -26
  117. data/lib/active_record/migration.rb +71 -46
  118. data/lib/active_record/migration/command_recorder.rb +19 -2
  119. data/lib/active_record/migration/join_table.rb +1 -1
  120. data/lib/active_record/model_schema.rb +52 -58
  121. data/lib/active_record/nested_attributes.rb +5 -5
  122. data/lib/active_record/no_touching.rb +1 -1
  123. data/lib/active_record/persistence.rb +46 -26
  124. data/lib/active_record/query_cache.rb +3 -3
  125. data/lib/active_record/querying.rb +10 -7
  126. data/lib/active_record/railtie.rb +18 -11
  127. data/lib/active_record/railties/databases.rake +50 -51
  128. data/lib/active_record/readonly_attributes.rb +0 -1
  129. data/lib/active_record/reflection.rb +273 -114
  130. data/lib/active_record/relation.rb +57 -25
  131. data/lib/active_record/relation/batches.rb +0 -2
  132. data/lib/active_record/relation/calculations.rb +41 -37
  133. data/lib/active_record/relation/finder_methods.rb +70 -47
  134. data/lib/active_record/relation/merger.rb +39 -29
  135. data/lib/active_record/relation/predicate_builder.rb +16 -8
  136. data/lib/active_record/relation/predicate_builder/array_handler.rb +32 -13
  137. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -5
  138. data/lib/active_record/relation/query_methods.rb +114 -65
  139. data/lib/active_record/relation/spawn_methods.rb +3 -0
  140. data/lib/active_record/result.rb +18 -7
  141. data/lib/active_record/sanitization.rb +12 -2
  142. data/lib/active_record/schema.rb +0 -1
  143. data/lib/active_record/schema_dumper.rb +59 -28
  144. data/lib/active_record/schema_migration.rb +5 -4
  145. data/lib/active_record/scoping/default.rb +6 -4
  146. data/lib/active_record/scoping/named.rb +4 -0
  147. data/lib/active_record/serializers/xml_serializer.rb +3 -7
  148. data/lib/active_record/statement_cache.rb +95 -10
  149. data/lib/active_record/store.rb +5 -5
  150. data/lib/active_record/tasks/database_tasks.rb +61 -6
  151. data/lib/active_record/tasks/mysql_database_tasks.rb +32 -17
  152. data/lib/active_record/tasks/postgresql_database_tasks.rb +20 -9
  153. data/lib/active_record/timestamp.rb +9 -7
  154. data/lib/active_record/transactions.rb +53 -27
  155. data/lib/active_record/type.rb +23 -0
  156. data/lib/active_record/type/big_integer.rb +13 -0
  157. data/lib/active_record/type/binary.rb +50 -0
  158. data/lib/active_record/type/boolean.rb +31 -0
  159. data/lib/active_record/type/date.rb +50 -0
  160. data/lib/active_record/type/date_time.rb +54 -0
  161. data/lib/active_record/type/decimal.rb +64 -0
  162. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  163. data/lib/active_record/type/decorator.rb +14 -0
  164. data/lib/active_record/type/float.rb +19 -0
  165. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  166. data/lib/active_record/type/integer.rb +59 -0
  167. data/lib/active_record/type/mutable.rb +16 -0
  168. data/lib/active_record/type/numeric.rb +36 -0
  169. data/lib/active_record/type/serialized.rb +62 -0
  170. data/lib/active_record/type/string.rb +40 -0
  171. data/lib/active_record/type/text.rb +11 -0
  172. data/lib/active_record/type/time.rb +26 -0
  173. data/lib/active_record/type/time_value.rb +38 -0
  174. data/lib/active_record/type/type_map.rb +64 -0
  175. data/lib/active_record/type/unsigned_integer.rb +15 -0
  176. data/lib/active_record/type/value.rb +110 -0
  177. data/lib/active_record/validations.rb +25 -19
  178. data/lib/active_record/validations/associated.rb +5 -3
  179. data/lib/active_record/validations/presence.rb +5 -3
  180. data/lib/active_record/validations/uniqueness.rb +25 -29
  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
@@ -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)
@@ -1,4 +1,3 @@
1
-
2
1
  module ActiveRecord
3
2
  module Batches
4
3
  # Looping through a collection of records from the database
@@ -115,7 +114,6 @@ module ActiveRecord
115
114
  end
116
115
 
117
116
  relation = relation.reorder(batch_order).limit(batch_size)
118
- relation.reverse_order_value = false
119
117
  records = start ? relation.where(table[primary_key].gteq(start)).to_a : relation.to_a
120
118
 
121
119
  while records.any?
@@ -20,17 +20,31 @@ module ActiveRecord
20
20
  # Person.group(:city).count
21
21
  # # => { 'Rome' => 5, 'Paris' => 3 }
22
22
  #
23
+ # If +count+ is used with +group+ for multiple columns, it returns a Hash whose
24
+ # keys are an array containing the individual values of each column and the value
25
+ # of each key would be the +count+.
26
+ #
27
+ # Article.group(:status, :category).count
28
+ # # => {["draft", "business"]=>10, ["draft", "technology"]=>4,
29
+ # ["published", "business"]=>0, ["published", "technology"]=>2}
30
+ #
23
31
  # If +count+ is used with +select+, it will count the selected columns:
24
32
  #
25
33
  # Person.select(:age).count
26
34
  # # => counts the number of different age values
27
35
  #
28
36
  # Note: not all valid +select+ expressions are valid +count+ expressions. The specifics differ
29
- # between databases. In invalid cases, an error from the databsae is thrown.
37
+ # between databases. In invalid cases, an error from the database is thrown.
30
38
  def count(column_name = nil, options = {})
39
+ if options.present? && !ActiveRecord.const_defined?(:DeprecatedFinders)
40
+ raise ArgumentError, "Relation#count does not support finder options anymore. " \
41
+ "Please build a scope and then call count on it or use the " \
42
+ "activerecord-deprecated_finders gem to enable this functionality."
43
+
44
+ end
45
+
31
46
  # TODO: Remove options argument as soon we remove support to
32
47
  # activerecord-deprecated_finders.
33
- column_name, options = nil, column_name if column_name.is_a?(Hash)
34
48
  calculate(:count, column_name, options)
35
49
  end
36
50
 
@@ -80,7 +94,7 @@ module ActiveRecord
80
94
  #
81
95
  # There are two basic forms of output:
82
96
  #
83
- # * Single aggregate value: The single value is type cast to Fixnum for COUNT, Float
97
+ # * Single aggregate value: The single value is type cast to Integer for COUNT, Float
84
98
  # for AVG, and the given column's type for everything else.
85
99
  #
86
100
  # * Grouped values: This returns an ordered hash of the values and groups them. It
@@ -169,22 +183,8 @@ module ActiveRecord
169
183
  relation.select_values = column_names.map { |cn|
170
184
  columns_hash.key?(cn) ? arel_table[cn] : cn
171
185
  }
172
- result = klass.connection.select_all(relation.arel, nil, bind_values)
173
- columns = result.columns.map do |key|
174
- klass.column_types.fetch(key) {
175
- result.column_types.fetch(key) { result.identity_type }
176
- }
177
- end
178
-
179
- result = result.rows.map do |values|
180
- values = result.columns.zip(values).map do |column_name, value|
181
- single_attr_hash = { column_name => value }
182
- klass.initialize_attributes(single_attr_hash).values.first
183
- end
184
-
185
- columns.zip(values).map { |column, value| column.type_cast value }
186
- end
187
- columns.one? ? result.map!(&:first) : result
186
+ result = klass.connection.select_all(relation.arel, nil, relation.arel.bind_values + bind_values)
187
+ result.cast_values(klass.column_types)
188
188
  end
189
189
  end
190
190
 
@@ -246,27 +246,32 @@ module ActiveRecord
246
246
 
247
247
  column_alias = column_name
248
248
 
249
+ bind_values = nil
250
+
249
251
  if operation == "count" && (relation.limit_value || relation.offset_value)
250
252
  # Shortcut when limit is zero.
251
253
  return 0 if relation.limit_value == 0
252
254
 
253
255
  query_builder = build_count_subquery(relation, column_name, distinct)
256
+ bind_values = query_builder.bind_values + relation.bind_values
254
257
  else
255
258
  column = aggregate_column(column_name)
256
259
 
257
260
  select_value = operation_over_aggregate_column(column, operation, distinct)
258
261
 
259
262
  column_alias = select_value.alias
263
+ column_alias ||= @klass.connection.column_name_for_operation(operation, select_value)
260
264
  relation.select_values = [select_value]
261
265
 
262
266
  query_builder = relation.arel
267
+ bind_values = query_builder.bind_values + relation.bind_values
263
268
  end
264
269
 
265
- result = @klass.connection.select_all(query_builder, nil, relation.bind_values)
270
+ result = @klass.connection.select_all(query_builder, nil, bind_values)
266
271
  row = result.first
267
272
  value = row && row.values.first
268
273
  column = result.column_types.fetch(column_alias) do
269
- column_for(column_name)
274
+ type_for(column_name)
270
275
  end
271
276
 
272
277
  type_cast_calculated_value(value, column, operation)
@@ -276,12 +281,13 @@ module ActiveRecord
276
281
  group_attrs = group_values
277
282
 
278
283
  if group_attrs.first.respond_to?(:to_sym)
279
- association = @klass._reflect_on_association(group_attrs.first.to_sym)
280
- associated = group_attrs.size == 1 && association && association.macro == :belongs_to # only count belongs_to associations
284
+ association = @klass._reflect_on_association(group_attrs.first)
285
+ associated = group_attrs.size == 1 && association && association.belongs_to? # only count belongs_to associations
281
286
  group_fields = Array(associated ? association.foreign_key : group_attrs)
282
287
  else
283
288
  group_fields = group_attrs
284
289
  end
290
+ group_fields = arel_columns(group_fields)
285
291
 
286
292
  group_aliases = group_fields.map { |field|
287
293
  column_alias_for(field)
@@ -304,7 +310,7 @@ module ActiveRecord
304
310
  operation,
305
311
  distinct).as(aggregate_alias)
306
312
  ]
307
- select_values += select_values unless having_values.empty?
313
+ select_values += self.select_values unless having_values.empty?
308
314
 
309
315
  select_values.concat group_fields.zip(group_aliases).map { |field,aliaz|
310
316
  if field.respond_to?(:as)
@@ -318,7 +324,7 @@ module ActiveRecord
318
324
  relation.group_values = group
319
325
  relation.select_values = select_values
320
326
 
321
- calculated_data = @klass.connection.select_all(relation, nil, bind_values)
327
+ calculated_data = @klass.connection.select_all(relation, nil, relation.arel.bind_values + bind_values)
322
328
 
323
329
  if association
324
330
  key_ids = calculated_data.collect { |row| row[group_aliases.first] }
@@ -329,14 +335,14 @@ module ActiveRecord
329
335
  Hash[calculated_data.map do |row|
330
336
  key = group_columns.map { |aliaz, col_name|
331
337
  column = calculated_data.column_types.fetch(aliaz) do
332
- column_for(col_name)
338
+ type_for(col_name)
333
339
  end
334
340
  type_cast_calculated_value(row[aliaz], column)
335
341
  }
336
342
  key = key.first if key.size == 1
337
343
  key = key_records[key] if associated
338
344
 
339
- column_type = calculated_data.column_types.fetch(aggregate_alias) { column_for(column_name) }
345
+ column_type = calculated_data.column_types.fetch(aggregate_alias) { type_for(column_name) }
340
346
  [key, type_cast_calculated_value(row[aggregate_alias], column_type, operation)]
341
347
  end]
342
348
  end
@@ -363,24 +369,20 @@ module ActiveRecord
363
369
  @klass.connection.table_alias_for(table_name)
364
370
  end
365
371
 
366
- def column_for(field)
372
+ def type_for(field)
367
373
  field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split('.').last
368
- @klass.columns_hash[field_name]
374
+ @klass.type_for_attribute(field_name)
369
375
  end
370
376
 
371
- def type_cast_calculated_value(value, column, operation = nil)
377
+ def type_cast_calculated_value(value, type, operation = nil)
372
378
  case operation
373
379
  when 'count' then value.to_i
374
- when 'sum' then type_cast_using_column(value || 0, column)
380
+ when 'sum' then type.type_cast_from_database(value || 0)
375
381
  when 'average' then value.respond_to?(:to_d) ? value.to_d : value
376
- else type_cast_using_column(value, column)
382
+ else type.type_cast_from_database(value)
377
383
  end
378
384
  end
379
385
 
380
- def type_cast_using_column(value, column)
381
- column ? column.type_cast(value) : value
382
- end
383
-
384
386
  # TODO: refactor to allow non-string `select_values` (eg. Arel nodes).
385
387
  def select_for_count
386
388
  if select_values.present?
@@ -396,9 +398,11 @@ module ActiveRecord
396
398
 
397
399
  aliased_column = aggregate_column(column_name == :all ? 1 : column_name).as(column_alias)
398
400
  relation.select_values = [aliased_column]
399
- subquery = relation.arel.as(subquery_alias)
401
+ arel = relation.arel
402
+ subquery = arel.as(subquery_alias)
400
403
 
401
404
  sm = Arel::SelectManager.new relation.engine
405
+ sm.bind_values = arel.bind_values
402
406
  select_value = operation_over_aggregate_column(column_alias, 'count', distinct)
403
407
  sm.project(select_value).from(subquery)
404
408
  end
@@ -1,3 +1,6 @@
1
+ require 'active_support/deprecation'
2
+ require 'active_support/core_ext/string/filters'
3
+
1
4
  module ActiveRecord
2
5
  module FinderMethods
3
6
  ONE_AS_ONE = '1 AS one'
@@ -79,12 +82,16 @@ module ActiveRecord
79
82
  # Post.find_by "published_at < ?", 2.weeks.ago
80
83
  def find_by(*args)
81
84
  where(*args).take
85
+ rescue RangeError
86
+ nil
82
87
  end
83
88
 
84
89
  # Like <tt>find_by</tt>, except that if no record is found, raises
85
90
  # an <tt>ActiveRecord::RecordNotFound</tt> error.
86
91
  def find_by!(*args)
87
92
  where(*args).take!
93
+ rescue RangeError
94
+ raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range value"
88
95
  end
89
96
 
90
97
  # Gives a record (or N records if a parameter is supplied) without any implied
@@ -101,42 +108,30 @@ module ActiveRecord
101
108
  # Same as +take+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
102
109
  # is found. Note that <tt>take!</tt> accepts no arguments.
103
110
  def take!
104
- take or raise RecordNotFound
111
+ take or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
105
112
  end
106
113
 
107
114
  # Find the first record (or first N records if a parameter is supplied).
108
115
  # If no order is defined it will order by primary key.
109
116
  #
110
- # Person.first # returns the first object fetched by SELECT * FROM people
117
+ # Person.first # returns the first object fetched by SELECT * FROM people ORDER BY people.id LIMIT 1
111
118
  # Person.where(["user_name = ?", user_name]).first
112
119
  # Person.where(["user_name = :u", { u: user_name }]).first
113
120
  # Person.order("created_on DESC").offset(5).first
114
- # Person.first(3) # returns the first three objects fetched by SELECT * FROM people LIMIT 3
115
- #
116
- # ==== Rails 3
117
- #
118
- # Person.first # SELECT "people".* FROM "people" LIMIT 1
119
- #
120
- # NOTE: Rails 3 may not order this query by the primary key and the order
121
- # will depend on the database implementation. In order to ensure that behavior,
122
- # use <tt>User.order(:id).first</tt> instead.
123
- #
124
- # ==== Rails 4
125
- #
126
- # Person.first # SELECT "people".* FROM "people" ORDER BY "people"."id" ASC LIMIT 1
121
+ # Person.first(3) # returns the first three objects fetched by SELECT * FROM people ORDER BY people.id LIMIT 3
127
122
  #
128
123
  def first(limit = nil)
129
124
  if limit
130
- find_nth_with_limit(offset_value, limit)
125
+ find_nth_with_limit(offset_index, limit)
131
126
  else
132
- find_nth(:first, offset_value)
127
+ find_nth(0, offset_index)
133
128
  end
134
129
  end
135
130
 
136
131
  # Same as +first+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
137
132
  # is found. Note that <tt>first!</tt> accepts no arguments.
138
133
  def first!
139
- first or raise RecordNotFound
134
+ find_nth! 0
140
135
  end
141
136
 
142
137
  # Find the last record (or last N records if a parameter is supplied).
@@ -169,7 +164,7 @@ module ActiveRecord
169
164
  # Same as +last+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
170
165
  # is found. Note that <tt>last!</tt> accepts no arguments.
171
166
  def last!
172
- last or raise RecordNotFound
167
+ last or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
173
168
  end
174
169
 
175
170
  # Find the second record.
@@ -179,13 +174,13 @@ module ActiveRecord
179
174
  # Person.offset(3).second # returns the second object from OFFSET 3 (which is OFFSET 4)
180
175
  # Person.where(["user_name = :u", { u: user_name }]).second
181
176
  def second
182
- find_nth(:second, offset_value ? offset_value + 1 : 1)
177
+ find_nth(1, offset_index)
183
178
  end
184
179
 
185
180
  # Same as +second+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
186
181
  # is found.
187
182
  def second!
188
- second or raise RecordNotFound
183
+ find_nth! 1
189
184
  end
190
185
 
191
186
  # Find the third record.
@@ -195,13 +190,13 @@ module ActiveRecord
195
190
  # Person.offset(3).third # returns the third object from OFFSET 3 (which is OFFSET 5)
196
191
  # Person.where(["user_name = :u", { u: user_name }]).third
197
192
  def third
198
- find_nth(:third, offset_value ? offset_value + 2 : 2)
193
+ find_nth(2, offset_index)
199
194
  end
200
195
 
201
196
  # Same as +third+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
202
197
  # is found.
203
198
  def third!
204
- third or raise RecordNotFound
199
+ find_nth! 2
205
200
  end
206
201
 
207
202
  # Find the fourth record.
@@ -211,13 +206,13 @@ module ActiveRecord
211
206
  # Person.offset(3).fourth # returns the fourth object from OFFSET 3 (which is OFFSET 6)
212
207
  # Person.where(["user_name = :u", { u: user_name }]).fourth
213
208
  def fourth
214
- find_nth(:fourth, offset_value ? offset_value + 3 : 3)
209
+ find_nth(3, offset_index)
215
210
  end
216
211
 
217
212
  # Same as +fourth+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
218
213
  # is found.
219
214
  def fourth!
220
- fourth or raise RecordNotFound
215
+ find_nth! 3
221
216
  end
222
217
 
223
218
  # Find the fifth record.
@@ -227,13 +222,13 @@ module ActiveRecord
227
222
  # Person.offset(3).fifth # returns the fifth object from OFFSET 3 (which is OFFSET 7)
228
223
  # Person.where(["user_name = :u", { u: user_name }]).fifth
229
224
  def fifth
230
- find_nth(:fifth, offset_value ? offset_value + 4 : 4)
225
+ find_nth(4, offset_index)
231
226
  end
232
227
 
233
228
  # Same as +fifth+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
234
229
  # is found.
235
230
  def fifth!
236
- fifth or raise RecordNotFound
231
+ find_nth! 4
237
232
  end
238
233
 
239
234
  # Find the forty-second record. Also known as accessing "the reddit".
@@ -243,13 +238,13 @@ module ActiveRecord
243
238
  # Person.offset(3).forty_two # returns the forty-second object from OFFSET 3 (which is OFFSET 44)
244
239
  # Person.where(["user_name = :u", { u: user_name }]).forty_two
245
240
  def forty_two
246
- find_nth(:forty_two, offset_value ? offset_value + 41 : 41)
241
+ find_nth(41, offset_index)
247
242
  end
248
243
 
249
244
  # Same as +forty_two+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
250
245
  # is found.
251
246
  def forty_two!
252
- forty_two or raise RecordNotFound
247
+ find_nth! 41
253
248
  end
254
249
 
255
250
  # Returns +true+ if a record exists in the table that matches the +id+ or
@@ -280,7 +275,14 @@ module ActiveRecord
280
275
  # Person.exists?(false)
281
276
  # Person.exists?
282
277
  def exists?(conditions = :none)
283
- conditions = conditions.id if Base === conditions
278
+ if Base === conditions
279
+ conditions = conditions.id
280
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
281
+ You are passing an instance of ActiveRecord::Base to `exists?`.
282
+ Please pass the id of the object by calling `.id`
283
+ MSG
284
+ end
285
+
284
286
  return false if !conditions
285
287
 
286
288
  relation = apply_join_dependency(self, construct_join_dependency)
@@ -292,10 +294,12 @@ module ActiveRecord
292
294
  when Array, Hash
293
295
  relation = relation.where(conditions)
294
296
  else
295
- relation = relation.where(table[primary_key].eq(conditions)) if conditions != :none
297
+ unless conditions == :none
298
+ relation = relation.where(primary_key => conditions)
299
+ end
296
300
  end
297
301
 
298
- connection.select_value(relation, "#{name} Exists", relation.bind_values) ? true : false
302
+ connection.select_value(relation, "#{name} Exists", relation.arel.bind_values + relation.bind_values) ? true : false
299
303
  end
300
304
 
301
305
  # This method is called whenever no records are found with either a single
@@ -322,6 +326,10 @@ module ActiveRecord
322
326
 
323
327
  private
324
328
 
329
+ def offset_index
330
+ offset_value || 0
331
+ end
332
+
325
333
  def find_with_associations
326
334
  # NOTE: the JoinDependency constructed here needs to know about
327
335
  # any joins already present in `self`, so pass them in
@@ -344,7 +352,8 @@ module ActiveRecord
344
352
  if ActiveRecord::NullRelation === relation
345
353
  []
346
354
  else
347
- rows = connection.select_all(relation.arel, 'SQL', relation.bind_values.dup)
355
+ arel = relation.arel
356
+ rows = connection.select_all(arel, 'SQL', arel.bind_values + relation.bind_values)
348
357
  join_dependency.instantiate(rows, aliases)
349
358
  end
350
359
  end
@@ -387,8 +396,9 @@ module ActiveRecord
387
396
  "#{quoted_table_name}.#{quoted_primary_key}", relation.order_values)
388
397
 
389
398
  relation = relation.except(:select).select(values).distinct!
399
+ arel = relation.arel
390
400
 
391
- id_rows = @klass.connection.select_all(relation.arel, 'SQL', relation.bind_values)
401
+ id_rows = @klass.connection.select_all(arel, 'SQL', arel.bind_values + relation.bind_values)
392
402
  id_rows.map {|row| row[primary_key]}
393
403
  end
394
404
 
@@ -415,15 +425,20 @@ module ActiveRecord
415
425
  else
416
426
  find_some(ids)
417
427
  end
428
+ rescue RangeError
429
+ raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range ID"
418
430
  end
419
431
 
420
432
  def find_one(id)
421
- id = id.id if ActiveRecord::Base === id
433
+ if ActiveRecord::Base === id
434
+ id = id.id
435
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
436
+ You are passing an instance of ActiveRecord::Base to `find`.
437
+ Please pass the id of the object by calling `.id`
438
+ MSG
439
+ end
422
440
 
423
- column = columns_hash[primary_key]
424
- substitute = connection.substitute_at(column, bind_values.length)
425
- relation = where(table[primary_key].eq(substitute))
426
- relation.bind_values += [[column, id]]
441
+ relation = where(primary_key => id)
427
442
  record = relation.take
428
443
 
429
444
  raise_record_not_found_exception!(id, 0, 1) unless record
@@ -432,7 +447,7 @@ module ActiveRecord
432
447
  end
433
448
 
434
449
  def find_some(ids)
435
- result = where(table[primary_key].in(ids)).to_a
450
+ result = where(primary_key => ids).to_a
436
451
 
437
452
  expected_size =
438
453
  if limit_value && ids.size > limit_value
@@ -461,20 +476,28 @@ module ActiveRecord
461
476
  end
462
477
  end
463
478
 
464
- def find_nth(ordinal, offset)
479
+ def find_nth(index, offset)
465
480
  if loaded?
466
- @records.send(ordinal)
481
+ @records[index]
467
482
  else
483
+ offset += index
468
484
  @offsets[offset] ||= find_nth_with_limit(offset, 1).first
469
485
  end
470
486
  end
471
487
 
488
+ def find_nth!(index)
489
+ find_nth(index, offset_index) or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
490
+ end
491
+
472
492
  def find_nth_with_limit(offset, limit)
473
- if order_values.empty? && primary_key
474
- order(arel_table[primary_key].asc).limit(limit).offset(offset).to_a
475
- else
476
- limit(limit).offset(offset).to_a
477
- end
493
+ relation = if order_values.empty? && primary_key
494
+ order(arel_table[primary_key].asc)
495
+ else
496
+ self
497
+ end
498
+
499
+ relation = relation.offset(offset) unless offset.zero?
500
+ relation.limit(limit).to_a
478
501
  end
479
502
 
480
503
  def find_last