activerecord 6.0.6.1 → 6.1.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 (242) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +764 -942
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +3 -3
  5. data/lib/active_record/aggregations.rb +1 -1
  6. data/lib/active_record/association_relation.rb +22 -14
  7. data/lib/active_record/associations/alias_tracker.rb +19 -15
  8. data/lib/active_record/associations/association.rb +39 -27
  9. data/lib/active_record/associations/association_scope.rb +11 -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 -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 +19 -13
  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 +29 -14
  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 +13 -5
  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 +114 -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 +4 -4
  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 +52 -48
  43. data/lib/active_record/attributes.rb +27 -7
  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 +32 -22
  47. data/lib/active_record/coders/yaml_column.rb +2 -24
  48. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +180 -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 +65 -22
  51. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -7
  52. data/lib/active_record/connection_adapters/abstract/quoting.rb +35 -44
  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 +110 -30
  56. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +224 -85
  58. data/lib/active_record/connection_adapters/abstract/transaction.rb +66 -24
  59. data/lib/active_record/connection_adapters/abstract_adapter.rb +31 -70
  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 +31 -0
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +22 -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 +1 -1
  67. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +33 -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 +1 -1
  70. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +3 -3
  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 +63 -0
  74. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  75. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  76. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +12 -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 -10
  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/money.rb +2 -2
  84. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -1
  86. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  87. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  88. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  89. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +5 -1
  90. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -29
  91. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  92. data/lib/active_record/connection_adapters/postgresql_adapter.rb +72 -55
  93. data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
  94. data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
  95. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +30 -5
  96. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
  97. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  98. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +36 -3
  99. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +48 -50
  100. data/lib/active_record/connection_adapters.rb +50 -0
  101. data/lib/active_record/connection_handling.rb +210 -71
  102. data/lib/active_record/core.rb +214 -58
  103. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  104. data/lib/active_record/database_configurations/database_config.rb +52 -9
  105. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  106. data/lib/active_record/database_configurations/url_config.rb +15 -40
  107. data/lib/active_record/database_configurations.rb +124 -85
  108. data/lib/active_record/delegated_type.rb +209 -0
  109. data/lib/active_record/destroy_association_async_job.rb +36 -0
  110. data/lib/active_record/enum.rb +33 -23
  111. data/lib/active_record/errors.rb +47 -12
  112. data/lib/active_record/explain.rb +9 -4
  113. data/lib/active_record/explain_subscriber.rb +1 -1
  114. data/lib/active_record/fixture_set/file.rb +10 -17
  115. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  116. data/lib/active_record/fixture_set/render_context.rb +1 -1
  117. data/lib/active_record/fixture_set/table_row.rb +2 -2
  118. data/lib/active_record/fixtures.rb +54 -8
  119. data/lib/active_record/gem_version.rb +3 -3
  120. data/lib/active_record/inheritance.rb +40 -18
  121. data/lib/active_record/insert_all.rb +32 -5
  122. data/lib/active_record/integration.rb +3 -5
  123. data/lib/active_record/internal_metadata.rb +15 -4
  124. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  125. data/lib/active_record/locking/optimistic.rb +13 -16
  126. data/lib/active_record/locking/pessimistic.rb +6 -2
  127. data/lib/active_record/log_subscriber.rb +26 -8
  128. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  129. data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
  130. data/lib/active_record/middleware/database_selector.rb +4 -1
  131. data/lib/active_record/migration/command_recorder.rb +47 -27
  132. data/lib/active_record/migration/compatibility.rb +67 -17
  133. data/lib/active_record/migration.rb +113 -83
  134. data/lib/active_record/model_schema.rb +88 -42
  135. data/lib/active_record/nested_attributes.rb +2 -3
  136. data/lib/active_record/no_touching.rb +1 -1
  137. data/lib/active_record/persistence.rb +50 -45
  138. data/lib/active_record/query_cache.rb +15 -5
  139. data/lib/active_record/querying.rb +11 -6
  140. data/lib/active_record/railtie.rb +64 -44
  141. data/lib/active_record/railties/databases.rake +253 -98
  142. data/lib/active_record/readonly_attributes.rb +4 -0
  143. data/lib/active_record/reflection.rb +59 -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 +100 -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 +2 -2
  152. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +3 -3
  153. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  154. data/lib/active_record/relation/predicate_builder.rb +57 -33
  155. data/lib/active_record/relation/query_methods.rb +319 -198
  156. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  157. data/lib/active_record/relation/spawn_methods.rb +6 -5
  158. data/lib/active_record/relation/where_clause.rb +104 -57
  159. data/lib/active_record/relation.rb +90 -64
  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 +0 -4
  165. data/lib/active_record/scoping/named.rb +1 -17
  166. data/lib/active_record/secure_token.rb +16 -8
  167. data/lib/active_record/serialization.rb +5 -3
  168. data/lib/active_record/signed_id.rb +116 -0
  169. data/lib/active_record/statement_cache.rb +20 -4
  170. data/lib/active_record/store.rb +2 -2
  171. data/lib/active_record/suppressor.rb +2 -2
  172. data/lib/active_record/table_metadata.rb +36 -52
  173. data/lib/active_record/tasks/database_tasks.rb +139 -113
  174. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -35
  175. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -26
  176. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -9
  177. data/lib/active_record/test_databases.rb +5 -4
  178. data/lib/active_record/test_fixtures.rb +36 -33
  179. data/lib/active_record/timestamp.rb +4 -6
  180. data/lib/active_record/touch_later.rb +21 -21
  181. data/lib/active_record/transactions.rb +15 -64
  182. data/lib/active_record/type/serialized.rb +6 -2
  183. data/lib/active_record/type.rb +8 -1
  184. data/lib/active_record/type_caster/connection.rb +0 -1
  185. data/lib/active_record/type_caster/map.rb +8 -5
  186. data/lib/active_record/validations/associated.rb +1 -1
  187. data/lib/active_record/validations/numericality.rb +35 -0
  188. data/lib/active_record/validations/uniqueness.rb +24 -4
  189. data/lib/active_record/validations.rb +1 -0
  190. data/lib/active_record.rb +7 -14
  191. data/lib/arel/attributes/attribute.rb +4 -0
  192. data/lib/arel/collectors/bind.rb +5 -0
  193. data/lib/arel/collectors/composite.rb +8 -0
  194. data/lib/arel/collectors/sql_string.rb +7 -0
  195. data/lib/arel/collectors/substitute_binds.rb +7 -0
  196. data/lib/arel/nodes/binary.rb +82 -8
  197. data/lib/arel/nodes/bind_param.rb +8 -0
  198. data/lib/arel/nodes/casted.rb +21 -9
  199. data/lib/arel/nodes/equality.rb +6 -9
  200. data/lib/arel/nodes/grouping.rb +3 -0
  201. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  202. data/lib/arel/nodes/in.rb +8 -1
  203. data/lib/arel/nodes/infix_operation.rb +13 -1
  204. data/lib/arel/nodes/join_source.rb +1 -1
  205. data/lib/arel/nodes/node.rb +7 -6
  206. data/lib/arel/nodes/ordering.rb +27 -0
  207. data/lib/arel/nodes/sql_literal.rb +3 -0
  208. data/lib/arel/nodes/table_alias.rb +7 -3
  209. data/lib/arel/nodes/unary.rb +0 -1
  210. data/lib/arel/nodes.rb +3 -1
  211. data/lib/arel/predications.rb +12 -18
  212. data/lib/arel/select_manager.rb +1 -2
  213. data/lib/arel/table.rb +13 -5
  214. data/lib/arel/visitors/dot.rb +14 -2
  215. data/lib/arel/visitors/mysql.rb +11 -1
  216. data/lib/arel/visitors/postgresql.rb +15 -4
  217. data/lib/arel/visitors/to_sql.rb +89 -78
  218. data/lib/arel/visitors.rb +0 -7
  219. data/lib/arel.rb +5 -13
  220. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  221. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  222. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +3 -3
  223. data/lib/rails/generators/active_record/migration.rb +6 -1
  224. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  225. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  226. metadata +30 -32
  227. data/lib/active_record/advisory_lock_base.rb +0 -18
  228. data/lib/active_record/attribute_decorators.rb +0 -88
  229. data/lib/active_record/connection_adapters/connection_specification.rb +0 -296
  230. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  231. data/lib/active_record/define_callbacks.rb +0 -22
  232. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  233. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  234. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  235. data/lib/arel/attributes.rb +0 -22
  236. data/lib/arel/visitors/depth_first.rb +0 -203
  237. data/lib/arel/visitors/ibm_db.rb +0 -34
  238. data/lib/arel/visitors/informix.rb +0 -62
  239. data/lib/arel/visitors/mssql.rb +0 -156
  240. data/lib/arel/visitors/oracle.rb +0 -158
  241. data/lib/arel/visitors/oracle12.rb +0 -65
  242. data/lib/arel/visitors/where_sql.rb +0 -22
@@ -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]&.to_s || derive_class_name)
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!
@@ -683,10 +676,6 @@ module ActiveRecord
683
676
  Associations::HasManyAssociation
684
677
  end
685
678
  end
686
-
687
- def association_primary_key(klass = nil)
688
- primary_key(klass || self.klass)
689
- end
690
679
  end
691
680
 
692
681
  class HasOneReflection < AssociationReflection # :nodoc:
@@ -721,6 +710,15 @@ module ActiveRecord
721
710
  end
722
711
  end
723
712
 
713
+ # klass option is necessary to support loading polymorphic associations
714
+ def association_primary_key(klass = nil)
715
+ if primary_key = options[:primary_key]
716
+ @association_primary_key ||= -primary_key.to_s
717
+ else
718
+ primary_key(klass || self.klass)
719
+ end
720
+ end
721
+
724
722
  def join_primary_key(klass = nil)
725
723
  polymorphic? ? association_primary_key(klass) : association_primary_key
726
724
  end
@@ -729,6 +727,10 @@ module ActiveRecord
729
727
  foreign_key
730
728
  end
731
729
 
730
+ def join_foreign_type
731
+ foreign_type
732
+ end
733
+
732
734
  private
733
735
  def can_find_inverse_of_automatically?(_)
734
736
  !polymorphic? && super
@@ -750,8 +752,8 @@ module ActiveRecord
750
752
  # Holds all the metadata about a :through association as it was specified
751
753
  # in the Active Record class.
752
754
  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
755
+ delegate :foreign_key, :foreign_type, :association_foreign_key, :join_id_for, :type,
756
+ :active_record_primary_key, :join_foreign_key, to: :source_reflection
755
757
 
756
758
  def initialize(delegate_reflection)
757
759
  @delegate_reflection = delegate_reflection
@@ -858,7 +860,15 @@ module ActiveRecord
858
860
  def association_primary_key(klass = nil)
859
861
  # Get the "actual" source reflection if the immediate source reflection has a
860
862
  # source reflection itself
861
- actual_source_reflection.options[:primary_key] || primary_key(klass || self.klass)
863
+ if primary_key = actual_source_reflection.options[:primary_key]
864
+ @association_primary_key ||= -primary_key.to_s
865
+ else
866
+ primary_key(klass || self.klass)
867
+ end
868
+ end
869
+
870
+ def join_primary_key(klass = self.klass)
871
+ source_reflection.join_primary_key(klass)
862
872
  end
863
873
 
864
874
  # Gets an array of possible <tt>:through</tt> source reflection names in both singular and plural form.
@@ -907,7 +917,7 @@ module ActiveRecord
907
917
 
908
918
  def check_validity!
909
919
  if through_reflection.nil?
910
- raise HasManyThroughAssociationNotFoundError.new(active_record.name, self)
920
+ raise HasManyThroughAssociationNotFoundError.new(active_record, self)
911
921
  end
912
922
 
913
923
  if through_reflection.polymorphic?
@@ -994,7 +1004,8 @@ module ActiveRecord
994
1004
  end
995
1005
 
996
1006
  class PolymorphicReflection < AbstractReflection # :nodoc:
997
- delegate :klass, :scope, :plural_name, :type, :get_join_keys, :scope_for, to: :@reflection
1007
+ delegate :klass, :scope, :plural_name, :type, :join_primary_key, :join_foreign_key,
1008
+ :name, :scope_for, to: :@reflection
998
1009
 
999
1010
  def initialize(reflection, previous_reflection)
1000
1011
  @reflection = reflection
@@ -1019,7 +1030,7 @@ module ActiveRecord
1019
1030
  end
1020
1031
 
1021
1032
  class RuntimeReflection < AbstractReflection # :nodoc:
1022
- delegate :scope, :type, :constraints, :get_join_keys, to: :@reflection
1033
+ delegate :scope, :type, :constraints, :join_foreign_key, to: :@reflection
1023
1034
 
1024
1035
  def initialize(reflection, association)
1025
1036
  @reflection = reflection
@@ -1031,7 +1042,11 @@ module ActiveRecord
1031
1042
  end
1032
1043
 
1033
1044
  def aliased_table
1034
- @aliased_table ||= Arel::Table.new(table_name, type_caster: klass.type_caster)
1045
+ klass.arel_table
1046
+ end
1047
+
1048
+ def join_primary_key(klass = self.klass)
1049
+ @reflection.join_primary_key(klass)
1035
1050
  end
1036
1051
 
1037
1052
  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)
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/enumerable"
4
+
3
5
  module ActiveRecord
4
6
  module Calculations
5
7
  # Count the records.
@@ -179,7 +181,7 @@ module ActiveRecord
179
181
  # See also #ids.
180
182
  #
181
183
  def pluck(*column_names)
182
- if loaded? && (column_names.map(&:to_s) - @klass.attribute_names - @klass.attribute_aliases.keys).empty?
184
+ if loaded? && all_attributes?(column_names)
183
185
  return records.pluck(*column_names)
184
186
  end
185
187
 
@@ -188,10 +190,17 @@ module ActiveRecord
188
190
  relation.pluck(*column_names)
189
191
  else
190
192
  klass.disallow_raw_sql!(column_names)
193
+ columns = arel_columns(column_names)
191
194
  relation = spawn
192
- relation.select_values = column_names
193
- result = skip_query_cache_if_necessary { klass.connection.select_all(relation.arel, nil) }
194
- result.cast_values(klass.attribute_types)
195
+ relation.select_values = columns
196
+ result = skip_query_cache_if_necessary do
197
+ if where_clause.contradiction?
198
+ ActiveRecord::Result.new([], [])
199
+ else
200
+ klass.connection.select_all(relation.arel, nil)
201
+ end
202
+ end
203
+ type_cast_pluck_values(result, columns)
195
204
  end
196
205
  end
197
206
 
@@ -210,6 +219,10 @@ module ActiveRecord
210
219
  # # SELECT people.name, people.email_address FROM people WHERE id = 1 LIMIT 1
211
220
  # # => [ 'David', 'david@loudthinking.com' ]
212
221
  def pick(*column_names)
222
+ if loaded? && all_attributes?(column_names)
223
+ return records.pick(*column_names)
224
+ end
225
+
213
226
  limit(1).pluck(*column_names).first
214
227
  end
215
228
 
@@ -222,6 +235,10 @@ module ActiveRecord
222
235
  end
223
236
 
224
237
  private
238
+ def all_attributes?(column_names)
239
+ (column_names.map(&:to_s) - @klass.attribute_names - @klass.attribute_aliases.keys).empty?
240
+ end
241
+
225
242
  def has_include?(column_name)
226
243
  eager_loading? || (includes_values.present? && column_name && column_name != :all)
227
244
  end
@@ -266,12 +283,10 @@ module ActiveRecord
266
283
  end
267
284
 
268
285
  def operation_over_aggregate_column(column, operation, distinct)
269
- operation == "count" ? column.count(distinct) : column.send(operation)
286
+ operation == "count" ? column.count(distinct) : column.public_send(operation)
270
287
  end
271
288
 
272
289
  def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
273
- column_alias = column_name
274
-
275
290
  if operation == "count" && (column_name == :all && distinct || has_limit_or_offset?)
276
291
  # Shortcut when limit is zero.
277
292
  return 0 if limit_value == 0
@@ -282,31 +297,35 @@ module ActiveRecord
282
297
  relation = unscope(:order).distinct!(false)
283
298
 
284
299
  column = aggregate_column(column_name)
285
-
286
300
  select_value = operation_over_aggregate_column(column, operation, distinct)
287
- if operation == "sum" && distinct
288
- select_value.distinct = true
289
- end
301
+ select_value.distinct = true if operation == "sum" && distinct
290
302
 
291
- column_alias = select_value.alias
292
- column_alias ||= @klass.connection.column_name_for_operation(operation, select_value)
293
303
  relation.select_values = [select_value]
294
304
 
295
305
  query_builder = relation.arel
296
306
  end
297
307
 
298
- result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder, nil) }
299
- row = result.first
300
- value = row && row.values.first
301
- type = result.column_types.fetch(column_alias) do
302
- type_for(column_name)
303
- end
308
+ result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder) }
304
309
 
305
- type_cast_calculated_value(value, type, operation)
310
+ type_cast_calculated_value(result.cast_values.first, operation) do |value|
311
+ type = column.try(:type_caster) ||
312
+ lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
313
+ type.deserialize(value)
314
+ end
306
315
  end
307
316
 
308
317
  def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
309
318
  group_fields = group_values
319
+ group_fields = group_fields.uniq if group_fields.size > 1
320
+
321
+ unless group_fields == group_values
322
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
323
+ `#{operation}` with group by duplicated fields does no longer affect to result in Rails 6.2.
324
+ To migrate to Rails 6.2's behavior, use `uniq!(:group)` to deduplicate group fields
325
+ (`#{klass.name&.tableize || klass.table_name}.uniq!(:group).#{operation}(#{column_name.inspect})`).
326
+ MSG
327
+ group_fields = group_values
328
+ end
310
329
 
311
330
  if group_fields.size == 1 && group_fields.first.respond_to?(:to_sym)
312
331
  association = klass._reflect_on_association(group_fields.first)
@@ -321,14 +340,12 @@ module ActiveRecord
321
340
  }
322
341
  group_columns = group_aliases.zip(group_fields)
323
342
 
324
- aggregate_alias = column_alias_for("#{operation} #{column_name.to_s.downcase}")
343
+ column = aggregate_column(column_name)
344
+ column_alias = column_alias_for("#{operation} #{column_name.to_s.downcase}")
345
+ select_value = operation_over_aggregate_column(column, operation, distinct)
346
+ select_value.as(column_alias)
325
347
 
326
- select_values = [
327
- operation_over_aggregate_column(
328
- aggregate_column(column_name),
329
- operation,
330
- distinct).as(aggregate_alias)
331
- ]
348
+ select_values = [select_value]
332
349
  select_values += self.select_values unless having_clause.empty?
333
350
 
334
351
  select_values.concat group_columns.map { |aliaz, field|
@@ -348,22 +365,33 @@ module ActiveRecord
348
365
  if association
349
366
  key_ids = calculated_data.collect { |row| row[group_aliases.first] }
350
367
  key_records = association.klass.base_class.where(association.klass.base_class.primary_key => key_ids)
351
- key_records = Hash[key_records.map { |r| [r.id, r] }]
368
+ key_records = key_records.index_by(&:id)
352
369
  end
353
370
 
354
- Hash[calculated_data.map do |row|
355
- key = group_columns.map { |aliaz, col_name|
356
- type = type_for(col_name) do
357
- calculated_data.column_types.fetch(aliaz, Type.default_value)
358
- end
359
- type_cast_calculated_value(row[aliaz], type)
360
- }
371
+ key_types = group_columns.each_with_object({}) do |(aliaz, col_name), types|
372
+ types[aliaz] = type_for(col_name) do
373
+ calculated_data.column_types.fetch(aliaz, Type.default_value)
374
+ end
375
+ end
376
+
377
+ hash_rows = calculated_data.cast_values(key_types).map! do |row|
378
+ calculated_data.columns.each_with_object({}).with_index do |(col_name, hash), i|
379
+ hash[col_name] = row[i]
380
+ end
381
+ end
382
+
383
+ type = nil
384
+ hash_rows.each_with_object({}) do |row, result|
385
+ key = group_aliases.map { |aliaz| row[aliaz] }
361
386
  key = key.first if key.size == 1
362
387
  key = key_records[key] if associated
363
388
 
364
- type = calculated_data.column_types.fetch(aggregate_alias) { type_for(column_name) }
365
- [key, type_cast_calculated_value(row[aggregate_alias], type, operation)]
366
- end]
389
+ result[key] = type_cast_calculated_value(row[column_alias], operation) do |value|
390
+ type ||= column.try(:type_caster) ||
391
+ lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
392
+ type.deserialize(value)
393
+ end
394
+ end
367
395
  end
368
396
 
369
397
  # Converts the given field to the value that the database adapter returns as
@@ -388,12 +416,41 @@ module ActiveRecord
388
416
  @klass.type_for_attribute(field_name, &block)
389
417
  end
390
418
 
391
- def type_cast_calculated_value(value, type, operation = nil)
419
+ def lookup_cast_type_from_join_dependencies(name, join_dependencies = build_join_dependencies)
420
+ each_join_dependencies(join_dependencies) do |join|
421
+ type = join.base_klass.attribute_types.fetch(name, nil)
422
+ return type if type
423
+ end
424
+ nil
425
+ end
426
+
427
+ def type_cast_pluck_values(result, columns)
428
+ cast_types = if result.columns.size != columns.size
429
+ klass.attribute_types
430
+ else
431
+ join_dependencies = nil
432
+ columns.map.with_index do |column, i|
433
+ column.try(:type_caster) ||
434
+ klass.attribute_types.fetch(name = result.columns[i]) do
435
+ join_dependencies ||= build_join_dependencies
436
+ lookup_cast_type_from_join_dependencies(name, join_dependencies) ||
437
+ result.column_types[name] || Type.default_value
438
+ end
439
+ end
440
+ end
441
+ result.cast_values(cast_types)
442
+ end
443
+
444
+ def type_cast_calculated_value(value, operation)
392
445
  case operation
393
- when "count" then value.to_i
394
- when "sum" then type.deserialize(value || 0)
395
- when "average" then value&.respond_to?(:to_d) ? value.to_d : value
396
- else type.deserialize(value)
446
+ when "count"
447
+ value.to_i
448
+ when "sum"
449
+ yield value || 0
450
+ when "average"
451
+ value&.respond_to?(:to_d) ? value.to_d : value
452
+ else # "minimum", "maximum"
453
+ yield value
397
454
  end
398
455
  end
399
456