activerecord 6.0.6.1 → 6.1.7.4

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 +1143 -780
  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
@@ -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,36 @@ 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 = type.subtype if Enum::EnumType === type
314
+ type.deserialize(value)
315
+ end
306
316
  end
307
317
 
308
318
  def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
309
319
  group_fields = group_values
320
+ group_fields = group_fields.uniq if group_fields.size > 1
321
+
322
+ unless group_fields == group_values
323
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
324
+ `#{operation}` with group by duplicated fields does no longer affect to result in Rails 7.0.
325
+ To migrate to Rails 7.0's behavior, use `uniq!(:group)` to deduplicate group fields
326
+ (`#{klass.name&.tableize || klass.table_name}.uniq!(:group).#{operation}(#{column_name.inspect})`).
327
+ MSG
328
+ group_fields = group_values
329
+ end
310
330
 
311
331
  if group_fields.size == 1 && group_fields.first.respond_to?(:to_sym)
312
332
  association = klass._reflect_on_association(group_fields.first)
@@ -321,14 +341,12 @@ module ActiveRecord
321
341
  }
322
342
  group_columns = group_aliases.zip(group_fields)
323
343
 
324
- aggregate_alias = column_alias_for("#{operation} #{column_name.to_s.downcase}")
344
+ column = aggregate_column(column_name)
345
+ column_alias = column_alias_for("#{operation} #{column_name.to_s.downcase}")
346
+ select_value = operation_over_aggregate_column(column, operation, distinct)
347
+ select_value.as(column_alias)
325
348
 
326
- select_values = [
327
- operation_over_aggregate_column(
328
- aggregate_column(column_name),
329
- operation,
330
- distinct).as(aggregate_alias)
331
- ]
349
+ select_values = [select_value]
332
350
  select_values += self.select_values unless having_clause.empty?
333
351
 
334
352
  select_values.concat group_columns.map { |aliaz, field|
@@ -348,22 +366,36 @@ module ActiveRecord
348
366
  if association
349
367
  key_ids = calculated_data.collect { |row| row[group_aliases.first] }
350
368
  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] }]
369
+ key_records = key_records.index_by(&:id)
352
370
  end
353
371
 
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
- }
372
+ key_types = group_columns.each_with_object({}) do |(aliaz, col_name), types|
373
+ types[aliaz] = type_for(col_name) do
374
+ calculated_data.column_types.fetch(aliaz, Type.default_value)
375
+ end
376
+ end
377
+
378
+ hash_rows = calculated_data.cast_values(key_types).map! do |row|
379
+ calculated_data.columns.each_with_object({}).with_index do |(col_name, hash), i|
380
+ hash[col_name] = row[i]
381
+ end
382
+ end
383
+
384
+ type = nil
385
+ hash_rows.each_with_object({}) do |row, result|
386
+ key = group_aliases.map { |aliaz| row[aliaz] }
361
387
  key = key.first if key.size == 1
362
388
  key = key_records[key] if associated
363
389
 
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]
390
+ result[key] = type_cast_calculated_value(row[column_alias], operation) do |value|
391
+ unless type
392
+ type = column.try(:type_caster) ||
393
+ lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
394
+ type = type.subtype if Enum::EnumType === type
395
+ end
396
+ type.deserialize(value)
397
+ end
398
+ end
367
399
  end
368
400
 
369
401
  # Converts the given field to the value that the database adapter returns as
@@ -388,12 +420,41 @@ module ActiveRecord
388
420
  @klass.type_for_attribute(field_name, &block)
389
421
  end
390
422
 
391
- def type_cast_calculated_value(value, type, operation = nil)
423
+ def lookup_cast_type_from_join_dependencies(name, join_dependencies = build_join_dependencies)
424
+ each_join_dependencies(join_dependencies) do |join|
425
+ type = join.base_klass.attribute_types.fetch(name, nil)
426
+ return type if type
427
+ end
428
+ nil
429
+ end
430
+
431
+ def type_cast_pluck_values(result, columns)
432
+ cast_types = if result.columns.size != columns.size
433
+ klass.attribute_types
434
+ else
435
+ join_dependencies = nil
436
+ columns.map.with_index do |column, i|
437
+ column.try(:type_caster) ||
438
+ klass.attribute_types.fetch(name = result.columns[i]) do
439
+ join_dependencies ||= build_join_dependencies
440
+ lookup_cast_type_from_join_dependencies(name, join_dependencies) ||
441
+ result.column_types[name] || Type.default_value
442
+ end
443
+ end
444
+ end
445
+ result.cast_values(cast_types)
446
+ end
447
+
448
+ def type_cast_calculated_value(value, operation)
392
449
  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)
450
+ when "count"
451
+ value.to_i
452
+ when "sum"
453
+ yield value || 0
454
+ when "average"
455
+ value&.respond_to?(:to_d) ? value.to_d : value
456
+ else # "minimum", "maximum"
457
+ yield value
397
458
  end
398
459
  end
399
460
 
@@ -114,6 +114,8 @@ module ActiveRecord
114
114
  # Person.first(3) # returns the first three objects fetched by SELECT * FROM people ORDER BY people.id LIMIT 3
115
115
  #
116
116
  def first(limit = nil)
117
+ check_reorder_deprecation unless loaded?
118
+
117
119
  if limit
118
120
  find_nth_with_limit(0, limit)
119
121
  else
@@ -275,9 +277,9 @@ module ActiveRecord
275
277
  # * Integer - Finds the record with this primary key.
276
278
  # * String - Finds the record with a primary key corresponding to this
277
279
  # string (such as <tt>'5'</tt>).
278
- # * Array - Finds the record that matches these +find+-style conditions
280
+ # * Array - Finds the record that matches these +where+-style conditions
279
281
  # (such as <tt>['name LIKE ?', "%#{query}%"]</tt>).
280
- # * Hash - Finds the record that matches these +find+-style conditions
282
+ # * Hash - Finds the record that matches these +where+-style conditions
281
283
  # (such as <tt>{name: 'David'}</tt>).
282
284
  # * +false+ - Returns always +false+.
283
285
  # * No args - Returns +false+ if the relation is empty, +true+ otherwise.
@@ -313,10 +315,26 @@ module ActiveRecord
313
315
  end
314
316
 
315
317
  relation = construct_relation_for_exists(conditions)
318
+ return false if relation.where_clause.contradiction?
319
+
320
+ skip_query_cache_if_necessary { connection.select_rows(relation.arel, "#{name} Exists?").size == 1 }
321
+ end
316
322
 
317
- skip_query_cache_if_necessary { connection.select_one(relation.arel, "#{name} Exists?") } ? true : false
323
+ # Returns true if the relation contains the given record or false otherwise.
324
+ #
325
+ # No query is performed if the relation is loaded; the given record is
326
+ # compared to the records in memory. If the relation is unloaded, an
327
+ # efficient existence query is performed, as in #exists?.
328
+ def include?(record)
329
+ if loaded? || offset_value || limit_value || having_clause.any?
330
+ records.include?(record)
331
+ else
332
+ record.is_a?(klass) && exists?(record.id)
333
+ end
318
334
  end
319
335
 
336
+ alias :member? :include?
337
+
320
338
  # This method is called whenever no records are found with either a single
321
339
  # id or multiple ids and raises an ActiveRecord::RecordNotFound exception.
322
340
  #
@@ -326,15 +344,15 @@ module ActiveRecord
326
344
  # the expected number of results should be provided in the +expected_size+
327
345
  # argument.
328
346
  def raise_record_not_found_exception!(ids = nil, result_size = nil, expected_size = nil, key = primary_key, not_found_ids = nil) # :nodoc:
329
- conditions = arel.where_sql(@klass)
330
- conditions = " [#{conditions}]" if conditions
347
+ conditions = " [#{arel.where_sql(klass)}]" unless where_clause.empty?
348
+
331
349
  name = @klass.name
332
350
 
333
351
  if ids.nil?
334
352
  error = +"Couldn't find #{name}"
335
353
  error << " with#{conditions}" if conditions
336
354
  raise RecordNotFound.new(error, name, key)
337
- elsif Array(ids).size == 1
355
+ elsif Array.wrap(ids).size == 1
338
356
  error = "Couldn't find #{name} with '#{key}'=#{ids}#{conditions}"
339
357
  raise RecordNotFound.new(error, name, key, ids)
340
358
  else
@@ -346,8 +364,15 @@ module ActiveRecord
346
364
  end
347
365
 
348
366
  private
349
- def offset_index
350
- offset_value || 0
367
+ def check_reorder_deprecation
368
+ if !order_values.empty? && order_values.all?(&:blank?)
369
+ blank_value = order_values.first
370
+ ActiveSupport::Deprecation.warn(<<~MSG.squish)
371
+ `.reorder(#{blank_value.inspect})` with `.first` / `.first!` no longer
372
+ takes non-deterministic result in Rails 7.0.
373
+ To continue taking non-deterministic result, use `.take` / `.take!` instead.
374
+ MSG
375
+ end
351
376
  end
352
377
 
353
378
  def construct_relation_for_exists(conditions)
@@ -401,14 +426,14 @@ module ActiveRecord
401
426
 
402
427
  def limited_ids_for(relation)
403
428
  values = @klass.connection.columns_for_distinct(
404
- connection.visitor.compile(arel_attribute(primary_key)),
429
+ connection.visitor.compile(table[primary_key]),
405
430
  relation.order_values
406
431
  )
407
432
 
408
433
  relation = relation.except(:select).select(values).distinct!
409
434
 
410
- id_rows = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, "SQL") }
411
- id_rows.map { |row| row[primary_key] }
435
+ id_rows = skip_query_cache_if_necessary { @klass.connection.select_rows(relation.arel, "SQL") }
436
+ id_rows.map(&:last)
412
437
  end
413
438
 
414
439
  def using_limitable_reflections?(reflections)
@@ -509,7 +534,8 @@ module ActiveRecord
509
534
  end
510
535
 
511
536
  def find_nth(index)
512
- @offsets[offset_index + index] ||= find_nth_with_limit(index, 1).first
537
+ @offsets ||= {}
538
+ @offsets[index] ||= find_nth_with_limit(index, 1).first
513
539
  end
514
540
 
515
541
  def find_nth_with_limit(index, limit)
@@ -523,7 +549,7 @@ module ActiveRecord
523
549
  end
524
550
 
525
551
  if limit > 0
526
- relation = relation.offset(offset_index + index) unless index.zero?
552
+ relation = relation.offset((offset_value || 0) + index) unless index.zero?
527
553
  relation.limit(limit).to_a
528
554
  else
529
555
  []
@@ -551,7 +577,11 @@ module ActiveRecord
551
577
 
552
578
  def ordered_relation
553
579
  if order_values.empty? && (implicit_order_column || primary_key)
554
- order(arel_attribute(implicit_order_column || primary_key).asc)
580
+ if implicit_order_column && primary_key && implicit_order_column != primary_key
581
+ order(table[implicit_order_column].asc, table[primary_key].asc)
582
+ else
583
+ order(table[implicit_order_column || primary_key].asc)
584
+ end
555
585
  else
556
586
  self
557
587
  end
@@ -23,7 +23,7 @@ module ActiveRecord
23
23
  end
24
24
 
25
25
  def self.empty
26
- @empty ||= new(nil, nil)
26
+ @empty ||= new(nil, nil).freeze
27
27
  end
28
28
  end
29
29
  end
@@ -7,15 +7,16 @@ module ActiveRecord
7
7
  class HashMerger # :nodoc:
8
8
  attr_reader :relation, :hash
9
9
 
10
- def initialize(relation, hash)
10
+ def initialize(relation, hash, rewhere = nil)
11
11
  hash.assert_valid_keys(*Relation::VALUE_METHODS)
12
12
 
13
13
  @relation = relation
14
14
  @hash = hash
15
+ @rewhere = rewhere
15
16
  end
16
17
 
17
- def merge #:nodoc:
18
- Merger.new(relation, other).merge
18
+ def merge
19
+ Merger.new(relation, other, @rewhere).merge
19
20
  end
20
21
 
21
22
  # Applying values to a relation has some side effects. E.g.
@@ -28,19 +29,14 @@ module ActiveRecord
28
29
  table: relation.table,
29
30
  predicate_builder: relation.predicate_builder
30
31
  )
31
- hash.each { |k, v|
32
- if k == :joins
33
- if Hash === v
34
- other.joins!(v)
35
- else
36
- other.joins!(*v)
37
- end
38
- elsif k == :select
39
- other._select!(v)
32
+ hash.each do |k, v|
33
+ k = :_select if k == :select
34
+ if Array === v
35
+ other.public_send("#{k}!", *v)
40
36
  else
41
- other.send("#{k}!", v)
37
+ other.public_send("#{k}!", v)
42
38
  end
43
- }
39
+ end
44
40
  other
45
41
  end
46
42
  end
@@ -48,10 +44,11 @@ module ActiveRecord
48
44
  class Merger # :nodoc:
49
45
  attr_reader :relation, :values, :other
50
46
 
51
- def initialize(relation, other)
47
+ def initialize(relation, other, rewhere = nil)
52
48
  @relation = relation
53
49
  @values = other.values
54
50
  @other = other
51
+ @rewhere = rewhere
55
52
  end
56
53
 
57
54
  NORMAL_VALUES = Relation::VALUE_METHODS -
@@ -73,7 +70,7 @@ module ActiveRecord
73
70
  if name == :select
74
71
  relation._select!(*value)
75
72
  else
76
- relation.send("#{name}!", *value)
73
+ relation.public_send("#{name}!", *value)
77
74
  end
78
75
  end
79
76
  end
@@ -93,8 +90,8 @@ module ActiveRecord
93
90
  return if other.preload_values.empty? && other.includes_values.empty?
94
91
 
95
92
  if other.klass == relation.klass
96
- relation.preload!(*other.preload_values) unless other.preload_values.empty?
97
- relation.includes!(other.includes_values) unless other.includes_values.empty?
93
+ relation.preload_values |= other.preload_values unless other.preload_values.empty?
94
+ relation.includes_values |= other.includes_values unless other.includes_values.empty?
98
95
  else
99
96
  reflection = relation.klass.reflect_on_all_associations.find do |r|
100
97
  r.class_name == other.klass.name
@@ -111,10 +108,10 @@ module ActiveRecord
111
108
  end
112
109
 
113
110
  def merge_joins
114
- return if other.joins_values.blank?
111
+ return if other.joins_values.empty?
115
112
 
116
113
  if other.klass == relation.klass
117
- relation.joins!(*other.joins_values)
114
+ relation.joins_values |= other.joins_values
118
115
  else
119
116
  associations, others = other.joins_values.partition do |join|
120
117
  case join
@@ -130,10 +127,10 @@ module ActiveRecord
130
127
  end
131
128
 
132
129
  def merge_outer_joins
133
- return if other.left_outer_joins_values.blank?
130
+ return if other.left_outer_joins_values.empty?
134
131
 
135
132
  if other.klass == relation.klass
136
- relation.left_outer_joins!(*other.left_outer_joins_values)
133
+ relation.left_outer_joins_values |= other.left_outer_joins_values
137
134
  else
138
135
  associations, others = other.left_outer_joins_values.partition do |join|
139
136
  case join
@@ -172,7 +169,7 @@ module ActiveRecord
172
169
  def merge_clauses
173
170
  relation.from_clause = other.from_clause if replace_from_clause?
174
171
 
175
- where_clause = relation.where_clause.merge(other.where_clause)
172
+ where_clause = relation.where_clause.merge(other.where_clause, @rewhere)
176
173
  relation.where_clause = where_clause unless where_clause.empty?
177
174
 
178
175
  having_clause = relation.having_clause.merge(other.having_clause)
@@ -20,20 +20,19 @@ module ActiveRecord
20
20
  case values.length
21
21
  when 0 then NullPredicate
22
22
  when 1 then predicate_builder.build(attribute, values.first)
23
- else
24
- values.map! do |v|
25
- predicate_builder.build_bind_attribute(attribute.name, v)
26
- end
27
- values.empty? ? NullPredicate : attribute.in(values)
23
+ else Arel::Nodes::HomogeneousIn.new(values, attribute, :in)
28
24
  end
29
25
 
30
26
  unless nils.empty?
31
- values_predicate = values_predicate.or(predicate_builder.build(attribute, nil))
27
+ values_predicate = values_predicate.or(attribute.eq(nil))
32
28
  end
33
29
 
34
- array_predicates = ranges.map { |range| predicate_builder.build(attribute, range) }
35
- array_predicates.unshift(values_predicate)
36
- array_predicates.inject(&:or)
30
+ if ranges.empty?
31
+ values_predicate
32
+ else
33
+ array_predicates = ranges.map! { |range| predicate_builder.build(attribute, range) }
34
+ array_predicates.inject(values_predicate, &:or)
35
+ end
37
36
  end
38
37
 
39
38
  private
@@ -9,7 +9,7 @@ module ActiveRecord
9
9
  end
10
10
 
11
11
  def queries
12
- [associated_table.association_join_foreign_key.to_s => ids]
12
+ [ associated_table.join_foreign_key => ids ]
13
13
  end
14
14
 
15
15
  private
@@ -27,13 +27,12 @@ module ActiveRecord
27
27
  end
28
28
 
29
29
  def primary_key
30
- associated_table.association_join_primary_key
30
+ associated_table.join_primary_key
31
31
  end
32
32
 
33
33
  def convert_to_id(value)
34
- case value
35
- when Base
36
- value._read_attribute(primary_key)
34
+ if value.respond_to?(primary_key)
35
+ value.public_send(primary_key)
37
36
  else
38
37
  value
39
38
  end
@@ -9,11 +9,13 @@ module ActiveRecord
9
9
  end
10
10
 
11
11
  def queries
12
+ return [ associated_table.join_foreign_key => values ] if values.empty?
13
+
12
14
  type_to_ids_mapping.map do |type, ids|
13
- {
14
- associated_table.association_foreign_type.to_s => type,
15
- associated_table.association_foreign_key.to_s => ids
16
- }
15
+ query = {}
16
+ query[associated_table.join_foreign_type] = type if type
17
+ query[associated_table.join_foreign_key] = ids
18
+ query
17
19
  end
18
20
  end
19
21
 
@@ -23,12 +25,12 @@ module ActiveRecord
23
25
  def type_to_ids_mapping
24
26
  default_hash = Hash.new { |hsh, key| hsh[key] = [] }
25
27
  values.each_with_object(default_hash) do |value, hash|
26
- hash[klass(value).polymorphic_name] << convert_to_id(value)
28
+ hash[klass(value)&.polymorphic_name] << convert_to_id(value)
27
29
  end
28
30
  end
29
31
 
30
32
  def primary_key(value)
31
- associated_table.association_join_primary_key(klass(value))
33
+ associated_table.join_primary_key(klass(value))
32
34
  end
33
35
 
34
36
  def klass(value)
@@ -46,6 +48,8 @@ module ActiveRecord
46
48
  value._read_attribute(primary_key(value))
47
49
  when Relation
48
50
  value.select(primary_key(value))
51
+ else
52
+ value
49
53
  end
50
54
  end
51
55
  end
@@ -9,7 +9,7 @@ module ActiveRecord
9
9
  end
10
10
 
11
11
  if value.select_values.empty?
12
- value = value.select(value.arel_attribute(value.klass.primary_key))
12
+ value = value.select(value.table[value.klass.primary_key])
13
13
  end
14
14
 
15
15
  attribute.in(value.arel)