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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +67 -0
- data/lib/active_record/associations/has_many_through_association.rb +7 -1
- data/lib/active_record/attribute_methods/primary_key.rb +2 -7
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +2 -12
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +26 -8
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +6 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +8 -2
- data/lib/active_record/connection_adapters/abstract_adapter.rb +0 -1
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +8 -4
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +0 -10
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -11
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +4 -8
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +8 -8
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +1 -1
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +0 -2
- data/lib/active_record/connection_adapters.rb +0 -56
- data/lib/active_record/core.rb +14 -11
- data/lib/active_record/enum.rb +54 -72
- data/lib/active_record/fixtures.rb +0 -1
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/locking/optimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +5 -11
- data/lib/active_record/marshalling.rb +4 -1
- data/lib/active_record/migration.rb +0 -5
- data/lib/active_record/model_schema.rb +2 -3
- data/lib/active_record/query_cache.rb +0 -4
- data/lib/active_record/query_logs.rb +5 -11
- data/lib/active_record/querying.rb +2 -2
- data/lib/active_record/railtie.rb +1 -24
- data/lib/active_record/railties/databases.rake +1 -1
- data/lib/active_record/reflection.rb +14 -19
- data/lib/active_record/relation/calculations.rb +24 -28
- data/lib/active_record/relation/predicate_builder.rb +8 -0
- data/lib/active_record/relation/query_methods.rb +66 -36
- data/lib/active_record/relation.rb +8 -1
- data/lib/active_record/result.rb +10 -9
- data/lib/active_record/table_metadata.rb +1 -3
- data/lib/active_record/tasks/database_tasks.rb +4 -31
- data/lib/active_record/testing/query_assertions.rb +2 -2
- data/lib/active_record.rb +0 -45
- data/lib/arel/table.rb +3 -7
- data/lib/arel/visitors/sqlite.rb +25 -0
- metadata +9 -10
- data/lib/active_record/relation/record_fetch_warning.rb +0 -52
data/lib/active_record/core.rb
CHANGED
@@ -370,7 +370,7 @@ module ActiveRecord
|
|
370
370
|
end
|
371
371
|
|
372
372
|
def predicate_builder # :nodoc:
|
373
|
-
@predicate_builder ||= PredicateBuilder.new(
|
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 =
|
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
|
data/lib/active_record/enum.rb
CHANGED
@@ -213,95 +213,77 @@ module ActiveRecord
|
|
213
213
|
attr_reader :name, :mapping
|
214
214
|
end
|
215
215
|
|
216
|
-
def enum(name
|
217
|
-
|
218
|
-
|
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
|
-
|
232
|
-
|
233
|
-
|
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
|
242
|
-
|
243
|
-
|
244
|
-
|
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
|
-
|
249
|
-
|
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
|
-
|
254
|
-
detect_enum_conflict!(name, "#{name}=")
|
232
|
+
attribute(name, **options)
|
255
233
|
|
256
|
-
|
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
|
-
|
259
|
-
|
260
|
-
|
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
|
-
|
266
|
-
|
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
|
-
|
270
|
-
|
271
|
-
|
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
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
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
|
-
|
285
|
-
|
286
|
-
|
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
|
-
|
289
|
-
|
264
|
+
method_friendly_label = label.gsub(/[\W&&[:ascii:]]+/, "_")
|
265
|
+
value_method_alias = "#{prefix}#{method_friendly_label}#{suffix}"
|
290
266
|
|
291
|
-
|
292
|
-
|
293
|
-
|
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
|
-
|
272
|
+
end
|
273
|
+
detect_negative_enum_conditions!(value_method_names) if scopes
|
298
274
|
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
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
|
-
|
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:
|
@@ -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
|
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
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
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)
|
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
|
284
|
+
# Returns a quoted version of the table name.
|
286
285
|
def quoted_table_name
|
287
|
-
|
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
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
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 =
|
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
|
-
|
535
|
-
Setting `query_constraints:` option on `#{active_record}.#{macro} :#{name}` is
|
536
|
-
To
|
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 =
|
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:
|
279
|
-
# # SELECT comments.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
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
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.
|
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
|
-
|
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
|
|