-
Ryuta Kamizono authored
I had found the issue while working on fixing #33525. That is if duplicated association has a scope which has `where` with explicit table name condition (e.g. `where("categories.name": "General")`), that condition in all duplicated associations will filter the first one only, other all duplicated associations are not filtered, since duplicated joins will be aliased except the first one (e.g. `INNER JOIN "categories" "categories_categorizations"`). ```ruby class Author < ActiveRecord::Base has_many :general_categorizations, -> { joins(:category).where("categories.name": "General") }, class_name: "Categorization" has_many :general_posts, through: :general_categorizations, source: :post end authors = Author.eager_load(:general_categorizations, :general_posts).to_a ``` Generated eager loading query: ```sql SELECT "authors"."id" AS t0_r0, ... FROM "authors" -- `has_many :general_categorizations, -> { joins(:category).where("categories.name": "General") }` LEFT OUTER JOIN "categorizations" ON "categorizations"."author_id" = "authors"."id" INNER JOIN "categories" ON "categories"."id" = "categorizations"."category_id" AND "categories"."name" = ? -- `has_many :general_posts, through: :general_categorizations, source: :post` ---- duplicated `through: :general_categorizations` part LEFT OUTER JOIN "categorizations" "general_categorizations_authors_join" ON "general_categorizations_authors_join"."author_id" = "authors"."id" INNER JOIN "categories" "categories_categorizations" ON "categories_categorizations"."id" = "general_categorizations_authors_join"."category_id" AND "categories"."name" = ? -- <-- filtering `"categories"."name" = ?` won't work ---- `source: :post` part LEFT OUTER JOIN "posts" ON "posts"."id" = "general_categorizations_authors_join"."post_id" ``` Originally eager loading with join scope didn't work before Rails 5.2 (#29413), and duplicated through association with join scope raised a duplicated alias error before alias tracking is improved in 590b045e. But now it will potentially be got incorrect result instead of an error, it is worse than an error. To fix the issue, it makes eager loading to deduplicate / re-use duplicated through association if possible, like as `preload`. ```sql SELECT "authors"."id" AS t0_r0, ... FROM "authors" -- `has_many :general_categorizations, -> { joins(:category).where("categories.name": "General") }` LEFT OUTER JOIN "categorizations" ON "categorizations"."author_id" = "authors"."id" INNER JOIN "categories" ON "categories"."id" = "categorizations"."category_id" AND "categories"."name" = ? -- `has_many :general_posts, through: :general_categorizations, source: :post` ---- `through: :general_categorizations` part is deduplicated / re-used LEFT OUTER JOIN "posts" ON "posts"."id" = "categorizations"."post_id" ``` Fixes #32819.
Ryuta Kamizono authoredI had found the issue while working on fixing #33525. That is if duplicated association has a scope which has `where` with explicit table name condition (e.g. `where("categories.name": "General")`), that condition in all duplicated associations will filter the first one only, other all duplicated associations are not filtered, since duplicated joins will be aliased except the first one (e.g. `INNER JOIN "categories" "categories_categorizations"`). ```ruby class Author < ActiveRecord::Base has_many :general_categorizations, -> { joins(:category).where("categories.name": "General") }, class_name: "Categorization" has_many :general_posts, through: :general_categorizations, source: :post end authors = Author.eager_load(:general_categorizations, :general_posts).to_a ``` Generated eager loading query: ```sql SELECT "authors"."id" AS t0_r0, ... FROM "authors" -- `has_many :general_categorizations, -> { joins(:category).where("categories.name": "General") }` LEFT OUTER JOIN "categorizations" ON "categorizations"."author_id" = "authors"."id" INNER JOIN "categories" ON "categories"."id" = "categorizations"."category_id" AND "categories"."name" = ? -- `has_many :general_posts, through: :general_categorizations, source: :post` ---- duplicated `through: :general_categorizations` part LEFT OUTER JOIN "categorizations" "general_categorizations_authors_join" ON "general_categorizations_authors_join"."author_id" = "authors"."id" INNER JOIN "categories" "categories_categorizations" ON "categories_categorizations"."id" = "general_categorizations_authors_join"."category_id" AND "categories"."name" = ? -- <-- filtering `"categories"."name" = ?` won't work ---- `source: :post` part LEFT OUTER JOIN "posts" ON "posts"."id" = "general_categorizations_authors_join"."post_id" ``` Originally eager loading with join scope didn't work before Rails 5.2 (#29413), and duplicated through association with join scope raised a duplicated alias error before alias tracking is improved in 590b045e. But now it will potentially be got incorrect result instead of an error, it is worse than an error. To fix the issue, it makes eager loading to deduplicate / re-use duplicated through association if possible, like as `preload`. ```sql SELECT "authors"."id" AS t0_r0, ... FROM "authors" -- `has_many :general_categorizations, -> { joins(:category).where("categories.name": "General") }` LEFT OUTER JOIN "categorizations" ON "categorizations"."author_id" = "authors"."id" INNER JOIN "categories" ON "categories"."id" = "categorizations"."category_id" AND "categories"."name" = ? -- `has_many :general_posts, through: :general_categorizations, source: :post` ---- `through: :general_categorizations` part is deduplicated / re-used LEFT OUTER JOIN "posts" ON "posts"."id" = "categorizations"."post_id" ``` Fixes #32819.
Loading