activerecord 8.0.0.beta1 → 8.0.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +67 -0
  3. data/lib/active_record/associations/has_many_through_association.rb +7 -1
  4. data/lib/active_record/attribute_methods/primary_key.rb +2 -7
  5. data/lib/active_record/attribute_methods/time_zone_conversion.rb +2 -12
  6. data/lib/active_record/callbacks.rb +1 -1
  7. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +26 -8
  8. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
  9. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +6 -1
  10. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +8 -2
  11. data/lib/active_record/connection_adapters/abstract_adapter.rb +0 -1
  12. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +8 -4
  13. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  14. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
  15. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +0 -10
  16. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -11
  17. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +1 -1
  18. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +4 -8
  19. data/lib/active_record/connection_adapters/postgresql_adapter.rb +8 -8
  20. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +1 -1
  21. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +0 -2
  22. data/lib/active_record/connection_adapters.rb +0 -56
  23. data/lib/active_record/core.rb +14 -11
  24. data/lib/active_record/enum.rb +54 -72
  25. data/lib/active_record/fixtures.rb +0 -1
  26. data/lib/active_record/gem_version.rb +1 -1
  27. data/lib/active_record/locking/optimistic.rb +1 -1
  28. data/lib/active_record/log_subscriber.rb +5 -11
  29. data/lib/active_record/marshalling.rb +4 -1
  30. data/lib/active_record/migration.rb +0 -5
  31. data/lib/active_record/model_schema.rb +2 -3
  32. data/lib/active_record/query_cache.rb +0 -4
  33. data/lib/active_record/query_logs.rb +5 -11
  34. data/lib/active_record/querying.rb +2 -2
  35. data/lib/active_record/railtie.rb +1 -24
  36. data/lib/active_record/railties/databases.rake +1 -1
  37. data/lib/active_record/reflection.rb +14 -19
  38. data/lib/active_record/relation/calculations.rb +24 -28
  39. data/lib/active_record/relation/predicate_builder.rb +8 -0
  40. data/lib/active_record/relation/query_methods.rb +66 -36
  41. data/lib/active_record/relation.rb +8 -1
  42. data/lib/active_record/result.rb +10 -9
  43. data/lib/active_record/table_metadata.rb +1 -3
  44. data/lib/active_record/tasks/database_tasks.rb +4 -31
  45. data/lib/active_record/testing/query_assertions.rb +2 -2
  46. data/lib/active_record.rb +0 -45
  47. data/lib/arel/table.rb +3 -7
  48. data/lib/arel/visitors/sqlite.rb +25 -0
  49. metadata +9 -10
  50. data/lib/active_record/relation/record_fetch_warning.rb +0 -52
@@ -370,7 +370,7 @@ module ActiveRecord
370
370
  end
371
371
 
372
372
  def predicate_builder # :nodoc:
373
- @predicate_builder ||= PredicateBuilder.new(table_metadata)
373
+ @predicate_builder ||= PredicateBuilder.new(TableMetadata.new(self, arel_table))
374
374
  end
375
375
 
376
376
  def type_caster # :nodoc:
@@ -415,10 +415,6 @@ module ActiveRecord
415
415
  end
416
416
  end
417
417
 
418
- def table_metadata
419
- TableMetadata.new(self, arel_table)
420
- end
421
-
422
418
  def cached_find_by(keys, values)
423
419
  with_connection do |connection|
424
420
  statement = cached_find_by_statement(connection, keys) { |params|
@@ -529,12 +525,7 @@ module ActiveRecord
529
525
 
530
526
  ##
531
527
  def initialize_dup(other) # :nodoc:
532
- @attributes = @attributes.deep_dup
533
- if self.class.composite_primary_key?
534
- @primary_key.each { |key| @attributes.reset(key) }
535
- else
536
- @attributes.reset(@primary_key)
537
- end
528
+ @attributes = init_attributes(other)
538
529
 
539
530
  _run_initialize_callbacks
540
531
 
@@ -546,6 +537,18 @@ module ActiveRecord
546
537
  super
547
538
  end
548
539
 
540
+ def init_attributes(_) # :nodoc:
541
+ attrs = @attributes.deep_dup
542
+
543
+ if self.class.composite_primary_key?
544
+ @primary_key.each { |key| attrs.reset(key) }
545
+ else
546
+ attrs.reset(@primary_key)
547
+ end
548
+
549
+ attrs
550
+ end
551
+
549
552
  # Populate +coder+ with attributes about this record that should be
550
553
  # serialized. The structure of +coder+ defined in this method is
551
554
  # guaranteed to match the structure of +coder+ passed to the #init_with
@@ -213,95 +213,77 @@ module ActiveRecord
213
213
  attr_reader :name, :mapping
214
214
  end
215
215
 
216
- def enum(name = nil, values = nil, **options)
217
- if name
218
- values, options = options, {} unless values
219
- return _enum(name, values, **options)
220
- end
221
-
222
- definitions = options.slice!(:_prefix, :_suffix, :_scopes, :_default, :_instance_methods)
223
- options.transform_keys! { |key| :"#{key[1..-1]}" }
224
-
225
- definitions.each { |name, values| _enum(name, values, **options) }
226
-
227
- ActiveRecord.deprecator.warn(<<~MSG)
228
- Defining enums with keyword arguments is deprecated and will be removed
229
- in Rails 8.0. Positional arguments should be used instead:
216
+ def enum(name, values, prefix: nil, suffix: nil, scopes: true, instance_methods: true, validate: false, **options)
217
+ assert_valid_enum_definition_values(values)
218
+ assert_valid_enum_options(options)
230
219
 
231
- #{definitions.map { |name, values| "enum :#{name}, #{values}" }.join("\n")}
232
- MSG
233
- end
234
-
235
- private
236
- def inherited(base)
237
- base.defined_enums = defined_enums.deep_dup
238
- super
239
- end
220
+ # statuses = { }
221
+ enum_values = ActiveSupport::HashWithIndifferentAccess.new
222
+ name = name.to_s
240
223
 
241
- def _enum(name, values, prefix: nil, suffix: nil, scopes: true, instance_methods: true, validate: false, **options)
242
- assert_valid_enum_definition_values(values)
243
- assert_valid_enum_options(options)
244
- # statuses = { }
245
- enum_values = ActiveSupport::HashWithIndifferentAccess.new
246
- name = name.to_s
224
+ # def self.statuses() statuses end
225
+ detect_enum_conflict!(name, name.pluralize, true)
226
+ singleton_class.define_method(name.pluralize) { enum_values }
227
+ defined_enums[name] = enum_values
247
228
 
248
- # def self.statuses() statuses end
249
- detect_enum_conflict!(name, name.pluralize, true)
250
- singleton_class.define_method(name.pluralize) { enum_values }
251
- defined_enums[name] = enum_values
229
+ detect_enum_conflict!(name, name)
230
+ detect_enum_conflict!(name, "#{name}=")
252
231
 
253
- detect_enum_conflict!(name, name)
254
- detect_enum_conflict!(name, "#{name}=")
232
+ attribute(name, **options)
255
233
 
256
- attribute(name, **options)
234
+ decorate_attributes([name]) do |_name, subtype|
235
+ if subtype == ActiveModel::Type.default_value
236
+ raise "Undeclared attribute type for enum '#{name}' in #{self.name}. Enums must be" \
237
+ " backed by a database column or declared with an explicit type" \
238
+ " via `attribute`."
239
+ end
257
240
 
258
- decorate_attributes([name]) do |_name, subtype|
259
- if subtype == ActiveModel::Type.default_value
260
- raise "Undeclared attribute type for enum '#{name}' in #{self.name}. Enums must be" \
261
- " backed by a database column or declared with an explicit type" \
262
- " via `attribute`."
263
- end
241
+ subtype = subtype.subtype if EnumType === subtype
242
+ EnumType.new(name, enum_values, subtype, raise_on_invalid_values: !validate)
243
+ end
264
244
 
265
- subtype = subtype.subtype if EnumType === subtype
266
- EnumType.new(name, enum_values, subtype, raise_on_invalid_values: !validate)
245
+ value_method_names = []
246
+ _enum_methods_module.module_eval do
247
+ prefix = if prefix
248
+ prefix == true ? "#{name}_" : "#{prefix}_"
267
249
  end
268
250
 
269
- value_method_names = []
270
- _enum_methods_module.module_eval do
271
- prefix = if prefix
272
- prefix == true ? "#{name}_" : "#{prefix}_"
273
- end
274
-
275
- suffix = if suffix
276
- suffix == true ? "_#{name}" : "_#{suffix}"
277
- end
251
+ suffix = if suffix
252
+ suffix == true ? "_#{name}" : "_#{suffix}"
253
+ end
278
254
 
279
- pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
280
- pairs.each do |label, value|
281
- enum_values[label] = value
282
- label = label.to_s
255
+ pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
256
+ pairs.each do |label, value|
257
+ enum_values[label] = value
258
+ label = label.to_s
283
259
 
284
- value_method_name = "#{prefix}#{label}#{suffix}"
285
- value_method_names << value_method_name
286
- define_enum_methods(name, value_method_name, value, scopes, instance_methods)
260
+ value_method_name = "#{prefix}#{label}#{suffix}"
261
+ value_method_names << value_method_name
262
+ define_enum_methods(name, value_method_name, value, scopes, instance_methods)
287
263
 
288
- method_friendly_label = label.gsub(/[\W&&[:ascii:]]+/, "_")
289
- value_method_alias = "#{prefix}#{method_friendly_label}#{suffix}"
264
+ method_friendly_label = label.gsub(/[\W&&[:ascii:]]+/, "_")
265
+ value_method_alias = "#{prefix}#{method_friendly_label}#{suffix}"
290
266
 
291
- if value_method_alias != value_method_name && !value_method_names.include?(value_method_alias)
292
- value_method_names << value_method_alias
293
- define_enum_methods(name, value_method_alias, value, scopes, instance_methods)
294
- end
267
+ if value_method_alias != value_method_name && !value_method_names.include?(value_method_alias)
268
+ value_method_names << value_method_alias
269
+ define_enum_methods(name, value_method_alias, value, scopes, instance_methods)
295
270
  end
296
271
  end
297
- detect_negative_enum_conditions!(value_method_names) if scopes
272
+ end
273
+ detect_negative_enum_conditions!(value_method_names) if scopes
298
274
 
299
- if validate
300
- validate = {} unless Hash === validate
301
- validates_inclusion_of name, in: enum_values.keys, **validate
302
- end
275
+ if validate
276
+ validate = {} unless Hash === validate
277
+ validates_inclusion_of name, in: enum_values.keys, **validate
278
+ end
279
+
280
+ enum_values.freeze
281
+ end
303
282
 
304
- enum_values.freeze
283
+ private
284
+ def inherited(base)
285
+ base.defined_enums = defined_enums.deep_dup
286
+ super
305
287
  end
306
288
 
307
289
  class EnumMethods < Module # :nodoc:
@@ -2,7 +2,6 @@
2
2
 
3
3
  require "erb"
4
4
  require "yaml"
5
- require "set"
6
5
  require "active_support/dependencies"
7
6
  require "active_support/core_ext/digest/uuid"
8
7
  require "active_record/test_fixtures"
@@ -10,7 +10,7 @@ module ActiveRecord
10
10
  MAJOR = 8
11
11
  MINOR = 0
12
12
  TINY = 0
13
- PRE = "beta1"
13
+ PRE = "rc1"
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -9,7 +9,7 @@ module ActiveRecord
9
9
  # it was opened, an ActiveRecord::StaleObjectError exception is thrown if that has occurred
10
10
  # and the update is ignored.
11
11
  #
12
- # Check out +ActiveRecord::Locking::Pessimistic+ for an alternative.
12
+ # Check out ActiveRecord::Locking::Pessimistic for an alternative.
13
13
  #
14
14
  # == Usage
15
15
  #
@@ -126,18 +126,12 @@ module ActiveRecord
126
126
  end
127
127
  end
128
128
 
129
- if Thread.respond_to?(:each_caller_location)
130
- def query_source_location
131
- Thread.each_caller_location do |location|
132
- frame = backtrace_cleaner.clean_frame(location)
133
- return frame if frame
134
- end
135
- nil
136
- end
137
- else
138
- def query_source_location
139
- backtrace_cleaner.clean(caller(1).lazy).first
129
+ def query_source_location
130
+ Thread.each_caller_location do |location|
131
+ frame = backtrace_cleaner.clean_frame(location)
132
+ return frame if frame
140
133
  end
134
+ nil
141
135
  end
142
136
 
143
137
  def filter(name, value)
@@ -25,7 +25,10 @@ module ActiveRecord
25
25
  payload = [attributes_for_database, new_record?]
26
26
 
27
27
  cached_associations = self.class.reflect_on_all_associations.select do |reflection|
28
- association_cached?(reflection.name) && association(reflection.name).loaded?
28
+ if association_cached?(reflection.name)
29
+ association = association(reflection.name)
30
+ association.loaded? || association.target.present?
31
+ end
29
32
  end
30
33
 
31
34
  unless cached_associations.empty?
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "set"
4
3
  require "active_support/core_ext/array/access"
5
4
  require "active_support/core_ext/enumerable"
6
5
  require "active_support/core_ext/module/attribute_accessors"
@@ -679,10 +678,6 @@ module ActiveRecord
679
678
  paths = all_configs.flat_map { |config| config.migrations_paths || Migrator.migrations_paths }.uniq
680
679
  @file_watcher.new([], paths.index_with(["rb"]), &block)
681
680
  end
682
-
683
- def connection
684
- ActiveRecord::Tasks::DatabaseTasks.migration_connection
685
- end
686
681
  end
687
682
 
688
683
  class << self
@@ -276,15 +276,14 @@ module ActiveRecord
276
276
  end
277
277
 
278
278
  @table_name = value
279
- @quoted_table_name = nil
280
279
  @arel_table = nil
281
280
  @sequence_name = nil unless @explicit_sequence_name
282
281
  @predicate_builder = nil
283
282
  end
284
283
 
285
- # Returns a quoted version of the table name, used to construct SQL statements.
284
+ # Returns a quoted version of the table name.
286
285
  def quoted_table_name
287
- @quoted_table_name ||= adapter_class.quote_table_name(table_name)
286
+ adapter_class.quote_table_name(table_name)
288
287
  end
289
288
 
290
289
  # Computes the table name, (re)sets it internally, and returns it.
@@ -43,10 +43,6 @@ module ActiveRecord
43
43
  pool.disable_query_cache!
44
44
  pool.clear_query_cache
45
45
  end
46
-
47
- ActiveRecord::Base.connection_handler.each_connection_pool do |pool|
48
- pool.release_connection if pool.active_connection? && !pool.lease_connection.transaction_open?
49
- end
50
46
  end
51
47
 
52
48
  def self.install_executor_hooks(executor = ActiveSupport::Executor)
@@ -152,18 +152,12 @@ module ActiveRecord
152
152
  self.cached_comment = nil
153
153
  end
154
154
 
155
- if Thread.respond_to?(:each_caller_location)
156
- def query_source_location # :nodoc:
157
- Thread.each_caller_location do |location|
158
- frame = LogSubscriber.backtrace_cleaner.clean_frame(location.path)
159
- return frame if frame
160
- end
161
- nil
162
- end
163
- else
164
- def query_source_location # :nodoc:
165
- LogSubscriber.backtrace_cleaner.clean(caller_locations(1).each).first
155
+ def query_source_location # :nodoc:
156
+ Thread.each_caller_location do |location|
157
+ frame = LogSubscriber.backtrace_cleaner.clean_frame(location)
158
+ return frame if frame
166
159
  end
160
+ nil
167
161
  end
168
162
 
169
163
  ActiveSupport::ExecutionContext.after_change { ActiveRecord::QueryLogs.clear_cache }
@@ -86,10 +86,10 @@ module ActiveRecord
86
86
 
87
87
  message_bus.instrument("instantiation.active_record", payload) do
88
88
  if result_set.includes_column?(inheritance_column)
89
- result_set.map { |record| instantiate(record, column_types, &block) }
89
+ result_set.indexed_rows.map { |record| instantiate(record, column_types, &block) }
90
90
  else
91
91
  # Instantiate a homogeneous set
92
- result_set.map { |record| instantiate_instance_of(self, record, column_types, &block) }
92
+ result_set.indexed_rows.map { |record| instantiate_instance_of(self, record, column_types, &block) }
93
93
  end
94
94
  end
95
95
  end
@@ -184,30 +184,6 @@ To keep using the current cache store, you can turn off cache versioning entirel
184
184
  end
185
185
  end
186
186
 
187
- initializer "active_record.warn_on_records_fetched_greater_than" do
188
- if config.active_record.warn_on_records_fetched_greater_than
189
- ActiveRecord.deprecator.warn <<~MSG.squish
190
- `config.active_record.warn_on_records_fetched_greater_than` is deprecated and will be
191
- removed in Rails 8.0.
192
- Please subscribe to `sql.active_record` notifications and access the row count field to
193
- detect large result set sizes.
194
- MSG
195
- ActiveSupport.on_load(:active_record) do
196
- require "active_record/relation/record_fetch_warning"
197
- end
198
- end
199
- end
200
-
201
- initializer "active_record.sqlite3_deprecated_warning" do
202
- if config.active_record.key?(:sqlite3_production_warning)
203
- config.active_record.delete(:sqlite3_production_warning)
204
- ActiveRecord.deprecator.warn <<~MSG.squish
205
- The `config.active_record.sqlite3_production_warning` configuration no longer has any effect
206
- and can be safely removed.
207
- MSG
208
- end
209
- end
210
-
211
187
  initializer "active_record.sqlite3_adapter_strict_strings_by_default" do
212
188
  config.after_initialize do
213
189
  if config.active_record.sqlite3_adapter_strict_strings_by_default
@@ -312,6 +288,7 @@ To keep using the current cache store, you can turn off cache versioning entirel
312
288
  initializer "active_record.set_executor_hooks" do
313
289
  ActiveRecord::QueryCache.install_executor_hooks
314
290
  ActiveRecord::AsynchronousQueriesTracker.install_executor_hooks
291
+ ActiveRecord::ConnectionAdapters::ConnectionPool.install_executor_hooks
315
292
  end
316
293
 
317
294
  initializer "active_record.add_watchable_files" do |app|
@@ -176,7 +176,7 @@ db_namespace = namespace :db do
176
176
  end
177
177
 
178
178
  # desc 'Resets your database using your migrations for the current environment'
179
- task reset: ["db:drop", "db:create", "db:migrate"]
179
+ task reset: ["db:drop", "db:create", "db:schema:dump", "db:migrate"]
180
180
 
181
181
  desc 'Run the "up" for a given migration VERSION.'
182
182
  task up: :load_config do
@@ -198,7 +198,7 @@ module ActiveRecord
198
198
  end
199
199
 
200
200
  def join_scope(table, foreign_table, foreign_klass)
201
- predicate_builder = predicate_builder(table)
201
+ predicate_builder = klass.predicate_builder.with(TableMetadata.new(klass, table))
202
202
  scope_chain_items = join_scopes(table, predicate_builder)
203
203
  klass_scope = klass_join_scope(table, predicate_builder)
204
204
 
@@ -224,7 +224,7 @@ module ActiveRecord
224
224
  klass_scope
225
225
  end
226
226
 
227
- def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc:
227
+ def join_scopes(table, predicate_builder = nil, klass = self.klass, record = nil) # :nodoc:
228
228
  if scope
229
229
  [scope_for(build_scope(table, predicate_builder, klass), record)]
230
230
  else
@@ -232,7 +232,7 @@ module ActiveRecord
232
232
  end
233
233
  end
234
234
 
235
- def klass_join_scope(table, predicate_builder) # :nodoc:
235
+ def klass_join_scope(table, predicate_builder = nil) # :nodoc:
236
236
  relation = build_scope(table, predicate_builder)
237
237
  klass.scope_for_association(relation)
238
238
  end
@@ -333,12 +333,8 @@ module ActiveRecord
333
333
  collect_join_chain
334
334
  end
335
335
 
336
- def build_scope(table, predicate_builder = predicate_builder(table), klass = self.klass)
337
- Relation.create(
338
- klass,
339
- table: table,
340
- predicate_builder: predicate_builder
341
- )
336
+ def build_scope(table, predicate_builder = nil, klass = self.klass)
337
+ Relation.create(klass, table:, predicate_builder:)
342
338
  end
343
339
 
344
340
  def strict_loading?
@@ -357,10 +353,6 @@ module ActiveRecord
357
353
  end
358
354
 
359
355
  private
360
- def predicate_builder(table)
361
- PredicateBuilder.new(TableMetadata.new(klass, table))
362
- end
363
-
364
356
  def primary_key(klass)
365
357
  klass.primary_key || raise(UnknownPrimaryKey.new(klass))
366
358
  end
@@ -531,9 +523,9 @@ module ActiveRecord
531
523
  @association_foreign_key = nil
532
524
  @association_primary_key = nil
533
525
  if options[:query_constraints]
534
- ActiveRecord.deprecator.warn <<~MSG.squish
535
- Setting `query_constraints:` option on `#{active_record}.#{macro} :#{name}` is deprecated.
536
- To maintain current behavior, use the `foreign_key` option instead.
526
+ raise ConfigurationError, <<~MSG.squish
527
+ Setting `query_constraints:` option on `#{active_record}.#{macro} :#{name}` is not allowed.
528
+ To get the same behavior, use the `foreign_key` option instead.
537
529
  MSG
538
530
  end
539
531
 
@@ -1070,7 +1062,7 @@ module ActiveRecord
1070
1062
  source_reflection.scopes + super
1071
1063
  end
1072
1064
 
1073
- def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc:
1065
+ def join_scopes(table, predicate_builder = nil, klass = self.klass, record = nil) # :nodoc:
1074
1066
  source_reflection.join_scopes(table, predicate_builder, klass, record) + super
1075
1067
  end
1076
1068
 
@@ -1243,8 +1235,11 @@ module ActiveRecord
1243
1235
  @previous_reflection = previous_reflection
1244
1236
  end
1245
1237
 
1246
- def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc:
1247
- scopes = @previous_reflection.join_scopes(table, predicate_builder, klass, record) + super
1238
+ def join_scopes(table, predicate_builder = nil, klass = self.klass, record = nil) # :nodoc:
1239
+ scopes = super
1240
+ unless @previous_reflection.through_reflection?
1241
+ scopes += @previous_reflection.join_scopes(table, predicate_builder, klass, record)
1242
+ end
1248
1243
  scopes << build_scope(table, predicate_builder, klass).instance_exec(record, &source_type_scope)
1249
1244
  end
1250
1245
 
@@ -275,10 +275,14 @@ module ActiveRecord
275
275
  # # SELECT people.id FROM people WHERE people.age = 21 LIMIT 5
276
276
  # # => [2, 3]
277
277
  #
278
- # Comment.joins(:person).pluck(:id, person: [:id])
279
- # # SELECT comments.id, people.id FROM comments INNER JOIN people on comments.person_id = people.id
278
+ # Comment.joins(:person).pluck(:id, person: :id)
279
+ # # SELECT comments.id, person.id FROM comments INNER JOIN people person ON person.id = comments.person_id
280
280
  # # => [[1, 2], [2, 2]]
281
281
  #
282
+ # Comment.joins(:person).pluck(:id, person: [:id, :name])
283
+ # # SELECT comments.id, person.id, person.name FROM comments INNER JOIN people person ON person.id = comments.person_id
284
+ # # => [[1, 2, 'David'], [2, 2, 'David']]
285
+ #
282
286
  # Person.pluck(Arel.sql('DATEDIFF(updated_at, created_at)'))
283
287
  # # SELECT DATEDIFF(updated_at, created_at) FROM people
284
288
  # # => ['0', '27761', '173']
@@ -307,8 +311,8 @@ module ActiveRecord
307
311
  relation.pluck(*column_names)
308
312
  else
309
313
  model.disallow_raw_sql!(flattened_args(column_names))
310
- columns = arel_columns(column_names)
311
314
  relation = spawn
315
+ columns = relation.arel_columns(column_names)
312
316
  relation.select_values = columns
313
317
  result = skip_query_cache_if_necessary do
314
318
  if where_clause.contradiction?
@@ -447,10 +451,13 @@ module ActiveRecord
447
451
  end
448
452
 
449
453
  def aggregate_column(column_name)
450
- return column_name if Arel::Expressions === column_name
451
-
452
- arel_column(column_name.to_s) do |name|
453
- column_name == :all ? Arel.sql("*", retryable: true) : Arel.sql(name)
454
+ case column_name
455
+ when Arel::Expressions
456
+ column_name
457
+ when :all
458
+ Arel.star
459
+ else
460
+ arel_column(column_name)
454
461
  end
455
462
  end
456
463
 
@@ -630,27 +637,12 @@ module ActiveRecord
630
637
  end
631
638
 
632
639
  def select_for_count
633
- if select_values.present?
634
- return select_values.first if select_values.one?
635
-
636
- adapter_class = model.adapter_class
637
- select_values.map do |field|
638
- column = if Arel.arel_node?(field)
639
- field
640
- else
641
- arel_column(field.to_s) do |attr_name|
642
- Arel.sql(attr_name)
643
- end
644
- end
645
-
646
- if column.is_a?(Arel::Nodes::SqlLiteral)
647
- column
648
- else
649
- "#{adapter_class.quote_table_name(column.relation.name)}.#{adapter_class.quote_column_name(column.name)}"
650
- end
651
- end.join(", ")
652
- else
640
+ if select_values.empty?
653
641
  :all
642
+ else
643
+ with_connection do |conn|
644
+ arel_columns(select_values).map { |column| conn.visitor.compile(column) }.join(", ")
645
+ end
654
646
  end
655
647
  end
656
648
 
@@ -673,7 +665,11 @@ module ActiveRecord
673
665
  subquery_alias = Arel.sql("subquery_for_count", retryable: true)
674
666
  select_value = operation_over_aggregate_column(column_alias, "count", false)
675
667
 
676
- relation.build_subquery(subquery_alias, select_value)
668
+ if column_name == :all
669
+ relation.unscope(:order).build_subquery(subquery_alias, select_value)
670
+ else
671
+ relation.build_subquery(subquery_alias, select_value)
672
+ end
677
673
  end
678
674
  end
679
675
  end
@@ -72,7 +72,15 @@ module ActiveRecord
72
72
  table.associated_table(table_name, &block).arel_table[column_name]
73
73
  end
74
74
 
75
+ def with(table)
76
+ other = dup
77
+ other.table = table
78
+ other
79
+ end
80
+
75
81
  protected
82
+ attr_writer :table
83
+
76
84
  def expand_from_hash(attributes, &block)
77
85
  return ["1=0"] if attributes.empty?
78
86