提交 5f59eac2 编写于 作者: R Ryuta Kamizono

Fix type casting aggregated values on association's attributes

Follow up of #39255.

Previously aggregation functions only use the model's attribute types on
the relation for type cast, this will be looking up association's
attribute and type caster if a column name is table name qualified.

Fixes #39248.
上级 8d597b54
......@@ -281,8 +281,7 @@ def aggregate_column(column_name)
end
end
def operation_over_aggregate_column(column_name, operation, distinct)
column = aggregate_column(column_name)
def operation_over_aggregate_column(column, operation, distinct)
operation == "count" ? column.count(distinct) : column.send(operation)
end
......@@ -296,7 +295,8 @@ def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
# PostgreSQL doesn't like ORDER BY when there are no GROUP BY
relation = unscope(:order).distinct!(false)
select_value = operation_over_aggregate_column(column_name, operation, distinct)
column = aggregate_column(column_name)
select_value = operation_over_aggregate_column(column, operation, distinct)
select_value.distinct = true if operation == "sum" && distinct
relation.select_values = [select_value]
......@@ -307,7 +307,7 @@ def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder) }
type_cast_calculated_value(result.cast_values.first, operation) do |value|
if type = klass.attribute_types[column_name.to_s]
if type = column.try(:type_caster) || klass.attribute_types[column_name.to_s]
type.deserialize(value)
else
value
......@@ -331,8 +331,9 @@ def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
}
group_columns = group_aliases.zip(group_fields)
column = aggregate_column(column_name)
column_alias = column_alias_for("#{operation} #{column_name.to_s.downcase}")
select_value = operation_over_aggregate_column(column_name, operation, distinct)
select_value = operation_over_aggregate_column(column, operation, distinct)
select_value.as(column_alias)
select_values = [select_value]
......@@ -365,8 +366,8 @@ def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
end
hash_rows = calculated_data.cast_values(key_types).map! do |row|
calculated_data.columns.each_with_object({}).with_index do |(column, hash), i|
hash[column] = row[i]
calculated_data.columns.each_with_object({}).with_index do |(col_name, hash), i|
hash[col_name] = row[i]
end
end
......@@ -377,7 +378,7 @@ def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
key = key_records[key] if associated
result[key] = type_cast_calculated_value(row[column_alias], operation) do |value|
if type ||= klass.attribute_types[column_name.to_s]
if type ||= column.try(:type_caster) || klass.attribute_types[column_name.to_s]
type.deserialize(value)
else
value
......
......@@ -1272,6 +1272,9 @@ def arel_column(field)
if klass.columns_hash.key?(field) && (!from || table_name_matches?(from))
arel_attribute(field)
elsif field.match?(/\A\w+\.\w+\z/)
table, column = field.split(".")
predicate_builder.resolve_arel_attribute(table, column)
else
yield field
end
......
......@@ -8,6 +8,7 @@
require "models/edge"
require "models/organization"
require "models/possession"
require "models/author"
require "models/topic"
require "models/reply"
require "models/numeric_data"
......@@ -22,7 +23,7 @@
require "support/stubs/strong_parameters"
class CalculationsTest < ActiveRecord::TestCase
fixtures :companies, :accounts, :topics, :speedometers, :minivans, :books, :posts, :comments
fixtures :companies, :accounts, :authors, :topics, :speedometers, :minivans, :books, :posts, :comments
def test_should_sum_field
assert_equal 318, Account.sum(:credit_limit)
......@@ -1098,6 +1099,29 @@ def assert_minimum_and_maximum_on_time_attributes(time_class)
assert_equal expected, actual
assert_instance_of time_class, actual[true]
assert_instance_of time_class, actual[true]
actual = Author.joins(:topics).maximum(:"topics.written_on")
assert_equal Time.utc(2004, 7, 15, 14, 28, 0, 9900), actual
assert_instance_of time_class, actual
actual = Author.joins(:topics).minimum(:"topics.written_on")
assert_equal Time.utc(2003, 7, 16, 14, 28, 11, 223300), actual
assert_instance_of time_class, actual
expected = {
1 => Time.utc(2003, 7, 16, 14, 28, 11, 223300),
2 => Time.utc(2004, 7, 15, 14, 28, 0, 9900),
}
actual = Author.joins(:topics).group(:id).maximum(:"topics.written_on")
assert_equal expected, actual
assert_instance_of time_class, actual[1]
assert_instance_of time_class, actual[2]
actual = Author.joins(:topics).group(:id).minimum(:"topics.written_on")
assert_equal expected, actual
assert_instance_of time_class, actual[1]
assert_instance_of time_class, actual[2]
end
private :assert_minimum_and_maximum_on_time_attributes
......
......@@ -174,6 +174,8 @@ def extension_method; end
has_many :top_posts, -> { order(id: :asc) }, class_name: "Post"
has_many :other_top_posts, -> { order(id: :asc) }, class_name: "Post"
has_many :topics, primary_key: "name", foreign_key: "author_name"
attr_accessor :post_log
after_initialize :set_post_log
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册