activerecord 6.1.7.2 → 7.0.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (243) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1295 -1007
  3. data/README.rdoc +1 -1
  4. data/lib/active_record/aggregations.rb +1 -1
  5. data/lib/active_record/association_relation.rb +0 -10
  6. data/lib/active_record/associations/association.rb +33 -17
  7. data/lib/active_record/associations/association_scope.rb +1 -3
  8. data/lib/active_record/associations/belongs_to_association.rb +15 -4
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  10. data/lib/active_record/associations/builder/association.rb +8 -2
  11. data/lib/active_record/associations/builder/belongs_to.rb +19 -6
  12. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  13. data/lib/active_record/associations/builder/has_many.rb +3 -2
  14. data/lib/active_record/associations/builder/has_one.rb +2 -1
  15. data/lib/active_record/associations/builder/singular_association.rb +2 -2
  16. data/lib/active_record/associations/collection_association.rb +19 -21
  17. data/lib/active_record/associations/collection_proxy.rb +10 -5
  18. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  19. data/lib/active_record/associations/has_many_association.rb +8 -5
  20. data/lib/active_record/associations/has_many_through_association.rb +2 -1
  21. data/lib/active_record/associations/has_one_association.rb +10 -7
  22. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  23. data/lib/active_record/associations/join_dependency.rb +23 -15
  24. data/lib/active_record/associations/preloader/association.rb +186 -52
  25. data/lib/active_record/associations/preloader/batch.rb +48 -0
  26. data/lib/active_record/associations/preloader/branch.rb +147 -0
  27. data/lib/active_record/associations/preloader/through_association.rb +49 -13
  28. data/lib/active_record/associations/preloader.rb +39 -113
  29. data/lib/active_record/associations/singular_association.rb +8 -2
  30. data/lib/active_record/associations/through_association.rb +3 -3
  31. data/lib/active_record/associations.rb +124 -95
  32. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  33. data/lib/active_record/attribute_assignment.rb +1 -1
  34. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  35. data/lib/active_record/attribute_methods/dirty.rb +49 -16
  36. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  37. data/lib/active_record/attribute_methods/query.rb +2 -2
  38. data/lib/active_record/attribute_methods/read.rb +7 -5
  39. data/lib/active_record/attribute_methods/serialization.rb +57 -19
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +8 -3
  41. data/lib/active_record/attribute_methods/write.rb +7 -10
  42. data/lib/active_record/attribute_methods.rb +14 -15
  43. data/lib/active_record/attributes.rb +24 -35
  44. data/lib/active_record/autosave_association.rb +8 -23
  45. data/lib/active_record/base.rb +19 -1
  46. data/lib/active_record/callbacks.rb +2 -2
  47. data/lib/active_record/coders/yaml_column.rb +4 -8
  48. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
  49. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
  50. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +47 -561
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -17
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +46 -22
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +42 -72
  56. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
  57. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +38 -13
  58. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +80 -24
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -22
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +149 -74
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +105 -81
  63. data/lib/active_record/connection_adapters/column.rb +4 -0
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +36 -24
  65. data/lib/active_record/connection_adapters/mysql/quoting.rb +37 -21
  66. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -1
  67. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +20 -1
  68. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
  69. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  70. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -1
  71. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -12
  72. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  75. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  76. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  79. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  80. data/lib/active_record/connection_adapters/postgresql/quoting.rb +71 -71
  81. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +34 -0
  82. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
  83. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
  84. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
  85. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +37 -19
  86. data/lib/active_record/connection_adapters/postgresql_adapter.rb +206 -105
  87. data/lib/active_record/connection_adapters/schema_cache.rb +39 -38
  88. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +25 -19
  89. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +28 -16
  90. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +17 -15
  91. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +96 -32
  92. data/lib/active_record/connection_adapters.rb +6 -5
  93. data/lib/active_record/connection_handling.rb +49 -55
  94. data/lib/active_record/core.rb +123 -148
  95. data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
  96. data/lib/active_record/database_configurations/database_config.rb +12 -9
  97. data/lib/active_record/database_configurations/hash_config.rb +63 -5
  98. data/lib/active_record/database_configurations/url_config.rb +2 -2
  99. data/lib/active_record/database_configurations.rb +15 -32
  100. data/lib/active_record/delegated_type.rb +53 -12
  101. data/lib/active_record/destroy_association_async_job.rb +1 -1
  102. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  103. data/lib/active_record/dynamic_matchers.rb +1 -1
  104. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  105. data/lib/active_record/encryption/cipher.rb +53 -0
  106. data/lib/active_record/encryption/config.rb +44 -0
  107. data/lib/active_record/encryption/configurable.rb +67 -0
  108. data/lib/active_record/encryption/context.rb +35 -0
  109. data/lib/active_record/encryption/contexts.rb +72 -0
  110. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  111. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  112. data/lib/active_record/encryption/encryptable_record.rb +206 -0
  113. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  114. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  115. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  116. data/lib/active_record/encryption/encryptor.rb +155 -0
  117. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  118. data/lib/active_record/encryption/errors.rb +15 -0
  119. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  120. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  121. data/lib/active_record/encryption/key.rb +28 -0
  122. data/lib/active_record/encryption/key_generator.rb +42 -0
  123. data/lib/active_record/encryption/key_provider.rb +46 -0
  124. data/lib/active_record/encryption/message.rb +33 -0
  125. data/lib/active_record/encryption/message_serializer.rb +90 -0
  126. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  127. data/lib/active_record/encryption/properties.rb +76 -0
  128. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  129. data/lib/active_record/encryption/scheme.rb +99 -0
  130. data/lib/active_record/encryption.rb +55 -0
  131. data/lib/active_record/enum.rb +50 -43
  132. data/lib/active_record/errors.rb +67 -4
  133. data/lib/active_record/explain_registry.rb +11 -6
  134. data/lib/active_record/fixture_set/file.rb +15 -1
  135. data/lib/active_record/fixture_set/table_row.rb +41 -6
  136. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  137. data/lib/active_record/fixtures.rb +20 -23
  138. data/lib/active_record/future_result.rb +139 -0
  139. data/lib/active_record/gem_version.rb +4 -4
  140. data/lib/active_record/inheritance.rb +55 -17
  141. data/lib/active_record/insert_all.rb +80 -14
  142. data/lib/active_record/integration.rb +4 -3
  143. data/lib/active_record/internal_metadata.rb +1 -5
  144. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  145. data/lib/active_record/locking/optimistic.rb +10 -9
  146. data/lib/active_record/locking/pessimistic.rb +10 -4
  147. data/lib/active_record/log_subscriber.rb +23 -7
  148. data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
  149. data/lib/active_record/middleware/database_selector.rb +18 -6
  150. data/lib/active_record/middleware/shard_selector.rb +60 -0
  151. data/lib/active_record/migration/command_recorder.rb +7 -7
  152. data/lib/active_record/migration/compatibility.rb +84 -2
  153. data/lib/active_record/migration/join_table.rb +1 -1
  154. data/lib/active_record/migration.rb +114 -83
  155. data/lib/active_record/model_schema.rb +58 -59
  156. data/lib/active_record/nested_attributes.rb +13 -12
  157. data/lib/active_record/no_touching.rb +3 -3
  158. data/lib/active_record/null_relation.rb +2 -6
  159. data/lib/active_record/persistence.rb +228 -60
  160. data/lib/active_record/query_cache.rb +2 -2
  161. data/lib/active_record/query_logs.rb +149 -0
  162. data/lib/active_record/querying.rb +16 -6
  163. data/lib/active_record/railtie.rb +136 -22
  164. data/lib/active_record/railties/controller_runtime.rb +1 -1
  165. data/lib/active_record/railties/databases.rake +78 -136
  166. data/lib/active_record/readonly_attributes.rb +11 -0
  167. data/lib/active_record/reflection.rb +73 -50
  168. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  169. data/lib/active_record/relation/batches.rb +6 -6
  170. data/lib/active_record/relation/calculations.rb +43 -38
  171. data/lib/active_record/relation/delegation.rb +7 -7
  172. data/lib/active_record/relation/finder_methods.rb +31 -35
  173. data/lib/active_record/relation/merger.rb +20 -13
  174. data/lib/active_record/relation/predicate_builder.rb +1 -6
  175. data/lib/active_record/relation/query_attribute.rb +5 -11
  176. data/lib/active_record/relation/query_methods.rb +276 -67
  177. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  178. data/lib/active_record/relation/spawn_methods.rb +2 -2
  179. data/lib/active_record/relation/where_clause.rb +10 -19
  180. data/lib/active_record/relation.rb +189 -88
  181. data/lib/active_record/result.rb +17 -7
  182. data/lib/active_record/runtime_registry.rb +9 -13
  183. data/lib/active_record/sanitization.rb +17 -12
  184. data/lib/active_record/schema.rb +38 -23
  185. data/lib/active_record/schema_dumper.rb +25 -19
  186. data/lib/active_record/schema_migration.rb +4 -4
  187. data/lib/active_record/scoping/default.rb +60 -13
  188. data/lib/active_record/scoping/named.rb +3 -11
  189. data/lib/active_record/scoping.rb +64 -34
  190. data/lib/active_record/serialization.rb +6 -1
  191. data/lib/active_record/signed_id.rb +3 -3
  192. data/lib/active_record/store.rb +1 -1
  193. data/lib/active_record/suppressor.rb +11 -15
  194. data/lib/active_record/tasks/database_tasks.rb +127 -60
  195. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  196. data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -13
  197. data/lib/active_record/test_databases.rb +1 -1
  198. data/lib/active_record/test_fixtures.rb +9 -6
  199. data/lib/active_record/timestamp.rb +3 -4
  200. data/lib/active_record/transactions.rb +9 -14
  201. data/lib/active_record/translation.rb +3 -3
  202. data/lib/active_record/type/adapter_specific_registry.rb +32 -7
  203. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  204. data/lib/active_record/type/internal/timezone.rb +2 -2
  205. data/lib/active_record/type/serialized.rb +1 -1
  206. data/lib/active_record/type/type_map.rb +17 -20
  207. data/lib/active_record/type.rb +1 -2
  208. data/lib/active_record/validations/associated.rb +4 -4
  209. data/lib/active_record/validations/presence.rb +2 -2
  210. data/lib/active_record/validations/uniqueness.rb +4 -4
  211. data/lib/active_record/version.rb +1 -1
  212. data/lib/active_record.rb +225 -27
  213. data/lib/arel/attributes/attribute.rb +0 -8
  214. data/lib/arel/crud.rb +28 -22
  215. data/lib/arel/delete_manager.rb +18 -4
  216. data/lib/arel/filter_predications.rb +9 -0
  217. data/lib/arel/insert_manager.rb +2 -3
  218. data/lib/arel/nodes/casted.rb +1 -1
  219. data/lib/arel/nodes/delete_statement.rb +12 -13
  220. data/lib/arel/nodes/filter.rb +10 -0
  221. data/lib/arel/nodes/function.rb +1 -0
  222. data/lib/arel/nodes/insert_statement.rb +2 -2
  223. data/lib/arel/nodes/select_core.rb +2 -2
  224. data/lib/arel/nodes/select_statement.rb +2 -2
  225. data/lib/arel/nodes/update_statement.rb +8 -3
  226. data/lib/arel/nodes.rb +1 -0
  227. data/lib/arel/predications.rb +11 -3
  228. data/lib/arel/select_manager.rb +10 -4
  229. data/lib/arel/table.rb +0 -1
  230. data/lib/arel/tree_manager.rb +0 -12
  231. data/lib/arel/update_manager.rb +18 -4
  232. data/lib/arel/visitors/dot.rb +80 -90
  233. data/lib/arel/visitors/mysql.rb +8 -2
  234. data/lib/arel/visitors/postgresql.rb +0 -10
  235. data/lib/arel/visitors/to_sql.rb +58 -2
  236. data/lib/arel.rb +2 -1
  237. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  238. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  239. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  240. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  241. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  242. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  243. metadata +53 -9
@@ -5,13 +5,27 @@ module ActiveRecord
5
5
  class BatchEnumerator
6
6
  include Enumerable
7
7
 
8
- def initialize(of: 1000, start: nil, finish: nil, relation:) #:nodoc:
8
+ def initialize(of: 1000, start: nil, finish: nil, relation:) # :nodoc:
9
9
  @of = of
10
10
  @relation = relation
11
11
  @start = start
12
12
  @finish = finish
13
13
  end
14
14
 
15
+ # The primary key value from which the BatchEnumerator starts, inclusive of the value.
16
+ attr_reader :start
17
+
18
+ # The primary key value at which the BatchEnumerator ends, inclusive of the value.
19
+ attr_reader :finish
20
+
21
+ # The relation from which the BatchEnumerator yields batches.
22
+ attr_reader :relation
23
+
24
+ # The size of the batches yielded by the BatchEnumerator.
25
+ def batch_size
26
+ @of
27
+ end
28
+
15
29
  # Looping through a collection of records from the database (using the
16
30
  # +all+ method, for example) is very inefficient since it will try to
17
31
  # instantiate all the objects at once.
@@ -33,11 +47,11 @@ module ActiveRecord
33
47
  # Person.in_batches.each_record.with_index do |person, index|
34
48
  # person.award_trophy(index + 1)
35
49
  # end
36
- def each_record
50
+ def each_record(&block)
37
51
  return to_enum(:each_record) unless block_given?
38
52
 
39
53
  @relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: true).each do |relation|
40
- relation.records.each { |record| yield record }
54
+ relation.records.each(&block)
41
55
  end
42
56
  end
43
57
 
@@ -75,9 +89,9 @@ module ActiveRecord
75
89
  # Person.in_batches.each do |relation|
76
90
  # relation.update_all(awesome: true)
77
91
  # end
78
- def each
92
+ def each(&block)
79
93
  enum = @relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: false)
80
- return enum.each { |relation| yield relation } if block_given?
94
+ return enum.each(&block) if block_given?
81
95
  enum
82
96
  end
83
97
  end
@@ -37,7 +37,7 @@ module ActiveRecord
37
37
  # * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
38
38
  # * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
39
39
  # an order is present in the relation.
40
- # * <tt>:order</tt> - Specifies the primary key order (can be :asc or :desc). Defaults to :asc.
40
+ # * <tt>:order</tt> - Specifies the primary key order (can be +:asc+ or +:desc+). Defaults to +:asc+.
41
41
  #
42
42
  # Limits are honored, and if present there is no requirement for the batch
43
43
  # size: it can be less than, equal to, or greater than the limit.
@@ -65,10 +65,10 @@ module ActiveRecord
65
65
  #
66
66
  # NOTE: By its nature, batch processing is subject to race conditions if
67
67
  # other processes are modifying the database.
68
- def find_each(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil, order: :asc)
68
+ def find_each(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil, order: :asc, &block)
69
69
  if block_given?
70
70
  find_in_batches(start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore, order: order) do |records|
71
- records.each { |record| yield record }
71
+ records.each(&block)
72
72
  end
73
73
  else
74
74
  enum_for(:find_each, start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore, order: order) do
@@ -102,7 +102,7 @@ module ActiveRecord
102
102
  # * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
103
103
  # * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
104
104
  # an order is present in the relation.
105
- # * <tt>:order</tt> - Specifies the primary key order (can be :asc or :desc). Defaults to :asc.
105
+ # * <tt>:order</tt> - Specifies the primary key order (can be +:asc+ or +:desc+). Defaults to +:asc+.
106
106
  #
107
107
  # Limits are honored, and if present there is no requirement for the batch
108
108
  # size: it can be less than, equal to, or greater than the limit.
@@ -167,7 +167,7 @@ module ActiveRecord
167
167
  # * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
168
168
  # * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
169
169
  # an order is present in the relation.
170
- # * <tt>:order</tt> - Specifies the primary key order (can be :asc or :desc). Defaults to :asc.
170
+ # * <tt>:order</tt> - Specifies the primary key order (can be +:asc+ or +:desc+). Defaults to +:asc+.
171
171
  #
172
172
  # Limits are honored, and if present there is no requirement for the batch
173
173
  # size, it can be less than, equal, or greater than the limit.
@@ -284,7 +284,7 @@ module ActiveRecord
284
284
  end
285
285
 
286
286
  def act_on_ignored_order(error_on_ignore)
287
- raise_error = (error_on_ignore.nil? ? klass.error_on_ignored_order : error_on_ignore)
287
+ raise_error = (error_on_ignore.nil? ? ActiveRecord.error_on_ignored_order : error_on_ignore)
288
288
 
289
289
  if raise_error
290
290
  raise ArgumentError.new(ORDER_IGNORE_MESSAGE)
@@ -31,7 +31,7 @@ module ActiveRecord
31
31
  #
32
32
  # Article.group(:status, :category).count
33
33
  # # => {["draft", "business"]=>10, ["draft", "technology"]=>4,
34
- # ["published", "business"]=>0, ["published", "technology"]=>2}
34
+ # # ["published", "business"]=>0, ["published", "technology"]=>2}
35
35
  #
36
36
  # If #count is used with {Relation#select}[rdoc-ref:QueryMethods#select], it will count the selected columns:
37
37
  #
@@ -83,15 +83,24 @@ module ActiveRecord
83
83
  # #calculate for examples with options.
84
84
  #
85
85
  # Person.sum(:age) # => 4562
86
- def sum(column_name = nil)
86
+ def sum(identity_or_column = nil, &block)
87
87
  if block_given?
88
- unless column_name.nil?
89
- raise ArgumentError, "Column name argument is not supported when a block is passed."
88
+ values = map(&block)
89
+ if identity_or_column.nil? && (values.first.is_a?(Numeric) || values.first(1) == [])
90
+ identity_or_column = 0
90
91
  end
91
92
 
92
- super()
93
+ if identity_or_column.nil?
94
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
95
+ Rails 7.0 has deprecated Enumerable.sum in favor of Ruby's native implementation available since 2.4.
96
+ Sum of non-numeric elements requires an initial argument.
97
+ MSG
98
+ values.inject(:+) || 0
99
+ else
100
+ values.sum(identity_or_column)
101
+ end
93
102
  else
94
- calculate(:sum, column_name)
103
+ calculate(:sum, identity_or_column)
95
104
  end
96
105
  end
97
106
 
@@ -146,7 +155,7 @@ module ActiveRecord
146
155
  end
147
156
 
148
157
  # Use #pluck as a shortcut to select one or more attributes without
149
- # loading a bunch of records just to grab the attributes you want.
158
+ # loading an entire record object per row.
150
159
  #
151
160
  # Person.pluck(:name)
152
161
  #
@@ -195,9 +204,9 @@ module ActiveRecord
195
204
  relation.select_values = columns
196
205
  result = skip_query_cache_if_necessary do
197
206
  if where_clause.contradiction?
198
- ActiveRecord::Result.new([], [])
207
+ ActiveRecord::Result.empty
199
208
  else
200
- klass.connection.select_all(relation.arel, nil)
209
+ klass.connection.select_all(relation.arel, "#{klass.name} Pluck")
201
210
  end
202
211
  end
203
212
  type_cast_pluck_values(result, columns)
@@ -286,7 +295,7 @@ module ActiveRecord
286
295
  operation == "count" ? column.count(distinct) : column.public_send(operation)
287
296
  end
288
297
 
289
- def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
298
+ def execute_simple_calculation(operation, column_name, distinct) # :nodoc:
290
299
  if operation == "count" && (column_name == :all && distinct || has_limit_or_offset?)
291
300
  # Shortcut when limit is zero.
292
301
  return 0 if limit_value == 0
@@ -305,29 +314,21 @@ module ActiveRecord
305
314
  query_builder = relation.arel
306
315
  end
307
316
 
308
- result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder) }
317
+ result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder, "#{@klass.name} #{operation.capitalize}") }
309
318
 
310
- type_cast_calculated_value(result.cast_values.first, operation) do |value|
319
+ if operation != "count"
311
320
  type = column.try(:type_caster) ||
312
321
  lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
313
322
  type = type.subtype if Enum::EnumType === type
314
- type.deserialize(value)
315
323
  end
324
+
325
+ type_cast_calculated_value(result.cast_values.first, operation, type)
316
326
  end
317
327
 
318
- def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
328
+ def execute_grouped_calculation(operation, column_name, distinct) # :nodoc:
319
329
  group_fields = group_values
320
330
  group_fields = group_fields.uniq if group_fields.size > 1
321
331
 
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
330
-
331
332
  if group_fields.size == 1 && group_fields.first.respond_to?(:to_sym)
332
333
  association = klass._reflect_on_association(group_fields.first)
333
334
  associated = association && association.belongs_to? # only count belongs_to associations
@@ -344,12 +345,13 @@ module ActiveRecord
344
345
  column = aggregate_column(column_name)
345
346
  column_alias = column_alias_for("#{operation} #{column_name.to_s.downcase}")
346
347
  select_value = operation_over_aggregate_column(column, operation, distinct)
347
- select_value.as(column_alias)
348
+ select_value.as(connection.quote_column_name(column_alias))
348
349
 
349
350
  select_values = [select_value]
350
351
  select_values += self.select_values unless having_clause.empty?
351
352
 
352
353
  select_values.concat group_columns.map { |aliaz, field|
354
+ aliaz = connection.quote_column_name(aliaz)
353
355
  if field.respond_to?(:as)
354
356
  field.as(aliaz)
355
357
  else
@@ -361,7 +363,7 @@ module ActiveRecord
361
363
  relation.group_values = group_fields
362
364
  relation.select_values = select_values
363
365
 
364
- calculated_data = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, nil) }
366
+ calculated_data = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, "#{@klass.name} #{operation.capitalize}") }
365
367
 
366
368
  if association
367
369
  key_ids = calculated_data.collect { |row| row[group_aliases.first] }
@@ -381,20 +383,18 @@ module ActiveRecord
381
383
  end
382
384
  end
383
385
 
384
- type = nil
386
+ if operation != "count"
387
+ type = column.try(:type_caster) ||
388
+ lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
389
+ type = type.subtype if Enum::EnumType === type
390
+ end
391
+
385
392
  hash_rows.each_with_object({}) do |row, result|
386
393
  key = group_aliases.map { |aliaz| row[aliaz] }
387
394
  key = key.first if key.size == 1
388
395
  key = key_records[key] if associated
389
396
 
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
397
+ result[key] = type_cast_calculated_value(row[column_alias], operation, type)
398
398
  end
399
399
  end
400
400
 
@@ -445,16 +445,21 @@ module ActiveRecord
445
445
  result.cast_values(cast_types)
446
446
  end
447
447
 
448
- def type_cast_calculated_value(value, operation)
448
+ def type_cast_calculated_value(value, operation, type)
449
449
  case operation
450
450
  when "count"
451
451
  value.to_i
452
452
  when "sum"
453
- yield value || 0
453
+ type.deserialize(value || 0)
454
454
  when "average"
455
- value&.respond_to?(:to_d) ? value.to_d : value
455
+ case type.type
456
+ when :integer, :decimal
457
+ value&.to_d
458
+ else
459
+ type.deserialize(value)
460
+ end
456
461
  else # "minimum", "maximum"
457
- yield value
462
+ type.deserialize(value)
458
463
  end
459
464
  end
460
465
 
@@ -15,7 +15,8 @@ module ActiveRecord
15
15
  [
16
16
  ActiveRecord::Relation,
17
17
  ActiveRecord::Associations::CollectionProxy,
18
- ActiveRecord::AssociationRelation
18
+ ActiveRecord::AssociationRelation,
19
+ ActiveRecord::DisableJoinsAssociationRelation
19
20
  ].each do |klass|
20
21
  delegate = Class.new(klass) {
21
22
  include ClassSpecificRelation
@@ -61,17 +62,16 @@ module ActiveRecord
61
62
  return if method_defined?(method)
62
63
 
63
64
  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
65
  module_eval <<-RUBY, __FILE__, __LINE__ + 1
66
- def #{method}(#{definition})
67
- scoping { klass.#{method}(#{definition}) }
66
+ def #{method}(...)
67
+ scoping { klass.#{method}(...) }
68
68
  end
69
69
  RUBY
70
70
  else
71
71
  define_method(method) do |*args, &block|
72
72
  scoping { klass.public_send(method, *args, &block) }
73
73
  end
74
- ruby2_keywords(method) if respond_to?(:ruby2_keywords, true)
74
+ ruby2_keywords(method)
75
75
  end
76
76
  end
77
77
  end
@@ -87,7 +87,7 @@ module ActiveRecord
87
87
 
88
88
  delegate :to_xml, :encode_with, :length, :each, :join,
89
89
  :[], :&, :|, :+, :-, :sample, :reverse, :rotate, :compact, :in_groups, :in_groups_of,
90
- :to_sentence, :to_formatted_s, :as_json,
90
+ :to_sentence, :to_fs, :to_formatted_s, :as_json,
91
91
  :shuffle, :split, :slice, :index, :rindex, to: :records
92
92
 
93
93
  delegate :primary_key, :connection, to: :klass
@@ -110,7 +110,7 @@ module ActiveRecord
110
110
  super
111
111
  end
112
112
  end
113
- ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
113
+ ruby2_keywords(:method_missing)
114
114
  end
115
115
 
116
116
  module ClassMethods # :nodoc:
@@ -104,6 +104,32 @@ module ActiveRecord
104
104
  take || raise_record_not_found_exception!
105
105
  end
106
106
 
107
+ # Finds the sole matching record. Raises ActiveRecord::RecordNotFound if no
108
+ # record is found. Raises ActiveRecord::SoleRecordExceeded if more than one
109
+ # record is found.
110
+ #
111
+ # Product.where(["price = %?", price]).sole
112
+ def sole
113
+ found, undesired = first(2)
114
+
115
+ if found.nil?
116
+ raise_record_not_found_exception!
117
+ elsif undesired.present?
118
+ raise ActiveRecord::SoleRecordExceeded.new(self)
119
+ else
120
+ found
121
+ end
122
+ end
123
+
124
+ # Finds the sole matching record. Raises ActiveRecord::RecordNotFound if no
125
+ # record is found. Raises ActiveRecord::SoleRecordExceeded if more than one
126
+ # record is found.
127
+ #
128
+ # Product.find_sole_by(["price = %?", price])
129
+ def find_sole_by(arg, *args)
130
+ where(arg, *args).sole
131
+ end
132
+
107
133
  # Find the first record (or first N records if a parameter is supplied).
108
134
  # If no order is defined it will order by primary key.
109
135
  #
@@ -114,8 +140,6 @@ module ActiveRecord
114
140
  # Person.first(3) # returns the first three objects fetched by SELECT * FROM people ORDER BY people.id LIMIT 3
115
141
  #
116
142
  def first(limit = nil)
117
- check_reorder_deprecation unless loaded?
118
-
119
143
  if limit
120
144
  find_nth_with_limit(0, limit)
121
145
  else
@@ -364,17 +388,6 @@ module ActiveRecord
364
388
  end
365
389
 
366
390
  private
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
376
- end
377
-
378
391
  def construct_relation_for_exists(conditions)
379
392
  conditions = sanitize_forbidden_attributes(conditions)
380
393
 
@@ -400,7 +413,7 @@ module ActiveRecord
400
413
  )
401
414
  relation = except(:includes, :eager_load, :preload).joins!(join_dependency)
402
415
 
403
- if eager_loading && !(
416
+ if eager_loading && has_limit_or_offset? && !(
404
417
  using_limitable_reflections?(join_dependency.reflections) &&
405
418
  using_limitable_reflections?(
406
419
  construct_join_dependency(
@@ -409,12 +422,10 @@ module ActiveRecord
409
422
  ), nil
410
423
  ).reflections
411
424
  )
412
- )
413
- if has_limit_or_offset?
414
- limited_ids = limited_ids_for(relation)
415
- limited_ids.empty? ? relation.none! : relation.where!(primary_key => limited_ids)
425
+ )
426
+ relation = skip_query_cache_if_necessary do
427
+ klass.connection.distinct_relation_for_primary_key(relation)
416
428
  end
417
- relation.limit_value = relation.offset_value = nil
418
429
  end
419
430
 
420
431
  if block_given?
@@ -424,18 +435,6 @@ module ActiveRecord
424
435
  end
425
436
  end
426
437
 
427
- def limited_ids_for(relation)
428
- values = @klass.connection.columns_for_distinct(
429
- connection.visitor.compile(table[primary_key]),
430
- relation.order_values
431
- )
432
-
433
- relation = relation.except(:select).select(values).distinct!
434
-
435
- id_rows = skip_query_cache_if_necessary { @klass.connection.select_rows(relation.arel, "SQL") }
436
- id_rows.map(&:last)
437
- end
438
-
439
438
  def using_limitable_reflections?(reflections)
440
439
  reflections.none?(&:collection?)
441
440
  end
@@ -508,10 +507,7 @@ module ActiveRecord
508
507
  result = except(:limit, :offset).where(primary_key => ids).records
509
508
 
510
509
  if result.size == ids.size
511
- pk_type = @klass.type_for_attribute(primary_key)
512
-
513
- records_by_id = result.index_by(&:id)
514
- ids.map { |id| records_by_id.fetch(pk_type.cast(id)) }
510
+ result.in_order_of(:id, ids.map { |id| @klass.type_for_attribute(primary_key).cast(id) })
515
511
  else
516
512
  raise_record_not_found_exception!(ids, result.size, ids.size)
517
513
  end
@@ -51,30 +51,25 @@ module ActiveRecord
51
51
  @rewhere = rewhere
52
52
  end
53
53
 
54
- NORMAL_VALUES = Relation::VALUE_METHODS -
55
- Relation::CLAUSE_METHODS -
56
- [:includes, :preload, :joins, :left_outer_joins, :order, :reverse_order, :lock, :create_with, :reordering] # :nodoc:
57
-
58
- def normal_values
59
- NORMAL_VALUES
60
- end
54
+ NORMAL_VALUES = Relation::VALUE_METHODS - Relation::CLAUSE_METHODS -
55
+ [
56
+ :select, :includes, :preload, :joins, :left_outer_joins,
57
+ :order, :reverse_order, :lock, :create_with, :reordering
58
+ ]
61
59
 
62
60
  def merge
63
- normal_values.each do |name|
61
+ NORMAL_VALUES.each do |name|
64
62
  value = values[name]
65
63
  # The unless clause is here mostly for performance reasons (since the `send` call might be moderately
66
64
  # expensive), most of the time the value is going to be `nil` or `.blank?`, the only catch is that
67
65
  # `false.blank?` returns `true`, so there needs to be an extra check so that explicit `false` values
68
66
  # don't fall through the cracks.
69
67
  unless value.nil? || (value.blank? && false != value)
70
- if name == :select
71
- relation._select!(*value)
72
- else
73
- relation.public_send("#{name}!", *value)
74
- end
68
+ relation.public_send(:"#{name}!", *value)
75
69
  end
76
70
  end
77
71
 
72
+ merge_select_values
78
73
  merge_multi_values
79
74
  merge_single_values
80
75
  merge_clauses
@@ -86,6 +81,18 @@ module ActiveRecord
86
81
  end
87
82
 
88
83
  private
84
+ def merge_select_values
85
+ return if other.select_values.empty?
86
+
87
+ if other.klass == relation.klass
88
+ relation.select_values |= other.select_values
89
+ else
90
+ relation.select_values |= other.instance_eval do
91
+ arel_columns(select_values)
92
+ end
93
+ end
94
+ end
95
+
89
96
  def merge_preloads
90
97
  return if other.preload_values.empty? && other.includes_values.empty?
91
98
 
@@ -9,10 +9,6 @@ module ActiveRecord
9
9
  require "active_record/relation/predicate_builder/association_query_value"
10
10
  require "active_record/relation/predicate_builder/polymorphic_array_value"
11
11
 
12
- # No-op BaseHandler to work Mashal.load(File.read("legacy_relation.dump")).
13
- # TODO: Remove the constant alias once Rails 6.1 has released.
14
- BaseHandler = BasicObjectHandler
15
-
16
12
  def initialize(table)
17
13
  @table = table
18
14
  @handlers = []
@@ -69,8 +65,7 @@ module ActiveRecord
69
65
  end
70
66
 
71
67
  def build_bind_attribute(column_name, value)
72
- attr = Relation::QueryAttribute.new(column_name, value, table.type(column_name))
73
- Arel::Nodes::BindParam.new(attr)
68
+ Relation::QueryAttribute.new(column_name, value, table.type(column_name))
74
69
  end
75
70
 
76
71
  def resolve_arel_attribute(table_name, column_name, &block)
@@ -20,25 +20,19 @@ module ActiveRecord
20
20
  def nil?
21
21
  unless value_before_type_cast.is_a?(StatementCache::Substitute)
22
22
  value_before_type_cast.nil? ||
23
- type.respond_to?(:subtype, true) && value_for_database.nil?
23
+ type.respond_to?(:subtype) && serializable? && value_for_database.nil?
24
24
  end
25
- rescue ::RangeError
26
25
  end
27
26
 
28
27
  def infinite?
29
- infinity?(value_before_type_cast) || infinity?(value_for_database)
30
- rescue ::RangeError
28
+ infinity?(value_before_type_cast) || serializable? && infinity?(value_for_database)
31
29
  end
32
30
 
33
31
  def unboundable?
34
- if defined?(@_unboundable)
35
- @_unboundable
36
- else
37
- value_for_database unless value_before_type_cast.is_a?(StatementCache::Substitute)
38
- @_unboundable = nil
32
+ unless defined?(@_unboundable)
33
+ serializable? { |value| @_unboundable = value <=> 0 } && @_unboundable = nil
39
34
  end
40
- rescue ::RangeError
41
- @_unboundable = type.cast(value_before_type_cast) <=> 0
35
+ @_unboundable
42
36
  end
43
37
 
44
38
  private