diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index 08a73fb9d8..abdcf826c2 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -3176,16 +3176,6 @@ remove_useless_results_recurse(PlannerInfo *root, Node *jtnode) jtnode = j->larg; } break; - case JOIN_RIGHT: - /* Mirror-image of the JOIN_LEFT case */ - if ((varno = get_result_relid(root, j->larg)) != 0 && - (j->quals == NULL || - !find_dependent_phvs(root, varno))) - { - remove_result_refs(root, varno, j->rarg); - jtnode = j->rarg; - } - break; case JOIN_SEMI: /* @@ -3194,14 +3184,17 @@ remove_useless_results_recurse(PlannerInfo *root, Node *jtnode) * LHS, since we should either return the LHS row or not. For * simplicity we inject the filter qual into a new FromExpr. * - * Unlike the LEFT/RIGHT cases, we just Assert that there are - * no PHVs that need to be evaluated at the semijoin's RHS, - * since the rest of the query couldn't reference any outputs - * of the semijoin's RHS. + * There is a fine point about PHVs that are supposed to be + * evaluated at the RHS. Such PHVs could only appear in the + * semijoin's qual, since the rest of the query cannot + * reference any outputs of the semijoin's RHS. Therefore, + * they can't actually go to null before being examined, and + * it'd be OK to just remove the PHV wrapping. We don't have + * infrastructure for that, but remove_result_refs() will + * relabel them as to be evaluated at the LHS, which is fine. */ if ((varno = get_result_relid(root, j->rarg)) != 0) { - Assert(!find_dependent_phvs(root, varno)); remove_result_refs(root, varno, j->larg); if (j->quals) jtnode = (Node *) @@ -3215,6 +3208,7 @@ remove_useless_results_recurse(PlannerInfo *root, Node *jtnode) /* We have no special smarts for these cases */ break; default: + /* Note: JOIN_RIGHT should be gone at this point */ elog(ERROR, "unrecognized join type: %d", (int) j->jointype); break; diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out index 3cef1b3525..8adaef05fa 100644 --- a/src/test/regress/expected/join.out +++ b/src/test/regress/expected/join.out @@ -3376,6 +3376,26 @@ where b; 0 | t | t (2 rows) +-- Test PHV in a semijoin qual, which confused useless-RTE removal (bug #17700) +explain (verbose, costs off) +with ctetable as not materialized ( select 1 as f1 ) +select * from ctetable c1 +where f1 in ( select c3.f1 from ctetable c2 full join ctetable c3 on true ); + QUERY PLAN +---------------------------- + Result + Output: 1 + One-Time Filter: (1 = 1) +(3 rows) + +with ctetable as not materialized ( select 1 as f1 ) +select * from ctetable c1 +where f1 in ( select c3.f1 from ctetable c2 full join ctetable c3 on true ); + f1 +---- + 1 +(1 row) + -- -- test inlining of immutable functions -- diff --git a/src/test/regress/sql/join.sql b/src/test/regress/sql/join.sql index 7a04d67a14..89e58bed7d 100644 --- a/src/test/regress/sql/join.sql +++ b/src/test/regress/sql/join.sql @@ -1083,6 +1083,16 @@ select * from select a as b) as t3 where b; +-- Test PHV in a semijoin qual, which confused useless-RTE removal (bug #17700) +explain (verbose, costs off) +with ctetable as not materialized ( select 1 as f1 ) +select * from ctetable c1 +where f1 in ( select c3.f1 from ctetable c2 full join ctetable c3 on true ); + +with ctetable as not materialized ( select 1 as f1 ) +select * from ctetable c1 +where f1 in ( select c3.f1 from ctetable c2 full join ctetable c3 on true ); + -- -- test inlining of immutable functions --