Fix `SELECT COUNT` queries when rendering ActiveRecord collections (#40870)

* Fix `SELECT COUNT` queries when rendering ActiveRecord collections

Fixes #40837

When rendering collections, calling `size` when the collection is an
ActiveRecord relation causes unwanted `SELECT COUNT(*)` queries. This
change ensures the collection is an array before getting the size, and
also loads the relation for any further array inspections.

* Test queries when rendering relation collections

* Add `length` support to partial collection iterator

Allows getting the size of a relation without duplicating records, but
still loads the relation. The length method existence needs to be
checked because you can pass in an `Enumerator`, which does not respond
to `length`.

* Ensure unsubscribed from notifications after tests

[Rafael Mendonça França + aar0nr]
This commit is contained in:
aaron 2020-12-18 13:58:11 -07:00 committed by GitHub
parent 50bccf249f
commit 432698ef2b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 33 additions and 1 deletions

View File

@ -47,6 +47,10 @@ module ActionView
def size
@collection.size
end
def length
@collection.respond_to?(:length) ? @collection.length : size
end
end
class SameCollectionIterator < CollectionIterator # :nodoc:
@ -144,7 +148,7 @@ module ActionView
"render_collection.action_view",
identifier: identifier,
layout: layout && layout.virtual_path,
count: collection.size
count: collection.length
) do |payload|
spacer = if @options.key?(:spacer_template)
spacer_template = find_template(@options[:spacer_template], @locals.keys)

View File

@ -0,0 +1,28 @@
# frozen_string_literal: true
require "active_record_unit"
class PartialRenderingQueryTest < ActiveRecordTestCase
def setup
@view = ActionView::Base
.with_empty_template_cache
.with_view_paths(ActionController::Base.view_paths, {})
@queries = []
@subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |*, payload|
@queries << payload[:sql] unless %w[ SCHEMA TRANSACTION ].include?(payload[:name])
end
end
def teardown
ActiveSupport::Notifications.unsubscribe(@subscriber)
end
def test_render_with_relation_collection
@view.render partial: "topics/topic", collection: Topic.all
assert_equal 1, @queries.size
assert_equal 'SELECT "topics".* FROM "topics"', @queries[0]
end
end