1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00
rails--rails/activerecord/test/cases/relation/select_test.rb
Ryuta Kamizono 4639318215 Fix eager loading that non-select columns will be loaded
Related to #35210.

We sometimes use `select` to limit unused columns for performance.

For example, `GET /posts/1` (post detail) usually use (almost) all
columns, but `GET /posts` (post list) does not always use all columns
(e.g. use `id` and `title` for the list view, but `body` is not used).

If an association is eager loaded, the limited `select` doesn't works as
expected, eager loading will load all columns on the model, plus also
load the `select` columns additionally. It works differently with
natural load and preload. It means that changing natural load or preload
to eager load (or vice versa) is unsafe.

This fixes eager loading that always load all columns (plus extra
`select` columns), to respect the `select` columns like as others.

```ruby
post = Post.select("UPPER(title) AS title").first
post.title # => "WELCOME TO THE WEBLOG"
post.body  # => ActiveModel::MissingAttributeError

# Rails 6.0 (ignore the `select` values)
post = Post.select("UPPER(title) AS title").eager_load(:comments).first
post.title # => "Welcome to the weblog"
post.body  # => "Such a lovely day"

# Rails 6.1 (respect the `select` values)
post = Post.select("UPPER(title) AS title").eager_load(:comments).first
post.title # => "WELCOME TO THE WEBLOG"
post.body  # => ActiveModel::MissingAttributeError
```
2020-08-17 12:05:43 +09:00

67 lines
2.3 KiB
Ruby

# frozen_string_literal: true
require "cases/helper"
require "models/post"
require "models/comment"
module ActiveRecord
class SelectTest < ActiveRecord::TestCase
fixtures :posts, :comments
def test_select_with_nil_argument
expected = Post.select(:title).to_sql
assert_equal expected, Post.select(nil).select(:title).to_sql
end
def test_reselect
expected = Post.select(:title).to_sql
assert_equal expected, Post.select(:title, :body).reselect(:title).to_sql
end
def test_reselect_with_default_scope_select
expected = Post.select(:title).to_sql
actual = PostWithDefaultSelect.reselect(:title).to_sql
assert_equal expected, actual
end
def test_non_select_columns_wont_be_loaded
posts = Post.select("UPPER(title) AS title")
assert_non_select_columns_wont_be_loaded(posts.first)
assert_non_select_columns_wont_be_loaded(posts.preload(:comments).first)
assert_non_select_columns_wont_be_loaded(posts.eager_load(:comments).first)
end
def assert_non_select_columns_wont_be_loaded(post)
assert_equal "WELCOME TO THE WEBLOG", post.title
assert_raise(ActiveModel::MissingAttributeError) do
post.body
end
end
private :assert_non_select_columns_wont_be_loaded
def test_type_casted_extra_select_with_eager_loading
posts = Post.select("posts.id * 1.1 AS foo").eager_load(:comments)
assert_equal 1.1, posts.first.foo
end
def test_aliased_select_using_as_with_joins_and_includes
posts = Post.select("posts.id AS field_alias").joins(:comments).includes(:comments)
assert_equal %w(id field_alias), posts.first.attributes.keys
end
def test_aliased_select_not_using_as_with_joins_and_includes
posts = Post.select("posts.id field_alias").joins(:comments).includes(:comments)
assert_equal %w(id field_alias), posts.first.attributes.keys
end
def test_star_select_with_joins_and_includes
posts = Post.select("posts.*").joins(:comments).includes(:comments)
assert_equal %w(
id author_id title body type legacy_comments_count taggings_with_delete_all_count taggings_with_destroy_count
tags_count indestructible_tags_count tags_with_destroy_count tags_with_nullify_count
), posts.first.attributes.keys
end
end
end