activerecord 6.0.1 → 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 (268) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +843 -626
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +3 -3
  5. data/lib/active_record/aggregations.rb +1 -2
  6. data/lib/active_record/association_relation.rb +18 -17
  7. data/lib/active_record/associations/alias_tracker.rb +19 -16
  8. data/lib/active_record/associations/association.rb +49 -37
  9. data/lib/active_record/associations/association_scope.rb +17 -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 -3
  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 +25 -8
  20. data/lib/active_record/associations/collection_proxy.rb +14 -7
  21. data/lib/active_record/associations/foreign_association.rb +13 -0
  22. data/lib/active_record/associations/has_many_association.rb +24 -3
  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 +36 -14
  26. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  27. data/lib/active_record/associations/join_dependency.rb +73 -42
  28. data/lib/active_record/associations/preloader/association.rb +51 -25
  29. data/lib/active_record/associations/preloader/through_association.rb +2 -2
  30. data/lib/active_record/associations/preloader.rb +12 -7
  31. data/lib/active_record/associations/singular_association.rb +1 -1
  32. data/lib/active_record/associations/through_association.rb +1 -1
  33. data/lib/active_record/associations.rb +115 -12
  34. data/lib/active_record/attribute_assignment.rb +10 -9
  35. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -10
  36. data/lib/active_record/attribute_methods/dirty.rb +3 -13
  37. data/lib/active_record/attribute_methods/primary_key.rb +6 -4
  38. data/lib/active_record/attribute_methods/query.rb +3 -6
  39. data/lib/active_record/attribute_methods/read.rb +8 -12
  40. data/lib/active_record/attribute_methods/serialization.rb +11 -6
  41. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
  42. data/lib/active_record/attribute_methods/write.rb +12 -21
  43. data/lib/active_record/attribute_methods.rb +64 -54
  44. data/lib/active_record/attributes.rb +32 -8
  45. data/lib/active_record/autosave_association.rb +56 -41
  46. data/lib/active_record/base.rb +2 -14
  47. data/lib/active_record/callbacks.rb +153 -24
  48. data/lib/active_record/coders/yaml_column.rb +1 -2
  49. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +190 -136
  50. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  51. data/lib/active_record/connection_adapters/abstract/database_statements.rb +82 -37
  52. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -8
  53. data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
  54. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +152 -116
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -52
  57. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  58. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +263 -107
  59. data/lib/active_record/connection_adapters/abstract/transaction.rb +82 -35
  60. data/lib/active_record/connection_adapters/abstract_adapter.rb +60 -73
  61. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +136 -111
  62. data/lib/active_record/connection_adapters/column.rb +15 -1
  63. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  64. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
  65. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  66. data/lib/active_record/connection_adapters/mysql/database_statements.rb +28 -36
  67. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  68. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  69. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -7
  70. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  71. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  72. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +17 -13
  73. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  74. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -13
  75. data/lib/active_record/connection_adapters/pool_config.rb +63 -0
  76. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  77. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  78. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -56
  79. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +0 -1
  80. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  81. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
  83. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  84. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +0 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
  87. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  89. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
  90. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -6
  91. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -2
  93. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  94. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  95. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
  96. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +7 -3
  97. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -1
  98. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  99. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +72 -54
  100. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  101. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  102. data/lib/active_record/connection_adapters/postgresql_adapter.rb +77 -57
  103. data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
  104. data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
  105. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +36 -12
  106. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -2
  107. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  108. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -5
  109. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +57 -57
  110. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  111. data/lib/active_record/connection_adapters.rb +50 -0
  112. data/lib/active_record/connection_handling.rb +210 -87
  113. data/lib/active_record/core.rb +229 -65
  114. data/lib/active_record/counter_cache.rb +4 -1
  115. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  116. data/lib/active_record/database_configurations/database_config.rb +52 -9
  117. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  118. data/lib/active_record/database_configurations/url_config.rb +15 -41
  119. data/lib/active_record/database_configurations.rb +124 -85
  120. data/lib/active_record/delegated_type.rb +209 -0
  121. data/lib/active_record/destroy_association_async_job.rb +36 -0
  122. data/lib/active_record/dynamic_matchers.rb +2 -3
  123. data/lib/active_record/enum.rb +40 -16
  124. data/lib/active_record/errors.rb +47 -12
  125. data/lib/active_record/explain.rb +9 -5
  126. data/lib/active_record/explain_subscriber.rb +1 -1
  127. data/lib/active_record/fixture_set/file.rb +10 -17
  128. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  129. data/lib/active_record/fixture_set/render_context.rb +1 -1
  130. data/lib/active_record/fixture_set/table_row.rb +2 -3
  131. data/lib/active_record/fixture_set/table_rows.rb +0 -1
  132. data/lib/active_record/fixtures.rb +54 -11
  133. data/lib/active_record/gem_version.rb +2 -2
  134. data/lib/active_record/inheritance.rb +40 -21
  135. data/lib/active_record/insert_all.rb +38 -9
  136. data/lib/active_record/integration.rb +3 -5
  137. data/lib/active_record/internal_metadata.rb +16 -7
  138. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  139. data/lib/active_record/locking/optimistic.rb +22 -17
  140. data/lib/active_record/locking/pessimistic.rb +6 -2
  141. data/lib/active_record/log_subscriber.rb +27 -9
  142. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  143. data/lib/active_record/middleware/database_selector/resolver.rb +6 -2
  144. data/lib/active_record/middleware/database_selector.rb +4 -2
  145. data/lib/active_record/migration/command_recorder.rb +53 -45
  146. data/lib/active_record/migration/compatibility.rb +70 -20
  147. data/lib/active_record/migration/join_table.rb +0 -1
  148. data/lib/active_record/migration.rb +114 -84
  149. data/lib/active_record/model_schema.rb +117 -15
  150. data/lib/active_record/nested_attributes.rb +2 -5
  151. data/lib/active_record/no_touching.rb +1 -1
  152. data/lib/active_record/null_relation.rb +0 -1
  153. data/lib/active_record/persistence.rb +50 -46
  154. data/lib/active_record/query_cache.rb +15 -5
  155. data/lib/active_record/querying.rb +12 -7
  156. data/lib/active_record/railtie.rb +65 -45
  157. data/lib/active_record/railties/databases.rake +267 -93
  158. data/lib/active_record/readonly_attributes.rb +4 -0
  159. data/lib/active_record/reflection.rb +77 -63
  160. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  161. data/lib/active_record/relation/batches.rb +38 -32
  162. data/lib/active_record/relation/calculations.rb +102 -45
  163. data/lib/active_record/relation/delegation.rb +9 -7
  164. data/lib/active_record/relation/finder_methods.rb +45 -16
  165. data/lib/active_record/relation/from_clause.rb +5 -1
  166. data/lib/active_record/relation/merger.rb +27 -26
  167. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  168. data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
  169. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +3 -3
  170. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  171. data/lib/active_record/relation/predicate_builder.rb +55 -35
  172. data/lib/active_record/relation/query_methods.rb +335 -187
  173. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  174. data/lib/active_record/relation/spawn_methods.rb +8 -8
  175. data/lib/active_record/relation/where_clause.rb +104 -58
  176. data/lib/active_record/relation.rb +108 -68
  177. data/lib/active_record/result.rb +41 -34
  178. data/lib/active_record/runtime_registry.rb +2 -2
  179. data/lib/active_record/sanitization.rb +6 -17
  180. data/lib/active_record/schema_dumper.rb +34 -4
  181. data/lib/active_record/schema_migration.rb +2 -8
  182. data/lib/active_record/scoping/default.rb +0 -1
  183. data/lib/active_record/scoping/named.rb +7 -18
  184. data/lib/active_record/scoping.rb +0 -1
  185. data/lib/active_record/secure_token.rb +16 -8
  186. data/lib/active_record/serialization.rb +5 -3
  187. data/lib/active_record/signed_id.rb +116 -0
  188. data/lib/active_record/statement_cache.rb +20 -4
  189. data/lib/active_record/store.rb +3 -3
  190. data/lib/active_record/suppressor.rb +2 -2
  191. data/lib/active_record/table_metadata.rb +39 -36
  192. data/lib/active_record/tasks/database_tasks.rb +139 -113
  193. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -36
  194. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -27
  195. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -10
  196. data/lib/active_record/test_databases.rb +5 -4
  197. data/lib/active_record/test_fixtures.rb +38 -16
  198. data/lib/active_record/timestamp.rb +4 -7
  199. data/lib/active_record/touch_later.rb +20 -21
  200. data/lib/active_record/transactions.rb +21 -70
  201. data/lib/active_record/type/adapter_specific_registry.rb +2 -5
  202. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  203. data/lib/active_record/type/serialized.rb +6 -3
  204. data/lib/active_record/type/time.rb +10 -0
  205. data/lib/active_record/type/type_map.rb +0 -1
  206. data/lib/active_record/type/unsigned_integer.rb +0 -1
  207. data/lib/active_record/type.rb +8 -2
  208. data/lib/active_record/type_caster/connection.rb +0 -1
  209. data/lib/active_record/type_caster/map.rb +8 -5
  210. data/lib/active_record/validations/associated.rb +1 -2
  211. data/lib/active_record/validations/numericality.rb +35 -0
  212. data/lib/active_record/validations/uniqueness.rb +24 -4
  213. data/lib/active_record/validations.rb +3 -3
  214. data/lib/active_record.rb +7 -13
  215. data/lib/arel/attributes/attribute.rb +4 -0
  216. data/lib/arel/collectors/bind.rb +5 -0
  217. data/lib/arel/collectors/composite.rb +8 -0
  218. data/lib/arel/collectors/sql_string.rb +7 -0
  219. data/lib/arel/collectors/substitute_binds.rb +7 -0
  220. data/lib/arel/nodes/binary.rb +82 -8
  221. data/lib/arel/nodes/bind_param.rb +8 -0
  222. data/lib/arel/nodes/casted.rb +21 -9
  223. data/lib/arel/nodes/equality.rb +6 -9
  224. data/lib/arel/nodes/grouping.rb +3 -0
  225. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  226. data/lib/arel/nodes/in.rb +8 -1
  227. data/lib/arel/nodes/infix_operation.rb +13 -1
  228. data/lib/arel/nodes/join_source.rb +1 -1
  229. data/lib/arel/nodes/node.rb +7 -6
  230. data/lib/arel/nodes/ordering.rb +27 -0
  231. data/lib/arel/nodes/sql_literal.rb +3 -0
  232. data/lib/arel/nodes/table_alias.rb +7 -3
  233. data/lib/arel/nodes/unary.rb +0 -1
  234. data/lib/arel/nodes.rb +3 -1
  235. data/lib/arel/predications.rb +17 -24
  236. data/lib/arel/select_manager.rb +1 -2
  237. data/lib/arel/table.rb +13 -5
  238. data/lib/arel/visitors/dot.rb +14 -3
  239. data/lib/arel/visitors/mysql.rb +11 -1
  240. data/lib/arel/visitors/postgresql.rb +15 -5
  241. data/lib/arel/visitors/sqlite.rb +0 -1
  242. data/lib/arel/visitors/to_sql.rb +89 -79
  243. data/lib/arel/visitors/visitor.rb +0 -1
  244. data/lib/arel/visitors.rb +0 -7
  245. data/lib/arel.rb +5 -9
  246. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  247. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  248. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  249. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
  250. data/lib/rails/generators/active_record/migration.rb +6 -2
  251. data/lib/rails/generators/active_record/model/model_generator.rb +38 -2
  252. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  253. metadata +26 -26
  254. data/lib/active_record/attribute_decorators.rb +0 -90
  255. data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
  256. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  257. data/lib/active_record/define_callbacks.rb +0 -22
  258. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  259. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  260. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  261. data/lib/arel/attributes.rb +0 -22
  262. data/lib/arel/visitors/depth_first.rb +0 -204
  263. data/lib/arel/visitors/ibm_db.rb +0 -34
  264. data/lib/arel/visitors/informix.rb +0 -62
  265. data/lib/arel/visitors/mssql.rb +0 -157
  266. data/lib/arel/visitors/oracle.rb +0 -159
  267. data/lib/arel/visitors/oracle12.rb +0 -66
  268. data/lib/arel/visitors/where_sql.rb +0 -23
@@ -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.
@@ -134,7 +136,7 @@ module ActiveRecord
134
136
  relation.select_values = [ klass.primary_key || table[Arel.star] ]
135
137
  end
136
138
  # PostgreSQL: ORDER BY expressions must appear in SELECT list when using DISTINCT
137
- relation.order_values = []
139
+ relation.order_values = [] if group_values.empty?
138
140
  end
139
141
 
140
142
  relation.calculate(operation, column_name)
@@ -172,14 +174,14 @@ module ActiveRecord
172
174
  # # SELECT people.id FROM people WHERE people.age = 21 LIMIT 5
173
175
  # # => [2, 3]
174
176
  #
175
- # Person.pluck('DATEDIFF(updated_at, created_at)')
177
+ # Person.pluck(Arel.sql('DATEDIFF(updated_at, created_at)'))
176
178
  # # SELECT DATEDIFF(updated_at, created_at) FROM people
177
179
  # # => ['0', '27761', '173']
178
180
  #
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
 
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "mutex_m"
4
+ require "active_support/core_ext/module/delegation"
4
5
 
5
6
  module ActiveRecord
6
7
  module Delegation # :nodoc:
@@ -59,16 +60,18 @@ module ActiveRecord
59
60
  synchronize do
60
61
  return if method_defined?(method)
61
62
 
62
- if /\A[a-zA-Z_]\w*[!?]?\z/.match?(method)
63
+ 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"
63
65
  module_eval <<-RUBY, __FILE__, __LINE__ + 1
64
- def #{method}(*args, &block)
65
- scoping { klass.#{method}(*args, &block) }
66
+ def #{method}(#{definition})
67
+ scoping { klass.#{method}(#{definition}) }
66
68
  end
67
69
  RUBY
68
70
  else
69
71
  define_method(method) do |*args, &block|
70
72
  scoping { klass.public_send(method, *args, &block) }
71
73
  end
74
+ ruby2_keywords(method) if respond_to?(:ruby2_keywords, true)
72
75
  end
73
76
  end
74
77
  end
@@ -99,7 +102,6 @@ module ActiveRecord
99
102
  end
100
103
 
101
104
  private
102
-
103
105
  def method_missing(method, *args, &block)
104
106
  if @klass.respond_to?(method)
105
107
  @klass.generate_relation_method(method)
@@ -108,15 +110,15 @@ module ActiveRecord
108
110
  super
109
111
  end
110
112
  end
113
+ ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
111
114
  end
112
115
 
113
116
  module ClassMethods # :nodoc:
114
- def create(klass, *args)
115
- relation_class_for(klass).new(klass, *args)
117
+ def create(klass, *args, **kwargs)
118
+ relation_class_for(klass).new(klass, *args, **kwargs)
116
119
  end
117
120
 
118
121
  private
119
-
120
122
  def relation_class_for(klass)
121
123
  klass.relation_delegate_class(self)
122
124
  end
@@ -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?
316
319
 
317
- skip_query_cache_if_necessary { connection.select_one(relation.arel, "#{name} Exists?") } ? true : false
320
+ skip_query_cache_if_necessary { connection.select_rows(relation.arel, "#{name} Exists?").size == 1 }
318
321
  end
319
322
 
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
334
+ end
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,9 +364,15 @@ module ActiveRecord
346
364
  end
347
365
 
348
366
  private
349
-
350
- def offset_index
351
- 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
352
376
  end
353
377
 
354
378
  def construct_relation_for_exists(conditions)
@@ -372,7 +396,7 @@ module ActiveRecord
372
396
 
373
397
  def apply_join_dependency(eager_loading: group_values.empty?)
374
398
  join_dependency = construct_join_dependency(
375
- eager_load_values + includes_values, Arel::Nodes::OuterJoin
399
+ eager_load_values | includes_values, Arel::Nodes::OuterJoin
376
400
  )
377
401
  relation = except(:includes, :eager_load, :preload).joins!(join_dependency)
378
402
 
@@ -402,14 +426,14 @@ module ActiveRecord
402
426
 
403
427
  def limited_ids_for(relation)
404
428
  values = @klass.connection.columns_for_distinct(
405
- connection.visitor.compile(arel_attribute(primary_key)),
429
+ connection.visitor.compile(table[primary_key]),
406
430
  relation.order_values
407
431
  )
408
432
 
409
433
  relation = relation.except(:select).select(values).distinct!
410
434
 
411
- id_rows = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, "SQL") }
412
- 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)
413
437
  end
414
438
 
415
439
  def using_limitable_reflections?(reflections)
@@ -510,7 +534,8 @@ module ActiveRecord
510
534
  end
511
535
 
512
536
  def find_nth(index)
513
- @offsets[offset_index + index] ||= find_nth_with_limit(index, 1).first
537
+ @offsets ||= {}
538
+ @offsets[index] ||= find_nth_with_limit(index, 1).first
514
539
  end
515
540
 
516
541
  def find_nth_with_limit(index, limit)
@@ -524,7 +549,7 @@ module ActiveRecord
524
549
  end
525
550
 
526
551
  if limit > 0
527
- relation = relation.offset(offset_index + index) unless index.zero?
552
+ relation = relation.offset((offset_value || 0) + index) unless index.zero?
528
553
  relation.limit(limit).to_a
529
554
  else
530
555
  []
@@ -552,7 +577,11 @@ module ActiveRecord
552
577
 
553
578
  def ordered_relation
554
579
  if order_values.empty? && (implicit_order_column || primary_key)
555
- 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
556
585
  else
557
586
  self
558
587
  end
@@ -18,8 +18,12 @@ module ActiveRecord
18
18
  value.nil?
19
19
  end
20
20
 
21
+ def ==(other)
22
+ self.class == other.class && value == other.value && name == other.name
23
+ end
24
+
21
25
  def self.empty
22
- @empty ||= new(nil, nil)
26
+ @empty ||= new(nil, nil).freeze
23
27
  end
24
28
  end
25
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
@@ -89,13 +86,12 @@ module ActiveRecord
89
86
  end
90
87
 
91
88
  private
92
-
93
89
  def merge_preloads
94
90
  return if other.preload_values.empty? && other.includes_values.empty?
95
91
 
96
92
  if other.klass == relation.klass
97
- relation.preload!(*other.preload_values) unless other.preload_values.empty?
98
- 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?
99
95
  else
100
96
  reflection = relation.klass.reflect_on_all_associations.find do |r|
101
97
  r.class_name == other.klass.name
@@ -112,10 +108,10 @@ module ActiveRecord
112
108
  end
113
109
 
114
110
  def merge_joins
115
- return if other.joins_values.blank?
111
+ return if other.joins_values.empty?
116
112
 
117
113
  if other.klass == relation.klass
118
- relation.joins!(*other.joins_values)
114
+ relation.joins_values |= other.joins_values
119
115
  else
120
116
  associations, others = other.joins_values.partition do |join|
121
117
  case join
@@ -131,16 +127,21 @@ module ActiveRecord
131
127
  end
132
128
 
133
129
  def merge_outer_joins
134
- return if other.left_outer_joins_values.blank?
130
+ return if other.left_outer_joins_values.empty?
135
131
 
136
132
  if other.klass == relation.klass
137
- relation.left_outer_joins!(*other.left_outer_joins_values)
133
+ relation.left_outer_joins_values |= other.left_outer_joins_values
138
134
  else
139
- associations = other.left_outer_joins_values
135
+ associations, others = other.left_outer_joins_values.partition do |join|
136
+ case join
137
+ when Hash, Symbol, Array; true
138
+ end
139
+ end
140
+
140
141
  join_dependency = other.construct_join_dependency(
141
142
  associations, Arel::Nodes::OuterJoin
142
143
  )
143
- relation.joins!(join_dependency)
144
+ relation.left_outer_joins!(join_dependency, *others)
144
145
  end
145
146
  end
146
147
 
@@ -168,7 +169,7 @@ module ActiveRecord
168
169
  def merge_clauses
169
170
  relation.from_clause = other.from_clause if replace_from_clause?
170
171
 
171
- where_clause = relation.where_clause.merge(other.where_clause)
172
+ where_clause = relation.where_clause.merge(other.where_clause, @rewhere)
172
173
  relation.where_clause = where_clause unless where_clause.empty?
173
174
 
174
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)