activerecord 5.1.0 → 5.2.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 (261) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +596 -450
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +5 -5
  5. data/examples/performance.rb +2 -0
  6. data/examples/simple.rb +2 -0
  7. data/lib/active_record.rb +11 -4
  8. data/lib/active_record/aggregations.rb +6 -5
  9. data/lib/active_record/association_relation.rb +7 -5
  10. data/lib/active_record/associations.rb +77 -85
  11. data/lib/active_record/associations/alias_tracker.rb +23 -32
  12. data/lib/active_record/associations/association.rb +49 -35
  13. data/lib/active_record/associations/association_scope.rb +55 -55
  14. data/lib/active_record/associations/belongs_to_association.rb +30 -11
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
  16. data/lib/active_record/associations/builder/association.rb +4 -7
  17. data/lib/active_record/associations/builder/belongs_to.rb +21 -8
  18. data/lib/active_record/associations/builder/collection_association.rb +1 -1
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -1
  20. data/lib/active_record/associations/builder/has_many.rb +2 -0
  21. data/lib/active_record/associations/builder/has_one.rb +2 -0
  22. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  23. data/lib/active_record/associations/collection_association.rb +66 -53
  24. data/lib/active_record/associations/collection_proxy.rb +30 -73
  25. data/lib/active_record/associations/foreign_association.rb +2 -0
  26. data/lib/active_record/associations/has_many_association.rb +13 -2
  27. data/lib/active_record/associations/has_many_through_association.rb +37 -19
  28. data/lib/active_record/associations/has_one_association.rb +14 -1
  29. data/lib/active_record/associations/has_one_through_association.rb +13 -8
  30. data/lib/active_record/associations/join_dependency.rb +52 -96
  31. data/lib/active_record/associations/join_dependency/join_association.rb +22 -75
  32. data/lib/active_record/associations/join_dependency/join_base.rb +9 -8
  33. data/lib/active_record/associations/join_dependency/join_part.rb +9 -9
  34. data/lib/active_record/associations/preloader.rb +17 -37
  35. data/lib/active_record/associations/preloader/association.rb +53 -92
  36. data/lib/active_record/associations/preloader/through_association.rb +72 -73
  37. data/lib/active_record/associations/singular_association.rb +14 -16
  38. data/lib/active_record/associations/through_association.rb +27 -12
  39. data/lib/active_record/attribute_assignment.rb +2 -5
  40. data/lib/active_record/attribute_decorators.rb +3 -2
  41. data/lib/active_record/attribute_methods.rb +65 -24
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -0
  43. data/lib/active_record/attribute_methods/dirty.rb +33 -216
  44. data/lib/active_record/attribute_methods/primary_key.rb +10 -13
  45. data/lib/active_record/attribute_methods/query.rb +2 -0
  46. data/lib/active_record/attribute_methods/read.rb +9 -3
  47. data/lib/active_record/attribute_methods/serialization.rb +23 -0
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +6 -8
  49. data/lib/active_record/attribute_methods/write.rb +22 -19
  50. data/lib/active_record/attributes.rb +7 -6
  51. data/lib/active_record/autosave_association.rb +15 -13
  52. data/lib/active_record/base.rb +2 -0
  53. data/lib/active_record/callbacks.rb +12 -6
  54. data/lib/active_record/coders/json.rb +2 -0
  55. data/lib/active_record/coders/yaml_column.rb +2 -0
  56. data/lib/active_record/collection_cache_key.rb +15 -11
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +120 -39
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -0
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +192 -37
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +13 -2
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -25
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -6
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +65 -7
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +158 -87
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +66 -21
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +86 -98
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +126 -189
  70. data/lib/active_record/connection_adapters/column.rb +4 -2
  71. data/lib/active_record/connection_adapters/connection_specification.rb +17 -3
  72. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +13 -2
  73. data/lib/active_record/connection_adapters/mysql/column.rb +2 -0
  74. data/lib/active_record/connection_adapters/mysql/database_statements.rb +45 -15
  75. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +2 -0
  76. data/lib/active_record/connection_adapters/mysql/quoting.rb +9 -10
  77. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +5 -3
  78. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +7 -10
  79. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +30 -23
  80. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +106 -1
  81. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +2 -0
  82. data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -2
  83. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
  84. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +6 -32
  85. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +2 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid.rb +3 -1
  87. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +13 -1
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +2 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  99. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -1
  101. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +2 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +8 -2
  104. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +4 -2
  106. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
  109. data/lib/active_record/connection_adapters/postgresql/quoting.rb +22 -1
  110. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
  111. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +50 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +24 -11
  113. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +20 -13
  114. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +258 -129
  115. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -0
  116. data/lib/active_record/connection_adapters/postgresql/utils.rb +3 -1
  117. data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -87
  118. data/lib/active_record/connection_adapters/schema_cache.rb +4 -2
  119. data/lib/active_record/connection_adapters/sql_type_metadata.rb +2 -0
  120. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +2 -0
  121. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +24 -1
  122. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +2 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +6 -15
  124. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +3 -2
  125. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +75 -1
  126. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +90 -96
  127. data/lib/active_record/connection_adapters/statement_pool.rb +2 -0
  128. data/lib/active_record/connection_handling.rb +4 -2
  129. data/lib/active_record/core.rb +41 -61
  130. data/lib/active_record/counter_cache.rb +20 -15
  131. data/lib/active_record/define_callbacks.rb +5 -3
  132. data/lib/active_record/dynamic_matchers.rb +9 -9
  133. data/lib/active_record/enum.rb +18 -13
  134. data/lib/active_record/errors.rb +60 -15
  135. data/lib/active_record/explain.rb +3 -1
  136. data/lib/active_record/explain_registry.rb +2 -0
  137. data/lib/active_record/explain_subscriber.rb +2 -0
  138. data/lib/active_record/fixture_set/file.rb +2 -0
  139. data/lib/active_record/fixtures.rb +67 -60
  140. data/lib/active_record/gem_version.rb +4 -2
  141. data/lib/active_record/inheritance.rb +49 -19
  142. data/lib/active_record/integration.rb +58 -19
  143. data/lib/active_record/internal_metadata.rb +2 -0
  144. data/lib/active_record/legacy_yaml_adapter.rb +3 -1
  145. data/lib/active_record/locking/optimistic.rb +30 -42
  146. data/lib/active_record/locking/pessimistic.rb +10 -7
  147. data/lib/active_record/log_subscriber.rb +46 -4
  148. data/lib/active_record/migration.rb +189 -139
  149. data/lib/active_record/migration/command_recorder.rb +11 -9
  150. data/lib/active_record/migration/compatibility.rb +81 -29
  151. data/lib/active_record/migration/join_table.rb +2 -0
  152. data/lib/active_record/model_schema.rb +74 -58
  153. data/lib/active_record/nested_attributes.rb +18 -6
  154. data/lib/active_record/no_touching.rb +3 -1
  155. data/lib/active_record/null_relation.rb +2 -0
  156. data/lib/active_record/persistence.rb +199 -54
  157. data/lib/active_record/query_cache.rb +8 -10
  158. data/lib/active_record/querying.rb +5 -3
  159. data/lib/active_record/railtie.rb +62 -6
  160. data/lib/active_record/railties/console_sandbox.rb +2 -0
  161. data/lib/active_record/railties/controller_runtime.rb +2 -0
  162. data/lib/active_record/railties/databases.rake +48 -38
  163. data/lib/active_record/readonly_attributes.rb +3 -2
  164. data/lib/active_record/reflection.rb +137 -207
  165. data/lib/active_record/relation.rb +132 -207
  166. data/lib/active_record/relation/batches.rb +32 -17
  167. data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
  168. data/lib/active_record/relation/calculations.rb +66 -25
  169. data/lib/active_record/relation/delegation.rb +45 -29
  170. data/lib/active_record/relation/finder_methods.rb +76 -85
  171. data/lib/active_record/relation/from_clause.rb +2 -8
  172. data/lib/active_record/relation/merger.rb +53 -23
  173. data/lib/active_record/relation/predicate_builder.rb +60 -79
  174. data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -7
  175. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  176. data/lib/active_record/relation/predicate_builder/base_handler.rb +2 -2
  177. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +12 -1
  178. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  179. data/lib/active_record/relation/predicate_builder/range_handler.rb +26 -9
  180. data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
  181. data/lib/active_record/relation/query_attribute.rb +28 -2
  182. data/lib/active_record/relation/query_methods.rb +135 -103
  183. data/lib/active_record/relation/record_fetch_warning.rb +2 -0
  184. data/lib/active_record/relation/spawn_methods.rb +4 -2
  185. data/lib/active_record/relation/where_clause.rb +65 -67
  186. data/lib/active_record/relation/where_clause_factory.rb +5 -48
  187. data/lib/active_record/result.rb +2 -0
  188. data/lib/active_record/runtime_registry.rb +2 -0
  189. data/lib/active_record/sanitization.rb +129 -121
  190. data/lib/active_record/schema.rb +4 -2
  191. data/lib/active_record/schema_dumper.rb +36 -26
  192. data/lib/active_record/schema_migration.rb +2 -0
  193. data/lib/active_record/scoping.rb +12 -10
  194. data/lib/active_record/scoping/default.rb +10 -7
  195. data/lib/active_record/scoping/named.rb +40 -12
  196. data/lib/active_record/secure_token.rb +2 -0
  197. data/lib/active_record/serialization.rb +2 -0
  198. data/lib/active_record/statement_cache.rb +22 -12
  199. data/lib/active_record/store.rb +3 -1
  200. data/lib/active_record/suppressor.rb +2 -0
  201. data/lib/active_record/table_metadata.rb +12 -3
  202. data/lib/active_record/tasks/database_tasks.rb +38 -26
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +11 -50
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -3
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -3
  206. data/lib/active_record/timestamp.rb +13 -6
  207. data/lib/active_record/touch_later.rb +2 -0
  208. data/lib/active_record/transactions.rb +32 -27
  209. data/lib/active_record/translation.rb +2 -0
  210. data/lib/active_record/type.rb +4 -1
  211. data/lib/active_record/type/adapter_specific_registry.rb +2 -0
  212. data/lib/active_record/type/date.rb +2 -0
  213. data/lib/active_record/type/date_time.rb +2 -0
  214. data/lib/active_record/type/decimal_without_scale.rb +2 -0
  215. data/lib/active_record/type/hash_lookup_type_map.rb +2 -0
  216. data/lib/active_record/type/internal/timezone.rb +2 -0
  217. data/lib/active_record/type/json.rb +30 -0
  218. data/lib/active_record/type/serialized.rb +6 -0
  219. data/lib/active_record/type/text.rb +2 -0
  220. data/lib/active_record/type/time.rb +2 -0
  221. data/lib/active_record/type/type_map.rb +2 -0
  222. data/lib/active_record/type/unsigned_integer.rb +2 -0
  223. data/lib/active_record/type_caster.rb +2 -0
  224. data/lib/active_record/type_caster/connection.rb +2 -0
  225. data/lib/active_record/type_caster/map.rb +3 -1
  226. data/lib/active_record/validations.rb +2 -0
  227. data/lib/active_record/validations/absence.rb +2 -0
  228. data/lib/active_record/validations/associated.rb +2 -0
  229. data/lib/active_record/validations/length.rb +2 -0
  230. data/lib/active_record/validations/presence.rb +2 -0
  231. data/lib/active_record/validations/uniqueness.rb +36 -6
  232. data/lib/active_record/version.rb +2 -0
  233. data/lib/rails/generators/active_record.rb +3 -1
  234. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  235. data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
  236. data/lib/rails/generators/active_record/migration.rb +2 -0
  237. data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -1
  238. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +0 -0
  239. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +0 -0
  240. data/lib/rails/generators/active_record/model/model_generator.rb +2 -23
  241. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +0 -0
  242. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  243. metadata +24 -36
  244. data/lib/active_record/associations/preloader/belongs_to.rb +0 -15
  245. data/lib/active_record/associations/preloader/collection_association.rb +0 -17
  246. data/lib/active_record/associations/preloader/has_many.rb +0 -15
  247. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  248. data/lib/active_record/associations/preloader/has_one.rb +0 -15
  249. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  250. data/lib/active_record/associations/preloader/singular_association.rb +0 -18
  251. data/lib/active_record/attribute.rb +0 -240
  252. data/lib/active_record/attribute/user_provided_default.rb +0 -30
  253. data/lib/active_record/attribute_mutation_tracker.rb +0 -113
  254. data/lib/active_record/attribute_set.rb +0 -113
  255. data/lib/active_record/attribute_set/builder.rb +0 -124
  256. data/lib/active_record/attribute_set/yaml_encoder.rb +0 -41
  257. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -10
  258. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  259. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
  260. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -59
  261. data/lib/active_record/type/internal/abstract_json.rb +0 -33
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_record/relation/batches/batch_enumerator"
2
4
 
3
5
  module ActiveRecord
@@ -30,14 +32,14 @@ module ActiveRecord
30
32
  # end
31
33
  #
32
34
  # ==== Options
33
- # * <tt>:batch_size</tt> - Specifies the size of the batch. Default to 1000.
35
+ # * <tt>:batch_size</tt> - Specifies the size of the batch. Defaults to 1000.
34
36
  # * <tt>:start</tt> - Specifies the primary key value to start from, inclusive of the value.
35
37
  # * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
36
38
  # * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
37
- # an order is present in the relation.
39
+ # an order is present in the relation.
38
40
  #
39
41
  # Limits are honored, and if present there is no requirement for the batch
40
- # size, it can be less than, equal, or greater than the limit.
42
+ # size: it can be less than, equal to, or greater than the limit.
41
43
  #
42
44
  # The options +start+ and +finish+ are especially useful if you want
43
45
  # multiple workers dealing with the same processing queue. You can make
@@ -45,7 +47,12 @@ module ActiveRecord
45
47
  # handle from 10000 and beyond by setting the +:start+ and +:finish+
46
48
  # option on each worker.
47
49
  #
48
- # # Let's process from record 10_000 on.
50
+ # # In worker 1, let's process until 9999 records.
51
+ # Person.find_each(finish: 9_999) do |person|
52
+ # person.party_all_night!
53
+ # end
54
+ #
55
+ # # In worker 2, let's process from record 10_000 and onwards.
49
56
  # Person.find_each(start: 10_000) do |person|
50
57
  # person.party_all_night!
51
58
  # end
@@ -89,14 +96,14 @@ module ActiveRecord
89
96
  # To be yielded each record one by one, use #find_each instead.
90
97
  #
91
98
  # ==== Options
92
- # * <tt>:batch_size</tt> - Specifies the size of the batch. Default to 1000.
99
+ # * <tt>:batch_size</tt> - Specifies the size of the batch. Defaults to 1000.
93
100
  # * <tt>:start</tt> - Specifies the primary key value to start from, inclusive of the value.
94
101
  # * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
95
102
  # * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
96
- # an order is present in the relation.
103
+ # an order is present in the relation.
97
104
  #
98
105
  # Limits are honored, and if present there is no requirement for the batch
99
- # size, it can be less than, equal, or greater than the limit.
106
+ # size: it can be less than, equal to, or greater than the limit.
100
107
  #
101
108
  # The options +start+ and +finish+ are especially useful if you want
102
109
  # multiple workers dealing with the same processing queue. You can make
@@ -140,9 +147,9 @@ module ActiveRecord
140
147
  # If you do not provide a block to #in_batches, it will return a
141
148
  # BatchEnumerator which is enumerable.
142
149
  #
143
- # Person.in_batches.with_index do |relation, batch_index|
150
+ # Person.in_batches.each_with_index do |relation, batch_index|
144
151
  # puts "Processing relation ##{batch_index}"
145
- # relation.each { |relation| relation.delete_all }
152
+ # relation.delete_all
146
153
  # end
147
154
  #
148
155
  # Examples of calling methods on the returned BatchEnumerator object:
@@ -152,12 +159,12 @@ module ActiveRecord
152
159
  # Person.in_batches.each_record(&:party_all_night!)
153
160
  #
154
161
  # ==== Options
155
- # * <tt>:of</tt> - Specifies the size of the batch. Default to 1000.
156
- # * <tt>:load</tt> - Specifies if the relation should be loaded. Default to false.
162
+ # * <tt>:of</tt> - Specifies the size of the batch. Defaults to 1000.
163
+ # * <tt>:load</tt> - Specifies if the relation should be loaded. Defaults to false.
157
164
  # * <tt>:start</tt> - Specifies the primary key value to start from, inclusive of the value.
158
165
  # * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
159
166
  # * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
160
- # an order is present in the relation.
167
+ # an order is present in the relation.
161
168
  #
162
169
  # Limits are honored, and if present there is no requirement for the batch
163
170
  # size, it can be less than, equal, or greater than the limit.
@@ -186,7 +193,7 @@ module ActiveRecord
186
193
  #
187
194
  # NOTE: It's not possible to set the order. That is automatically set to
188
195
  # ascending on the primary key ("id ASC") to make the batch ordering
189
- # consistent. Therefore the primary key must be orderable, e.g an integer
196
+ # consistent. Therefore the primary key must be orderable, e.g. an integer
190
197
  # or a string.
191
198
  #
192
199
  # NOTE: By its nature, batch processing is subject to race conditions if
@@ -209,6 +216,7 @@ module ActiveRecord
209
216
 
210
217
  relation = relation.reorder(batch_order).limit(batch_limit)
211
218
  relation = apply_limits(relation, start, finish)
219
+ relation.skip_query_cache! # Retaining the results in the query cache would undermine the point of batching
212
220
  batch_relation = relation
213
221
 
214
222
  loop do
@@ -243,20 +251,27 @@ module ActiveRecord
243
251
  end
244
252
  end
245
253
 
246
- batch_relation = relation.where(arel_attribute(primary_key).gt(primary_key_offset))
254
+ attr = Relation::QueryAttribute.new(primary_key, primary_key_offset, klass.type_for_attribute(primary_key))
255
+ batch_relation = relation.where(arel_attribute(primary_key).gt(Arel::Nodes::BindParam.new(attr)))
247
256
  end
248
257
  end
249
258
 
250
259
  private
251
260
 
252
261
  def apply_limits(relation, start, finish)
253
- relation = relation.where(arel_attribute(primary_key).gteq(start)) if start
254
- relation = relation.where(arel_attribute(primary_key).lteq(finish)) if finish
262
+ if start
263
+ attr = Relation::QueryAttribute.new(primary_key, start, klass.type_for_attribute(primary_key))
264
+ relation = relation.where(arel_attribute(primary_key).gteq(Arel::Nodes::BindParam.new(attr)))
265
+ end
266
+ if finish
267
+ attr = Relation::QueryAttribute.new(primary_key, finish, klass.type_for_attribute(primary_key))
268
+ relation = relation.where(arel_attribute(primary_key).lteq(Arel::Nodes::BindParam.new(attr)))
269
+ end
255
270
  relation
256
271
  end
257
272
 
258
273
  def batch_order
259
- "#{quoted_table_name}.#{quoted_primary_key} ASC"
274
+ arel_attribute(primary_key).asc
260
275
  end
261
276
 
262
277
  def act_on_ignored_order(error_on_ignore)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Batches
3
5
  class BatchEnumerator
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Calculations
3
5
  # Count the records.
@@ -37,7 +39,16 @@ module ActiveRecord
37
39
  # Note: not all valid {Relation#select}[rdoc-ref:QueryMethods#select] expressions are valid #count expressions. The specifics differ
38
40
  # between databases. In invalid cases, an error from the database is thrown.
39
41
  def count(column_name = nil)
40
- return super() if block_given?
42
+ if block_given?
43
+ unless column_name.nil?
44
+ ActiveSupport::Deprecation.warn \
45
+ "When `count' is called with a block, it ignores other arguments. " \
46
+ "This behavior is now deprecated and will result in an ArgumentError in Rails 6.0."
47
+ end
48
+
49
+ return super()
50
+ end
51
+
41
52
  calculate(:count, column_name)
42
53
  end
43
54
 
@@ -73,7 +84,16 @@ module ActiveRecord
73
84
  #
74
85
  # Person.sum(:age) # => 4562
75
86
  def sum(column_name = nil)
76
- return super() if block_given?
87
+ if block_given?
88
+ unless column_name.nil?
89
+ ActiveSupport::Deprecation.warn \
90
+ "When `sum' is called with a block, it ignores other arguments. " \
91
+ "This behavior is now deprecated and will result in an ArgumentError in Rails 6.0."
92
+ end
93
+
94
+ return super()
95
+ end
96
+
77
97
  calculate(:sum, column_name)
78
98
  end
79
99
 
@@ -110,8 +130,15 @@ module ActiveRecord
110
130
  # end
111
131
  def calculate(operation, column_name)
112
132
  if has_include?(column_name)
113
- relation = construct_relation_for_association_calculations
114
- relation = relation.distinct if operation.to_s.downcase == "count"
133
+ relation = apply_join_dependency
134
+
135
+ if operation.to_s.downcase == "count"
136
+ relation.distinct!
137
+ # PostgreSQL: ORDER BY expressions must appear in SELECT list when using DISTINCT
138
+ if (column_name == :all || column_name.nil?) && select_values.empty?
139
+ relation.order_values = []
140
+ end
141
+ end
115
142
 
116
143
  relation.calculate(operation, column_name)
117
144
  else
@@ -160,13 +187,13 @@ module ActiveRecord
160
187
  end
161
188
 
162
189
  if has_include?(column_names.first)
163
- construct_relation_for_association_calculations.pluck(*column_names)
190
+ relation = apply_join_dependency
191
+ relation.pluck(*column_names)
164
192
  else
193
+ klass.enforce_raw_sql_whitelist(column_names)
165
194
  relation = spawn
166
- relation.select_values = column_names.map { |cn|
167
- @klass.has_attribute?(cn) || @klass.attribute_alias?(cn) ? arel_attribute(cn) : cn
168
- }
169
- result = klass.connection.select_all(relation.arel, nil, bound_attributes)
195
+ relation.select_values = column_names
196
+ result = skip_query_cache_if_necessary { klass.connection.select_all(relation.arel, nil) }
170
197
  result.cast_values(klass.attribute_types)
171
198
  end
172
199
  end
@@ -180,7 +207,6 @@ module ActiveRecord
180
207
  end
181
208
 
182
209
  private
183
-
184
210
  def has_include?(column_name)
185
211
  eager_loading? || (includes_values.present? && column_name && column_name != :all)
186
212
  end
@@ -194,8 +220,15 @@ module ActiveRecord
194
220
 
195
221
  if operation == "count"
196
222
  column_name ||= select_for_count
197
- column_name = primary_key if column_name == :all && distinct
198
- distinct = nil if column_name =~ /\s*DISTINCT[\s(]+/i
223
+ if column_name == :all
224
+ if !distinct
225
+ distinct = distinct_select?(select_for_count) if group_values.empty?
226
+ elsif group_values.any? || select_values.empty? && order_values.empty?
227
+ column_name = primary_key
228
+ end
229
+ elsif distinct_select?(column_name)
230
+ distinct = nil
231
+ end
199
232
  end
200
233
 
201
234
  if group_values.any?
@@ -205,10 +238,14 @@ module ActiveRecord
205
238
  end
206
239
  end
207
240
 
241
+ def distinct_select?(column_name)
242
+ column_name.is_a?(::String) && /\bDISTINCT[\s(]/i.match?(column_name)
243
+ end
244
+
208
245
  def aggregate_column(column_name)
209
246
  return column_name if Arel::Expressions === column_name
210
247
 
211
- if @klass.has_attribute?(column_name.to_s) || @klass.attribute_alias?(column_name.to_s)
248
+ if @klass.has_attribute?(column_name) || @klass.attribute_alias?(column_name)
212
249
  @klass.arel_attribute(column_name)
213
250
  else
214
251
  Arel.sql(column_name == :all ? "*" : column_name.to_s)
@@ -222,7 +259,7 @@ module ActiveRecord
222
259
  def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
223
260
  column_alias = column_name
224
261
 
225
- if operation == "count" && (limit_value || offset_value)
262
+ if operation == "count" && (column_name == :all && distinct || has_limit_or_offset?)
226
263
  # Shortcut when limit is zero.
227
264
  return 0 if limit_value == 0
228
265
 
@@ -234,6 +271,9 @@ module ActiveRecord
234
271
  column = aggregate_column(column_name)
235
272
 
236
273
  select_value = operation_over_aggregate_column(column, operation, distinct)
274
+ if operation == "sum" && distinct
275
+ select_value.distinct = true
276
+ end
237
277
 
238
278
  column_alias = select_value.alias
239
279
  column_alias ||= @klass.connection.column_name_for_operation(operation, select_value)
@@ -242,7 +282,7 @@ module ActiveRecord
242
282
  query_builder = relation.arel
243
283
  end
244
284
 
245
- result = @klass.connection.select_all(query_builder, nil, bound_attributes)
285
+ result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder, nil) }
246
286
  row = result.first
247
287
  value = row && row.values.first
248
288
  type = result.column_types.fetch(column_alias) do
@@ -293,7 +333,7 @@ module ActiveRecord
293
333
  relation.group_values = group_fields
294
334
  relation.select_values = select_values
295
335
 
296
- calculated_data = @klass.connection.select_all(relation, nil, relation.bound_attributes)
336
+ calculated_data = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, nil) }
297
337
 
298
338
  if association
299
339
  key_ids = calculated_data.collect { |row| row[group_aliases.first] }
@@ -346,7 +386,7 @@ module ActiveRecord
346
386
  case operation
347
387
  when "count" then value.to_i
348
388
  when "sum" then type.deserialize(value || 0)
349
- when "average" then value.respond_to?(:to_d) ? value.to_d : value
389
+ when "average" then value && value.respond_to?(:to_d) ? value.to_d : value
350
390
  else type.deserialize(value)
351
391
  end
352
392
  end
@@ -361,16 +401,17 @@ module ActiveRecord
361
401
  end
362
402
 
363
403
  def build_count_subquery(relation, column_name, distinct)
364
- column_alias = Arel.sql("count_column")
365
- subquery_alias = Arel.sql("subquery_for_count")
404
+ if column_name == :all
405
+ relation.select_values = [ Arel.sql(FinderMethods::ONE_AS_ONE) ] unless distinct
406
+ else
407
+ column_alias = Arel.sql("count_column")
408
+ relation.select_values = [ aggregate_column(column_name).as(column_alias) ]
409
+ end
366
410
 
367
- aliased_column = aggregate_column(column_name == :all ? 1 : column_name).as(column_alias)
368
- relation.select_values = [aliased_column]
369
- subquery = relation.arel.as(subquery_alias)
411
+ subquery = relation.arel.as(Arel.sql("subquery_for_count"))
412
+ select_value = operation_over_aggregate_column(column_alias || Arel.star, "count", false)
370
413
 
371
- sm = Arel::SelectManager.new relation.engine
372
- select_value = operation_over_aggregate_column(column_alias, "count", distinct)
373
- sm.project(select_value).from(subquery)
414
+ Arel::SelectManager.new(subquery).project(select_value)
374
415
  end
375
416
  end
376
417
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Delegation # :nodoc:
3
5
  module DelegateCache # :nodoc:
@@ -15,6 +17,7 @@ module ActiveRecord
15
17
  delegate = Class.new(klass) {
16
18
  include ClassSpecificRelation
17
19
  }
20
+ include_relation_methods(delegate)
18
21
  mangled_name = klass.name.gsub("::".freeze, "_".freeze)
19
22
  const_set mangled_name, delegate
20
23
  private_constant mangled_name
@@ -25,10 +28,37 @@ module ActiveRecord
25
28
 
26
29
  def inherited(child_class)
27
30
  child_class.initialize_relation_delegate_cache
28
- delegate = child_class.relation_delegate_class(ActiveRecord::Associations::CollectionProxy)
29
- delegate.include ActiveRecord::Associations::CollectionProxy::DelegateExtending
30
31
  super
31
32
  end
33
+
34
+ protected
35
+ def include_relation_methods(delegate)
36
+ superclass.include_relation_methods(delegate) unless base_class == self
37
+ delegate.include generated_relation_methods
38
+ end
39
+
40
+ private
41
+ def generated_relation_methods
42
+ @generated_relation_methods ||= Module.new.tap do |mod|
43
+ mod_name = "GeneratedRelationMethods"
44
+ const_set mod_name, mod
45
+ private_constant mod_name
46
+ end
47
+ end
48
+
49
+ def generate_relation_method(method)
50
+ if /\A[a-zA-Z_]\w*[!?]?\z/.match?(method)
51
+ generated_relation_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
52
+ def #{method}(*args, &block)
53
+ scoping { klass.#{method}(*args, &block) }
54
+ end
55
+ RUBY
56
+ else
57
+ generated_relation_methods.send(:define_method, method) do |*args, &block|
58
+ scoping { klass.public_send(method, *args, &block) }
59
+ end
60
+ end
61
+ end
32
62
  end
33
63
 
34
64
  extend ActiveSupport::Concern
@@ -38,13 +68,12 @@ module ActiveRecord
38
68
  # may vary depending on the klass of a relation, so we create a subclass of Relation
39
69
  # for each different klass, and the delegations are compiled into that subclass only.
40
70
 
41
- delegate :to_xml, :encode_with, :length, :each, :uniq, :to_ary, :join,
42
- :[], :&, :|, :+, :-, :sample, :reverse, :compact, :in_groups, :in_groups_of,
71
+ delegate :to_xml, :encode_with, :length, :each, :uniq, :join,
72
+ :[], :&, :|, :+, :-, :sample, :reverse, :rotate, :compact, :in_groups, :in_groups_of,
43
73
  :to_sentence, :to_formatted_s, :as_json,
44
- :shuffle, :split, :index, to: :records
74
+ :shuffle, :split, :slice, :index, :rindex, to: :records
45
75
 
46
- delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key,
47
- :connection, :columns_hash, to: :klass
76
+ delegate :primary_key, :connection, to: :klass
48
77
 
49
78
  module ClassSpecificRelation # :nodoc:
50
79
  extend ActiveSupport::Concern
@@ -75,13 +104,6 @@ module ActiveRecord
75
104
  end
76
105
  end
77
106
  end
78
-
79
- def delegate(method, opts = {})
80
- @delegation_mutex.synchronize do
81
- return if method_defined?(method)
82
- super
83
- end
84
- end
85
107
  end
86
108
 
87
109
  private
@@ -90,8 +112,14 @@ module ActiveRecord
90
112
  if @klass.respond_to?(method)
91
113
  self.class.delegate_to_scoped_klass(method)
92
114
  scoping { @klass.public_send(method, *args, &block) }
115
+ elsif @delegate_to_klass && @klass.respond_to?(method, true)
116
+ ActiveSupport::Deprecation.warn \
117
+ "Delegating missing #{method} method to #{@klass}. " \
118
+ "Accessibility of private/protected class methods in :scope is deprecated and will be removed in Rails 6.0."
119
+ @klass.send(method, *args, &block)
93
120
  elsif arel.respond_to?(method)
94
- self.class.delegate method, to: :arel
121
+ ActiveSupport::Deprecation.warn \
122
+ "Delegating #{method} to arel is deprecated and will be removed in Rails 6.0."
95
123
  arel.public_send(method, *args, &block)
96
124
  else
97
125
  super
@@ -111,21 +139,9 @@ module ActiveRecord
111
139
  end
112
140
  end
113
141
 
114
- def respond_to_missing?(method, include_private = false)
115
- super || @klass.respond_to?(method, include_private) ||
116
- arel.respond_to?(method, include_private)
117
- end
118
-
119
142
  private
120
-
121
- def method_missing(method, *args, &block)
122
- if @klass.respond_to?(method)
123
- scoping { @klass.public_send(method, *args, &block) }
124
- elsif arel.respond_to?(method)
125
- arel.public_send(method, *args, &block)
126
- else
127
- super
128
- end
143
+ def respond_to_missing?(method, _)
144
+ super || @klass.respond_to?(method) || arel.respond_to?(method)
129
145
  end
130
146
  end
131
147
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext/string/filters"
2
4
 
3
5
  module ActiveRecord
@@ -16,9 +18,10 @@ module ActiveRecord
16
18
  # Person.find([1]) # returns an array for the object with ID = 1
17
19
  # Person.where("administrator = 1").order("created_on DESC").find(1)
18
20
  #
19
- # NOTE: The returned records may not be in the same order as the ids you
20
- # provide since database rows are unordered. You will need to provide an explicit QueryMethods#order
21
- # option if you want the results to be sorted.
21
+ # NOTE: The returned records are in the same order as the ids you provide.
22
+ # If you want the results to be sorted by database, you can use ActiveRecord::QueryMethods#where
23
+ # method and provide an explicit ActiveRecord::QueryMethods#order option.
24
+ # But ActiveRecord::QueryMethods#where method doesn't raise ActiveRecord::RecordNotFound.
22
25
  #
23
26
  # ==== Find with lock
24
27
  #
@@ -86,7 +89,7 @@ module ActiveRecord
86
89
  where(arg, *args).take!
87
90
  rescue ::RangeError
88
91
  raise RecordNotFound.new("Couldn't find #{@klass.name} with an out of range value",
89
- @klass.name)
92
+ @klass.name, @klass.primary_key)
90
93
  end
91
94
 
92
95
  # Gives a record (or N records if a parameter is supplied) without any implied
@@ -145,10 +148,9 @@ module ActiveRecord
145
148
  #
146
149
  # [#<Person id:4>, #<Person id:3>, #<Person id:2>]
147
150
  def last(limit = nil)
148
- return find_last(limit) if loaded? || limit_value
151
+ return find_last(limit) if loaded? || has_limit_or_offset?
149
152
 
150
- result = limit(limit)
151
- result.order!(arel_attribute(primary_key)) if order_values.empty? && primary_key
153
+ result = ordered_relation.limit(limit)
152
154
  result = result.reverse_order!
153
155
 
154
156
  limit ? result.reverse : result.first
@@ -283,7 +285,7 @@ module ActiveRecord
283
285
  # * Hash - Finds the record that matches these +find+-style conditions
284
286
  # (such as <tt>{name: 'David'}</tt>).
285
287
  # * +false+ - Returns always +false+.
286
- # * No args - Returns +false+ if the table is empty, +true+ otherwise.
288
+ # * No args - Returns +false+ if the relation is empty, +true+ otherwise.
287
289
  #
288
290
  # For more information about specifying conditions as a hash or array,
289
291
  # see the Conditions section in the introduction to ActiveRecord::Base.
@@ -299,6 +301,7 @@ module ActiveRecord
299
301
  # Person.exists?(name: 'David')
300
302
  # Person.exists?(false)
301
303
  # Person.exists?
304
+ # Person.where(name: 'Spartacus', rating: 4).exists?
302
305
  def exists?(conditions = :none)
303
306
  if Base === conditions
304
307
  raise ArgumentError, <<-MSG.squish
@@ -307,14 +310,16 @@ module ActiveRecord
307
310
  MSG
308
311
  end
309
312
 
310
- return false if !conditions
313
+ return false if !conditions || limit_value == 0
311
314
 
312
- relation = apply_join_dependency(self, construct_join_dependency(eager_loading: false))
313
- return false if ActiveRecord::NullRelation === relation
315
+ if eager_loading?
316
+ relation = apply_join_dependency(eager_loading: false)
317
+ return relation.exists?(conditions)
318
+ end
314
319
 
315
- relation = construct_relation_for_exists(relation, conditions)
320
+ relation = construct_relation_for_exists(conditions)
316
321
 
317
- connection.select_value(relation, "#{name} Exists", relation.bound_attributes) ? true : false
322
+ skip_query_cache_if_necessary { connection.select_one(relation.arel, "#{name} Exists") } ? true : false
318
323
  rescue ::RangeError
319
324
  false
320
325
  end
@@ -327,23 +332,23 @@ module ActiveRecord
327
332
  # of results obtained should be provided in the +result_size+ argument and
328
333
  # the expected number of results should be provided in the +expected_size+
329
334
  # argument.
330
- def raise_record_not_found_exception!(ids = nil, result_size = nil, expected_size = nil, key = primary_key) # :nodoc:
331
- conditions = arel.where_sql(@klass.arel_engine)
335
+ def raise_record_not_found_exception!(ids = nil, result_size = nil, expected_size = nil, key = primary_key, not_found_ids = nil) # :nodoc:
336
+ conditions = arel.where_sql(@klass)
332
337
  conditions = " [#{conditions}]" if conditions
333
338
  name = @klass.name
334
339
 
335
340
  if ids.nil?
336
- error = "Couldn't find #{name}"
341
+ error = "Couldn't find #{name}".dup
337
342
  error << " with#{conditions}" if conditions
338
- raise RecordNotFound.new(error, name)
343
+ raise RecordNotFound.new(error, name, key)
339
344
  elsif Array(ids).size == 1
340
345
  error = "Couldn't find #{name} with '#{key}'=#{ids}#{conditions}"
341
346
  raise RecordNotFound.new(error, name, key, ids)
342
347
  else
343
- error = "Couldn't find all #{name.pluralize} with '#{key}': "
344
- error << "(#{ids.join(", ")})#{conditions} (found #{result_size} results, but was looking for #{expected_size})"
345
-
346
- raise RecordNotFound.new(error, name, primary_key, ids)
348
+ error = "Couldn't find all #{name.pluralize} with '#{key}': ".dup
349
+ error << "(#{ids.join(", ")})#{conditions} (found #{result_size} results, but was looking for #{expected_size})."
350
+ error << " Couldn't find #{name.pluralize(not_found_ids.size)} with #{key.to_s.pluralize(not_found_ids.size)} #{not_found_ids.join(', ')}." if not_found_ids
351
+ raise RecordNotFound.new(error, name, key, ids)
347
352
  end
348
353
  end
349
354
 
@@ -353,41 +358,16 @@ module ActiveRecord
353
358
  offset_value || 0
354
359
  end
355
360
 
356
- def find_with_associations
357
- # NOTE: the JoinDependency constructed here needs to know about
358
- # any joins already present in `self`, so pass them in
359
- #
360
- # failing to do so means that in cases like activerecord/test/cases/associations/inner_join_association_test.rb:136
361
- # incorrect SQL is generated. In that case, the join dependency for
362
- # SpecialCategorizations is constructed without knowledge of the
363
- # preexisting join in joins_values to categorizations (by way of
364
- # the `has_many :through` for categories).
365
- #
366
- join_dependency = construct_join_dependency(joins_values)
367
-
368
- aliases = join_dependency.aliases
369
- relation = select aliases.columns
370
- relation = apply_join_dependency(relation, join_dependency)
371
-
372
- if block_given?
373
- yield relation
361
+ def construct_relation_for_exists(conditions)
362
+ if distinct_value && offset_value
363
+ relation = limit(1)
374
364
  else
375
- if ActiveRecord::NullRelation === relation
376
- []
377
- else
378
- arel = relation.arel
379
- rows = connection.select_all(arel, "SQL", relation.bound_attributes)
380
- join_dependency.instantiate(rows, aliases)
381
- end
365
+ relation = except(:select, :distinct, :order)._select!(ONE_AS_ONE).limit!(1)
382
366
  end
383
- end
384
-
385
- def construct_relation_for_exists(relation, conditions)
386
- relation = relation.except(:select, :distinct, :order)._select!(ONE_AS_ONE).limit!(1)
387
367
 
388
368
  case conditions
389
369
  when Array, Hash
390
- relation.where!(conditions)
370
+ relation.where!(conditions) unless conditions.empty?
391
371
  else
392
372
  relation.where!(primary_key => conditions) unless conditions == :none
393
373
  end
@@ -395,37 +375,41 @@ module ActiveRecord
395
375
  relation
396
376
  end
397
377
 
398
- def construct_join_dependency(joins = [], eager_loading: true)
378
+ def construct_join_dependency
399
379
  including = eager_load_values + includes_values
400
- ActiveRecord::Associations::JoinDependency.new(@klass, including, joins, eager_loading: eager_loading)
380
+ ActiveRecord::Associations::JoinDependency.new(
381
+ klass, table, including
382
+ )
401
383
  end
402
384
 
403
- def construct_relation_for_association_calculations
404
- apply_join_dependency(self, construct_join_dependency(joins_values))
405
- end
385
+ def apply_join_dependency(eager_loading: group_values.empty?)
386
+ join_dependency = construct_join_dependency
387
+ relation = except(:includes, :eager_load, :preload).joins!(join_dependency)
406
388
 
407
- def apply_join_dependency(relation, join_dependency)
408
- relation = relation.except(:includes, :eager_load, :preload).joins!(join_dependency)
409
-
410
- if using_limitable_reflections?(join_dependency.reflections)
411
- relation
412
- else
413
- if relation.limit_value
389
+ if eager_loading && !using_limitable_reflections?(join_dependency.reflections)
390
+ if has_limit_or_offset?
414
391
  limited_ids = limited_ids_for(relation)
415
392
  limited_ids.empty? ? relation.none! : relation.where!(primary_key => limited_ids)
416
393
  end
417
- relation.except(:limit, :offset)
394
+ relation.limit_value = relation.offset_value = nil
395
+ end
396
+
397
+ if block_given?
398
+ yield relation, join_dependency
399
+ else
400
+ relation
418
401
  end
419
402
  end
420
403
 
421
404
  def limited_ids_for(relation)
422
405
  values = @klass.connection.columns_for_distinct(
423
- "#{quoted_table_name}.#{quoted_primary_key}", relation.order_values)
406
+ connection.column_name_from_arel_node(arel_attribute(primary_key)),
407
+ relation.order_values
408
+ )
424
409
 
425
410
  relation = relation.except(:select).select(values).distinct!
426
- arel = relation.arel
427
411
 
428
- id_rows = @klass.connection.select_all(arel, "SQL", relation.bound_attributes)
412
+ id_rows = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, "SQL") }
429
413
  id_rows.map { |row| row[primary_key] }
430
414
  end
431
415
 
@@ -437,13 +421,16 @@ module ActiveRecord
437
421
  raise UnknownPrimaryKey.new(@klass) if primary_key.nil?
438
422
 
439
423
  expects_array = ids.first.kind_of?(Array)
440
- return ids.first if expects_array && ids.first.empty?
424
+ return [] if expects_array && ids.first.empty?
441
425
 
442
426
  ids = ids.flatten.compact.uniq
443
427
 
428
+ model_name = @klass.name
429
+
444
430
  case ids.size
445
431
  when 0
446
- raise RecordNotFound, "Couldn't find #{@klass.name} without an ID"
432
+ error_message = "Couldn't find #{model_name} without an ID"
433
+ raise RecordNotFound.new(error_message, model_name, primary_key)
447
434
  when 1
448
435
  result = find_one(ids.first)
449
436
  expects_array ? [ result ] : result
@@ -451,7 +438,8 @@ module ActiveRecord
451
438
  find_some(ids)
452
439
  end
453
440
  rescue ::RangeError
454
- raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range ID"
441
+ error_message = "Couldn't find #{model_name} with an out of range ID"
442
+ raise RecordNotFound.new(error_message, model_name, primary_key, ids)
455
443
  end
456
444
 
457
445
  def find_one(id)
@@ -533,13 +521,13 @@ module ActiveRecord
533
521
  if loaded?
534
522
  records[index, limit] || []
535
523
  else
536
- relation = if order_values.empty? && primary_key
537
- order(arel_attribute(primary_key).asc)
538
- else
539
- self
524
+ relation = ordered_relation
525
+
526
+ if limit_value
527
+ limit = [limit_value - index, limit].min
540
528
  end
541
529
 
542
- if limit_value.nil? || index < limit_value
530
+ if limit > 0
543
531
  relation = relation.offset(offset_index + index) unless index.zero?
544
532
  relation.limit(limit).to_a
545
533
  else
@@ -552,23 +540,26 @@ module ActiveRecord
552
540
  if loaded?
553
541
  records[-index]
554
542
  else
555
- relation = if order_values.empty? && primary_key
556
- order(arel_attribute(primary_key).asc)
543
+ relation = ordered_relation
544
+
545
+ if equal?(relation) || has_limit_or_offset?
546
+ relation.records[-index]
557
547
  else
558
- self
548
+ relation.last(index)[-index]
559
549
  end
560
-
561
- relation.to_a[-index]
562
- # TODO: can be made more performant on large result sets by
563
- # for instance, last(index)[-index] (which would require
564
- # refactoring the last(n) finder method to make test suite pass),
565
- # or by using a combination of reverse_order, limit, and offset,
566
- # e.g., reverse_order.offset(index-1).first
567
550
  end
568
551
  end
569
552
 
570
553
  def find_last(limit)
571
554
  limit ? records.last(limit) : records.last
572
555
  end
556
+
557
+ def ordered_relation
558
+ if order_values.empty? && primary_key
559
+ order(arel_attribute(primary_key).asc)
560
+ else
561
+ self
562
+ end
563
+ end
573
564
  end
574
565
  end