activerecord 6.1.7.8 → 7.0.8.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (251) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1582 -1018
  3. data/README.rdoc +3 -3
  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 +20 -22
  17. data/lib/active_record/associations/collection_proxy.rb +15 -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 +50 -14
  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 +138 -100
  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 +8 -6
  39. data/lib/active_record/attribute_methods/serialization.rb +57 -19
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
  41. data/lib/active_record/attribute_methods/write.rb +7 -10
  42. data/lib/active_record/attribute_methods.rb +19 -22
  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 +14 -16
  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 +52 -23
  58. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +82 -25
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -22
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +144 -82
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +115 -85
  63. data/lib/active_record/connection_adapters/column.rb +4 -0
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +37 -25
  65. data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -23
  66. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +4 -1
  67. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -1
  68. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +20 -1
  69. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
  70. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  71. data/lib/active_record/connection_adapters/postgresql/column.rb +19 -1
  72. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +20 -17
  73. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  74. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  77. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  78. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  81. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  82. data/lib/active_record/connection_adapters/postgresql/quoting.rb +76 -73
  83. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +34 -0
  84. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
  85. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
  86. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
  87. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +40 -21
  88. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  89. data/lib/active_record/connection_adapters/postgresql_adapter.rb +207 -106
  90. data/lib/active_record/connection_adapters/schema_cache.rb +39 -38
  91. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +25 -19
  92. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +33 -18
  93. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +6 -0
  94. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +19 -17
  95. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +98 -36
  96. data/lib/active_record/connection_adapters.rb +6 -5
  97. data/lib/active_record/connection_handling.rb +49 -55
  98. data/lib/active_record/core.rb +123 -148
  99. data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
  100. data/lib/active_record/database_configurations/database_config.rb +12 -9
  101. data/lib/active_record/database_configurations/hash_config.rb +63 -5
  102. data/lib/active_record/database_configurations/url_config.rb +2 -2
  103. data/lib/active_record/database_configurations.rb +15 -32
  104. data/lib/active_record/delegated_type.rb +53 -12
  105. data/lib/active_record/destroy_association_async_job.rb +1 -1
  106. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  107. data/lib/active_record/dynamic_matchers.rb +1 -1
  108. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  109. data/lib/active_record/encryption/cipher.rb +53 -0
  110. data/lib/active_record/encryption/config.rb +44 -0
  111. data/lib/active_record/encryption/configurable.rb +67 -0
  112. data/lib/active_record/encryption/context.rb +35 -0
  113. data/lib/active_record/encryption/contexts.rb +72 -0
  114. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  115. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  116. data/lib/active_record/encryption/encryptable_record.rb +206 -0
  117. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  118. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  119. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  120. data/lib/active_record/encryption/encryptor.rb +155 -0
  121. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  122. data/lib/active_record/encryption/errors.rb +15 -0
  123. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  124. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  125. data/lib/active_record/encryption/key.rb +28 -0
  126. data/lib/active_record/encryption/key_generator.rb +42 -0
  127. data/lib/active_record/encryption/key_provider.rb +46 -0
  128. data/lib/active_record/encryption/message.rb +33 -0
  129. data/lib/active_record/encryption/message_serializer.rb +90 -0
  130. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  131. data/lib/active_record/encryption/properties.rb +76 -0
  132. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  133. data/lib/active_record/encryption/scheme.rb +99 -0
  134. data/lib/active_record/encryption.rb +55 -0
  135. data/lib/active_record/enum.rb +50 -43
  136. data/lib/active_record/errors.rb +67 -4
  137. data/lib/active_record/explain_registry.rb +11 -6
  138. data/lib/active_record/explain_subscriber.rb +1 -1
  139. data/lib/active_record/fixture_set/file.rb +15 -1
  140. data/lib/active_record/fixture_set/table_row.rb +41 -6
  141. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  142. data/lib/active_record/fixtures.rb +20 -23
  143. data/lib/active_record/future_result.rb +139 -0
  144. data/lib/active_record/gem_version.rb +5 -5
  145. data/lib/active_record/inheritance.rb +55 -17
  146. data/lib/active_record/insert_all.rb +80 -14
  147. data/lib/active_record/integration.rb +4 -3
  148. data/lib/active_record/internal_metadata.rb +1 -5
  149. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  150. data/lib/active_record/locking/optimistic.rb +36 -21
  151. data/lib/active_record/locking/pessimistic.rb +10 -4
  152. data/lib/active_record/log_subscriber.rb +23 -7
  153. data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
  154. data/lib/active_record/middleware/database_selector.rb +18 -6
  155. data/lib/active_record/middleware/shard_selector.rb +60 -0
  156. data/lib/active_record/migration/command_recorder.rb +8 -9
  157. data/lib/active_record/migration/compatibility.rb +93 -46
  158. data/lib/active_record/migration/join_table.rb +1 -1
  159. data/lib/active_record/migration.rb +167 -87
  160. data/lib/active_record/model_schema.rb +58 -59
  161. data/lib/active_record/nested_attributes.rb +13 -12
  162. data/lib/active_record/no_touching.rb +3 -3
  163. data/lib/active_record/null_relation.rb +2 -6
  164. data/lib/active_record/persistence.rb +231 -61
  165. data/lib/active_record/query_cache.rb +2 -2
  166. data/lib/active_record/query_logs.rb +149 -0
  167. data/lib/active_record/querying.rb +16 -6
  168. data/lib/active_record/railtie.rb +136 -22
  169. data/lib/active_record/railties/controller_runtime.rb +4 -5
  170. data/lib/active_record/railties/databases.rake +78 -136
  171. data/lib/active_record/readonly_attributes.rb +11 -0
  172. data/lib/active_record/reflection.rb +80 -49
  173. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  174. data/lib/active_record/relation/batches.rb +6 -6
  175. data/lib/active_record/relation/calculations.rb +92 -60
  176. data/lib/active_record/relation/delegation.rb +7 -7
  177. data/lib/active_record/relation/finder_methods.rb +31 -35
  178. data/lib/active_record/relation/merger.rb +20 -13
  179. data/lib/active_record/relation/predicate_builder/association_query_value.rb +20 -1
  180. data/lib/active_record/relation/predicate_builder.rb +1 -6
  181. data/lib/active_record/relation/query_attribute.rb +28 -11
  182. data/lib/active_record/relation/query_methods.rb +304 -68
  183. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  184. data/lib/active_record/relation/spawn_methods.rb +2 -2
  185. data/lib/active_record/relation/where_clause.rb +10 -19
  186. data/lib/active_record/relation.rb +189 -88
  187. data/lib/active_record/result.rb +23 -11
  188. data/lib/active_record/runtime_registry.rb +9 -13
  189. data/lib/active_record/sanitization.rb +17 -12
  190. data/lib/active_record/schema.rb +38 -23
  191. data/lib/active_record/schema_dumper.rb +29 -19
  192. data/lib/active_record/schema_migration.rb +4 -4
  193. data/lib/active_record/scoping/default.rb +60 -13
  194. data/lib/active_record/scoping/named.rb +3 -11
  195. data/lib/active_record/scoping.rb +64 -34
  196. data/lib/active_record/serialization.rb +6 -1
  197. data/lib/active_record/signed_id.rb +3 -3
  198. data/lib/active_record/store.rb +2 -2
  199. data/lib/active_record/suppressor.rb +11 -15
  200. data/lib/active_record/table_metadata.rb +6 -2
  201. data/lib/active_record/tasks/database_tasks.rb +127 -60
  202. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  203. data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -13
  204. data/lib/active_record/test_databases.rb +1 -1
  205. data/lib/active_record/test_fixtures.rb +9 -6
  206. data/lib/active_record/timestamp.rb +3 -4
  207. data/lib/active_record/transactions.rb +12 -17
  208. data/lib/active_record/translation.rb +3 -3
  209. data/lib/active_record/type/adapter_specific_registry.rb +32 -7
  210. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  211. data/lib/active_record/type/internal/timezone.rb +2 -2
  212. data/lib/active_record/type/serialized.rb +9 -5
  213. data/lib/active_record/type/type_map.rb +17 -20
  214. data/lib/active_record/type.rb +1 -2
  215. data/lib/active_record/validations/associated.rb +4 -4
  216. data/lib/active_record/validations/presence.rb +2 -2
  217. data/lib/active_record/validations/uniqueness.rb +4 -4
  218. data/lib/active_record/version.rb +1 -1
  219. data/lib/active_record.rb +225 -27
  220. data/lib/arel/attributes/attribute.rb +0 -8
  221. data/lib/arel/crud.rb +28 -22
  222. data/lib/arel/delete_manager.rb +18 -4
  223. data/lib/arel/filter_predications.rb +9 -0
  224. data/lib/arel/insert_manager.rb +2 -3
  225. data/lib/arel/nodes/and.rb +4 -0
  226. data/lib/arel/nodes/casted.rb +1 -1
  227. data/lib/arel/nodes/delete_statement.rb +12 -13
  228. data/lib/arel/nodes/filter.rb +10 -0
  229. data/lib/arel/nodes/function.rb +1 -0
  230. data/lib/arel/nodes/insert_statement.rb +2 -2
  231. data/lib/arel/nodes/select_core.rb +2 -2
  232. data/lib/arel/nodes/select_statement.rb +2 -2
  233. data/lib/arel/nodes/update_statement.rb +8 -3
  234. data/lib/arel/nodes.rb +1 -0
  235. data/lib/arel/predications.rb +11 -3
  236. data/lib/arel/select_manager.rb +10 -4
  237. data/lib/arel/table.rb +0 -1
  238. data/lib/arel/tree_manager.rb +0 -12
  239. data/lib/arel/update_manager.rb +18 -4
  240. data/lib/arel/visitors/dot.rb +80 -90
  241. data/lib/arel/visitors/mysql.rb +8 -2
  242. data/lib/arel/visitors/postgresql.rb +0 -10
  243. data/lib/arel/visitors/to_sql.rb +58 -2
  244. data/lib/arel.rb +2 -1
  245. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  246. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  247. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  248. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  249. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  250. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  251. 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)
@@ -4,6 +4,47 @@ require "active_support/core_ext/enumerable"
4
4
 
5
5
  module ActiveRecord
6
6
  module Calculations
7
+ class ColumnAliasTracker # :nodoc:
8
+ def initialize(connection)
9
+ @connection = connection
10
+ @aliases = Hash.new(0)
11
+ end
12
+
13
+ def alias_for(field)
14
+ aliased_name = column_alias_for(field)
15
+
16
+ if @aliases[aliased_name] == 0
17
+ @aliases[aliased_name] = 1
18
+ aliased_name
19
+ else
20
+ # Update the count
21
+ count = @aliases[aliased_name] += 1
22
+ "#{truncate(aliased_name)}_#{count}"
23
+ end
24
+ end
25
+
26
+ private
27
+ # Converts the given field to the value that the database adapter returns as
28
+ # a usable column name:
29
+ #
30
+ # column_alias_for("users.id") # => "users_id"
31
+ # column_alias_for("sum(id)") # => "sum_id"
32
+ # column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
33
+ # column_alias_for("count(*)") # => "count_all"
34
+ def column_alias_for(field)
35
+ column_alias = +field
36
+ column_alias.gsub!(/\*/, "all")
37
+ column_alias.gsub!(/\W+/, " ")
38
+ column_alias.strip!
39
+ column_alias.gsub!(/ +/, "_")
40
+ @connection.table_alias_for(column_alias)
41
+ end
42
+
43
+ def truncate(name)
44
+ name.slice(0, @connection.table_alias_length - 2)
45
+ end
46
+ end
47
+
7
48
  # Count the records.
8
49
  #
9
50
  # Person.count
@@ -31,7 +72,7 @@ module ActiveRecord
31
72
  #
32
73
  # Article.group(:status, :category).count
33
74
  # # => {["draft", "business"]=>10, ["draft", "technology"]=>4,
34
- # ["published", "business"]=>0, ["published", "technology"]=>2}
75
+ # # ["published", "business"]=>0, ["published", "technology"]=>2}
35
76
  #
36
77
  # If #count is used with {Relation#select}[rdoc-ref:QueryMethods#select], it will count the selected columns:
37
78
  #
@@ -83,15 +124,24 @@ module ActiveRecord
83
124
  # #calculate for examples with options.
84
125
  #
85
126
  # Person.sum(:age) # => 4562
86
- def sum(column_name = nil)
127
+ def sum(identity_or_column = nil, &block)
87
128
  if block_given?
88
- unless column_name.nil?
89
- raise ArgumentError, "Column name argument is not supported when a block is passed."
129
+ values = map(&block)
130
+ if identity_or_column.nil? && (values.first.is_a?(Numeric) || values.first(1) == [] || values.first.respond_to?(:coerce))
131
+ identity_or_column = 0
90
132
  end
91
133
 
92
- super()
134
+ if identity_or_column.nil?
135
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
136
+ Rails 7.0 has deprecated Enumerable.sum in favor of Ruby's native implementation available since 2.4.
137
+ Sum of non-numeric elements requires an initial argument.
138
+ MSG
139
+ values.inject(:+) || 0
140
+ else
141
+ values.sum(identity_or_column)
142
+ end
93
143
  else
94
- calculate(:sum, column_name)
144
+ calculate(:sum, identity_or_column)
95
145
  end
96
146
  end
97
147
 
@@ -146,7 +196,7 @@ module ActiveRecord
146
196
  end
147
197
 
148
198
  # 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.
199
+ # loading an entire record object per row.
150
200
  #
151
201
  # Person.pluck(:name)
152
202
  #
@@ -195,9 +245,9 @@ module ActiveRecord
195
245
  relation.select_values = columns
196
246
  result = skip_query_cache_if_necessary do
197
247
  if where_clause.contradiction?
198
- ActiveRecord::Result.new([], [])
248
+ ActiveRecord::Result.empty
199
249
  else
200
- klass.connection.select_all(relation.arel, nil)
250
+ klass.connection.select_all(relation.arel, "#{klass.name} Pluck")
201
251
  end
202
252
  end
203
253
  type_cast_pluck_values(result, columns)
@@ -286,7 +336,7 @@ module ActiveRecord
286
336
  operation == "count" ? column.count(distinct) : column.public_send(operation)
287
337
  end
288
338
 
289
- def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
339
+ def execute_simple_calculation(operation, column_name, distinct) # :nodoc:
290
340
  if operation == "count" && (column_name == :all && distinct || has_limit_or_offset?)
291
341
  # Shortcut when limit is zero.
292
342
  return 0 if limit_value == 0
@@ -305,29 +355,21 @@ module ActiveRecord
305
355
  query_builder = relation.arel
306
356
  end
307
357
 
308
- result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder) }
358
+ result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder, "#{@klass.name} #{operation.capitalize}") }
309
359
 
310
- type_cast_calculated_value(result.cast_values.first, operation) do |value|
360
+ if operation != "count"
311
361
  type = column.try(:type_caster) ||
312
362
  lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
313
363
  type = type.subtype if Enum::EnumType === type
314
- type.deserialize(value)
315
364
  end
365
+
366
+ type_cast_calculated_value(result.cast_values.first, operation, type)
316
367
  end
317
368
 
318
- def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
369
+ def execute_grouped_calculation(operation, column_name, distinct) # :nodoc:
319
370
  group_fields = group_values
320
371
  group_fields = group_fields.uniq if group_fields.size > 1
321
372
 
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
373
  if group_fields.size == 1 && group_fields.first.respond_to?(:to_sym)
332
374
  association = klass._reflect_on_association(group_fields.first)
333
375
  associated = association && association.belongs_to? # only count belongs_to associations
@@ -335,21 +377,24 @@ module ActiveRecord
335
377
  end
336
378
  group_fields = arel_columns(group_fields)
337
379
 
380
+ column_alias_tracker = ColumnAliasTracker.new(connection)
381
+
338
382
  group_aliases = group_fields.map { |field|
339
383
  field = connection.visitor.compile(field) if Arel.arel_node?(field)
340
- column_alias_for(field.to_s.downcase)
384
+ column_alias_tracker.alias_for(field.to_s.downcase)
341
385
  }
342
386
  group_columns = group_aliases.zip(group_fields)
343
387
 
344
388
  column = aggregate_column(column_name)
345
- column_alias = column_alias_for("#{operation} #{column_name.to_s.downcase}")
389
+ column_alias = column_alias_tracker.alias_for("#{operation} #{column_name.to_s.downcase}")
346
390
  select_value = operation_over_aggregate_column(column, operation, distinct)
347
- select_value.as(column_alias)
391
+ select_value.as(connection.quote_column_name(column_alias))
348
392
 
349
393
  select_values = [select_value]
350
394
  select_values += self.select_values unless having_clause.empty?
351
395
 
352
396
  select_values.concat group_columns.map { |aliaz, field|
397
+ aliaz = connection.quote_column_name(aliaz)
353
398
  if field.respond_to?(:as)
354
399
  field.as(aliaz)
355
400
  else
@@ -361,7 +406,7 @@ module ActiveRecord
361
406
  relation.group_values = group_fields
362
407
  relation.select_values = select_values
363
408
 
364
- calculated_data = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, nil) }
409
+ calculated_data = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, "#{@klass.name} #{operation.capitalize}") }
365
410
 
366
411
  if association
367
412
  key_ids = calculated_data.collect { |row| row[group_aliases.first] }
@@ -370,9 +415,10 @@ module ActiveRecord
370
415
  end
371
416
 
372
417
  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
418
+ types[aliaz] = col_name.try(:type_caster) ||
419
+ type_for(col_name) do
420
+ calculated_data.column_types.fetch(aliaz, Type.default_value)
421
+ end
376
422
  end
377
423
 
378
424
  hash_rows = calculated_data.cast_values(key_types).map! do |row|
@@ -381,40 +427,21 @@ module ActiveRecord
381
427
  end
382
428
  end
383
429
 
384
- type = nil
430
+ if operation != "count"
431
+ type = column.try(:type_caster) ||
432
+ lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
433
+ type = type.subtype if Enum::EnumType === type
434
+ end
435
+
385
436
  hash_rows.each_with_object({}) do |row, result|
386
437
  key = group_aliases.map { |aliaz| row[aliaz] }
387
438
  key = key.first if key.size == 1
388
439
  key = key_records[key] if associated
389
440
 
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
441
+ result[key] = type_cast_calculated_value(row[column_alias], operation, type)
398
442
  end
399
443
  end
400
444
 
401
- # Converts the given field to the value that the database adapter returns as
402
- # a usable column name:
403
- #
404
- # column_alias_for("users.id") # => "users_id"
405
- # column_alias_for("sum(id)") # => "sum_id"
406
- # column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
407
- # column_alias_for("count(*)") # => "count_all"
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)
416
- end
417
-
418
445
  def type_for(field, &block)
419
446
  field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split(".").last
420
447
  @klass.type_for_attribute(field_name, &block)
@@ -445,16 +472,21 @@ module ActiveRecord
445
472
  result.cast_values(cast_types)
446
473
  end
447
474
 
448
- def type_cast_calculated_value(value, operation)
475
+ def type_cast_calculated_value(value, operation, type)
449
476
  case operation
450
477
  when "count"
451
478
  value.to_i
452
479
  when "sum"
453
- yield value || 0
480
+ type.deserialize(value || 0)
454
481
  when "average"
455
- value&.respond_to?(:to_d) ? value.to_d : value
482
+ case type.type
483
+ when :integer, :decimal
484
+ value&.to_d
485
+ else
486
+ type.deserialize(value)
487
+ end
456
488
  else # "minimum", "maximum"
457
- yield value
489
+ type.deserialize(value)
458
490
  end
459
491
  end
460
492
 
@@ -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
 
@@ -18,7 +18,10 @@ module ActiveRecord
18
18
  def ids
19
19
  case value
20
20
  when Relation
21
- value.select_values.empty? ? value.select(primary_key) : value
21
+ relation = value
22
+ relation = relation.select(primary_key) if select_clause?
23
+ relation = relation.where(primary_type => polymorphic_name) if polymorphic_clause?
24
+ relation
22
25
  when Array
23
26
  value.map { |v| convert_to_id(v) }
24
27
  else
@@ -30,6 +33,22 @@ module ActiveRecord
30
33
  associated_table.join_primary_key
31
34
  end
32
35
 
36
+ def primary_type
37
+ associated_table.join_primary_type
38
+ end
39
+
40
+ def polymorphic_name
41
+ associated_table.polymorphic_name_association
42
+ end
43
+
44
+ def select_clause?
45
+ value.select_values.empty?
46
+ end
47
+
48
+ def polymorphic_clause?
49
+ primary_type && !value.where_values_hash.has_key?(primary_type)
50
+ end
51
+
33
52
  def convert_to_id(value)
34
53
  if value.respond_to?(primary_key)
35
54
  value.public_send(primary_key)
@@ -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)