activerecord 6.1.3.2 → 7.0.0.alpha2

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 (229) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +734 -1058
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/active_record/aggregations.rb +1 -1
  6. data/lib/active_record/association_relation.rb +0 -10
  7. data/lib/active_record/associations/association.rb +35 -7
  8. data/lib/active_record/associations/association_scope.rb +1 -3
  9. data/lib/active_record/associations/belongs_to_association.rb +16 -6
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  11. data/lib/active_record/associations/builder/association.rb +8 -2
  12. data/lib/active_record/associations/builder/belongs_to.rb +19 -6
  13. data/lib/active_record/associations/builder/collection_association.rb +1 -1
  14. data/lib/active_record/associations/builder/has_many.rb +3 -2
  15. data/lib/active_record/associations/builder/has_one.rb +2 -1
  16. data/lib/active_record/associations/builder/singular_association.rb +2 -2
  17. data/lib/active_record/associations/collection_association.rb +24 -25
  18. data/lib/active_record/associations/collection_proxy.rb +8 -3
  19. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  20. data/lib/active_record/associations/has_many_association.rb +1 -1
  21. data/lib/active_record/associations/has_many_through_association.rb +2 -1
  22. data/lib/active_record/associations/has_one_association.rb +10 -7
  23. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  24. data/lib/active_record/associations/preloader/association.rb +161 -49
  25. data/lib/active_record/associations/preloader/batch.rb +51 -0
  26. data/lib/active_record/associations/preloader/branch.rb +147 -0
  27. data/lib/active_record/associations/preloader/through_association.rb +37 -11
  28. data/lib/active_record/associations/preloader.rb +46 -110
  29. data/lib/active_record/associations/singular_association.rb +8 -2
  30. data/lib/active_record/associations/through_association.rb +1 -1
  31. data/lib/active_record/associations.rb +76 -81
  32. data/lib/active_record/asynchronous_queries_tracker.rb +57 -0
  33. data/lib/active_record/attribute_assignment.rb +1 -1
  34. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  35. data/lib/active_record/attribute_methods/dirty.rb +41 -16
  36. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  37. data/lib/active_record/attribute_methods/query.rb +2 -2
  38. data/lib/active_record/attribute_methods/read.rb +7 -5
  39. data/lib/active_record/attribute_methods/serialization.rb +66 -12
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
  41. data/lib/active_record/attribute_methods/write.rb +7 -10
  42. data/lib/active_record/attribute_methods.rb +6 -9
  43. data/lib/active_record/attributes.rb +24 -35
  44. data/lib/active_record/autosave_association.rb +3 -18
  45. data/lib/active_record/base.rb +19 -1
  46. data/lib/active_record/callbacks.rb +2 -2
  47. data/lib/active_record/coders/yaml_column.rb +11 -1
  48. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +312 -0
  49. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
  50. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +31 -558
  52. data/lib/active_record/connection_adapters/abstract/database_statements.rb +45 -21
  53. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
  54. data/lib/active_record/connection_adapters/abstract/quoting.rb +14 -7
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +5 -18
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -9
  57. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +60 -16
  58. data/lib/active_record/connection_adapters/abstract/transaction.rb +17 -6
  59. data/lib/active_record/connection_adapters/abstract_adapter.rb +115 -69
  60. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +96 -81
  61. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +6 -2
  62. data/lib/active_record/connection_adapters/mysql/database_statements.rb +33 -21
  63. data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -1
  64. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +3 -0
  65. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
  66. data/lib/active_record/connection_adapters/pool_config.rb +1 -3
  67. data/lib/active_record/connection_adapters/pool_manager.rb +5 -1
  68. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -12
  69. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  70. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  72. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  73. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +28 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  76. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  77. data/lib/active_record/connection_adapters/postgresql/quoting.rb +6 -6
  78. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -0
  79. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +5 -1
  80. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +12 -12
  81. data/lib/active_record/connection_adapters/postgresql_adapter.rb +157 -100
  82. data/lib/active_record/connection_adapters/schema_cache.rb +35 -4
  83. data/lib/active_record/connection_adapters/sql_type_metadata.rb +0 -2
  84. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +23 -17
  85. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -2
  86. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -30
  87. data/lib/active_record/connection_adapters.rb +8 -5
  88. data/lib/active_record/connection_handling.rb +20 -38
  89. data/lib/active_record/core.rb +129 -117
  90. data/lib/active_record/database_configurations/database_config.rb +12 -0
  91. data/lib/active_record/database_configurations/hash_config.rb +27 -1
  92. data/lib/active_record/database_configurations/url_config.rb +2 -2
  93. data/lib/active_record/database_configurations.rb +18 -9
  94. data/lib/active_record/delegated_type.rb +33 -11
  95. data/lib/active_record/destroy_association_async_job.rb +1 -1
  96. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  97. data/lib/active_record/dynamic_matchers.rb +1 -1
  98. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  99. data/lib/active_record/encryption/cipher.rb +53 -0
  100. data/lib/active_record/encryption/config.rb +44 -0
  101. data/lib/active_record/encryption/configurable.rb +61 -0
  102. data/lib/active_record/encryption/context.rb +35 -0
  103. data/lib/active_record/encryption/contexts.rb +72 -0
  104. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  105. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  106. data/lib/active_record/encryption/encryptable_record.rb +208 -0
  107. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  108. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  109. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  110. data/lib/active_record/encryption/encryptor.rb +155 -0
  111. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  112. data/lib/active_record/encryption/errors.rb +15 -0
  113. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  114. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +29 -0
  115. data/lib/active_record/encryption/key.rb +28 -0
  116. data/lib/active_record/encryption/key_generator.rb +42 -0
  117. data/lib/active_record/encryption/key_provider.rb +46 -0
  118. data/lib/active_record/encryption/message.rb +33 -0
  119. data/lib/active_record/encryption/message_serializer.rb +80 -0
  120. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  121. data/lib/active_record/encryption/properties.rb +76 -0
  122. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  123. data/lib/active_record/encryption/scheme.rb +99 -0
  124. data/lib/active_record/encryption.rb +55 -0
  125. data/lib/active_record/enum.rb +44 -46
  126. data/lib/active_record/errors.rb +66 -3
  127. data/lib/active_record/fixture_set/file.rb +15 -1
  128. data/lib/active_record/fixture_set/table_row.rb +40 -5
  129. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  130. data/lib/active_record/fixtures.rb +16 -11
  131. data/lib/active_record/future_result.rb +139 -0
  132. data/lib/active_record/gem_version.rb +4 -4
  133. data/lib/active_record/inheritance.rb +55 -17
  134. data/lib/active_record/insert_all.rb +39 -6
  135. data/lib/active_record/integration.rb +1 -1
  136. data/lib/active_record/internal_metadata.rb +3 -5
  137. data/lib/active_record/legacy_yaml_adapter.rb +1 -1
  138. data/lib/active_record/locking/optimistic.rb +10 -9
  139. data/lib/active_record/log_subscriber.rb +6 -2
  140. data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
  141. data/lib/active_record/middleware/database_selector.rb +8 -3
  142. data/lib/active_record/migration/command_recorder.rb +4 -4
  143. data/lib/active_record/migration/compatibility.rb +83 -1
  144. data/lib/active_record/migration/join_table.rb +1 -1
  145. data/lib/active_record/migration.rb +109 -79
  146. data/lib/active_record/model_schema.rb +46 -32
  147. data/lib/active_record/nested_attributes.rb +3 -3
  148. data/lib/active_record/no_touching.rb +2 -2
  149. data/lib/active_record/null_relation.rb +2 -6
  150. data/lib/active_record/persistence.rb +134 -45
  151. data/lib/active_record/query_cache.rb +2 -2
  152. data/lib/active_record/query_logs.rb +203 -0
  153. data/lib/active_record/querying.rb +15 -5
  154. data/lib/active_record/railtie.rb +117 -17
  155. data/lib/active_record/railties/controller_runtime.rb +1 -1
  156. data/lib/active_record/railties/databases.rake +83 -58
  157. data/lib/active_record/readonly_attributes.rb +11 -0
  158. data/lib/active_record/reflection.rb +45 -44
  159. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  160. data/lib/active_record/relation/batches.rb +3 -3
  161. data/lib/active_record/relation/calculations.rb +42 -25
  162. data/lib/active_record/relation/delegation.rb +6 -6
  163. data/lib/active_record/relation/finder_methods.rb +32 -23
  164. data/lib/active_record/relation/merger.rb +20 -13
  165. data/lib/active_record/relation/predicate_builder.rb +1 -6
  166. data/lib/active_record/relation/query_attribute.rb +5 -11
  167. data/lib/active_record/relation/query_methods.rb +233 -50
  168. data/lib/active_record/relation/record_fetch_warning.rb +2 -2
  169. data/lib/active_record/relation/spawn_methods.rb +2 -2
  170. data/lib/active_record/relation/where_clause.rb +22 -15
  171. data/lib/active_record/relation.rb +170 -87
  172. data/lib/active_record/result.rb +17 -2
  173. data/lib/active_record/runtime_registry.rb +2 -4
  174. data/lib/active_record/sanitization.rb +11 -7
  175. data/lib/active_record/schema_dumper.rb +3 -3
  176. data/lib/active_record/schema_migration.rb +0 -4
  177. data/lib/active_record/scoping/default.rb +62 -15
  178. data/lib/active_record/scoping/named.rb +3 -11
  179. data/lib/active_record/scoping.rb +40 -22
  180. data/lib/active_record/serialization.rb +1 -1
  181. data/lib/active_record/signed_id.rb +1 -1
  182. data/lib/active_record/statement_cache.rb +2 -2
  183. data/lib/active_record/tasks/database_tasks.rb +107 -23
  184. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  185. data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -11
  186. data/lib/active_record/test_databases.rb +1 -1
  187. data/lib/active_record/test_fixtures.rb +45 -4
  188. data/lib/active_record/timestamp.rb +3 -4
  189. data/lib/active_record/transactions.rb +9 -14
  190. data/lib/active_record/translation.rb +2 -2
  191. data/lib/active_record/type/adapter_specific_registry.rb +32 -7
  192. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  193. data/lib/active_record/type/internal/timezone.rb +2 -2
  194. data/lib/active_record/type/serialized.rb +1 -1
  195. data/lib/active_record/type/type_map.rb +17 -20
  196. data/lib/active_record/type.rb +1 -2
  197. data/lib/active_record/validations/associated.rb +1 -1
  198. data/lib/active_record/validations/numericality.rb +1 -1
  199. data/lib/active_record.rb +170 -2
  200. data/lib/arel/attributes/attribute.rb +0 -8
  201. data/lib/arel/collectors/bind.rb +2 -2
  202. data/lib/arel/collectors/composite.rb +3 -3
  203. data/lib/arel/collectors/sql_string.rb +1 -1
  204. data/lib/arel/collectors/substitute_binds.rb +1 -1
  205. data/lib/arel/crud.rb +18 -22
  206. data/lib/arel/delete_manager.rb +2 -4
  207. data/lib/arel/insert_manager.rb +2 -3
  208. data/lib/arel/nodes/casted.rb +1 -1
  209. data/lib/arel/nodes/delete_statement.rb +8 -13
  210. data/lib/arel/nodes/homogeneous_in.rb +4 -0
  211. data/lib/arel/nodes/insert_statement.rb +2 -2
  212. data/lib/arel/nodes/select_core.rb +2 -2
  213. data/lib/arel/nodes/select_statement.rb +2 -2
  214. data/lib/arel/nodes/update_statement.rb +3 -2
  215. data/lib/arel/predications.rb +3 -3
  216. data/lib/arel/select_manager.rb +10 -4
  217. data/lib/arel/table.rb +0 -1
  218. data/lib/arel/tree_manager.rb +0 -12
  219. data/lib/arel/update_manager.rb +2 -4
  220. data/lib/arel/visitors/dot.rb +80 -90
  221. data/lib/arel/visitors/mysql.rb +6 -1
  222. data/lib/arel/visitors/postgresql.rb +0 -10
  223. data/lib/arel/visitors/to_sql.rb +44 -3
  224. data/lib/arel.rb +1 -1
  225. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  226. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  227. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  228. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  229. metadata +55 -16
@@ -5,13 +5,27 @@ module ActiveRecord
5
5
  class BatchEnumerator
6
6
  include Enumerable
7
7
 
8
- def initialize(of: 1000, start: nil, finish: nil, relation:) #:nodoc:
8
+ def initialize(of: 1000, start: nil, finish: nil, relation:) # :nodoc:
9
9
  @of = of
10
10
  @relation = relation
11
11
  @start = start
12
12
  @finish = finish
13
13
  end
14
14
 
15
+ # The primary key value from which the BatchEnumerator starts, inclusive of the value.
16
+ attr_reader :start
17
+
18
+ # The primary key value at which the BatchEnumerator ends, inclusive of the value.
19
+ attr_reader :finish
20
+
21
+ # The relation from which the BatchEnumerator yields batches.
22
+ attr_reader :relation
23
+
24
+ # The size of the batches yielded by the BatchEnumerator.
25
+ def batch_size
26
+ @of
27
+ end
28
+
15
29
  # Looping through a collection of records from the database (using the
16
30
  # +all+ method, for example) is very inefficient since it will try to
17
31
  # instantiate all the objects at once.
@@ -33,11 +47,11 @@ module ActiveRecord
33
47
  # Person.in_batches.each_record.with_index do |person, index|
34
48
  # person.award_trophy(index + 1)
35
49
  # end
36
- def each_record
50
+ def each_record(&block)
37
51
  return to_enum(:each_record) unless block_given?
38
52
 
39
53
  @relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: true).each do |relation|
40
- relation.records.each { |record| yield record }
54
+ relation.records.each(&block)
41
55
  end
42
56
  end
43
57
 
@@ -75,9 +89,9 @@ module ActiveRecord
75
89
  # Person.in_batches.each do |relation|
76
90
  # relation.update_all(awesome: true)
77
91
  # end
78
- def each
92
+ def each(&block)
79
93
  enum = @relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: false)
80
- return enum.each { |relation| yield relation } if block_given?
94
+ return enum.each(&block) if block_given?
81
95
  enum
82
96
  end
83
97
  end
@@ -65,10 +65,10 @@ module ActiveRecord
65
65
  #
66
66
  # NOTE: By its nature, batch processing is subject to race conditions if
67
67
  # other processes are modifying the database.
68
- def find_each(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil, order: :asc)
68
+ def find_each(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil, order: :asc, &block)
69
69
  if block_given?
70
70
  find_in_batches(start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore, order: order) do |records|
71
- records.each { |record| yield record }
71
+ records.each(&block)
72
72
  end
73
73
  else
74
74
  enum_for(:find_each, start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore, order: order) do
@@ -284,7 +284,7 @@ module ActiveRecord
284
284
  end
285
285
 
286
286
  def act_on_ignored_order(error_on_ignore)
287
- raise_error = (error_on_ignore.nil? ? klass.error_on_ignored_order : error_on_ignore)
287
+ raise_error = (error_on_ignore.nil? ? ActiveRecord.error_on_ignored_order : error_on_ignore)
288
288
 
289
289
  if raise_error
290
290
  raise ArgumentError.new(ORDER_IGNORE_MESSAGE)
@@ -83,15 +83,24 @@ module ActiveRecord
83
83
  # #calculate for examples with options.
84
84
  #
85
85
  # Person.sum(:age) # => 4562
86
- def sum(column_name = nil)
86
+ def sum(identity_or_column = nil, &block)
87
87
  if block_given?
88
- unless column_name.nil?
89
- raise ArgumentError, "Column name argument is not supported when a block is passed."
88
+ values = map(&block)
89
+ if identity_or_column.nil? && (values.first.is_a?(Numeric) || values.first(1) == [])
90
+ identity_or_column = 0
90
91
  end
91
92
 
92
- super()
93
+ if identity_or_column.nil?
94
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
95
+ Rails 7.0 has deprecated Enumerable.sum in favor of Ruby's native implementation available since 2.4.
96
+ Sum of non-numeric elements requires an initial argument.
97
+ MSG
98
+ values.inject(:+) || 0
99
+ else
100
+ values.sum(identity_or_column)
101
+ end
93
102
  else
94
- calculate(:sum, column_name)
103
+ calculate(:sum, identity_or_column)
95
104
  end
96
105
  end
97
106
 
@@ -195,9 +204,9 @@ module ActiveRecord
195
204
  relation.select_values = columns
196
205
  result = skip_query_cache_if_necessary do
197
206
  if where_clause.contradiction?
198
- ActiveRecord::Result.new([], [])
207
+ ActiveRecord::Result.empty
199
208
  else
200
- klass.connection.select_all(relation.arel, nil)
209
+ klass.connection.select_all(relation.arel, "#{klass.name} Pluck")
201
210
  end
202
211
  end
203
212
  type_cast_pluck_values(result, columns)
@@ -286,7 +295,7 @@ module ActiveRecord
286
295
  operation == "count" ? column.count(distinct) : column.public_send(operation)
287
296
  end
288
297
 
289
- def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
298
+ def execute_simple_calculation(operation, column_name, distinct) # :nodoc:
290
299
  if operation == "count" && (column_name == :all && distinct || has_limit_or_offset?)
291
300
  # Shortcut when limit is zero.
292
301
  return 0 if limit_value == 0
@@ -305,23 +314,25 @@ module ActiveRecord
305
314
  query_builder = relation.arel
306
315
  end
307
316
 
308
- result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder) }
317
+ result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder, "#{@klass.name} #{operation.capitalize}") }
309
318
 
310
- type_cast_calculated_value(result.cast_values.first, operation) do |value|
319
+ if operation != "count"
311
320
  type = column.try(:type_caster) ||
312
321
  lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
313
- type.deserialize(value)
322
+ type = type.subtype if Enum::EnumType === type
314
323
  end
324
+
325
+ type_cast_calculated_value(result.cast_values.first, operation, type)
315
326
  end
316
327
 
317
- def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
328
+ def execute_grouped_calculation(operation, column_name, distinct) # :nodoc:
318
329
  group_fields = group_values
319
330
  group_fields = group_fields.uniq if group_fields.size > 1
320
331
 
321
332
  unless group_fields == group_values
322
333
  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
334
+ `#{operation}` with group by duplicated fields does no longer affect to result in Rails 7.0.
335
+ To migrate to Rails 7.0's behavior, use `uniq!(:group)` to deduplicate group fields
325
336
  (`#{klass.name&.tableize || klass.table_name}.uniq!(:group).#{operation}(#{column_name.inspect})`).
326
337
  MSG
327
338
  group_fields = group_values
@@ -360,7 +371,7 @@ module ActiveRecord
360
371
  relation.group_values = group_fields
361
372
  relation.select_values = select_values
362
373
 
363
- calculated_data = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, nil) }
374
+ calculated_data = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, "#{@klass.name} #{operation.capitalize}") }
364
375
 
365
376
  if association
366
377
  key_ids = calculated_data.collect { |row| row[group_aliases.first] }
@@ -380,17 +391,18 @@ module ActiveRecord
380
391
  end
381
392
  end
382
393
 
383
- type = nil
394
+ if operation != "count"
395
+ type = column.try(:type_caster) ||
396
+ lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
397
+ type = type.subtype if Enum::EnumType === type
398
+ end
399
+
384
400
  hash_rows.each_with_object({}) do |row, result|
385
401
  key = group_aliases.map { |aliaz| row[aliaz] }
386
402
  key = key.first if key.size == 1
387
403
  key = key_records[key] if associated
388
404
 
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
405
+ result[key] = type_cast_calculated_value(row[column_alias], operation, type)
394
406
  end
395
407
  end
396
408
 
@@ -441,16 +453,21 @@ module ActiveRecord
441
453
  result.cast_values(cast_types)
442
454
  end
443
455
 
444
- def type_cast_calculated_value(value, operation)
456
+ def type_cast_calculated_value(value, operation, type)
445
457
  case operation
446
458
  when "count"
447
459
  value.to_i
448
460
  when "sum"
449
- yield value || 0
461
+ type.deserialize(value || 0)
450
462
  when "average"
451
- value&.respond_to?(:to_d) ? value.to_d : value
463
+ case type.type
464
+ when :integer, :decimal
465
+ value&.to_d
466
+ else
467
+ type.deserialize(value)
468
+ end
452
469
  else # "minimum", "maximum"
453
- yield value
470
+ type.deserialize(value)
454
471
  end
455
472
  end
456
473
 
@@ -15,7 +15,8 @@ module ActiveRecord
15
15
  [
16
16
  ActiveRecord::Relation,
17
17
  ActiveRecord::Associations::CollectionProxy,
18
- ActiveRecord::AssociationRelation
18
+ ActiveRecord::AssociationRelation,
19
+ ActiveRecord::DisableJoinsAssociationRelation
19
20
  ].each do |klass|
20
21
  delegate = Class.new(klass) {
21
22
  include ClassSpecificRelation
@@ -61,17 +62,16 @@ module ActiveRecord
61
62
  return if method_defined?(method)
62
63
 
63
64
  if /\A[a-zA-Z_]\w*[!?]?\z/.match?(method) && !DELEGATION_RESERVED_METHOD_NAMES.include?(method.to_s)
64
- definition = RUBY_VERSION >= "2.7" ? "..." : "*args, &block"
65
65
  module_eval <<-RUBY, __FILE__, __LINE__ + 1
66
- def #{method}(#{definition})
67
- scoping { klass.#{method}(#{definition}) }
66
+ def #{method}(...)
67
+ scoping { klass.#{method}(...) }
68
68
  end
69
69
  RUBY
70
70
  else
71
71
  define_method(method) do |*args, &block|
72
72
  scoping { klass.public_send(method, *args, &block) }
73
73
  end
74
- ruby2_keywords(method) if respond_to?(:ruby2_keywords, true)
74
+ ruby2_keywords(method)
75
75
  end
76
76
  end
77
77
  end
@@ -110,7 +110,7 @@ module ActiveRecord
110
110
  super
111
111
  end
112
112
  end
113
- ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
113
+ ruby2_keywords(:method_missing)
114
114
  end
115
115
 
116
116
  module ClassMethods # :nodoc:
@@ -104,6 +104,32 @@ module ActiveRecord
104
104
  take || raise_record_not_found_exception!
105
105
  end
106
106
 
107
+ # Finds the sole matching record. Raises ActiveRecord::RecordNotFound if no
108
+ # record is found. Raises ActiveRecord::SoleRecordExceeded if more than one
109
+ # record is found.
110
+ #
111
+ # Product.where(["price = %?", price]).sole
112
+ def sole
113
+ found, undesired = first(2)
114
+
115
+ if found.nil?
116
+ raise_record_not_found_exception!
117
+ elsif undesired.present?
118
+ raise ActiveRecord::SoleRecordExceeded.new(self)
119
+ else
120
+ found
121
+ end
122
+ end
123
+
124
+ # Finds the sole matching record. Raises ActiveRecord::RecordNotFound if no
125
+ # record is found. Raises ActiveRecord::SoleRecordExceeded if more than one
126
+ # record is found.
127
+ #
128
+ # Product.find_sole_by(["price = %?", price])
129
+ def find_sole_by(arg, *args)
130
+ where(arg, *args).sole
131
+ end
132
+
107
133
  # Find the first record (or first N records if a parameter is supplied).
108
134
  # If no order is defined it will order by primary key.
109
135
  #
@@ -369,7 +395,7 @@ module ActiveRecord
369
395
  blank_value = order_values.first
370
396
  ActiveSupport::Deprecation.warn(<<~MSG.squish)
371
397
  `.reorder(#{blank_value.inspect})` with `.first` / `.first!` no longer
372
- takes non-deterministic result in Rails 6.2.
398
+ takes non-deterministic result in Rails 7.0.
373
399
  To continue taking non-deterministic result, use `.take` / `.take!` instead.
374
400
  MSG
375
401
  end
@@ -400,7 +426,7 @@ module ActiveRecord
400
426
  )
401
427
  relation = except(:includes, :eager_load, :preload).joins!(join_dependency)
402
428
 
403
- if eager_loading && !(
429
+ if eager_loading && has_limit_or_offset? && !(
404
430
  using_limitable_reflections?(join_dependency.reflections) &&
405
431
  using_limitable_reflections?(
406
432
  construct_join_dependency(
@@ -409,12 +435,10 @@ module ActiveRecord
409
435
  ), nil
410
436
  ).reflections
411
437
  )
412
- )
413
- if has_limit_or_offset?
414
- limited_ids = limited_ids_for(relation)
415
- limited_ids.empty? ? relation.none! : relation.where!(primary_key => limited_ids)
438
+ )
439
+ relation = skip_query_cache_if_necessary do
440
+ klass.connection.distinct_relation_for_primary_key(relation)
416
441
  end
417
- relation.limit_value = relation.offset_value = nil
418
442
  end
419
443
 
420
444
  if block_given?
@@ -424,18 +448,6 @@ module ActiveRecord
424
448
  end
425
449
  end
426
450
 
427
- def limited_ids_for(relation)
428
- values = @klass.connection.columns_for_distinct(
429
- connection.visitor.compile(table[primary_key]),
430
- relation.order_values
431
- )
432
-
433
- relation = relation.except(:select).select(values).distinct!
434
-
435
- id_rows = skip_query_cache_if_necessary { @klass.connection.select_rows(relation.arel, "SQL") }
436
- id_rows.map(&:last)
437
- end
438
-
439
451
  def using_limitable_reflections?(reflections)
440
452
  reflections.none?(&:collection?)
441
453
  end
@@ -508,10 +520,7 @@ module ActiveRecord
508
520
  result = except(:limit, :offset).where(primary_key => ids).records
509
521
 
510
522
  if result.size == ids.size
511
- pk_type = @klass.type_for_attribute(primary_key)
512
-
513
- records_by_id = result.index_by(&:id)
514
- ids.map { |id| records_by_id.fetch(pk_type.cast(id)) }
523
+ result.in_order_of(:id, ids.map { |id| @klass.type_for_attribute(primary_key).cast(id) })
515
524
  else
516
525
  raise_record_not_found_exception!(ids, result.size, ids.size)
517
526
  end
@@ -51,30 +51,25 @@ module ActiveRecord
51
51
  @rewhere = rewhere
52
52
  end
53
53
 
54
- NORMAL_VALUES = Relation::VALUE_METHODS -
55
- Relation::CLAUSE_METHODS -
56
- [:includes, :preload, :joins, :left_outer_joins, :order, :reverse_order, :lock, :create_with, :reordering] # :nodoc:
57
-
58
- def normal_values
59
- NORMAL_VALUES
60
- end
54
+ NORMAL_VALUES = Relation::VALUE_METHODS - Relation::CLAUSE_METHODS -
55
+ [
56
+ :select, :includes, :preload, :joins, :left_outer_joins,
57
+ :order, :reverse_order, :lock, :create_with, :reordering
58
+ ]
61
59
 
62
60
  def merge
63
- normal_values.each do |name|
61
+ NORMAL_VALUES.each do |name|
64
62
  value = values[name]
65
63
  # The unless clause is here mostly for performance reasons (since the `send` call might be moderately
66
64
  # expensive), most of the time the value is going to be `nil` or `.blank?`, the only catch is that
67
65
  # `false.blank?` returns `true`, so there needs to be an extra check so that explicit `false` values
68
66
  # don't fall through the cracks.
69
67
  unless value.nil? || (value.blank? && false != value)
70
- if name == :select
71
- relation._select!(*value)
72
- else
73
- relation.public_send("#{name}!", *value)
74
- end
68
+ relation.public_send(:"#{name}!", *value)
75
69
  end
76
70
  end
77
71
 
72
+ merge_select_values
78
73
  merge_multi_values
79
74
  merge_single_values
80
75
  merge_clauses
@@ -86,6 +81,18 @@ module ActiveRecord
86
81
  end
87
82
 
88
83
  private
84
+ def merge_select_values
85
+ return if other.select_values.empty?
86
+
87
+ if other.klass == relation.klass
88
+ relation.select_values |= other.select_values
89
+ else
90
+ relation.select_values |= other.instance_eval do
91
+ arel_columns(select_values)
92
+ end
93
+ end
94
+ end
95
+
89
96
  def merge_preloads
90
97
  return if other.preload_values.empty? && other.includes_values.empty?
91
98
 
@@ -9,10 +9,6 @@ module ActiveRecord
9
9
  require "active_record/relation/predicate_builder/association_query_value"
10
10
  require "active_record/relation/predicate_builder/polymorphic_array_value"
11
11
 
12
- # No-op BaseHandler to work Mashal.load(File.read("legacy_relation.dump")).
13
- # TODO: Remove the constant alias once Rails 6.1 has released.
14
- BaseHandler = BasicObjectHandler
15
-
16
12
  def initialize(table)
17
13
  @table = table
18
14
  @handlers = []
@@ -69,8 +65,7 @@ module ActiveRecord
69
65
  end
70
66
 
71
67
  def build_bind_attribute(column_name, value)
72
- attr = Relation::QueryAttribute.new(column_name, value, table.type(column_name))
73
- Arel::Nodes::BindParam.new(attr)
68
+ Relation::QueryAttribute.new(column_name, value, table.type(column_name))
74
69
  end
75
70
 
76
71
  def resolve_arel_attribute(table_name, column_name, &block)
@@ -20,25 +20,19 @@ module ActiveRecord
20
20
  def nil?
21
21
  unless value_before_type_cast.is_a?(StatementCache::Substitute)
22
22
  value_before_type_cast.nil? ||
23
- type.respond_to?(:subtype, true) && value_for_database.nil?
23
+ type.respond_to?(:subtype) && serializable? && value_for_database.nil?
24
24
  end
25
- rescue ::RangeError
26
25
  end
27
26
 
28
27
  def infinite?
29
- infinity?(value_before_type_cast) || infinity?(value_for_database)
30
- rescue ::RangeError
28
+ infinity?(value_before_type_cast) || serializable? && infinity?(value_for_database)
31
29
  end
32
30
 
33
31
  def unboundable?
34
- if defined?(@_unboundable)
35
- @_unboundable
36
- else
37
- value_for_database unless value_before_type_cast.is_a?(StatementCache::Substitute)
38
- @_unboundable = nil
32
+ unless defined?(@_unboundable)
33
+ serializable? { |value| @_unboundable = value <=> 0 } && @_unboundable = nil
39
34
  end
40
- rescue ::RangeError
41
- @_unboundable = type.cast(value_before_type_cast) <=> 0
35
+ @_unboundable
42
36
  end
43
37
 
44
38
  private