Skip to content

Commit 122ba5d

Browse files
committed
Flatten join alias Vars before pulling up targetlist items from a subquery.
pullup_replace_vars()'s decisions about whether a pulled-up replacement expression needs to be wrapped in a PlaceHolderVar depend on the assumption that what looks like a Var behaves like a Var. However, if the Var is a join alias reference, later flattening of join aliases might replace the Var with something that's not a Var at all, and should have been wrapped. To fix, do a forcible pass of flatten_join_alias_vars() on the subquery targetlist before we start to copy items out of it. We'll re-run that processing on the pulled-up expressions later, but that's harmless. Per report from Ken Tanzer; the added regression test case is based on his example. This bug has been there since the PlaceHolderVar mechanism was invented, but has escaped detection because the circumstances that trigger it are fairly narrow. You need a flattenable query underneath an outer join, which contains another flattenable query inside a join of its own, with a dangerous expression (a constant or something else non-strict) in that one's targetlist. Having seen this, I'm wondering if it wouldn't be prudent to do all alias-variable flattening earlier, perhaps even in the rewriter. But that would probably not be a back-patchable change.
1 parent 6765a2c commit 122ba5d

File tree

4 files changed

+52
-6
lines changed

4 files changed

+52
-6
lines changed

src/backend/optimizer/prep/prepjointree.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,18 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
728728
return jtnode;
729729
}
730730

731+
/*
732+
* We must flatten any join alias Vars in the subquery's targetlist,
733+
* because pulling up the subquery's subqueries might have changed their
734+
* expansions into arbitrary expressions, which could affect
735+
* pullup_replace_vars' decisions about whether PlaceHolderVar wrappers
736+
* are needed for tlist entries. (Likely it'd be better to do
737+
* flatten_join_alias_vars on the whole query tree at some earlier stage,
738+
* maybe even in the rewriter; but for now let's just fix this case here.)
739+
*/
740+
subquery->targetList = (List *)
741+
flatten_join_alias_vars(subroot, (Node *) subquery->targetList);
742+
731743
/*
732744
* Adjust level-0 varnos in subquery so that we can append its rangetable
733745
* to upper query's. We have to fix the subquery's append_rel_list as

src/backend/optimizer/util/var.c

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -750,16 +750,14 @@ flatten_join_alias_vars_mutator(Node *node,
750750
/* Ignore dropped columns */
751751
if (newvar == NULL)
752752
continue;
753+
newvar = copyObject(newvar);
753754

754755
/*
755756
* If we are expanding an alias carried down from an upper
756757
* query, must adjust its varlevelsup fields.
757758
*/
758759
if (context->sublevels_up != 0)
759-
{
760-
newvar = copyObject(newvar);
761760
IncrementVarSublevelsUp(newvar, context->sublevels_up, 0);
762-
}
763761
/* Recurse in case join input is itself a join */
764762
/* (also takes care of setting inserted_sublink if needed) */
765763
newvar = flatten_join_alias_vars_mutator(newvar, context);
@@ -779,16 +777,14 @@ flatten_join_alias_vars_mutator(Node *node,
779777
Assert(var->varattno > 0);
780778
newvar = (Node *) list_nth(rte->joinaliasvars, var->varattno - 1);
781779
Assert(newvar != NULL);
780+
newvar = copyObject(newvar);
782781

783782
/*
784783
* If we are expanding an alias carried down from an upper query, must
785784
* adjust its varlevelsup fields.
786785
*/
787786
if (context->sublevels_up != 0)
788-
{
789-
newvar = copyObject(newvar);
790787
IncrementVarSublevelsUp(newvar, context->sublevels_up, 0);
791-
}
792788

793789
/* Recurse in case join input is itself a join */
794790
newvar = flatten_join_alias_vars_mutator(newvar, context);

src/test/regress/expected/join.out

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2595,3 +2595,24 @@ select f1, unique2, case when unique2 is null then f1 else 0 end
25952595
0 | 0 | 0
25962596
(1 row)
25972597

2598+
--
2599+
-- check handling of join aliases when flattening multiple levels of subquery
2600+
--
2601+
select foo1.join_key as foo1_id, foo3.join_key AS foo3_id, bug_field from
2602+
(values (0),(1)) foo1(join_key)
2603+
left join
2604+
(select join_key, bug_field from
2605+
(select ss1.join_key, ss1.bug_field from
2606+
(select f1 as join_key, 666 as bug_field from int4_tbl i1) ss1
2607+
) foo2
2608+
left join
2609+
(select unique2 as join_key from tenk1 i2) ss2
2610+
using (join_key)
2611+
) foo3
2612+
using (join_key);
2613+
foo1_id | foo3_id | bug_field
2614+
---------+---------+-----------
2615+
0 | 0 | 666
2616+
1 | |
2617+
(2 rows)
2618+

src/test/regress/sql/join.sql

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,3 +651,20 @@ select q1, unique2, thousand, hundred
651651
select f1, unique2, case when unique2 is null then f1 else 0 end
652652
from int4_tbl a left join tenk1 b on f1 = unique2
653653
where (case when unique2 is null then f1 else 0 end) = 0;
654+
655+
--
656+
-- check handling of join aliases when flattening multiple levels of subquery
657+
--
658+
659+
select foo1.join_key as foo1_id, foo3.join_key AS foo3_id, bug_field from
660+
(values (0),(1)) foo1(join_key)
661+
left join
662+
(select join_key, bug_field from
663+
(select ss1.join_key, ss1.bug_field from
664+
(select f1 as join_key, 666 as bug_field from int4_tbl i1) ss1
665+
) foo2
666+
left join
667+
(select unique2 as join_key from tenk1 i2) ss2
668+
using (join_key)
669+
) foo3
670+
using (join_key);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy