activerecord 6.0.0 → 6.1.7.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (270) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1413 -614
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -4
  5. data/lib/active_record/aggregations.rb +5 -6
  6. data/lib/active_record/association_relation.rb +30 -10
  7. data/lib/active_record/associations/alias_tracker.rb +19 -16
  8. data/lib/active_record/associations/association.rb +55 -29
  9. data/lib/active_record/associations/association_scope.rb +19 -15
  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 -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 +38 -13
  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 +39 -16
  26. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  27. data/lib/active_record/associations/join_dependency.rb +77 -42
  28. data/lib/active_record/associations/preloader/association.rb +49 -25
  29. data/lib/active_record/associations/preloader/through_association.rb +2 -2
  30. data/lib/active_record/associations/preloader.rb +13 -8
  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 +120 -13
  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 +33 -9
  45. data/lib/active_record/autosave_association.rb +63 -44
  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 +24 -3
  49. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +202 -138
  50. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  51. data/lib/active_record/connection_adapters/abstract/database_statements.rb +87 -38
  52. data/lib/active_record/connection_adapters/abstract/query_cache.rb +5 -10
  53. data/lib/active_record/connection_adapters/abstract/quoting.rb +44 -35
  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 +145 -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 +267 -105
  59. data/lib/active_record/connection_adapters/abstract/transaction.rb +94 -36
  60. data/lib/active_record/connection_adapters/abstract_adapter.rb +76 -79
  61. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +149 -115
  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 +35 -0
  65. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  66. data/lib/active_record/connection_adapters/mysql/database_statements.rb +32 -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 +18 -3
  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 +5 -2
  72. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +20 -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 +73 -0
  76. data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
  77. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  78. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +23 -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/money.rb +2 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
  91. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -6
  92. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -2
  94. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  95. data/lib/active_record/connection_adapters/postgresql/quoting.rb +30 -4
  96. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
  97. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +7 -3
  98. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -1
  99. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  100. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +72 -54
  101. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  102. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  103. data/lib/active_record/connection_adapters/postgresql_adapter.rb +84 -66
  104. data/lib/active_record/connection_adapters/schema_cache.rb +130 -15
  105. data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
  106. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +40 -12
  107. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -2
  108. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  109. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -5
  110. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -57
  111. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  112. data/lib/active_record/connection_adapters.rb +52 -0
  113. data/lib/active_record/connection_handling.rb +219 -81
  114. data/lib/active_record/core.rb +283 -71
  115. data/lib/active_record/counter_cache.rb +4 -1
  116. data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -0
  117. data/lib/active_record/database_configurations/database_config.rb +52 -9
  118. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  119. data/lib/active_record/database_configurations/url_config.rb +15 -41
  120. data/lib/active_record/database_configurations.rb +125 -85
  121. data/lib/active_record/delegated_type.rb +209 -0
  122. data/lib/active_record/destroy_association_async_job.rb +36 -0
  123. data/lib/active_record/dynamic_matchers.rb +2 -3
  124. data/lib/active_record/enum.rb +80 -38
  125. data/lib/active_record/errors.rb +47 -12
  126. data/lib/active_record/explain.rb +9 -5
  127. data/lib/active_record/explain_subscriber.rb +1 -1
  128. data/lib/active_record/fixture_set/file.rb +10 -17
  129. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  130. data/lib/active_record/fixture_set/render_context.rb +1 -1
  131. data/lib/active_record/fixture_set/table_row.rb +2 -3
  132. data/lib/active_record/fixture_set/table_rows.rb +0 -1
  133. data/lib/active_record/fixtures.rb +58 -12
  134. data/lib/active_record/gem_version.rb +3 -3
  135. data/lib/active_record/inheritance.rb +40 -21
  136. data/lib/active_record/insert_all.rb +43 -10
  137. data/lib/active_record/integration.rb +3 -5
  138. data/lib/active_record/internal_metadata.rb +18 -7
  139. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  140. data/lib/active_record/locking/optimistic.rb +33 -18
  141. data/lib/active_record/locking/pessimistic.rb +6 -2
  142. data/lib/active_record/log_subscriber.rb +28 -9
  143. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  144. data/lib/active_record/middleware/database_selector/resolver.rb +14 -14
  145. data/lib/active_record/middleware/database_selector.rb +4 -2
  146. data/lib/active_record/migration/command_recorder.rb +53 -45
  147. data/lib/active_record/migration/compatibility.rb +75 -21
  148. data/lib/active_record/migration/join_table.rb +0 -1
  149. data/lib/active_record/migration.rb +115 -85
  150. data/lib/active_record/model_schema.rb +120 -15
  151. data/lib/active_record/nested_attributes.rb +2 -5
  152. data/lib/active_record/no_touching.rb +1 -1
  153. data/lib/active_record/null_relation.rb +0 -1
  154. data/lib/active_record/persistence.rb +50 -46
  155. data/lib/active_record/query_cache.rb +15 -5
  156. data/lib/active_record/querying.rb +12 -7
  157. data/lib/active_record/railtie.rb +65 -45
  158. data/lib/active_record/railties/console_sandbox.rb +2 -4
  159. data/lib/active_record/railties/databases.rake +280 -99
  160. data/lib/active_record/readonly_attributes.rb +4 -0
  161. data/lib/active_record/reflection.rb +77 -63
  162. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  163. data/lib/active_record/relation/batches.rb +38 -32
  164. data/lib/active_record/relation/calculations.rb +106 -45
  165. data/lib/active_record/relation/delegation.rb +9 -7
  166. data/lib/active_record/relation/finder_methods.rb +55 -17
  167. data/lib/active_record/relation/from_clause.rb +5 -1
  168. data/lib/active_record/relation/merger.rb +27 -26
  169. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  170. data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
  171. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -6
  172. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  173. data/lib/active_record/relation/predicate_builder.rb +59 -40
  174. data/lib/active_record/relation/query_methods.rb +346 -181
  175. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  176. data/lib/active_record/relation/spawn_methods.rb +8 -8
  177. data/lib/active_record/relation/where_clause.rb +111 -62
  178. data/lib/active_record/relation.rb +116 -82
  179. data/lib/active_record/result.rb +41 -34
  180. data/lib/active_record/runtime_registry.rb +2 -2
  181. data/lib/active_record/sanitization.rb +6 -17
  182. data/lib/active_record/schema_dumper.rb +34 -4
  183. data/lib/active_record/schema_migration.rb +2 -8
  184. data/lib/active_record/scoping/default.rb +1 -4
  185. data/lib/active_record/scoping/named.rb +7 -18
  186. data/lib/active_record/scoping.rb +0 -1
  187. data/lib/active_record/secure_token.rb +16 -8
  188. data/lib/active_record/serialization.rb +5 -3
  189. data/lib/active_record/signed_id.rb +116 -0
  190. data/lib/active_record/statement_cache.rb +20 -4
  191. data/lib/active_record/store.rb +9 -4
  192. data/lib/active_record/suppressor.rb +2 -2
  193. data/lib/active_record/table_metadata.rb +42 -36
  194. data/lib/active_record/tasks/database_tasks.rb +140 -113
  195. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -36
  196. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -27
  197. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -10
  198. data/lib/active_record/test_databases.rb +5 -4
  199. data/lib/active_record/test_fixtures.rb +87 -20
  200. data/lib/active_record/timestamp.rb +4 -7
  201. data/lib/active_record/touch_later.rb +20 -21
  202. data/lib/active_record/transactions.rb +26 -73
  203. data/lib/active_record/type/adapter_specific_registry.rb +2 -5
  204. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  205. data/lib/active_record/type/serialized.rb +6 -3
  206. data/lib/active_record/type/time.rb +10 -0
  207. data/lib/active_record/type/type_map.rb +0 -1
  208. data/lib/active_record/type/unsigned_integer.rb +0 -1
  209. data/lib/active_record/type.rb +8 -2
  210. data/lib/active_record/type_caster/connection.rb +0 -1
  211. data/lib/active_record/type_caster/map.rb +8 -5
  212. data/lib/active_record/validations/associated.rb +1 -2
  213. data/lib/active_record/validations/numericality.rb +35 -0
  214. data/lib/active_record/validations/uniqueness.rb +24 -4
  215. data/lib/active_record/validations.rb +3 -3
  216. data/lib/active_record.rb +7 -13
  217. data/lib/arel/attributes/attribute.rb +4 -0
  218. data/lib/arel/collectors/bind.rb +5 -0
  219. data/lib/arel/collectors/composite.rb +8 -0
  220. data/lib/arel/collectors/sql_string.rb +7 -0
  221. data/lib/arel/collectors/substitute_binds.rb +7 -0
  222. data/lib/arel/nodes/binary.rb +82 -8
  223. data/lib/arel/nodes/bind_param.rb +8 -0
  224. data/lib/arel/nodes/casted.rb +21 -9
  225. data/lib/arel/nodes/equality.rb +6 -9
  226. data/lib/arel/nodes/grouping.rb +3 -0
  227. data/lib/arel/nodes/homogeneous_in.rb +76 -0
  228. data/lib/arel/nodes/in.rb +8 -1
  229. data/lib/arel/nodes/infix_operation.rb +13 -1
  230. data/lib/arel/nodes/join_source.rb +1 -1
  231. data/lib/arel/nodes/node.rb +7 -6
  232. data/lib/arel/nodes/ordering.rb +27 -0
  233. data/lib/arel/nodes/sql_literal.rb +3 -0
  234. data/lib/arel/nodes/table_alias.rb +7 -3
  235. data/lib/arel/nodes/unary.rb +0 -1
  236. data/lib/arel/nodes.rb +3 -1
  237. data/lib/arel/predications.rb +17 -24
  238. data/lib/arel/select_manager.rb +1 -2
  239. data/lib/arel/table.rb +13 -5
  240. data/lib/arel/visitors/dot.rb +14 -3
  241. data/lib/arel/visitors/mysql.rb +11 -1
  242. data/lib/arel/visitors/postgresql.rb +15 -5
  243. data/lib/arel/visitors/sqlite.rb +0 -1
  244. data/lib/arel/visitors/to_sql.rb +89 -79
  245. data/lib/arel/visitors/visitor.rb +0 -1
  246. data/lib/arel/visitors.rb +0 -7
  247. data/lib/arel.rb +15 -12
  248. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  249. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  250. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  251. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
  252. data/lib/rails/generators/active_record/migration.rb +6 -2
  253. data/lib/rails/generators/active_record/model/model_generator.rb +38 -2
  254. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  255. metadata +31 -27
  256. data/lib/active_record/attribute_decorators.rb +0 -90
  257. data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
  258. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  259. data/lib/active_record/define_callbacks.rb +0 -22
  260. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  261. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  262. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  263. data/lib/arel/attributes.rb +0 -22
  264. data/lib/arel/visitors/depth_first.rb +0 -204
  265. data/lib/arel/visitors/ibm_db.rb +0 -34
  266. data/lib/arel/visitors/informix.rb +0 -62
  267. data/lib/arel/visitors/mssql.rb +0 -157
  268. data/lib/arel/visitors/oracle.rb +0 -159
  269. data/lib/arel/visitors/oracle12.rb +0 -66
  270. 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,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
 
@@ -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 || having_clause.any?
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 7.0.
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,11 +396,20 @@ 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
 
379
- if eager_loading && !using_limitable_reflections?(join_dependency.reflections)
403
+ if eager_loading && !(
404
+ using_limitable_reflections?(join_dependency.reflections) &&
405
+ using_limitable_reflections?(
406
+ construct_join_dependency(
407
+ select_association_list(joins_values).concat(
408
+ select_association_list(left_outer_joins_values)
409
+ ), nil
410
+ ).reflections
411
+ )
412
+ )
380
413
  if has_limit_or_offset?
381
414
  limited_ids = limited_ids_for(relation)
382
415
  limited_ids.empty? ? relation.none! : relation.where!(primary_key => limited_ids)
@@ -393,14 +426,14 @@ module ActiveRecord
393
426
 
394
427
  def limited_ids_for(relation)
395
428
  values = @klass.connection.columns_for_distinct(
396
- connection.visitor.compile(arel_attribute(primary_key)),
429
+ connection.visitor.compile(table[primary_key]),
397
430
  relation.order_values
398
431
  )
399
432
 
400
433
  relation = relation.except(:select).select(values).distinct!
401
434
 
402
- id_rows = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, "SQL") }
403
- 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)
404
437
  end
405
438
 
406
439
  def using_limitable_reflections?(reflections)
@@ -501,7 +534,8 @@ module ActiveRecord
501
534
  end
502
535
 
503
536
  def find_nth(index)
504
- @offsets[offset_index + index] ||= find_nth_with_limit(index, 1).first
537
+ @offsets ||= {}
538
+ @offsets[index] ||= find_nth_with_limit(index, 1).first
505
539
  end
506
540
 
507
541
  def find_nth_with_limit(index, limit)
@@ -515,7 +549,7 @@ module ActiveRecord
515
549
  end
516
550
 
517
551
  if limit > 0
518
- relation = relation.offset(offset_index + index) unless index.zero?
552
+ relation = relation.offset((offset_value || 0) + index) unless index.zero?
519
553
  relation.limit(limit).to_a
520
554
  else
521
555
  []
@@ -543,7 +577,11 @@ module ActiveRecord
543
577
 
544
578
  def ordered_relation
545
579
  if order_values.empty? && (implicit_order_column || primary_key)
546
- 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
547
585
  else
548
586
  self
549
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?(primary_key)
35
+ value.public_send(primary_key)
37
36
  else
38
37
  value
39
38
  end