activerecord 5.2.8.1 → 6.1.6.1

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 (316) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1255 -596
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +7 -5
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/aggregations.rb +9 -8
  7. data/lib/active_record/association_relation.rb +30 -10
  8. data/lib/active_record/associations/alias_tracker.rb +19 -16
  9. data/lib/active_record/associations/association.rb +100 -41
  10. data/lib/active_record/associations/association_scope.rb +23 -21
  11. data/lib/active_record/associations/belongs_to_association.rb +55 -48
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -6
  13. data/lib/active_record/associations/builder/association.rb +45 -22
  14. data/lib/active_record/associations/builder/belongs_to.rb +29 -59
  15. data/lib/active_record/associations/builder/collection_association.rb +8 -17
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -41
  17. data/lib/active_record/associations/builder/has_many.rb +8 -2
  18. data/lib/active_record/associations/builder/has_one.rb +33 -2
  19. data/lib/active_record/associations/builder/singular_association.rb +3 -1
  20. data/lib/active_record/associations/collection_association.rb +44 -34
  21. data/lib/active_record/associations/collection_proxy.rb +25 -21
  22. data/lib/active_record/associations/foreign_association.rb +20 -0
  23. data/lib/active_record/associations/has_many_association.rb +26 -13
  24. data/lib/active_record/associations/has_many_through_association.rb +24 -18
  25. data/lib/active_record/associations/has_one_association.rb +43 -31
  26. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  27. data/lib/active_record/associations/join_dependency/join_association.rb +44 -22
  28. data/lib/active_record/associations/join_dependency/join_part.rb +5 -5
  29. data/lib/active_record/associations/join_dependency.rb +91 -60
  30. data/lib/active_record/associations/preloader/association.rb +69 -43
  31. data/lib/active_record/associations/preloader/through_association.rb +49 -40
  32. data/lib/active_record/associations/preloader.rb +47 -34
  33. data/lib/active_record/associations/singular_association.rb +3 -17
  34. data/lib/active_record/associations/through_association.rb +1 -1
  35. data/lib/active_record/associations.rb +137 -25
  36. data/lib/active_record/attribute_assignment.rb +17 -19
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -7
  38. data/lib/active_record/attribute_methods/dirty.rb +101 -40
  39. data/lib/active_record/attribute_methods/primary_key.rb +20 -25
  40. data/lib/active_record/attribute_methods/query.rb +4 -8
  41. data/lib/active_record/attribute_methods/read.rb +14 -56
  42. data/lib/active_record/attribute_methods/serialization.rb +12 -7
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
  44. data/lib/active_record/attribute_methods/write.rb +18 -34
  45. data/lib/active_record/attribute_methods.rb +81 -143
  46. data/lib/active_record/attributes.rb +46 -9
  47. data/lib/active_record/autosave_association.rb +57 -42
  48. data/lib/active_record/base.rb +4 -17
  49. data/lib/active_record/callbacks.rb +158 -43
  50. data/lib/active_record/coders/yaml_column.rb +1 -2
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +272 -130
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -36
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +167 -146
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +18 -14
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +98 -47
  56. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -110
  58. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +211 -90
  59. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -4
  60. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +385 -144
  61. data/lib/active_record/connection_adapters/abstract/transaction.rb +167 -69
  62. data/lib/active_record/connection_adapters/abstract_adapter.rb +229 -99
  63. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +243 -275
  64. data/lib/active_record/connection_adapters/column.rb +30 -12
  65. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  66. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
  67. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  68. data/lib/active_record/connection_adapters/mysql/database_statements.rb +88 -32
  69. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  70. data/lib/active_record/connection_adapters/mysql/quoting.rb +59 -7
  71. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +34 -10
  72. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +48 -32
  73. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +18 -7
  74. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +142 -19
  75. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +14 -9
  76. data/lib/active_record/connection_adapters/mysql2_adapter.rb +53 -18
  77. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  78. data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
  79. data/lib/active_record/connection_adapters/postgresql/column.rb +37 -28
  80. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +40 -54
  81. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  83. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  84. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  87. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +3 -4
  90. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +3 -4
  93. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +25 -7
  94. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  96. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +15 -3
  97. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  98. data/lib/active_record/connection_adapters/postgresql/quoting.rb +47 -10
  99. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
  100. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +19 -4
  101. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  102. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  103. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +120 -100
  104. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +31 -26
  105. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  106. data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -120
  107. data/lib/active_record/connection_adapters/schema_cache.rb +159 -21
  108. data/lib/active_record/connection_adapters/sql_type_metadata.rb +17 -6
  109. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +146 -0
  110. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -7
  111. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  112. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +77 -13
  113. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +174 -186
  114. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  115. data/lib/active_record/connection_adapters.rb +52 -0
  116. data/lib/active_record/connection_handling.rb +293 -33
  117. data/lib/active_record/core.rb +333 -98
  118. data/lib/active_record/counter_cache.rb +8 -30
  119. data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -0
  120. data/lib/active_record/database_configurations/database_config.rb +80 -0
  121. data/lib/active_record/database_configurations/hash_config.rb +96 -0
  122. data/lib/active_record/database_configurations/url_config.rb +53 -0
  123. data/lib/active_record/database_configurations.rb +273 -0
  124. data/lib/active_record/delegated_type.rb +209 -0
  125. data/lib/active_record/destroy_association_async_job.rb +36 -0
  126. data/lib/active_record/dynamic_matchers.rb +3 -4
  127. data/lib/active_record/enum.rb +108 -36
  128. data/lib/active_record/errors.rb +62 -19
  129. data/lib/active_record/explain.rb +10 -6
  130. data/lib/active_record/explain_subscriber.rb +1 -1
  131. data/lib/active_record/fixture_set/file.rb +10 -17
  132. data/lib/active_record/fixture_set/model_metadata.rb +32 -0
  133. data/lib/active_record/fixture_set/render_context.rb +17 -0
  134. data/lib/active_record/fixture_set/table_row.rb +152 -0
  135. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  136. data/lib/active_record/fixtures.rb +200 -481
  137. data/lib/active_record/gem_version.rb +3 -3
  138. data/lib/active_record/inheritance.rb +53 -24
  139. data/lib/active_record/insert_all.rb +212 -0
  140. data/lib/active_record/integration.rb +67 -17
  141. data/lib/active_record/internal_metadata.rb +28 -9
  142. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  143. data/lib/active_record/locking/optimistic.rb +37 -23
  144. data/lib/active_record/locking/pessimistic.rb +9 -5
  145. data/lib/active_record/log_subscriber.rb +35 -35
  146. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  147. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  148. data/lib/active_record/middleware/database_selector.rb +77 -0
  149. data/lib/active_record/migration/command_recorder.rb +96 -44
  150. data/lib/active_record/migration/compatibility.rb +145 -64
  151. data/lib/active_record/migration/join_table.rb +0 -1
  152. data/lib/active_record/migration.rb +206 -157
  153. data/lib/active_record/model_schema.rb +148 -22
  154. data/lib/active_record/nested_attributes.rb +4 -7
  155. data/lib/active_record/no_touching.rb +8 -1
  156. data/lib/active_record/null_relation.rb +0 -1
  157. data/lib/active_record/persistence.rb +267 -59
  158. data/lib/active_record/query_cache.rb +21 -4
  159. data/lib/active_record/querying.rb +40 -23
  160. data/lib/active_record/railtie.rb +116 -59
  161. data/lib/active_record/railties/console_sandbox.rb +2 -4
  162. data/lib/active_record/railties/controller_runtime.rb +30 -35
  163. data/lib/active_record/railties/databases.rake +411 -80
  164. data/lib/active_record/readonly_attributes.rb +4 -0
  165. data/lib/active_record/reflection.rb +109 -93
  166. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  167. data/lib/active_record/relation/batches.rb +44 -35
  168. data/lib/active_record/relation/calculations.rb +157 -90
  169. data/lib/active_record/relation/delegation.rb +35 -50
  170. data/lib/active_record/relation/finder_methods.rb +64 -39
  171. data/lib/active_record/relation/from_clause.rb +5 -1
  172. data/lib/active_record/relation/merger.rb +32 -40
  173. data/lib/active_record/relation/predicate_builder/array_handler.rb +13 -13
  174. data/lib/active_record/relation/predicate_builder/association_query_value.rb +5 -9
  175. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  176. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +11 -10
  177. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  179. data/lib/active_record/relation/predicate_builder.rb +62 -45
  180. data/lib/active_record/relation/query_attribute.rb +13 -8
  181. data/lib/active_record/relation/query_methods.rb +476 -187
  182. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  183. data/lib/active_record/relation/spawn_methods.rb +9 -9
  184. data/lib/active_record/relation/where_clause.rb +115 -62
  185. data/lib/active_record/relation.rb +379 -115
  186. data/lib/active_record/result.rb +64 -38
  187. data/lib/active_record/runtime_registry.rb +2 -2
  188. data/lib/active_record/sanitization.rb +22 -41
  189. data/lib/active_record/schema.rb +2 -11
  190. data/lib/active_record/schema_dumper.rb +54 -9
  191. data/lib/active_record/schema_migration.rb +7 -9
  192. data/lib/active_record/scoping/default.rb +4 -8
  193. data/lib/active_record/scoping/named.rb +17 -24
  194. data/lib/active_record/scoping.rb +8 -9
  195. data/lib/active_record/secure_token.rb +16 -8
  196. data/lib/active_record/serialization.rb +5 -3
  197. data/lib/active_record/signed_id.rb +116 -0
  198. data/lib/active_record/statement_cache.rb +49 -6
  199. data/lib/active_record/store.rb +88 -9
  200. data/lib/active_record/suppressor.rb +2 -2
  201. data/lib/active_record/table_metadata.rb +42 -43
  202. data/lib/active_record/tasks/database_tasks.rb +277 -81
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +37 -39
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +27 -32
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -17
  206. data/lib/active_record/test_databases.rb +24 -0
  207. data/lib/active_record/test_fixtures.rb +287 -0
  208. data/lib/active_record/timestamp.rb +43 -32
  209. data/lib/active_record/touch_later.rb +23 -22
  210. data/lib/active_record/transactions.rb +62 -118
  211. data/lib/active_record/translation.rb +1 -1
  212. data/lib/active_record/type/adapter_specific_registry.rb +3 -13
  213. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  214. data/lib/active_record/type/serialized.rb +6 -3
  215. data/lib/active_record/type/time.rb +10 -0
  216. data/lib/active_record/type/type_map.rb +0 -1
  217. data/lib/active_record/type/unsigned_integer.rb +0 -1
  218. data/lib/active_record/type.rb +10 -5
  219. data/lib/active_record/type_caster/connection.rb +15 -15
  220. data/lib/active_record/type_caster/map.rb +8 -8
  221. data/lib/active_record/validations/associated.rb +1 -2
  222. data/lib/active_record/validations/numericality.rb +35 -0
  223. data/lib/active_record/validations/uniqueness.rb +38 -30
  224. data/lib/active_record/validations.rb +4 -3
  225. data/lib/active_record.rb +13 -12
  226. data/lib/arel/alias_predication.rb +9 -0
  227. data/lib/arel/attributes/attribute.rb +41 -0
  228. data/lib/arel/collectors/bind.rb +29 -0
  229. data/lib/arel/collectors/composite.rb +39 -0
  230. data/lib/arel/collectors/plain_string.rb +20 -0
  231. data/lib/arel/collectors/sql_string.rb +27 -0
  232. data/lib/arel/collectors/substitute_binds.rb +35 -0
  233. data/lib/arel/crud.rb +42 -0
  234. data/lib/arel/delete_manager.rb +18 -0
  235. data/lib/arel/errors.rb +9 -0
  236. data/lib/arel/expressions.rb +29 -0
  237. data/lib/arel/factory_methods.rb +49 -0
  238. data/lib/arel/insert_manager.rb +49 -0
  239. data/lib/arel/math.rb +45 -0
  240. data/lib/arel/nodes/and.rb +32 -0
  241. data/lib/arel/nodes/ascending.rb +23 -0
  242. data/lib/arel/nodes/binary.rb +126 -0
  243. data/lib/arel/nodes/bind_param.rb +44 -0
  244. data/lib/arel/nodes/case.rb +55 -0
  245. data/lib/arel/nodes/casted.rb +62 -0
  246. data/lib/arel/nodes/comment.rb +29 -0
  247. data/lib/arel/nodes/count.rb +12 -0
  248. data/lib/arel/nodes/delete_statement.rb +45 -0
  249. data/lib/arel/nodes/descending.rb +23 -0
  250. data/lib/arel/nodes/equality.rb +15 -0
  251. data/lib/arel/nodes/extract.rb +24 -0
  252. data/lib/arel/nodes/false.rb +16 -0
  253. data/lib/arel/nodes/full_outer_join.rb +8 -0
  254. data/lib/arel/nodes/function.rb +44 -0
  255. data/lib/arel/nodes/grouping.rb +11 -0
  256. data/lib/arel/nodes/homogeneous_in.rb +76 -0
  257. data/lib/arel/nodes/in.rb +15 -0
  258. data/lib/arel/nodes/infix_operation.rb +92 -0
  259. data/lib/arel/nodes/inner_join.rb +8 -0
  260. data/lib/arel/nodes/insert_statement.rb +37 -0
  261. data/lib/arel/nodes/join_source.rb +20 -0
  262. data/lib/arel/nodes/matches.rb +18 -0
  263. data/lib/arel/nodes/named_function.rb +23 -0
  264. data/lib/arel/nodes/node.rb +51 -0
  265. data/lib/arel/nodes/node_expression.rb +13 -0
  266. data/lib/arel/nodes/ordering.rb +27 -0
  267. data/lib/arel/nodes/outer_join.rb +8 -0
  268. data/lib/arel/nodes/over.rb +15 -0
  269. data/lib/arel/nodes/regexp.rb +16 -0
  270. data/lib/arel/nodes/right_outer_join.rb +8 -0
  271. data/lib/arel/nodes/select_core.rb +67 -0
  272. data/lib/arel/nodes/select_statement.rb +41 -0
  273. data/lib/arel/nodes/sql_literal.rb +19 -0
  274. data/lib/arel/nodes/string_join.rb +11 -0
  275. data/lib/arel/nodes/table_alias.rb +31 -0
  276. data/lib/arel/nodes/terminal.rb +16 -0
  277. data/lib/arel/nodes/true.rb +16 -0
  278. data/lib/arel/nodes/unary.rb +44 -0
  279. data/lib/arel/nodes/unary_operation.rb +20 -0
  280. data/lib/arel/nodes/unqualified_column.rb +22 -0
  281. data/lib/arel/nodes/update_statement.rb +41 -0
  282. data/lib/arel/nodes/values_list.rb +9 -0
  283. data/lib/arel/nodes/window.rb +126 -0
  284. data/lib/arel/nodes/with.rb +11 -0
  285. data/lib/arel/nodes.rb +70 -0
  286. data/lib/arel/order_predications.rb +13 -0
  287. data/lib/arel/predications.rb +250 -0
  288. data/lib/arel/select_manager.rb +270 -0
  289. data/lib/arel/table.rb +118 -0
  290. data/lib/arel/tree_manager.rb +72 -0
  291. data/lib/arel/update_manager.rb +34 -0
  292. data/lib/arel/visitors/dot.rb +308 -0
  293. data/lib/arel/visitors/mysql.rb +93 -0
  294. data/lib/arel/visitors/postgresql.rb +120 -0
  295. data/lib/arel/visitors/sqlite.rb +38 -0
  296. data/lib/arel/visitors/to_sql.rb +899 -0
  297. data/lib/arel/visitors/visitor.rb +45 -0
  298. data/lib/arel/visitors.rb +13 -0
  299. data/lib/arel/window_predications.rb +9 -0
  300. data/lib/arel.rb +54 -0
  301. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  302. data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -5
  303. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +3 -1
  304. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +7 -5
  305. data/lib/rails/generators/active_record/migration.rb +19 -2
  306. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  307. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  308. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  309. metadata +116 -30
  310. data/lib/active_record/attribute_decorators.rb +0 -90
  311. data/lib/active_record/collection_cache_key.rb +0 -53
  312. data/lib/active_record/connection_adapters/connection_specification.rb +0 -287
  313. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -33
  314. data/lib/active_record/define_callbacks.rb +0 -22
  315. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -19
  316. data/lib/active_record/relation/where_clause_factory.rb +0 -34
@@ -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.
@@ -41,15 +43,13 @@ module ActiveRecord
41
43
  def count(column_name = nil)
42
44
  if block_given?
43
45
  unless column_name.nil?
44
- ActiveSupport::Deprecation.warn \
45
- "When `count' is called with a block, it ignores other arguments. " \
46
- "This behavior is now deprecated and will result in an ArgumentError in Rails 6.0."
46
+ raise ArgumentError, "Column name argument is not supported when a block is passed."
47
47
  end
48
48
 
49
- return super()
49
+ super()
50
+ else
51
+ calculate(:count, column_name)
50
52
  end
51
-
52
- calculate(:count, column_name)
53
53
  end
54
54
 
55
55
  # Calculates the average value on a given column. Returns +nil+ if there's
@@ -86,15 +86,13 @@ module ActiveRecord
86
86
  def sum(column_name = nil)
87
87
  if block_given?
88
88
  unless column_name.nil?
89
- ActiveSupport::Deprecation.warn \
90
- "When `sum' is called with a block, it ignores other arguments. " \
91
- "This behavior is now deprecated and will result in an ArgumentError in Rails 6.0."
89
+ raise ArgumentError, "Column name argument is not supported when a block is passed."
92
90
  end
93
91
 
94
- return super()
92
+ super()
93
+ else
94
+ calculate(:sum, column_name)
95
95
  end
96
-
97
- calculate(:sum, column_name)
98
96
  end
99
97
 
100
98
  # This calculates aggregate values in the given column. Methods for #count, #sum, #average,
@@ -138,7 +136,7 @@ module ActiveRecord
138
136
  relation.select_values = [ klass.primary_key || table[Arel.star] ]
139
137
  end
140
138
  # PostgreSQL: ORDER BY expressions must appear in SELECT list when using DISTINCT
141
- relation.order_values = []
139
+ relation.order_values = [] if group_values.empty?
142
140
  end
143
141
 
144
142
  relation.calculate(operation, column_name)
@@ -176,14 +174,14 @@ module ActiveRecord
176
174
  # # SELECT people.id FROM people WHERE people.age = 21 LIMIT 5
177
175
  # # => [2, 3]
178
176
  #
179
- # Person.pluck('DATEDIFF(updated_at, created_at)')
177
+ # Person.pluck(Arel.sql('DATEDIFF(updated_at, created_at)'))
180
178
  # # SELECT DATEDIFF(updated_at, created_at) FROM people
181
179
  # # => ['0', '27761', '173']
182
180
  #
183
181
  # See also #ids.
184
182
  #
185
183
  def pluck(*column_names)
186
- if loaded? && (column_names.map(&:to_s) - @klass.attribute_names - @klass.attribute_aliases.keys).empty?
184
+ if loaded? && all_attributes?(column_names)
187
185
  return records.pluck(*column_names)
188
186
  end
189
187
 
@@ -191,14 +189,43 @@ module ActiveRecord
191
189
  relation = apply_join_dependency
192
190
  relation.pluck(*column_names)
193
191
  else
194
- klass.enforce_raw_sql_whitelist(column_names)
192
+ klass.disallow_raw_sql!(column_names)
193
+ columns = arel_columns(column_names)
195
194
  relation = spawn
196
- relation.select_values = column_names
197
- result = skip_query_cache_if_necessary { klass.connection.select_all(relation.arel, nil) }
198
- 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)
199
204
  end
200
205
  end
201
206
 
207
+ # Pick the value(s) from the named column(s) in the current relation.
208
+ # This is short-hand for <tt>relation.limit(1).pluck(*column_names).first</tt>, and is primarily useful
209
+ # when you have a relation that's already narrowed down to a single row.
210
+ #
211
+ # Just like #pluck, #pick will only load the actual value, not the entire record object, so it's also
212
+ # more efficient. The value is, again like with pluck, typecast by the column type.
213
+ #
214
+ # Person.where(id: 1).pick(:name)
215
+ # # SELECT people.name FROM people WHERE id = 1 LIMIT 1
216
+ # # => 'David'
217
+ #
218
+ # Person.where(id: 1).pick(:name, :email_address)
219
+ # # SELECT people.name, people.email_address FROM people WHERE id = 1 LIMIT 1
220
+ # # => [ 'David', 'david@loudthinking.com' ]
221
+ def pick(*column_names)
222
+ if loaded? && all_attributes?(column_names)
223
+ return records.pick(*column_names)
224
+ end
225
+
226
+ limit(1).pluck(*column_names).first
227
+ end
228
+
202
229
  # Pluck all the ID's for the relation using the table's primary key
203
230
  #
204
231
  # Person.ids # SELECT people.id FROM people
@@ -208,6 +235,10 @@ module ActiveRecord
208
235
  end
209
236
 
210
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
+
211
242
  def has_include?(column_name)
212
243
  eager_loading? || (includes_values.present? && column_name && column_name != :all)
213
244
  end
@@ -246,20 +277,16 @@ module ActiveRecord
246
277
  def aggregate_column(column_name)
247
278
  return column_name if Arel::Expressions === column_name
248
279
 
249
- if @klass.has_attribute?(column_name) || @klass.attribute_alias?(column_name)
250
- @klass.arel_attribute(column_name)
251
- else
252
- Arel.sql(column_name == :all ? "*" : column_name.to_s)
280
+ arel_column(column_name.to_s) do |name|
281
+ Arel.sql(column_name == :all ? "*" : name)
253
282
  end
254
283
  end
255
284
 
256
285
  def operation_over_aggregate_column(column, operation, distinct)
257
- operation == "count" ? column.count(distinct) : column.send(operation)
286
+ operation == "count" ? column.count(distinct) : column.public_send(operation)
258
287
  end
259
288
 
260
289
  def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
261
- column_alias = column_name
262
-
263
290
  if operation == "count" && (column_name == :all && distinct || has_limit_or_offset?)
264
291
  # Shortcut when limit is zero.
265
292
  return 0 if limit_value == 0
@@ -270,56 +297,56 @@ module ActiveRecord
270
297
  relation = unscope(:order).distinct!(false)
271
298
 
272
299
  column = aggregate_column(column_name)
273
-
274
300
  select_value = operation_over_aggregate_column(column, operation, distinct)
275
- if operation == "sum" && distinct
276
- select_value.distinct = true
277
- end
301
+ select_value.distinct = true if operation == "sum" && distinct
278
302
 
279
- column_alias = select_value.alias
280
- column_alias ||= @klass.connection.column_name_for_operation(operation, select_value)
281
303
  relation.select_values = [select_value]
282
304
 
283
305
  query_builder = relation.arel
284
306
  end
285
307
 
286
- result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder, nil) }
287
- row = result.first
288
- value = row && row.values.first
289
- type = result.column_types.fetch(column_alias) do
290
- type_for(column_name)
291
- end
308
+ result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder) }
292
309
 
293
- 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
294
316
  end
295
317
 
296
318
  def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
297
- group_attrs = group_values
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
298
330
 
299
- if group_attrs.first.respond_to?(:to_sym)
300
- association = @klass._reflect_on_association(group_attrs.first)
301
- associated = group_attrs.size == 1 && association && association.belongs_to? # only count belongs_to associations
302
- group_fields = Array(associated ? association.foreign_key : group_attrs)
303
- else
304
- group_fields = group_attrs
331
+ if group_fields.size == 1 && group_fields.first.respond_to?(:to_sym)
332
+ association = klass._reflect_on_association(group_fields.first)
333
+ associated = association && association.belongs_to? # only count belongs_to associations
334
+ group_fields = Array(association.foreign_key) if associated
305
335
  end
306
336
  group_fields = arel_columns(group_fields)
307
337
 
308
- group_aliases = group_fields.map { |field| column_alias_for(field) }
338
+ group_aliases = group_fields.map { |field|
339
+ field = connection.visitor.compile(field) if Arel.arel_node?(field)
340
+ column_alias_for(field.to_s.downcase)
341
+ }
309
342
  group_columns = group_aliases.zip(group_fields)
310
343
 
311
- if operation == "count" && column_name == :all
312
- aggregate_alias = "count_all"
313
- else
314
- aggregate_alias = column_alias_for([operation, column_name].join(" "))
315
- end
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)
316
348
 
317
- select_values = [
318
- operation_over_aggregate_column(
319
- aggregate_column(column_name),
320
- operation,
321
- distinct).as(aggregate_alias)
322
- ]
349
+ select_values = [select_value]
323
350
  select_values += self.select_values unless having_clause.empty?
324
351
 
325
352
  select_values.concat group_columns.map { |aliaz, field|
@@ -339,43 +366,53 @@ module ActiveRecord
339
366
  if association
340
367
  key_ids = calculated_data.collect { |row| row[group_aliases.first] }
341
368
  key_records = association.klass.base_class.where(association.klass.base_class.primary_key => key_ids)
342
- key_records = Hash[key_records.map { |r| [r.id, r] }]
369
+ key_records = key_records.index_by(&:id)
343
370
  end
344
371
 
345
- Hash[calculated_data.map do |row|
346
- key = group_columns.map { |aliaz, col_name|
347
- type = type_for(col_name) do
348
- calculated_data.column_types.fetch(aliaz, Type.default_value)
349
- end
350
- type_cast_calculated_value(row[aliaz], type)
351
- }
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] }
352
387
  key = key.first if key.size == 1
353
388
  key = key_records[key] if associated
354
389
 
355
- type = calculated_data.column_types.fetch(aggregate_alias) { type_for(column_name) }
356
- [key, type_cast_calculated_value(row[aggregate_alias], type, operation)]
357
- 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
358
399
  end
359
400
 
360
- # Converts the given keys to the value that the database adapter returns as
401
+ # Converts the given field to the value that the database adapter returns as
361
402
  # a usable column name:
362
403
  #
363
404
  # column_alias_for("users.id") # => "users_id"
364
405
  # column_alias_for("sum(id)") # => "sum_id"
365
406
  # column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
366
407
  # column_alias_for("count(*)") # => "count_all"
367
- def column_alias_for(keys)
368
- if keys.respond_to? :name
369
- keys = "#{keys.relation.name}.#{keys.name}"
370
- end
371
-
372
- table_name = keys.to_s.downcase
373
- table_name.gsub!(/\*/, "all")
374
- table_name.gsub!(/\W+/, " ")
375
- table_name.strip!
376
- table_name.gsub!(/ +/, "_")
377
-
378
- @klass.connection.table_alias_for(table_name)
408
+ def column_alias_for(field)
409
+ column_alias = +field
410
+ column_alias.gsub!(/\*/, "all")
411
+ column_alias.gsub!(/\W+/, " ")
412
+ column_alias.strip!
413
+ column_alias.gsub!(/ +/, "_")
414
+
415
+ connection.table_alias_for(column_alias)
379
416
  end
380
417
 
381
418
  def type_for(field, &block)
@@ -383,12 +420,41 @@ module ActiveRecord
383
420
  @klass.type_for_attribute(field_name, &block)
384
421
  end
385
422
 
386
- 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)
387
449
  case operation
388
- when "count" then value.to_i
389
- when "sum" then type.deserialize(value || 0)
390
- when "average" then value && value.respond_to?(:to_d) ? value.to_d : value
391
- 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
392
458
  end
393
459
  end
394
460
 
@@ -403,16 +469,17 @@ module ActiveRecord
403
469
 
404
470
  def build_count_subquery(relation, column_name, distinct)
405
471
  if column_name == :all
472
+ column_alias = Arel.star
406
473
  relation.select_values = [ Arel.sql(FinderMethods::ONE_AS_ONE) ] unless distinct
407
474
  else
408
475
  column_alias = Arel.sql("count_column")
409
476
  relation.select_values = [ aggregate_column(column_name).as(column_alias) ]
410
477
  end
411
478
 
412
- subquery = relation.arel.as(Arel.sql("subquery_for_count"))
413
- select_value = operation_over_aggregate_column(column_alias || Arel.star, "count", false)
479
+ subquery_alias = Arel.sql("subquery_for_count")
480
+ select_value = operation_over_aggregate_column(column_alias, "count", false)
414
481
 
415
- Arel::SelectManager.new(subquery).project(select_value)
482
+ relation.build_subquery(subquery_alias, select_value)
416
483
  end
417
484
  end
418
485
  end
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "mutex_m"
4
+ require "active_support/core_ext/module/delegation"
5
+
3
6
  module ActiveRecord
4
7
  module Delegation # :nodoc:
5
8
  module DelegateCache # :nodoc:
@@ -18,7 +21,7 @@ module ActiveRecord
18
21
  include ClassSpecificRelation
19
22
  }
20
23
  include_relation_methods(delegate)
21
- mangled_name = klass.name.gsub("::".freeze, "_".freeze)
24
+ mangled_name = klass.name.gsub("::", "_")
22
25
  const_set mangled_name, delegate
23
26
  private_constant mangled_name
24
27
 
@@ -31,35 +34,49 @@ module ActiveRecord
31
34
  super
32
35
  end
33
36
 
37
+ def generate_relation_method(method)
38
+ generated_relation_methods.generate_method(method)
39
+ end
40
+
34
41
  protected
35
42
  def include_relation_methods(delegate)
36
- superclass.include_relation_methods(delegate) unless base_class == self
43
+ superclass.include_relation_methods(delegate) unless base_class?
37
44
  delegate.include generated_relation_methods
38
45
  end
39
46
 
40
47
  private
41
48
  def generated_relation_methods
42
- @generated_relation_methods ||= Module.new.tap do |mod|
43
- mod_name = "GeneratedRelationMethods"
44
- const_set mod_name, mod
45
- private_constant mod_name
49
+ @generated_relation_methods ||= GeneratedRelationMethods.new.tap do |mod|
50
+ const_set(:GeneratedRelationMethods, mod)
51
+ private_constant :GeneratedRelationMethods
46
52
  end
47
53
  end
54
+ end
48
55
 
49
- def generate_relation_method(method)
50
- if /\A[a-zA-Z_]\w*[!?]?\z/.match?(method)
51
- generated_relation_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
52
- def #{method}(*args, &block)
53
- scoping { klass.#{method}(*args, &block) }
56
+ class GeneratedRelationMethods < Module # :nodoc:
57
+ include Mutex_m
58
+
59
+ def generate_method(method)
60
+ synchronize do
61
+ return if method_defined?(method)
62
+
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"
65
+ module_eval <<-RUBY, __FILE__, __LINE__ + 1
66
+ def #{method}(#{definition})
67
+ scoping { klass.#{method}(#{definition}) }
54
68
  end
55
69
  RUBY
56
70
  else
57
- generated_relation_methods.send(:define_method, method) do |*args, &block|
71
+ define_method(method) do |*args, &block|
58
72
  scoping { klass.public_send(method, *args, &block) }
59
73
  end
74
+ ruby2_keywords(method) if respond_to?(:ruby2_keywords, true)
60
75
  end
61
76
  end
77
+ end
62
78
  end
79
+ private_constant :GeneratedRelationMethods
63
80
 
64
81
  extend ActiveSupport::Concern
65
82
 
@@ -68,7 +85,7 @@ module ActiveRecord
68
85
  # may vary depending on the klass of a relation, so we create a subclass of Relation
69
86
  # for each different klass, and the delegations are compiled into that subclass only.
70
87
 
71
- delegate :to_xml, :encode_with, :length, :each, :uniq, :join,
88
+ delegate :to_xml, :encode_with, :length, :each, :join,
72
89
  :[], :&, :|, :+, :-, :sample, :reverse, :rotate, :compact, :in_groups, :in_groups_of,
73
90
  :to_sentence, :to_formatted_s, :as_json,
74
91
  :shuffle, :split, :slice, :index, :rindex, to: :records
@@ -78,62 +95,30 @@ module ActiveRecord
78
95
  module ClassSpecificRelation # :nodoc:
79
96
  extend ActiveSupport::Concern
80
97
 
81
- included do
82
- @delegation_mutex = Mutex.new
83
- end
84
-
85
98
  module ClassMethods # :nodoc:
86
99
  def name
87
100
  superclass.name
88
101
  end
89
-
90
- def delegate_to_scoped_klass(method)
91
- @delegation_mutex.synchronize do
92
- return if method_defined?(method)
93
-
94
- if /\A[a-zA-Z_]\w*[!?]?\z/.match?(method)
95
- module_eval <<-RUBY, __FILE__, __LINE__ + 1
96
- def #{method}(*args, &block)
97
- scoping { @klass.#{method}(*args, &block) }
98
- end
99
- RUBY
100
- else
101
- define_method method do |*args, &block|
102
- scoping { @klass.public_send(method, *args, &block) }
103
- end
104
- end
105
- end
106
- end
107
102
  end
108
103
 
109
104
  private
110
-
111
105
  def method_missing(method, *args, &block)
112
106
  if @klass.respond_to?(method)
113
- self.class.delegate_to_scoped_klass(method)
107
+ @klass.generate_relation_method(method)
114
108
  scoping { @klass.public_send(method, *args, &block) }
115
- elsif @delegate_to_klass && @klass.respond_to?(method, true)
116
- ActiveSupport::Deprecation.warn \
117
- "Delegating missing #{method} method to #{@klass}. " \
118
- "Accessibility of private/protected class methods in :scope is deprecated and will be removed in Rails 6.0."
119
- @klass.send(method, *args, &block)
120
- elsif arel.respond_to?(method)
121
- ActiveSupport::Deprecation.warn \
122
- "Delegating #{method} to arel is deprecated and will be removed in Rails 6.0."
123
- arel.public_send(method, *args, &block)
124
109
  else
125
110
  super
126
111
  end
127
112
  end
113
+ ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
128
114
  end
129
115
 
130
116
  module ClassMethods # :nodoc:
131
- def create(klass, *args)
132
- relation_class_for(klass).new(klass, *args)
117
+ def create(klass, *args, **kwargs)
118
+ relation_class_for(klass).new(klass, *args, **kwargs)
133
119
  end
134
120
 
135
121
  private
136
-
137
122
  def relation_class_for(klass)
138
123
  klass.relation_delegate_class(self)
139
124
  end
@@ -141,7 +126,7 @@ module ActiveRecord
141
126
 
142
127
  private
143
128
  def respond_to_missing?(method, _)
144
- super || @klass.respond_to?(method) || arel.respond_to?(method)
129
+ super || @klass.respond_to?(method)
145
130
  end
146
131
  end
147
132
  end