activerecord 6.0.6.1 → 6.1.7.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (243) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1152 -779
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -2
  5. data/lib/active_record/aggregations.rb +5 -5
  6. data/lib/active_record/association_relation.rb +30 -12
  7. data/lib/active_record/associations/alias_tracker.rb +19 -15
  8. data/lib/active_record/associations/association.rb +49 -26
  9. data/lib/active_record/associations/association_scope.rb +18 -20
  10. data/lib/active_record/associations/belongs_to_association.rb +23 -10
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -3
  12. data/lib/active_record/associations/builder/association.rb +32 -5
  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 -1
  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 +32 -18
  20. data/lib/active_record/associations/collection_proxy.rb +12 -5
  21. data/lib/active_record/associations/foreign_association.rb +13 -0
  22. data/lib/active_record/associations/has_many_association.rb +24 -2
  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 +37 -21
  26. data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
  27. data/lib/active_record/associations/join_dependency.rb +63 -49
  28. data/lib/active_record/associations/preloader/association.rb +14 -8
  29. data/lib/active_record/associations/preloader/through_association.rb +1 -1
  30. data/lib/active_record/associations/preloader.rb +5 -3
  31. data/lib/active_record/associations/singular_association.rb +1 -1
  32. data/lib/active_record/associations.rb +118 -11
  33. data/lib/active_record/attribute_assignment.rb +10 -8
  34. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
  35. data/lib/active_record/attribute_methods/dirty.rb +1 -11
  36. data/lib/active_record/attribute_methods/primary_key.rb +6 -2
  37. data/lib/active_record/attribute_methods/query.rb +3 -6
  38. data/lib/active_record/attribute_methods/read.rb +8 -11
  39. data/lib/active_record/attribute_methods/serialization.rb +11 -5
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -13
  41. data/lib/active_record/attribute_methods/write.rb +12 -20
  42. data/lib/active_record/attribute_methods.rb +64 -54
  43. data/lib/active_record/attributes.rb +33 -8
  44. data/lib/active_record/autosave_association.rb +47 -30
  45. data/lib/active_record/base.rb +2 -14
  46. data/lib/active_record/callbacks.rb +152 -22
  47. data/lib/active_record/coders/yaml_column.rb +1 -1
  48. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +185 -134
  49. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  50. data/lib/active_record/connection_adapters/abstract/database_statements.rb +66 -23
  51. data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -8
  52. data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
  53. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  54. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
  55. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +114 -26
  56. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +228 -83
  58. data/lib/active_record/connection_adapters/abstract/transaction.rb +92 -33
  59. data/lib/active_record/connection_adapters/abstract_adapter.rb +52 -76
  60. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +123 -87
  61. data/lib/active_record/connection_adapters/column.rb +15 -1
  62. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  63. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +24 -24
  65. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -1
  66. data/lib/active_record/connection_adapters/mysql/quoting.rb +18 -3
  67. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -6
  68. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  69. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +5 -2
  70. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +7 -4
  71. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  72. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -12
  73. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  74. data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
  75. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  76. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +14 -53
  77. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  78. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  79. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
  80. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -2
  84. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  86. data/lib/active_record/connection_adapters/postgresql/quoting.rb +30 -4
  87. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  88. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +5 -1
  89. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -29
  90. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  91. data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -64
  92. data/lib/active_record/connection_adapters/schema_cache.rb +130 -15
  93. data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
  94. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +32 -5
  95. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
  96. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  97. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +36 -3
  98. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +48 -50
  99. data/lib/active_record/connection_adapters.rb +52 -0
  100. data/lib/active_record/connection_handling.rb +218 -71
  101. data/lib/active_record/core.rb +264 -63
  102. data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -0
  103. data/lib/active_record/database_configurations/database_config.rb +52 -9
  104. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  105. data/lib/active_record/database_configurations/url_config.rb +15 -40
  106. data/lib/active_record/database_configurations.rb +125 -85
  107. data/lib/active_record/delegated_type.rb +209 -0
  108. data/lib/active_record/destroy_association_async_job.rb +36 -0
  109. data/lib/active_record/enum.rb +69 -34
  110. data/lib/active_record/errors.rb +47 -12
  111. data/lib/active_record/explain.rb +9 -4
  112. data/lib/active_record/explain_subscriber.rb +1 -1
  113. data/lib/active_record/fixture_set/file.rb +10 -17
  114. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  115. data/lib/active_record/fixture_set/render_context.rb +1 -1
  116. data/lib/active_record/fixture_set/table_row.rb +2 -2
  117. data/lib/active_record/fixtures.rb +58 -9
  118. data/lib/active_record/gem_version.rb +3 -3
  119. data/lib/active_record/inheritance.rb +40 -18
  120. data/lib/active_record/insert_all.rb +38 -5
  121. data/lib/active_record/integration.rb +3 -5
  122. data/lib/active_record/internal_metadata.rb +18 -7
  123. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  124. data/lib/active_record/locking/optimistic.rb +24 -17
  125. data/lib/active_record/locking/pessimistic.rb +6 -2
  126. data/lib/active_record/log_subscriber.rb +27 -8
  127. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  128. data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
  129. data/lib/active_record/middleware/database_selector.rb +4 -1
  130. data/lib/active_record/migration/command_recorder.rb +47 -27
  131. data/lib/active_record/migration/compatibility.rb +72 -18
  132. data/lib/active_record/migration.rb +114 -84
  133. data/lib/active_record/model_schema.rb +89 -14
  134. data/lib/active_record/nested_attributes.rb +2 -3
  135. data/lib/active_record/no_touching.rb +1 -1
  136. data/lib/active_record/persistence.rb +50 -45
  137. data/lib/active_record/query_cache.rb +15 -5
  138. data/lib/active_record/querying.rb +11 -6
  139. data/lib/active_record/railtie.rb +64 -44
  140. data/lib/active_record/railties/console_sandbox.rb +2 -4
  141. data/lib/active_record/railties/databases.rake +279 -101
  142. data/lib/active_record/readonly_attributes.rb +4 -0
  143. data/lib/active_record/reflection.rb +60 -44
  144. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  145. data/lib/active_record/relation/batches.rb +38 -31
  146. data/lib/active_record/relation/calculations.rb +104 -43
  147. data/lib/active_record/relation/finder_methods.rb +44 -14
  148. data/lib/active_record/relation/from_clause.rb +1 -1
  149. data/lib/active_record/relation/merger.rb +20 -23
  150. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  151. data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
  152. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -6
  153. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  154. data/lib/active_record/relation/predicate_builder.rb +61 -38
  155. data/lib/active_record/relation/query_methods.rb +322 -196
  156. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  157. data/lib/active_record/relation/spawn_methods.rb +8 -7
  158. data/lib/active_record/relation/where_clause.rb +111 -61
  159. data/lib/active_record/relation.rb +100 -81
  160. data/lib/active_record/result.rb +41 -33
  161. data/lib/active_record/runtime_registry.rb +2 -2
  162. data/lib/active_record/sanitization.rb +6 -17
  163. data/lib/active_record/schema_dumper.rb +34 -4
  164. data/lib/active_record/schema_migration.rb +2 -8
  165. data/lib/active_record/scoping/default.rb +1 -3
  166. data/lib/active_record/scoping/named.rb +1 -17
  167. data/lib/active_record/secure_token.rb +16 -8
  168. data/lib/active_record/serialization.rb +5 -3
  169. data/lib/active_record/signed_id.rb +116 -0
  170. data/lib/active_record/statement_cache.rb +20 -4
  171. data/lib/active_record/store.rb +8 -3
  172. data/lib/active_record/suppressor.rb +2 -2
  173. data/lib/active_record/table_metadata.rb +42 -51
  174. data/lib/active_record/tasks/database_tasks.rb +140 -113
  175. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -35
  176. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -26
  177. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -9
  178. data/lib/active_record/test_databases.rb +5 -4
  179. data/lib/active_record/test_fixtures.rb +79 -31
  180. data/lib/active_record/timestamp.rb +4 -6
  181. data/lib/active_record/touch_later.rb +21 -21
  182. data/lib/active_record/transactions.rb +19 -66
  183. data/lib/active_record/type/serialized.rb +6 -2
  184. data/lib/active_record/type.rb +8 -1
  185. data/lib/active_record/type_caster/connection.rb +0 -1
  186. data/lib/active_record/type_caster/map.rb +8 -5
  187. data/lib/active_record/validations/associated.rb +1 -1
  188. data/lib/active_record/validations/numericality.rb +35 -0
  189. data/lib/active_record/validations/uniqueness.rb +24 -4
  190. data/lib/active_record/validations.rb +1 -0
  191. data/lib/active_record.rb +7 -14
  192. data/lib/arel/attributes/attribute.rb +4 -0
  193. data/lib/arel/collectors/bind.rb +5 -0
  194. data/lib/arel/collectors/composite.rb +8 -0
  195. data/lib/arel/collectors/sql_string.rb +7 -0
  196. data/lib/arel/collectors/substitute_binds.rb +7 -0
  197. data/lib/arel/nodes/binary.rb +82 -8
  198. data/lib/arel/nodes/bind_param.rb +8 -0
  199. data/lib/arel/nodes/casted.rb +21 -9
  200. data/lib/arel/nodes/equality.rb +6 -9
  201. data/lib/arel/nodes/grouping.rb +3 -0
  202. data/lib/arel/nodes/homogeneous_in.rb +76 -0
  203. data/lib/arel/nodes/in.rb +8 -1
  204. data/lib/arel/nodes/infix_operation.rb +13 -1
  205. data/lib/arel/nodes/join_source.rb +1 -1
  206. data/lib/arel/nodes/node.rb +7 -6
  207. data/lib/arel/nodes/ordering.rb +27 -0
  208. data/lib/arel/nodes/sql_literal.rb +3 -0
  209. data/lib/arel/nodes/table_alias.rb +7 -3
  210. data/lib/arel/nodes/unary.rb +0 -1
  211. data/lib/arel/nodes.rb +3 -1
  212. data/lib/arel/predications.rb +12 -18
  213. data/lib/arel/select_manager.rb +1 -2
  214. data/lib/arel/table.rb +13 -5
  215. data/lib/arel/visitors/dot.rb +14 -2
  216. data/lib/arel/visitors/mysql.rb +11 -1
  217. data/lib/arel/visitors/postgresql.rb +15 -4
  218. data/lib/arel/visitors/to_sql.rb +89 -78
  219. data/lib/arel/visitors.rb +0 -7
  220. data/lib/arel.rb +5 -13
  221. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  222. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  223. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +3 -3
  224. data/lib/rails/generators/active_record/migration.rb +6 -1
  225. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  226. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  227. metadata +25 -26
  228. data/lib/active_record/advisory_lock_base.rb +0 -18
  229. data/lib/active_record/attribute_decorators.rb +0 -88
  230. data/lib/active_record/connection_adapters/connection_specification.rb +0 -296
  231. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  232. data/lib/active_record/define_callbacks.rb +0 -22
  233. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  234. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  235. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  236. data/lib/arel/attributes.rb +0 -22
  237. data/lib/arel/visitors/depth_first.rb +0 -203
  238. data/lib/arel/visitors/ibm_db.rb +0 -34
  239. data/lib/arel/visitors/informix.rb +0 -62
  240. data/lib/arel/visitors/mssql.rb +0 -156
  241. data/lib/arel/visitors/oracle.rb +0 -158
  242. data/lib/arel/visitors/oracle12.rb +0 -65
  243. data/lib/arel/visitors/where_sql.rb +0 -22
@@ -19,6 +19,10 @@ module ActiveRecord
19
19
  def readonly_attributes
20
20
  _attr_readonly
21
21
  end
22
+
23
+ def readonly_attribute?(name) # :nodoc:
24
+ _attr_readonly.include?(name)
25
+ end
22
26
  end
23
27
  end
24
28
  end
@@ -162,13 +162,7 @@ module ActiveRecord
162
162
  # <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>'Money'</tt>
163
163
  # <tt>has_many :clients</tt> returns <tt>'Client'</tt>
164
164
  def class_name
165
- @class_name ||= (options[:class_name] || derive_class_name).to_s
166
- end
167
-
168
- JoinKeys = Struct.new(:key, :foreign_key) # :nodoc:
169
-
170
- def join_keys
171
- @join_keys ||= get_join_keys(klass)
165
+ @class_name ||= -(options[:class_name] || derive_class_name).to_s
172
166
  end
173
167
 
174
168
  # Returns a list of scopes that should be applied for this Reflection
@@ -188,10 +182,10 @@ module ActiveRecord
188
182
 
189
183
  scope_chain_items.inject(klass_scope, &:merge!)
190
184
 
191
- key = join_keys.key
192
- foreign_key = join_keys.foreign_key
185
+ primary_key = join_primary_key
186
+ foreign_key = join_foreign_key
193
187
 
194
- klass_scope.where!(table[key].eq(foreign_table[foreign_key]))
188
+ klass_scope.where!(table[primary_key].eq(foreign_table[foreign_key]))
195
189
 
196
190
  if klass.finder_needs_type_condition?
197
191
  klass_scope.where!(klass.send(:type_condition, table))
@@ -218,14 +212,14 @@ module ActiveRecord
218
212
  end
219
213
 
220
214
  def counter_cache_column
221
- if belongs_to?
215
+ @counter_cache_column ||= if belongs_to?
222
216
  if options[:counter_cache] == true
223
- "#{active_record.name.demodulize.underscore.pluralize}_count"
217
+ -"#{active_record.name.demodulize.underscore.pluralize}_count"
224
218
  elsif options[:counter_cache]
225
- options[:counter_cache].to_s
219
+ -options[:counter_cache].to_s
226
220
  end
227
221
  else
228
- options[:counter_cache] ? options[:counter_cache].to_s : "#{name}_count"
222
+ -(options[:counter_cache]&.to_s || "#{name}_count")
229
223
  end
230
224
  end
231
225
 
@@ -272,7 +266,7 @@ module ActiveRecord
272
266
  def has_cached_counter?
273
267
  options[:counter_cache] ||
274
268
  inverse_which_updates_counter_cache && inverse_which_updates_counter_cache.options[:counter_cache] &&
275
- !!active_record.columns_hash[counter_cache_column]
269
+ active_record.has_attribute?(counter_cache_column)
276
270
  end
277
271
 
278
272
  def counter_must_be_updated_by_has_many?
@@ -287,10 +281,6 @@ module ActiveRecord
287
281
  collect_join_chain
288
282
  end
289
283
 
290
- def get_join_keys(association_klass)
291
- JoinKeys.new(join_primary_key(association_klass), join_foreign_key)
292
- end
293
-
294
284
  def build_scope(table, predicate_builder = predicate_builder(table), klass = self.klass)
295
285
  Relation.create(
296
286
  klass,
@@ -299,12 +289,8 @@ module ActiveRecord
299
289
  )
300
290
  end
301
291
 
302
- def join_primary_key(*)
303
- foreign_key
304
- end
305
-
306
- def join_foreign_key
307
- active_record_primary_key
292
+ def strict_loading?
293
+ options[:strict_loading]
308
294
  end
309
295
 
310
296
  protected
@@ -428,8 +414,8 @@ module ActiveRecord
428
414
 
429
415
  def initialize(name, scope, options, active_record)
430
416
  super
431
- @type = options[:as] && (options[:foreign_type] || "#{options[:as]}_type")
432
- @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]
433
419
  @constructable = calculate_constructable(macro, options)
434
420
 
435
421
  if options[:class_name] && options[:class_name].class == Class
@@ -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!
@@ -630,6 +623,7 @@ module ActiveRecord
630
623
  # with the current reflection's klass name.
631
624
  def valid_inverse_reflection?(reflection)
632
625
  reflection &&
626
+ foreign_key == reflection.foreign_key &&
633
627
  klass <= reflection.active_record &&
634
628
  can_find_inverse_of_automatically?(reflection)
635
629
  end
@@ -683,10 +677,6 @@ module ActiveRecord
683
677
  Associations::HasManyAssociation
684
678
  end
685
679
  end
686
-
687
- def association_primary_key(klass = nil)
688
- primary_key(klass || self.klass)
689
- end
690
680
  end
691
681
 
692
682
  class HasOneReflection < AssociationReflection # :nodoc:
@@ -721,6 +711,15 @@ module ActiveRecord
721
711
  end
722
712
  end
723
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
+
724
723
  def join_primary_key(klass = nil)
725
724
  polymorphic? ? association_primary_key(klass) : association_primary_key
726
725
  end
@@ -729,6 +728,10 @@ module ActiveRecord
729
728
  foreign_key
730
729
  end
731
730
 
731
+ def join_foreign_type
732
+ foreign_type
733
+ end
734
+
732
735
  private
733
736
  def can_find_inverse_of_automatically?(_)
734
737
  !polymorphic? && super
@@ -750,8 +753,8 @@ module ActiveRecord
750
753
  # Holds all the metadata about a :through association as it was specified
751
754
  # in the Active Record class.
752
755
  class ThroughReflection < AbstractReflection #:nodoc:
753
- delegate :foreign_key, :foreign_type, :association_foreign_key, :join_id_for,
754
- :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
755
758
 
756
759
  def initialize(delegate_reflection)
757
760
  @delegate_reflection = delegate_reflection
@@ -858,7 +861,15 @@ module ActiveRecord
858
861
  def association_primary_key(klass = nil)
859
862
  # Get the "actual" source reflection if the immediate source reflection has a
860
863
  # source reflection itself
861
- 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)
862
873
  end
863
874
 
864
875
  # Gets an array of possible <tt>:through</tt> source reflection names in both singular and plural form.
@@ -907,7 +918,7 @@ module ActiveRecord
907
918
 
908
919
  def check_validity!
909
920
  if through_reflection.nil?
910
- raise HasManyThroughAssociationNotFoundError.new(active_record.name, self)
921
+ raise HasManyThroughAssociationNotFoundError.new(active_record, self)
911
922
  end
912
923
 
913
924
  if through_reflection.polymorphic?
@@ -994,7 +1005,8 @@ module ActiveRecord
994
1005
  end
995
1006
 
996
1007
  class PolymorphicReflection < AbstractReflection # :nodoc:
997
- 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
998
1010
 
999
1011
  def initialize(reflection, previous_reflection)
1000
1012
  @reflection = reflection
@@ -1019,7 +1031,7 @@ module ActiveRecord
1019
1031
  end
1020
1032
 
1021
1033
  class RuntimeReflection < AbstractReflection # :nodoc:
1022
- delegate :scope, :type, :constraints, :get_join_keys, to: :@reflection
1034
+ delegate :scope, :type, :constraints, :join_foreign_key, to: :@reflection
1023
1035
 
1024
1036
  def initialize(reflection, association)
1025
1037
  @reflection = reflection
@@ -1031,7 +1043,11 @@ module ActiveRecord
1031
1043
  end
1032
1044
 
1033
1045
  def aliased_table
1034
- @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)
1035
1051
  end
1036
1052
 
1037
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,28 +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
- def apply_limits(relation, start, finish)
262
- relation = apply_start_limit(relation, start) if start
263
- 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
264
271
  relation
265
272
  end
266
273
 
267
- def apply_start_limit(relation, start)
268
- 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])
269
276
  end
270
277
 
271
- def apply_finish_limit(relation, finish)
272
- 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])
273
280
  end
274
281
 
275
- def batch_order
276
- arel_attribute(primary_key).asc
282
+ def batch_order(order)
283
+ table[primary_key].public_send(order)
277
284
  end
278
285
 
279
286
  def act_on_ignored_order(error_on_ignore)