activerecord 5.1.0 → 5.2.0.rc1

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 (260) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +410 -530
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -4
  5. data/examples/performance.rb +2 -0
  6. data/examples/simple.rb +2 -0
  7. data/lib/active_record/aggregations.rb +6 -5
  8. data/lib/active_record/association_relation.rb +4 -2
  9. data/lib/active_record/associations/alias_tracker.rb +23 -32
  10. data/lib/active_record/associations/association.rb +20 -21
  11. data/lib/active_record/associations/association_scope.rb +49 -49
  12. data/lib/active_record/associations/belongs_to_association.rb +12 -10
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +4 -7
  14. data/lib/active_record/associations/builder/association.rb +4 -7
  15. data/lib/active_record/associations/builder/belongs_to.rb +10 -6
  16. data/lib/active_record/associations/builder/collection_association.rb +1 -1
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -1
  18. data/lib/active_record/associations/builder/has_many.rb +2 -0
  19. data/lib/active_record/associations/builder/has_one.rb +2 -0
  20. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  21. data/lib/active_record/associations/collection_association.rb +50 -41
  22. data/lib/active_record/associations/collection_proxy.rb +22 -39
  23. data/lib/active_record/associations/foreign_association.rb +2 -0
  24. data/lib/active_record/associations/has_many_association.rb +4 -2
  25. data/lib/active_record/associations/has_many_through_association.rb +12 -18
  26. data/lib/active_record/associations/has_one_association.rb +5 -1
  27. data/lib/active_record/associations/has_one_through_association.rb +8 -7
  28. data/lib/active_record/associations/join_dependency/join_association.rb +17 -64
  29. data/lib/active_record/associations/join_dependency/join_base.rb +9 -8
  30. data/lib/active_record/associations/join_dependency/join_part.rb +2 -9
  31. data/lib/active_record/associations/join_dependency.rb +27 -44
  32. data/lib/active_record/associations/preloader/association.rb +53 -92
  33. data/lib/active_record/associations/preloader/through_association.rb +72 -73
  34. data/lib/active_record/associations/preloader.rb +17 -37
  35. data/lib/active_record/associations/singular_association.rb +14 -10
  36. data/lib/active_record/associations/through_association.rb +26 -11
  37. data/lib/active_record/associations.rb +68 -76
  38. data/lib/active_record/attribute_assignment.rb +2 -0
  39. data/lib/active_record/attribute_decorators.rb +3 -2
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -0
  41. data/lib/active_record/attribute_methods/dirty.rb +24 -214
  42. data/lib/active_record/attribute_methods/primary_key.rb +10 -13
  43. data/lib/active_record/attribute_methods/query.rb +2 -0
  44. data/lib/active_record/attribute_methods/read.rb +8 -2
  45. data/lib/active_record/attribute_methods/serialization.rb +23 -0
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +6 -8
  47. data/lib/active_record/attribute_methods/write.rb +22 -19
  48. data/lib/active_record/attribute_methods.rb +48 -12
  49. data/lib/active_record/attributes.rb +7 -6
  50. data/lib/active_record/autosave_association.rb +8 -11
  51. data/lib/active_record/base.rb +2 -0
  52. data/lib/active_record/callbacks.rb +8 -6
  53. data/lib/active_record/coders/json.rb +2 -0
  54. data/lib/active_record/coders/yaml_column.rb +2 -0
  55. data/lib/active_record/collection_cache_key.rb +14 -10
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +110 -35
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -0
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +175 -33
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +8 -2
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +13 -24
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -6
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +58 -3
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +165 -85
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +45 -9
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +83 -97
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +118 -180
  69. data/lib/active_record/connection_adapters/column.rb +4 -2
  70. data/lib/active_record/connection_adapters/connection_specification.rb +17 -3
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +2 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +2 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +11 -17
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +2 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +9 -10
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +5 -3
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +7 -10
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +30 -23
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +106 -1
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +2 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -2
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +6 -32
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +2 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +2 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +2 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +2 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  96. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -1
  98. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +2 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +4 -2
  101. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +4 -2
  103. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +3 -1
  104. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -1
  107. data/lib/active_record/connection_adapters/postgresql/quoting.rb +22 -1
  108. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
  109. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +14 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +24 -11
  111. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +20 -13
  112. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +269 -126
  113. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -0
  114. data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -0
  115. data/lib/active_record/connection_adapters/postgresql_adapter.rb +64 -85
  116. data/lib/active_record/connection_adapters/schema_cache.rb +4 -2
  117. data/lib/active_record/connection_adapters/sql_type_metadata.rb +2 -0
  118. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +2 -0
  119. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +18 -0
  120. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +2 -0
  121. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +6 -15
  122. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +3 -2
  123. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +71 -1
  124. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +92 -95
  125. data/lib/active_record/connection_adapters/statement_pool.rb +2 -0
  126. data/lib/active_record/connection_handling.rb +4 -2
  127. data/lib/active_record/core.rb +39 -60
  128. data/lib/active_record/counter_cache.rb +3 -2
  129. data/lib/active_record/define_callbacks.rb +5 -3
  130. data/lib/active_record/dynamic_matchers.rb +9 -9
  131. data/lib/active_record/enum.rb +17 -13
  132. data/lib/active_record/errors.rb +42 -3
  133. data/lib/active_record/explain.rb +3 -1
  134. data/lib/active_record/explain_registry.rb +2 -0
  135. data/lib/active_record/explain_subscriber.rb +2 -0
  136. data/lib/active_record/fixture_set/file.rb +2 -0
  137. data/lib/active_record/fixtures.rb +67 -60
  138. data/lib/active_record/gem_version.rb +4 -2
  139. data/lib/active_record/inheritance.rb +9 -9
  140. data/lib/active_record/integration.rb +58 -19
  141. data/lib/active_record/internal_metadata.rb +2 -0
  142. data/lib/active_record/legacy_yaml_adapter.rb +3 -1
  143. data/lib/active_record/locking/optimistic.rb +8 -6
  144. data/lib/active_record/locking/pessimistic.rb +9 -6
  145. data/lib/active_record/log_subscriber.rb +46 -4
  146. data/lib/active_record/migration/command_recorder.rb +11 -9
  147. data/lib/active_record/migration/compatibility.rb +74 -22
  148. data/lib/active_record/migration/join_table.rb +2 -0
  149. data/lib/active_record/migration.rb +181 -137
  150. data/lib/active_record/model_schema.rb +73 -58
  151. data/lib/active_record/nested_attributes.rb +18 -6
  152. data/lib/active_record/no_touching.rb +3 -1
  153. data/lib/active_record/null_relation.rb +2 -0
  154. data/lib/active_record/persistence.rb +153 -18
  155. data/lib/active_record/query_cache.rb +17 -12
  156. data/lib/active_record/querying.rb +4 -2
  157. data/lib/active_record/railtie.rb +61 -3
  158. data/lib/active_record/railties/console_sandbox.rb +2 -0
  159. data/lib/active_record/railties/controller_runtime.rb +2 -0
  160. data/lib/active_record/railties/databases.rake +47 -37
  161. data/lib/active_record/readonly_attributes.rb +3 -2
  162. data/lib/active_record/reflection.rb +131 -204
  163. data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
  164. data/lib/active_record/relation/batches.rb +32 -17
  165. data/lib/active_record/relation/calculations.rb +58 -20
  166. data/lib/active_record/relation/delegation.rb +10 -29
  167. data/lib/active_record/relation/finder_methods.rb +74 -85
  168. data/lib/active_record/relation/from_clause.rb +2 -8
  169. data/lib/active_record/relation/merger.rb +51 -20
  170. data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -7
  171. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  172. data/lib/active_record/relation/predicate_builder/base_handler.rb +2 -2
  173. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +12 -1
  174. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +54 -0
  175. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -6
  176. data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
  177. data/lib/active_record/relation/predicate_builder.rb +53 -78
  178. data/lib/active_record/relation/query_attribute.rb +9 -2
  179. data/lib/active_record/relation/query_methods.rb +101 -95
  180. data/lib/active_record/relation/record_fetch_warning.rb +2 -0
  181. data/lib/active_record/relation/spawn_methods.rb +3 -1
  182. data/lib/active_record/relation/where_clause.rb +65 -67
  183. data/lib/active_record/relation/where_clause_factory.rb +5 -48
  184. data/lib/active_record/relation.rb +99 -202
  185. data/lib/active_record/result.rb +2 -0
  186. data/lib/active_record/runtime_registry.rb +2 -0
  187. data/lib/active_record/sanitization.rb +129 -121
  188. data/lib/active_record/schema.rb +4 -2
  189. data/lib/active_record/schema_dumper.rb +36 -26
  190. data/lib/active_record/schema_migration.rb +2 -0
  191. data/lib/active_record/scoping/default.rb +10 -7
  192. data/lib/active_record/scoping/named.rb +38 -12
  193. data/lib/active_record/scoping.rb +12 -10
  194. data/lib/active_record/secure_token.rb +2 -0
  195. data/lib/active_record/serialization.rb +2 -0
  196. data/lib/active_record/statement_cache.rb +22 -12
  197. data/lib/active_record/store.rb +3 -1
  198. data/lib/active_record/suppressor.rb +2 -0
  199. data/lib/active_record/table_metadata.rb +12 -3
  200. data/lib/active_record/tasks/database_tasks.rb +37 -25
  201. data/lib/active_record/tasks/mysql_database_tasks.rb +11 -50
  202. data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -3
  203. data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -3
  204. data/lib/active_record/timestamp.rb +5 -5
  205. data/lib/active_record/touch_later.rb +2 -0
  206. data/lib/active_record/transactions.rb +9 -7
  207. data/lib/active_record/translation.rb +2 -0
  208. data/lib/active_record/type/adapter_specific_registry.rb +2 -0
  209. data/lib/active_record/type/date.rb +2 -0
  210. data/lib/active_record/type/date_time.rb +2 -0
  211. data/lib/active_record/type/decimal_without_scale.rb +2 -0
  212. data/lib/active_record/type/hash_lookup_type_map.rb +2 -0
  213. data/lib/active_record/type/internal/timezone.rb +2 -0
  214. data/lib/active_record/type/json.rb +30 -0
  215. data/lib/active_record/type/serialized.rb +2 -0
  216. data/lib/active_record/type/text.rb +2 -0
  217. data/lib/active_record/type/time.rb +2 -0
  218. data/lib/active_record/type/type_map.rb +2 -0
  219. data/lib/active_record/type/unsigned_integer.rb +2 -0
  220. data/lib/active_record/type.rb +4 -1
  221. data/lib/active_record/type_caster/connection.rb +2 -0
  222. data/lib/active_record/type_caster/map.rb +3 -1
  223. data/lib/active_record/type_caster.rb +2 -0
  224. data/lib/active_record/validations/absence.rb +2 -0
  225. data/lib/active_record/validations/associated.rb +2 -0
  226. data/lib/active_record/validations/length.rb +2 -0
  227. data/lib/active_record/validations/presence.rb +2 -0
  228. data/lib/active_record/validations/uniqueness.rb +35 -5
  229. data/lib/active_record/validations.rb +2 -0
  230. data/lib/active_record/version.rb +2 -0
  231. data/lib/active_record.rb +11 -4
  232. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  233. data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
  234. data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -1
  235. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +0 -0
  236. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +0 -0
  237. data/lib/rails/generators/active_record/migration.rb +2 -0
  238. data/lib/rails/generators/active_record/model/model_generator.rb +2 -23
  239. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +0 -0
  240. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  241. data/lib/rails/generators/active_record.rb +3 -1
  242. metadata +25 -37
  243. data/lib/active_record/associations/preloader/belongs_to.rb +0 -15
  244. data/lib/active_record/associations/preloader/collection_association.rb +0 -17
  245. data/lib/active_record/associations/preloader/has_many.rb +0 -15
  246. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  247. data/lib/active_record/associations/preloader/has_one.rb +0 -15
  248. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  249. data/lib/active_record/associations/preloader/singular_association.rb +0 -18
  250. data/lib/active_record/attribute/user_provided_default.rb +0 -30
  251. data/lib/active_record/attribute.rb +0 -240
  252. data/lib/active_record/attribute_mutation_tracker.rb +0 -113
  253. data/lib/active_record/attribute_set/builder.rb +0 -124
  254. data/lib/active_record/attribute_set/yaml_encoder.rb +0 -41
  255. data/lib/active_record/attribute_set.rb +0 -113
  256. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -10
  257. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  258. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
  259. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -59
  260. 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 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" && !distinct_value
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,15 @@ 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
+ enforce_raw_sql_whitelist(column_names)
165
194
  relation = spawn
166
195
  relation.select_values = column_names.map { |cn|
167
196
  @klass.has_attribute?(cn) || @klass.attribute_alias?(cn) ? arel_attribute(cn) : cn
168
197
  }
169
- result = klass.connection.select_all(relation.arel, nil, bound_attributes)
198
+ result = skip_query_cache_if_necessary { klass.connection.select_all(relation.arel, nil) }
170
199
  result.cast_values(klass.attribute_types)
171
200
  end
172
201
  end
@@ -194,8 +223,13 @@ module ActiveRecord
194
223
 
195
224
  if operation == "count"
196
225
  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
226
+ if column_name == :all
227
+ if distinct && (group_values.any? || select_values.empty? && order_values.empty?)
228
+ column_name = primary_key
229
+ end
230
+ elsif column_name =~ /\s*DISTINCT[\s(]+/i
231
+ distinct = nil
232
+ end
199
233
  end
200
234
 
201
235
  if group_values.any?
@@ -208,7 +242,7 @@ module ActiveRecord
208
242
  def aggregate_column(column_name)
209
243
  return column_name if Arel::Expressions === column_name
210
244
 
211
- if @klass.has_attribute?(column_name.to_s) || @klass.attribute_alias?(column_name.to_s)
245
+ if @klass.has_attribute?(column_name) || @klass.attribute_alias?(column_name)
212
246
  @klass.arel_attribute(column_name)
213
247
  else
214
248
  Arel.sql(column_name == :all ? "*" : column_name.to_s)
@@ -222,7 +256,7 @@ module ActiveRecord
222
256
  def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
223
257
  column_alias = column_name
224
258
 
225
- if operation == "count" && (limit_value || offset_value)
259
+ if operation == "count" && (column_name == :all && distinct || has_limit_or_offset?)
226
260
  # Shortcut when limit is zero.
227
261
  return 0 if limit_value == 0
228
262
 
@@ -234,6 +268,9 @@ module ActiveRecord
234
268
  column = aggregate_column(column_name)
235
269
 
236
270
  select_value = operation_over_aggregate_column(column, operation, distinct)
271
+ if operation == "sum" && distinct
272
+ select_value.distinct = true
273
+ end
237
274
 
238
275
  column_alias = select_value.alias
239
276
  column_alias ||= @klass.connection.column_name_for_operation(operation, select_value)
@@ -242,7 +279,7 @@ module ActiveRecord
242
279
  query_builder = relation.arel
243
280
  end
244
281
 
245
- result = @klass.connection.select_all(query_builder, nil, bound_attributes)
282
+ result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder, nil) }
246
283
  row = result.first
247
284
  value = row && row.values.first
248
285
  type = result.column_types.fetch(column_alias) do
@@ -293,7 +330,7 @@ module ActiveRecord
293
330
  relation.group_values = group_fields
294
331
  relation.select_values = select_values
295
332
 
296
- calculated_data = @klass.connection.select_all(relation, nil, relation.bound_attributes)
333
+ calculated_data = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, nil) }
297
334
 
298
335
  if association
299
336
  key_ids = calculated_data.collect { |row| row[group_aliases.first] }
@@ -361,16 +398,17 @@ module ActiveRecord
361
398
  end
362
399
 
363
400
  def build_count_subquery(relation, column_name, distinct)
364
- column_alias = Arel.sql("count_column")
365
- subquery_alias = Arel.sql("subquery_for_count")
401
+ if column_name == :all
402
+ relation.select_values = [ Arel.sql(FinderMethods::ONE_AS_ONE) ] unless distinct
403
+ else
404
+ column_alias = Arel.sql("count_column")
405
+ relation.select_values = [ aggregate_column(column_name).as(column_alias) ]
406
+ end
366
407
 
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)
408
+ subquery = relation.arel.as(Arel.sql("subquery_for_count"))
409
+ select_value = operation_over_aggregate_column(column_alias || Arel.star, "count", false)
370
410
 
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)
411
+ Arel::SelectManager.new(subquery).project(select_value)
374
412
  end
375
413
  end
376
414
  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:
@@ -25,8 +27,6 @@ module ActiveRecord
25
27
 
26
28
  def inherited(child_class)
27
29
  child_class.initialize_relation_delegate_cache
28
- delegate = child_class.relation_delegate_class(ActiveRecord::Associations::CollectionProxy)
29
- delegate.include ActiveRecord::Associations::CollectionProxy::DelegateExtending
30
30
  super
31
31
  end
32
32
  end
@@ -38,13 +38,12 @@ module ActiveRecord
38
38
  # may vary depending on the klass of a relation, so we create a subclass of Relation
39
39
  # for each different klass, and the delegations are compiled into that subclass only.
40
40
 
41
- delegate :to_xml, :encode_with, :length, :each, :uniq, :to_ary, :join,
42
- :[], :&, :|, :+, :-, :sample, :reverse, :compact, :in_groups, :in_groups_of,
41
+ delegate :to_xml, :encode_with, :length, :each, :uniq, :join,
42
+ :[], :&, :|, :+, :-, :sample, :reverse, :rotate, :compact, :in_groups, :in_groups_of,
43
43
  :to_sentence, :to_formatted_s, :as_json,
44
- :shuffle, :split, :index, to: :records
44
+ :shuffle, :split, :slice, :index, :rindex, to: :records
45
45
 
46
- delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key,
47
- :connection, :columns_hash, to: :klass
46
+ delegate :primary_key, :connection, to: :klass
48
47
 
49
48
  module ClassSpecificRelation # :nodoc:
50
49
  extend ActiveSupport::Concern
@@ -75,13 +74,6 @@ module ActiveRecord
75
74
  end
76
75
  end
77
76
  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
77
  end
86
78
 
87
79
  private
@@ -91,7 +83,8 @@ module ActiveRecord
91
83
  self.class.delegate_to_scoped_klass(method)
92
84
  scoping { @klass.public_send(method, *args, &block) }
93
85
  elsif arel.respond_to?(method)
94
- self.class.delegate method, to: :arel
86
+ ActiveSupport::Deprecation.warn \
87
+ "Delegating #{method} to arel is deprecated and will be removed in Rails 6.0."
95
88
  arel.public_send(method, *args, &block)
96
89
  else
97
90
  super
@@ -111,21 +104,9 @@ module ActiveRecord
111
104
  end
112
105
  end
113
106
 
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
107
  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
108
+ def respond_to_missing?(method, _)
109
+ super || @klass.respond_to?(method) || arel.respond_to?(method)
129
110
  end
130
111
  end
131
112
  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_value(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,37 +358,8 @@ 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
374
- 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
382
- 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)
361
+ def construct_relation_for_exists(conditions)
362
+ relation = except(:select, :distinct, :order)._select!(ONE_AS_ONE).limit!(1)
387
363
 
388
364
  case conditions
389
365
  when Array, Hash
@@ -395,37 +371,43 @@ module ActiveRecord
395
371
  relation
396
372
  end
397
373
 
398
- def construct_join_dependency(joins = [], eager_loading: true)
374
+ def construct_join_dependency(eager_loading: true)
399
375
  including = eager_load_values + includes_values
400
- ActiveRecord::Associations::JoinDependency.new(@klass, including, joins, eager_loading: eager_loading)
401
- end
402
-
403
- def construct_relation_for_association_calculations
404
- apply_join_dependency(self, construct_join_dependency(joins_values))
376
+ joins = joins_values.select { |join| join.is_a?(Arel::Nodes::Join) }
377
+ ActiveRecord::Associations::JoinDependency.new(
378
+ klass, table, including, alias_tracker(joins), eager_loading: eager_loading
379
+ )
405
380
  end
406
381
 
407
- def apply_join_dependency(relation, join_dependency)
408
- relation = relation.except(:includes, :eager_load, :preload).joins!(join_dependency)
382
+ def apply_join_dependency(eager_loading: true)
383
+ join_dependency = construct_join_dependency(eager_loading: eager_loading)
384
+ relation = except(:includes, :eager_load, :preload).joins!(join_dependency)
409
385
 
410
- if using_limitable_reflections?(join_dependency.reflections)
411
- relation
412
- else
413
- if relation.limit_value
386
+ if eager_loading && !using_limitable_reflections?(join_dependency.reflections)
387
+ if has_limit_or_offset?
414
388
  limited_ids = limited_ids_for(relation)
415
389
  limited_ids.empty? ? relation.none! : relation.where!(primary_key => limited_ids)
416
390
  end
417
- relation.except(:limit, :offset)
391
+ relation.limit_value = relation.offset_value = nil
392
+ end
393
+
394
+ if block_given?
395
+ relation._select!(join_dependency.aliases.columns)
396
+ yield relation, join_dependency
397
+ else
398
+ relation
418
399
  end
419
400
  end
420
401
 
421
402
  def limited_ids_for(relation)
422
403
  values = @klass.connection.columns_for_distinct(
423
- "#{quoted_table_name}.#{quoted_primary_key}", relation.order_values)
404
+ connection.column_name_from_arel_node(arel_attribute(primary_key)),
405
+ relation.order_values
406
+ )
424
407
 
425
408
  relation = relation.except(:select).select(values).distinct!
426
- arel = relation.arel
427
409
 
428
- id_rows = @klass.connection.select_all(arel, "SQL", relation.bound_attributes)
410
+ id_rows = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, "SQL") }
429
411
  id_rows.map { |row| row[primary_key] }
430
412
  end
431
413
 
@@ -441,9 +423,12 @@ module ActiveRecord
441
423
 
442
424
  ids = ids.flatten.compact.uniq
443
425
 
426
+ model_name = @klass.name
427
+
444
428
  case ids.size
445
429
  when 0
446
- raise RecordNotFound, "Couldn't find #{@klass.name} without an ID"
430
+ error_message = "Couldn't find #{model_name} without an ID"
431
+ raise RecordNotFound.new(error_message, model_name, primary_key)
447
432
  when 1
448
433
  result = find_one(ids.first)
449
434
  expects_array ? [ result ] : result
@@ -451,7 +436,8 @@ module ActiveRecord
451
436
  find_some(ids)
452
437
  end
453
438
  rescue ::RangeError
454
- raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range ID"
439
+ error_message = "Couldn't find #{model_name} with an out of range ID"
440
+ raise RecordNotFound.new(error_message, model_name, primary_key, ids)
455
441
  end
456
442
 
457
443
  def find_one(id)
@@ -533,13 +519,13 @@ module ActiveRecord
533
519
  if loaded?
534
520
  records[index, limit] || []
535
521
  else
536
- relation = if order_values.empty? && primary_key
537
- order(arel_attribute(primary_key).asc)
538
- else
539
- self
522
+ relation = ordered_relation
523
+
524
+ if limit_value
525
+ limit = [limit_value - index, limit].min
540
526
  end
541
527
 
542
- if limit_value.nil? || index < limit_value
528
+ if limit > 0
543
529
  relation = relation.offset(offset_index + index) unless index.zero?
544
530
  relation.limit(limit).to_a
545
531
  else
@@ -552,23 +538,26 @@ module ActiveRecord
552
538
  if loaded?
553
539
  records[-index]
554
540
  else
555
- relation = if order_values.empty? && primary_key
556
- order(arel_attribute(primary_key).asc)
541
+ relation = ordered_relation
542
+
543
+ if equal?(relation) || has_limit_or_offset?
544
+ relation.records[-index]
557
545
  else
558
- self
546
+ relation.last(index)[-index]
559
547
  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
548
  end
568
549
  end
569
550
 
570
551
  def find_last(limit)
571
552
  limit ? records.last(limit) : records.last
572
553
  end
554
+
555
+ def ordered_relation
556
+ if order_values.empty? && primary_key
557
+ order(arel_attribute(primary_key).asc)
558
+ else
559
+ self
560
+ end
561
+ end
573
562
  end
574
563
  end