activerecord 6.0.1 → 6.1.0

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 (268) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +843 -626
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +3 -3
  5. data/lib/active_record/aggregations.rb +1 -2
  6. data/lib/active_record/association_relation.rb +18 -17
  7. data/lib/active_record/associations/alias_tracker.rb +19 -16
  8. data/lib/active_record/associations/association.rb +49 -37
  9. data/lib/active_record/associations/association_scope.rb +17 -15
  10. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  12. data/lib/active_record/associations/builder/association.rb +9 -3
  13. data/lib/active_record/associations/builder/belongs_to.rb +10 -7
  14. data/lib/active_record/associations/builder/collection_association.rb +5 -4
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -3
  16. data/lib/active_record/associations/builder/has_many.rb +6 -2
  17. data/lib/active_record/associations/builder/has_one.rb +11 -14
  18. data/lib/active_record/associations/builder/singular_association.rb +1 -1
  19. data/lib/active_record/associations/collection_association.rb +25 -8
  20. data/lib/active_record/associations/collection_proxy.rb +14 -7
  21. data/lib/active_record/associations/foreign_association.rb +13 -0
  22. data/lib/active_record/associations/has_many_association.rb +24 -3
  23. data/lib/active_record/associations/has_many_through_association.rb +10 -4
  24. data/lib/active_record/associations/has_one_association.rb +15 -1
  25. data/lib/active_record/associations/join_dependency/join_association.rb +36 -14
  26. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  27. data/lib/active_record/associations/join_dependency.rb +73 -42
  28. data/lib/active_record/associations/preloader/association.rb +51 -25
  29. data/lib/active_record/associations/preloader/through_association.rb +2 -2
  30. data/lib/active_record/associations/preloader.rb +12 -7
  31. data/lib/active_record/associations/singular_association.rb +1 -1
  32. data/lib/active_record/associations/through_association.rb +1 -1
  33. data/lib/active_record/associations.rb +115 -12
  34. data/lib/active_record/attribute_assignment.rb +10 -9
  35. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -10
  36. data/lib/active_record/attribute_methods/dirty.rb +3 -13
  37. data/lib/active_record/attribute_methods/primary_key.rb +6 -4
  38. data/lib/active_record/attribute_methods/query.rb +3 -6
  39. data/lib/active_record/attribute_methods/read.rb +8 -12
  40. data/lib/active_record/attribute_methods/serialization.rb +11 -6
  41. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
  42. data/lib/active_record/attribute_methods/write.rb +12 -21
  43. data/lib/active_record/attribute_methods.rb +64 -54
  44. data/lib/active_record/attributes.rb +32 -8
  45. data/lib/active_record/autosave_association.rb +56 -41
  46. data/lib/active_record/base.rb +2 -14
  47. data/lib/active_record/callbacks.rb +153 -24
  48. data/lib/active_record/coders/yaml_column.rb +1 -2
  49. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +190 -136
  50. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  51. data/lib/active_record/connection_adapters/abstract/database_statements.rb +82 -37
  52. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -8
  53. data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
  54. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +152 -116
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -52
  57. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  58. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +263 -107
  59. data/lib/active_record/connection_adapters/abstract/transaction.rb +82 -35
  60. data/lib/active_record/connection_adapters/abstract_adapter.rb +60 -73
  61. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +136 -111
  62. data/lib/active_record/connection_adapters/column.rb +15 -1
  63. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  64. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
  65. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  66. data/lib/active_record/connection_adapters/mysql/database_statements.rb +28 -36
  67. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  68. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  69. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -7
  70. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  71. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  72. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +17 -13
  73. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  74. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -13
  75. data/lib/active_record/connection_adapters/pool_config.rb +63 -0
  76. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  77. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  78. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -56
  79. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +0 -1
  80. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  81. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
  83. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  84. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +0 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
  87. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  89. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
  90. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -6
  91. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -2
  93. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  94. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  95. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
  96. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +7 -3
  97. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -1
  98. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  99. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +72 -54
  100. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  101. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  102. data/lib/active_record/connection_adapters/postgresql_adapter.rb +77 -57
  103. data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
  104. data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
  105. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +36 -12
  106. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -2
  107. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  108. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -5
  109. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +57 -57
  110. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  111. data/lib/active_record/connection_adapters.rb +50 -0
  112. data/lib/active_record/connection_handling.rb +210 -87
  113. data/lib/active_record/core.rb +229 -65
  114. data/lib/active_record/counter_cache.rb +4 -1
  115. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  116. data/lib/active_record/database_configurations/database_config.rb +52 -9
  117. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  118. data/lib/active_record/database_configurations/url_config.rb +15 -41
  119. data/lib/active_record/database_configurations.rb +124 -85
  120. data/lib/active_record/delegated_type.rb +209 -0
  121. data/lib/active_record/destroy_association_async_job.rb +36 -0
  122. data/lib/active_record/dynamic_matchers.rb +2 -3
  123. data/lib/active_record/enum.rb +40 -16
  124. data/lib/active_record/errors.rb +47 -12
  125. data/lib/active_record/explain.rb +9 -5
  126. data/lib/active_record/explain_subscriber.rb +1 -1
  127. data/lib/active_record/fixture_set/file.rb +10 -17
  128. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  129. data/lib/active_record/fixture_set/render_context.rb +1 -1
  130. data/lib/active_record/fixture_set/table_row.rb +2 -3
  131. data/lib/active_record/fixture_set/table_rows.rb +0 -1
  132. data/lib/active_record/fixtures.rb +54 -11
  133. data/lib/active_record/gem_version.rb +2 -2
  134. data/lib/active_record/inheritance.rb +40 -21
  135. data/lib/active_record/insert_all.rb +38 -9
  136. data/lib/active_record/integration.rb +3 -5
  137. data/lib/active_record/internal_metadata.rb +16 -7
  138. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  139. data/lib/active_record/locking/optimistic.rb +22 -17
  140. data/lib/active_record/locking/pessimistic.rb +6 -2
  141. data/lib/active_record/log_subscriber.rb +27 -9
  142. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  143. data/lib/active_record/middleware/database_selector/resolver.rb +6 -2
  144. data/lib/active_record/middleware/database_selector.rb +4 -2
  145. data/lib/active_record/migration/command_recorder.rb +53 -45
  146. data/lib/active_record/migration/compatibility.rb +70 -20
  147. data/lib/active_record/migration/join_table.rb +0 -1
  148. data/lib/active_record/migration.rb +114 -84
  149. data/lib/active_record/model_schema.rb +117 -15
  150. data/lib/active_record/nested_attributes.rb +2 -5
  151. data/lib/active_record/no_touching.rb +1 -1
  152. data/lib/active_record/null_relation.rb +0 -1
  153. data/lib/active_record/persistence.rb +50 -46
  154. data/lib/active_record/query_cache.rb +15 -5
  155. data/lib/active_record/querying.rb +12 -7
  156. data/lib/active_record/railtie.rb +65 -45
  157. data/lib/active_record/railties/databases.rake +267 -93
  158. data/lib/active_record/readonly_attributes.rb +4 -0
  159. data/lib/active_record/reflection.rb +77 -63
  160. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  161. data/lib/active_record/relation/batches.rb +38 -32
  162. data/lib/active_record/relation/calculations.rb +102 -45
  163. data/lib/active_record/relation/delegation.rb +9 -7
  164. data/lib/active_record/relation/finder_methods.rb +45 -16
  165. data/lib/active_record/relation/from_clause.rb +5 -1
  166. data/lib/active_record/relation/merger.rb +27 -26
  167. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  168. data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
  169. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +3 -3
  170. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  171. data/lib/active_record/relation/predicate_builder.rb +55 -35
  172. data/lib/active_record/relation/query_methods.rb +335 -187
  173. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  174. data/lib/active_record/relation/spawn_methods.rb +8 -8
  175. data/lib/active_record/relation/where_clause.rb +104 -58
  176. data/lib/active_record/relation.rb +108 -68
  177. data/lib/active_record/result.rb +41 -34
  178. data/lib/active_record/runtime_registry.rb +2 -2
  179. data/lib/active_record/sanitization.rb +6 -17
  180. data/lib/active_record/schema_dumper.rb +34 -4
  181. data/lib/active_record/schema_migration.rb +2 -8
  182. data/lib/active_record/scoping/default.rb +0 -1
  183. data/lib/active_record/scoping/named.rb +7 -18
  184. data/lib/active_record/scoping.rb +0 -1
  185. data/lib/active_record/secure_token.rb +16 -8
  186. data/lib/active_record/serialization.rb +5 -3
  187. data/lib/active_record/signed_id.rb +116 -0
  188. data/lib/active_record/statement_cache.rb +20 -4
  189. data/lib/active_record/store.rb +3 -3
  190. data/lib/active_record/suppressor.rb +2 -2
  191. data/lib/active_record/table_metadata.rb +39 -36
  192. data/lib/active_record/tasks/database_tasks.rb +139 -113
  193. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -36
  194. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -27
  195. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -10
  196. data/lib/active_record/test_databases.rb +5 -4
  197. data/lib/active_record/test_fixtures.rb +38 -16
  198. data/lib/active_record/timestamp.rb +4 -7
  199. data/lib/active_record/touch_later.rb +20 -21
  200. data/lib/active_record/transactions.rb +21 -70
  201. data/lib/active_record/type/adapter_specific_registry.rb +2 -5
  202. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  203. data/lib/active_record/type/serialized.rb +6 -3
  204. data/lib/active_record/type/time.rb +10 -0
  205. data/lib/active_record/type/type_map.rb +0 -1
  206. data/lib/active_record/type/unsigned_integer.rb +0 -1
  207. data/lib/active_record/type.rb +8 -2
  208. data/lib/active_record/type_caster/connection.rb +0 -1
  209. data/lib/active_record/type_caster/map.rb +8 -5
  210. data/lib/active_record/validations/associated.rb +1 -2
  211. data/lib/active_record/validations/numericality.rb +35 -0
  212. data/lib/active_record/validations/uniqueness.rb +24 -4
  213. data/lib/active_record/validations.rb +3 -3
  214. data/lib/active_record.rb +7 -13
  215. data/lib/arel/attributes/attribute.rb +4 -0
  216. data/lib/arel/collectors/bind.rb +5 -0
  217. data/lib/arel/collectors/composite.rb +8 -0
  218. data/lib/arel/collectors/sql_string.rb +7 -0
  219. data/lib/arel/collectors/substitute_binds.rb +7 -0
  220. data/lib/arel/nodes/binary.rb +82 -8
  221. data/lib/arel/nodes/bind_param.rb +8 -0
  222. data/lib/arel/nodes/casted.rb +21 -9
  223. data/lib/arel/nodes/equality.rb +6 -9
  224. data/lib/arel/nodes/grouping.rb +3 -0
  225. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  226. data/lib/arel/nodes/in.rb +8 -1
  227. data/lib/arel/nodes/infix_operation.rb +13 -1
  228. data/lib/arel/nodes/join_source.rb +1 -1
  229. data/lib/arel/nodes/node.rb +7 -6
  230. data/lib/arel/nodes/ordering.rb +27 -0
  231. data/lib/arel/nodes/sql_literal.rb +3 -0
  232. data/lib/arel/nodes/table_alias.rb +7 -3
  233. data/lib/arel/nodes/unary.rb +0 -1
  234. data/lib/arel/nodes.rb +3 -1
  235. data/lib/arel/predications.rb +17 -24
  236. data/lib/arel/select_manager.rb +1 -2
  237. data/lib/arel/table.rb +13 -5
  238. data/lib/arel/visitors/dot.rb +14 -3
  239. data/lib/arel/visitors/mysql.rb +11 -1
  240. data/lib/arel/visitors/postgresql.rb +15 -5
  241. data/lib/arel/visitors/sqlite.rb +0 -1
  242. data/lib/arel/visitors/to_sql.rb +89 -79
  243. data/lib/arel/visitors/visitor.rb +0 -1
  244. data/lib/arel/visitors.rb +0 -7
  245. data/lib/arel.rb +5 -9
  246. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  247. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  248. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  249. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
  250. data/lib/rails/generators/active_record/migration.rb +6 -2
  251. data/lib/rails/generators/active_record/model/model_generator.rb +38 -2
  252. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  253. metadata +26 -26
  254. data/lib/active_record/attribute_decorators.rb +0 -90
  255. data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
  256. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  257. data/lib/active_record/define_callbacks.rb +0 -22
  258. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  259. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  260. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  261. data/lib/arel/attributes.rb +0 -22
  262. data/lib/arel/visitors/depth_first.rb +0 -204
  263. data/lib/arel/visitors/ibm_db.rb +0 -34
  264. data/lib/arel/visitors/informix.rb +0 -62
  265. data/lib/arel/visitors/mssql.rb +0 -157
  266. data/lib/arel/visitors/oracle.rb +0 -159
  267. data/lib/arel/visitors/oracle12.rb +0 -66
  268. data/lib/arel/visitors/where_sql.rb +0 -23
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/core_ext/string/filters"
4
- require "concurrent/map"
5
4
 
6
5
  module ActiveRecord
7
6
  # = Active Record Reflection
@@ -163,13 +162,7 @@ module ActiveRecord
163
162
  # <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>'Money'</tt>
164
163
  # <tt>has_many :clients</tt> returns <tt>'Client'</tt>
165
164
  def class_name
166
- @class_name ||= (options[:class_name] || derive_class_name).to_s
167
- end
168
-
169
- JoinKeys = Struct.new(:key, :foreign_key) # :nodoc:
170
-
171
- def join_keys
172
- @join_keys ||= get_join_keys(klass)
165
+ @class_name ||= -(options[:class_name]&.to_s || derive_class_name)
173
166
  end
174
167
 
175
168
  # Returns a list of scopes that should be applied for this Reflection
@@ -183,25 +176,27 @@ module ActiveRecord
183
176
  scope_chain_items = join_scopes(table, predicate_builder)
184
177
  klass_scope = klass_join_scope(table, predicate_builder)
185
178
 
186
- key = join_keys.key
187
- foreign_key = join_keys.foreign_key
188
-
189
- klass_scope.where!(table[key].eq(foreign_table[foreign_key]))
190
-
191
179
  if type
192
180
  klass_scope.where!(type => foreign_klass.polymorphic_name)
193
181
  end
194
182
 
183
+ scope_chain_items.inject(klass_scope, &:merge!)
184
+
185
+ primary_key = join_primary_key
186
+ foreign_key = join_foreign_key
187
+
188
+ klass_scope.where!(table[primary_key].eq(foreign_table[foreign_key]))
189
+
195
190
  if klass.finder_needs_type_condition?
196
191
  klass_scope.where!(klass.send(:type_condition, table))
197
192
  end
198
193
 
199
- scope_chain_items.inject(klass_scope, &:merge!)
194
+ klass_scope
200
195
  end
201
196
 
202
- def join_scopes(table, predicate_builder) # :nodoc:
197
+ def join_scopes(table, predicate_builder, klass = self.klass) # :nodoc:
203
198
  if scope
204
- [scope_for(build_scope(table, predicate_builder))]
199
+ [scope_for(build_scope(table, predicate_builder, klass))]
205
200
  else
206
201
  []
207
202
  end
@@ -217,14 +212,14 @@ module ActiveRecord
217
212
  end
218
213
 
219
214
  def counter_cache_column
220
- if belongs_to?
215
+ @counter_cache_column ||= if belongs_to?
221
216
  if options[:counter_cache] == true
222
- "#{active_record.name.demodulize.underscore.pluralize}_count"
217
+ -"#{active_record.name.demodulize.underscore.pluralize}_count"
223
218
  elsif options[:counter_cache]
224
- options[:counter_cache].to_s
219
+ -options[:counter_cache].to_s
225
220
  end
226
221
  else
227
- options[:counter_cache] ? options[:counter_cache].to_s : "#{name}_count"
222
+ -(options[:counter_cache]&.to_s || "#{name}_count")
228
223
  end
229
224
  end
230
225
 
@@ -271,7 +266,7 @@ module ActiveRecord
271
266
  def has_cached_counter?
272
267
  options[:counter_cache] ||
273
268
  inverse_which_updates_counter_cache && inverse_which_updates_counter_cache.options[:counter_cache] &&
274
- !!active_record.columns_hash[counter_cache_column]
269
+ active_record.has_attribute?(counter_cache_column)
275
270
  end
276
271
 
277
272
  def counter_must_be_updated_by_has_many?
@@ -286,11 +281,7 @@ module ActiveRecord
286
281
  collect_join_chain
287
282
  end
288
283
 
289
- def get_join_keys(association_klass)
290
- JoinKeys.new(join_primary_key(association_klass), join_foreign_key)
291
- end
292
-
293
- def build_scope(table, predicate_builder = predicate_builder(table))
284
+ def build_scope(table, predicate_builder = predicate_builder(table), klass = self.klass)
294
285
  Relation.create(
295
286
  klass,
296
287
  table: table,
@@ -298,12 +289,8 @@ module ActiveRecord
298
289
  )
299
290
  end
300
291
 
301
- def join_primary_key(*)
302
- foreign_key
303
- end
304
-
305
- def join_foreign_key
306
- active_record_primary_key
292
+ def strict_loading?
293
+ options[:strict_loading]
307
294
  end
308
295
 
309
296
  protected
@@ -427,22 +414,21 @@ module ActiveRecord
427
414
 
428
415
  def initialize(name, scope, options, active_record)
429
416
  super
430
- @type = options[:as] && (options[:foreign_type] || "#{options[:as]}_type")
431
- @foreign_type = options[:polymorphic] && (options[:foreign_type] || "#{name}_type")
417
+ @type = -(options[:foreign_type]&.to_s || "#{options[:as]}_type") if options[:as]
418
+ @foreign_type = -(options[:foreign_type]&.to_s || "#{name}_type") if options[:polymorphic]
432
419
  @constructable = calculate_constructable(macro, options)
433
- @association_scope_cache = Concurrent::Map.new
434
420
 
435
421
  if options[:class_name] && options[:class_name].class == Class
436
422
  raise ArgumentError, "A class was passed to `:class_name` but we are expecting a string."
437
423
  end
438
424
  end
439
425
 
440
- def association_scope_cache(conn, owner, &block)
441
- key = conn.prepared_statements
426
+ def association_scope_cache(klass, owner, &block)
427
+ key = self
442
428
  if polymorphic?
443
429
  key = [key, owner._read_attribute(@foreign_type)]
444
430
  end
445
- @association_scope_cache.compute_if_absent(key) { StatementCache.create(conn, &block) }
431
+ klass.cached_find_by_statement(key, &block)
446
432
  end
447
433
 
448
434
  def constructable? # :nodoc:
@@ -450,24 +436,31 @@ module ActiveRecord
450
436
  end
451
437
 
452
438
  def join_table
453
- @join_table ||= options[:join_table] || derive_join_table
439
+ @join_table ||= -(options[:join_table]&.to_s || derive_join_table)
454
440
  end
455
441
 
456
442
  def foreign_key
457
- @foreign_key ||= options[:foreign_key] || derive_foreign_key.freeze
443
+ @foreign_key ||= -(options[:foreign_key]&.to_s || derive_foreign_key)
458
444
  end
459
445
 
460
446
  def association_foreign_key
461
- @association_foreign_key ||= options[:association_foreign_key] || class_name.foreign_key
447
+ @association_foreign_key ||= -(options[:association_foreign_key]&.to_s || class_name.foreign_key)
462
448
  end
463
449
 
464
- # klass option is necessary to support loading polymorphic associations
465
450
  def association_primary_key(klass = nil)
466
- options[:primary_key] || primary_key(klass || self.klass)
451
+ primary_key(klass || self.klass)
467
452
  end
468
453
 
469
454
  def active_record_primary_key
470
- @active_record_primary_key ||= options[:primary_key] || primary_key(active_record)
455
+ @active_record_primary_key ||= -(options[:primary_key]&.to_s || primary_key(active_record))
456
+ end
457
+
458
+ def join_primary_key(klass = nil)
459
+ foreign_key
460
+ end
461
+
462
+ def join_foreign_key
463
+ active_record_primary_key
471
464
  end
472
465
 
473
466
  def check_validity!
@@ -508,7 +501,7 @@ module ActiveRecord
508
501
  # This is for clearing cache on the reflection. Useful for tests that need to compare
509
502
  # SQL queries on associations.
510
503
  def clear_association_scope_cache # :nodoc:
511
- @association_scope_cache.clear
504
+ klass.initialize_find_by_cache
512
505
  end
513
506
 
514
507
  def nested?
@@ -590,7 +583,6 @@ module ActiveRecord
590
583
  end
591
584
 
592
585
  private
593
-
594
586
  def calculate_constructable(macro, options)
595
587
  true
596
588
  end
@@ -620,7 +612,7 @@ module ActiveRecord
620
612
  end
621
613
 
622
614
  if valid_inverse_reflection?(reflection)
623
- return inverse_name
615
+ inverse_name
624
616
  end
625
617
  end
626
618
  end
@@ -631,6 +623,7 @@ module ActiveRecord
631
623
  # with the current reflection's klass name.
632
624
  def valid_inverse_reflection?(reflection)
633
625
  reflection &&
626
+ foreign_key == reflection.foreign_key &&
634
627
  klass <= reflection.active_record &&
635
628
  can_find_inverse_of_automatically?(reflection)
636
629
  end
@@ -684,10 +677,6 @@ module ActiveRecord
684
677
  Associations::HasManyAssociation
685
678
  end
686
679
  end
687
-
688
- def association_primary_key(klass = nil)
689
- primary_key(klass || self.klass)
690
- end
691
680
  end
692
681
 
693
682
  class HasOneReflection < AssociationReflection # :nodoc:
@@ -704,7 +693,6 @@ module ActiveRecord
704
693
  end
705
694
 
706
695
  private
707
-
708
696
  def calculate_constructable(macro, options)
709
697
  !options[:through]
710
698
  end
@@ -723,6 +711,15 @@ module ActiveRecord
723
711
  end
724
712
  end
725
713
 
714
+ # klass option is necessary to support loading polymorphic associations
715
+ def association_primary_key(klass = nil)
716
+ if primary_key = options[:primary_key]
717
+ @association_primary_key ||= -primary_key.to_s
718
+ else
719
+ primary_key(klass || self.klass)
720
+ end
721
+ end
722
+
726
723
  def join_primary_key(klass = nil)
727
724
  polymorphic? ? association_primary_key(klass) : association_primary_key
728
725
  end
@@ -731,6 +728,10 @@ module ActiveRecord
731
728
  foreign_key
732
729
  end
733
730
 
731
+ def join_foreign_type
732
+ foreign_type
733
+ end
734
+
734
735
  private
735
736
  def can_find_inverse_of_automatically?(_)
736
737
  !polymorphic? && super
@@ -752,8 +753,8 @@ module ActiveRecord
752
753
  # Holds all the metadata about a :through association as it was specified
753
754
  # in the Active Record class.
754
755
  class ThroughReflection < AbstractReflection #:nodoc:
755
- delegate :foreign_key, :foreign_type, :association_foreign_key, :join_id_for,
756
- :active_record_primary_key, :type, :get_join_keys, to: :source_reflection
756
+ delegate :foreign_key, :foreign_type, :association_foreign_key, :join_id_for, :type,
757
+ :active_record_primary_key, :join_foreign_key, to: :source_reflection
757
758
 
758
759
  def initialize(delegate_reflection)
759
760
  @delegate_reflection = delegate_reflection
@@ -839,8 +840,8 @@ module ActiveRecord
839
840
  source_reflection.scopes + super
840
841
  end
841
842
 
842
- def join_scopes(table, predicate_builder) # :nodoc:
843
- source_reflection.join_scopes(table, predicate_builder) + super
843
+ def join_scopes(table, predicate_builder, klass = self.klass) # :nodoc:
844
+ source_reflection.join_scopes(table, predicate_builder, klass) + super
844
845
  end
845
846
 
846
847
  def has_scope?
@@ -860,7 +861,15 @@ module ActiveRecord
860
861
  def association_primary_key(klass = nil)
861
862
  # Get the "actual" source reflection if the immediate source reflection has a
862
863
  # source reflection itself
863
- actual_source_reflection.options[:primary_key] || primary_key(klass || self.klass)
864
+ if primary_key = actual_source_reflection.options[:primary_key]
865
+ @association_primary_key ||= -primary_key.to_s
866
+ else
867
+ primary_key(klass || self.klass)
868
+ end
869
+ end
870
+
871
+ def join_primary_key(klass = self.klass)
872
+ source_reflection.join_primary_key(klass)
864
873
  end
865
874
 
866
875
  # Gets an array of possible <tt>:through</tt> source reflection names in both singular and plural form.
@@ -909,7 +918,7 @@ module ActiveRecord
909
918
 
910
919
  def check_validity!
911
920
  if through_reflection.nil?
912
- raise HasManyThroughAssociationNotFoundError.new(active_record.name, self)
921
+ raise HasManyThroughAssociationNotFoundError.new(active_record, self)
913
922
  end
914
923
 
915
924
  if through_reflection.polymorphic?
@@ -996,16 +1005,17 @@ module ActiveRecord
996
1005
  end
997
1006
 
998
1007
  class PolymorphicReflection < AbstractReflection # :nodoc:
999
- delegate :klass, :scope, :plural_name, :type, :get_join_keys, :scope_for, to: :@reflection
1008
+ delegate :klass, :scope, :plural_name, :type, :join_primary_key, :join_foreign_key,
1009
+ :name, :scope_for, to: :@reflection
1000
1010
 
1001
1011
  def initialize(reflection, previous_reflection)
1002
1012
  @reflection = reflection
1003
1013
  @previous_reflection = previous_reflection
1004
1014
  end
1005
1015
 
1006
- def join_scopes(table, predicate_builder) # :nodoc:
1016
+ def join_scopes(table, predicate_builder, klass = self.klass) # :nodoc:
1007
1017
  scopes = @previous_reflection.join_scopes(table, predicate_builder) + super
1008
- scopes << build_scope(table, predicate_builder).instance_exec(nil, &source_type_scope)
1018
+ scopes << build_scope(table, predicate_builder, klass).instance_exec(nil, &source_type_scope)
1009
1019
  end
1010
1020
 
1011
1021
  def constraints
@@ -1021,7 +1031,7 @@ module ActiveRecord
1021
1031
  end
1022
1032
 
1023
1033
  class RuntimeReflection < AbstractReflection # :nodoc:
1024
- delegate :scope, :type, :constraints, :get_join_keys, to: :@reflection
1034
+ delegate :scope, :type, :constraints, :join_foreign_key, to: :@reflection
1025
1035
 
1026
1036
  def initialize(reflection, association)
1027
1037
  @reflection = reflection
@@ -1033,7 +1043,11 @@ module ActiveRecord
1033
1043
  end
1034
1044
 
1035
1045
  def aliased_table
1036
- @aliased_table ||= Arel::Table.new(table_name, type_caster: klass.type_caster)
1046
+ klass.arel_table
1047
+ end
1048
+
1049
+ def join_primary_key(klass = self.klass)
1050
+ @reflection.join_primary_key(klass)
1037
1051
  end
1038
1052
 
1039
1053
  def all_includes; yield; end
@@ -41,19 +41,35 @@ module ActiveRecord
41
41
  end
42
42
  end
43
43
 
44
- # Delegates #delete_all, #update_all, #destroy_all methods to each batch.
44
+ # Deletes records in batches. Returns the total number of rows affected.
45
45
  #
46
- # People.in_batches.delete_all
47
- # People.where('age < 10').in_batches.destroy_all
48
- # People.in_batches.update_all('age = age + 1')
49
- [:delete_all, :update_all, :destroy_all].each do |method|
50
- define_method(method) do |*args, &block|
51
- @relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: false).each do |relation|
52
- relation.send(method, *args, &block)
53
- end
46
+ # Person.in_batches.delete_all
47
+ #
48
+ # See Relation#delete_all for details of how each batch is deleted.
49
+ def delete_all
50
+ sum(&:delete_all)
51
+ end
52
+
53
+ # Updates records in batches. Returns the total number of rows affected.
54
+ #
55
+ # Person.in_batches.update_all("age = age + 1")
56
+ #
57
+ # See Relation#update_all for details of how each batch is updated.
58
+ def update_all(updates)
59
+ sum do |relation|
60
+ relation.update_all(updates)
54
61
  end
55
62
  end
56
63
 
64
+ # Destroys records in batches.
65
+ #
66
+ # Person.where("age < 10").in_batches.destroy_all
67
+ #
68
+ # See Relation#destroy_all for details of how each batch is destroyed.
69
+ def destroy_all
70
+ each(&:destroy_all)
71
+ end
72
+
57
73
  # Yields an ActiveRecord::Relation object for each batch of records.
58
74
  #
59
75
  # Person.in_batches.each do |relation|
@@ -37,6 +37,7 @@ module ActiveRecord
37
37
  # * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
38
38
  # * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
39
39
  # an order is present in the relation.
40
+ # * <tt>:order</tt> - Specifies the primary key order (can be :asc or :desc). Defaults to :asc.
40
41
  #
41
42
  # Limits are honored, and if present there is no requirement for the batch
42
43
  # size: it can be less than, equal to, or greater than the limit.
@@ -57,22 +58,22 @@ module ActiveRecord
57
58
  # person.party_all_night!
58
59
  # end
59
60
  #
60
- # NOTE: It's not possible to set the order. That is automatically set to
61
- # ascending on the primary key ("id ASC") to make the batch ordering
62
- # work. This also means that this method only works when the primary key is
61
+ # NOTE: Order can be ascending (:asc) or descending (:desc). It is automatically set to
62
+ # ascending on the primary key ("id ASC").
63
+ # This also means that this method only works when the primary key is
63
64
  # orderable (e.g. an integer or string).
64
65
  #
65
66
  # NOTE: By its nature, batch processing is subject to race conditions if
66
67
  # other processes are modifying the database.
67
- def find_each(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil)
68
+ def find_each(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil, order: :asc)
68
69
  if block_given?
69
- find_in_batches(start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore) do |records|
70
+ find_in_batches(start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore, order: order) do |records|
70
71
  records.each { |record| yield record }
71
72
  end
72
73
  else
73
- enum_for(:find_each, start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore) do
74
+ enum_for(:find_each, start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore, order: order) do
74
75
  relation = self
75
- apply_limits(relation, start, finish).size
76
+ apply_limits(relation, start, finish, order).size
76
77
  end
77
78
  end
78
79
  end
@@ -101,6 +102,7 @@ module ActiveRecord
101
102
  # * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
102
103
  # * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
103
104
  # an order is present in the relation.
105
+ # * <tt>:order</tt> - Specifies the primary key order (can be :asc or :desc). Defaults to :asc.
104
106
  #
105
107
  # Limits are honored, and if present there is no requirement for the batch
106
108
  # size: it can be less than, equal to, or greater than the limit.
@@ -116,23 +118,23 @@ module ActiveRecord
116
118
  # group.each { |person| person.party_all_night! }
117
119
  # end
118
120
  #
119
- # NOTE: It's not possible to set the order. That is automatically set to
120
- # ascending on the primary key ("id ASC") to make the batch ordering
121
- # work. This also means that this method only works when the primary key is
121
+ # NOTE: Order can be ascending (:asc) or descending (:desc). It is automatically set to
122
+ # ascending on the primary key ("id ASC").
123
+ # This also means that this method only works when the primary key is
122
124
  # orderable (e.g. an integer or string).
123
125
  #
124
126
  # NOTE: By its nature, batch processing is subject to race conditions if
125
127
  # other processes are modifying the database.
126
- def find_in_batches(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil)
128
+ def find_in_batches(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil, order: :asc)
127
129
  relation = self
128
130
  unless block_given?
129
- return to_enum(:find_in_batches, start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore) do
130
- total = apply_limits(relation, start, finish).size
131
+ return to_enum(:find_in_batches, start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore, order: order) do
132
+ total = apply_limits(relation, start, finish, order).size
131
133
  (total - 1).div(batch_size) + 1
132
134
  end
133
135
  end
134
136
 
135
- in_batches(of: batch_size, start: start, finish: finish, load: true, error_on_ignore: error_on_ignore) do |batch|
137
+ in_batches(of: batch_size, start: start, finish: finish, load: true, error_on_ignore: error_on_ignore, order: order) do |batch|
136
138
  yield batch.to_a
137
139
  end
138
140
  end
@@ -165,6 +167,7 @@ module ActiveRecord
165
167
  # * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
166
168
  # * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
167
169
  # an order is present in the relation.
170
+ # * <tt>:order</tt> - Specifies the primary key order (can be :asc or :desc). Defaults to :asc.
168
171
  #
169
172
  # Limits are honored, and if present there is no requirement for the batch
170
173
  # size, it can be less than, equal, or greater than the limit.
@@ -191,19 +194,23 @@ module ActiveRecord
191
194
  #
192
195
  # Person.in_batches.each_record(&:party_all_night!)
193
196
  #
194
- # NOTE: It's not possible to set the order. That is automatically set to
195
- # ascending on the primary key ("id ASC") to make the batch ordering
196
- # consistent. Therefore the primary key must be orderable, e.g. an integer
197
- # or a string.
197
+ # NOTE: Order can be ascending (:asc) or descending (:desc). It is automatically set to
198
+ # ascending on the primary key ("id ASC").
199
+ # This also means that this method only works when the primary key is
200
+ # orderable (e.g. an integer or string).
198
201
  #
199
202
  # NOTE: By its nature, batch processing is subject to race conditions if
200
203
  # other processes are modifying the database.
201
- def in_batches(of: 1000, start: nil, finish: nil, load: false, error_on_ignore: nil)
204
+ def in_batches(of: 1000, start: nil, finish: nil, load: false, error_on_ignore: nil, order: :asc)
202
205
  relation = self
203
206
  unless block_given?
204
207
  return BatchEnumerator.new(of: of, start: start, finish: finish, relation: self)
205
208
  end
206
209
 
210
+ unless [:asc, :desc].include?(order)
211
+ raise ArgumentError, ":order must be :asc or :desc, got #{order.inspect}"
212
+ end
213
+
207
214
  if arel.orders.present?
208
215
  act_on_ignored_order(error_on_ignore)
209
216
  end
@@ -214,8 +221,8 @@ module ActiveRecord
214
221
  batch_limit = remaining if remaining < batch_limit
215
222
  end
216
223
 
217
- relation = relation.reorder(batch_order).limit(batch_limit)
218
- relation = apply_limits(relation, start, finish)
224
+ relation = relation.reorder(batch_order(order)).limit(batch_limit)
225
+ relation = apply_limits(relation, start, finish, order)
219
226
  relation.skip_query_cache! # Retaining the results in the query cache would undermine the point of batching
220
227
  batch_relation = relation
221
228
 
@@ -252,29 +259,28 @@ module ActiveRecord
252
259
  end
253
260
 
254
261
  batch_relation = relation.where(
255
- bind_attribute(primary_key, primary_key_offset) { |attr, bind| attr.gt(bind) }
262
+ predicate_builder[primary_key, primary_key_offset, order == :desc ? :lt : :gt]
256
263
  )
257
264
  end
258
265
  end
259
266
 
260
267
  private
261
-
262
- def apply_limits(relation, start, finish)
263
- relation = apply_start_limit(relation, start) if start
264
- relation = apply_finish_limit(relation, finish) if finish
268
+ def apply_limits(relation, start, finish, order)
269
+ relation = apply_start_limit(relation, start, order) if start
270
+ relation = apply_finish_limit(relation, finish, order) if finish
265
271
  relation
266
272
  end
267
273
 
268
- def apply_start_limit(relation, start)
269
- relation.where(bind_attribute(primary_key, start) { |attr, bind| attr.gteq(bind) })
274
+ def apply_start_limit(relation, start, order)
275
+ relation.where(predicate_builder[primary_key, start, order == :desc ? :lteq : :gteq])
270
276
  end
271
277
 
272
- def apply_finish_limit(relation, finish)
273
- relation.where(bind_attribute(primary_key, finish) { |attr, bind| attr.lteq(bind) })
278
+ def apply_finish_limit(relation, finish, order)
279
+ relation.where(predicate_builder[primary_key, finish, order == :desc ? :gteq : :lteq])
274
280
  end
275
281
 
276
- def batch_order
277
- arel_attribute(primary_key).asc
282
+ def batch_order(order)
283
+ table[primary_key].public_send(order)
278
284
  end
279
285
 
280
286
  def act_on_ignored_order(error_on_ignore)