activerecord 8.0.0.beta1 → 8.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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