Merge pull request #39111 from kamipo/fix_min_and_max_on_non_numeric_column

Fix `minimum` and `maximum` on non numeric column
This commit is contained in:
Ryuta Kamizono 2020-05-02 15:11:34 +09:00 committed by GitHub
commit 483330eeea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 48 additions and 13 deletions

View File

@ -304,8 +304,17 @@ module ActiveRecord
query_builder = relation.arel
end
value = skip_query_cache_if_necessary { @klass.connection.select_value(query_builder) }
type_cast_calculated_value(value, operation)
result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder) }
type_cast_calculated_value(result.cast_values.first, operation) do |value|
if value.is_a?(String) &&
column = klass.columns_hash[column_name.to_s]
type = connection.lookup_cast_type_from_column(column)
type.deserialize(value)
else
value
end
end
end
def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
@ -351,17 +360,33 @@ module ActiveRecord
key_records = key_records.index_by(&:id)
end
calculated_data.each_with_object({}) do |row, result|
key = group_columns.map { |aliaz, col_name|
type = type_for(col_name) do
calculated_data.column_types.fetch(aliaz, Type.default_value)
end
type.deserialize(row[aliaz])
}
key_types = group_columns.each_with_object({}) do |(aliaz, col_name), types|
types[aliaz] = type_for(col_name) do
calculated_data.column_types.fetch(aliaz, Type.default_value)
end
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]
end
end
type = nil
hash_rows.each_with_object({}) do |row, result|
key = group_aliases.map { |aliaz| row[aliaz] }
key = key.first if key.size == 1
key = key_records[key] if associated
result[key] = type_cast_calculated_value(row[column_alias], operation)
result[key] = type_cast_calculated_value(row[column_alias], operation) do |value|
if value.is_a?(String) &&
(type || column = klass.columns_hash[column_name.to_s])
type ||= connection.lookup_cast_type_from_column(column)
type.deserialize(value)
else
value
end
end
end
end
@ -389,9 +414,12 @@ module ActiveRecord
def type_cast_calculated_value(value, operation)
case operation
when "sum" then value || 0
when "average" then value&.respond_to?(:to_d) ? value.to_d : value
else value
when "count", "sum"
value || 0
when "average"
value&.respond_to?(:to_d) ? value.to_d : value
else # "minimum", "maximum"
yield value
end
end

View File

@ -1023,6 +1023,13 @@ class CalculationsTest < ActiveRecord::TestCase
assert_equal({ "proposed" => 0, "published" => 1 }, Book.group(:status).maximum(:difficulty))
end
def test_minimum_and_maximum_on_non_numeric_type
assert_equal Date.new(2004, 4, 15), Topic.minimum(:last_read)
assert_equal Date.new(2004, 4, 15), Topic.maximum(:last_read)
assert_equal({ false => Date.new(2004, 4, 15), true => nil }, Topic.group(:approved).minimum(:last_read))
assert_equal({ false => Date.new(2004, 4, 15), true => nil }, Topic.group(:approved).maximum(:last_read))
end
def test_select_avg_with_group_by_as_virtual_attribute_with_sql
rails_core = companies(:rails_core)