mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Fix count(:all)
to correctly work distinct
with custom SELECT list
Currently `count(:all)` with `distinct` doesn't work correctly because SELECT list is always replaced to `*` or primary key in that case even if having custom SELECT list. And also, PostgreSQL has a limitation that ORDER BY expressions must appear in select list for SELECT DISTINCT. Therefore, we should not replace custom SELECT list when using `count(:all)` with `distinct`. Closes #31277.
This commit is contained in:
parent
eb6baccda2
commit
c6cd9a59f2
4 changed files with 24 additions and 10 deletions
|
@ -1,3 +1,7 @@
|
|||
* Fix `count(:all)` to correctly work `distinct` with custom SELECT list.
|
||||
|
||||
*Ryuta Kamizono*
|
||||
|
||||
* Using subselect for `delete_all` with `limit` or `offset`.
|
||||
|
||||
*Ryuta Kamizono*
|
||||
|
|
|
@ -217,7 +217,7 @@ module ActiveRecord
|
|||
if operation == "count"
|
||||
column_name ||= select_for_count
|
||||
if column_name == :all
|
||||
if distinct && (group_values.any? || !(has_limit_or_offset? && order_values.any?))
|
||||
if distinct && (group_values.any? || select_values.empty? && order_values.empty?)
|
||||
column_name = primary_key
|
||||
end
|
||||
elsif column_name =~ /\s*DISTINCT[\s(]+/i
|
||||
|
@ -249,7 +249,7 @@ module ActiveRecord
|
|||
def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
|
||||
column_alias = column_name
|
||||
|
||||
if operation == "count" && has_limit_or_offset?
|
||||
if operation == "count" && (column_name == :all && distinct || has_limit_or_offset?)
|
||||
# Shortcut when limit is zero.
|
||||
return 0 if limit_value == 0
|
||||
|
||||
|
@ -391,14 +391,12 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def build_count_subquery(relation, column_name, distinct)
|
||||
relation.select_values = [
|
||||
if column_name == :all
|
||||
distinct ? table[Arel.star] : Arel.sql(FinderMethods::ONE_AS_ONE)
|
||||
else
|
||||
column_alias = Arel.sql("count_column")
|
||||
aggregate_column(column_name).as(column_alias)
|
||||
end
|
||||
]
|
||||
if column_name == :all
|
||||
relation.select_values = [ Arel.sql(FinderMethods::ONE_AS_ONE) ] unless distinct
|
||||
else
|
||||
column_alias = Arel.sql("count_column")
|
||||
relation.select_values = [ aggregate_column(column_name).as(column_alias) ]
|
||||
end
|
||||
|
||||
subquery = relation.arel.as(Arel.sql("subquery_for_count"))
|
||||
select_value = operation_over_aggregate_column(column_alias || Arel.star, "count", false)
|
||||
|
|
|
@ -236,6 +236,12 @@ class CalculationsTest < ActiveRecord::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_distinct_count_all_with_custom_select_and_order
|
||||
accounts = Account.distinct.select("credit_limit % 10").order(Arel.sql("credit_limit % 10"))
|
||||
assert_queries(1) { assert_equal 3, accounts.count(:all) }
|
||||
assert_queries(1) { assert_equal 3, accounts.load.size }
|
||||
end
|
||||
|
||||
def test_distinct_count_with_order_and_limit
|
||||
assert_equal 4, Account.distinct.order(:firm_id).limit(4).count
|
||||
end
|
||||
|
|
|
@ -963,6 +963,12 @@ class RelationTest < ActiveRecord::TestCase
|
|||
assert_equal 11, posts.distinct(false).select(:comments_count).count
|
||||
end
|
||||
|
||||
def test_size_with_distinct
|
||||
posts = Post.distinct.select(:author_id, :comments_count)
|
||||
assert_queries(1) { assert_equal 8, posts.size }
|
||||
assert_queries(1) { assert_equal 8, posts.load.size }
|
||||
end
|
||||
|
||||
def test_update_all_with_scope
|
||||
tag = Tag.first
|
||||
Post.tagged_with(tag.id).update_all title: "rofl"
|
||||
|
|
Loading…
Reference in a new issue