activerecord 6.0.6 → 6.1.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (242) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +783 -910
  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 +43 -26
  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 +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 +32 -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 +152 -22
  47. data/lib/active_record/coders/yaml_column.rb +2 -24
  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 +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 +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 +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 +80 -32
  59. data/lib/active_record/connection_adapters/abstract_adapter.rb +49 -72
  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 +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 +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 -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/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 +223 -66
  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 +27 -10
  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 +2 -2
  120. data/lib/active_record/inheritance.rb +40 -18
  121. data/lib/active_record/insert_all.rb +34 -5
  122. data/lib/active_record/integration.rb +3 -5
  123. data/lib/active_record/internal_metadata.rb +16 -7
  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 -13
  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 +266 -95
  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 +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 +4 -5
  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 +318 -195
  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 +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 +2 -8
  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 +39 -51
  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 +28 -30
  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
@@ -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
 
@@ -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
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 6.2.
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?(:id)
35
+ value.id
37
36
  else
38
37
  value
39
38
  end
@@ -11,8 +11,8 @@ module ActiveRecord
11
11
  def queries
12
12
  type_to_ids_mapping.map do |type, ids|
13
13
  {
14
- associated_table.association_foreign_type.to_s => type,
15
- associated_table.association_foreign_key.to_s => ids
14
+ associated_table.join_foreign_type => type,
15
+ associated_table.join_foreign_key => ids
16
16
  }
17
17
  end
18
18
  end
@@ -28,7 +28,7 @@ module ActiveRecord
28
28
  end
29
29
 
30
30
  def primary_key(value)
31
- associated_table.association_join_primary_key(klass(value))
31
+ associated_table.join_primary_key(klass(value))
32
32
  end
33
33
 
34
34
  def klass(value)
@@ -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)