diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 5531178e33..e852515353 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,14 @@ +* Avoid queries when performing calculations on contradictory relations. + + Previously calculations would make a query even when passed a + contradiction, such as `User.where(id: []).count`. We no longer perform a + query in that scenario. + + This applies to the following calculations: `count`, `sum`, `average`, + `minimum` and `maximum` + + *Luan Vieira, John Hawthorn and Daniel Colson* + * Allow using aliased attributes with `insert_all`/`upsert_all`. ```ruby diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 6cbe5541f6..9245327d04 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -342,6 +342,7 @@ module ActiveRecord # Shortcut when limit is zero. return 0 if limit_value == 0 + relation = self query_builder = build_count_subquery(spawn, column_name, distinct) else # PostgreSQL doesn't like ORDER BY when there are no GROUP BY @@ -356,8 +357,12 @@ module ActiveRecord query_builder = relation.arel end - query_result = skip_query_cache_if_necessary do - @klass.connection.select_all(query_builder, "#{@klass.name} #{operation.capitalize}", async: @async) + query_result = if relation.where_clause.contradiction? + ActiveRecord::Result.empty + else + skip_query_cache_if_necessary do + @klass.connection.select_all(query_builder, "#{@klass.name} #{operation.capitalize}", async: @async) + end end query_result.then do |result| diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb index 655ac653bb..c0ff7fd85b 100644 --- a/activerecord/test/cases/calculations_test.rb +++ b/activerecord/test/cases/calculations_test.rb @@ -717,6 +717,36 @@ class CalculationsTest < ActiveRecord::TestCase Account.where("credit_limit > 50").from("accounts").maximum(:credit_limit) end + def test_no_queries_for_empty_relation_on_count + assert_queries(0) do + assert_equal 0, Post.where(id: []).count + end + end + + def test_no_queries_for_empty_relation_on_sum + assert_queries(0) do + assert_equal 0, Post.where(id: []).sum(:tags_count) + end + end + + def test_no_queries_for_empty_relation_on_average + assert_queries(0) do + assert_nil Post.where(id: []).average(:tags_count) + end + end + + def test_no_queries_for_empty_relation_on_minimum + assert_queries(0) do + assert_nil Account.where(id: []).minimum(:id) + end + end + + def test_no_queries_for_empty_relation_on_maximum + assert_queries(0) do + assert_nil Account.where(id: []).maximum(:id) + end + end + def test_maximum_with_not_auto_table_name_prefix_if_column_included Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)])