Commit c8c03d72 authored by Tom Lane's avatar Tom Lane

Fix another join removal bug: the check on PlaceHolderVars was wrong.

The previous coding would decide that join removal was unsafe upon finding
a PlaceHolderVar that needed to be evaluated at the inner rel and then used
above the join.  However, this fails to cover the case of PlaceHolderVars
that refer to both the inner rel and some other rels.  Per bug report from
Andrus.
parent 635de836
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include "optimizer/pathnode.h" #include "optimizer/pathnode.h"
#include "optimizer/paths.h" #include "optimizer/paths.h"
#include "optimizer/planmain.h" #include "optimizer/planmain.h"
#include "optimizer/var.h"
/* local functions */ /* local functions */
static bool join_is_removable(PlannerInfo *root, SpecialJoinInfo *sjinfo); static bool join_is_removable(PlannerInfo *root, SpecialJoinInfo *sjinfo);
...@@ -197,16 +198,23 @@ join_is_removable(PlannerInfo *root, SpecialJoinInfo *sjinfo) ...@@ -197,16 +198,23 @@ join_is_removable(PlannerInfo *root, SpecialJoinInfo *sjinfo)
} }
/* /*
* Similarly check that the inner rel doesn't produce any PlaceHolderVars * Similarly check that the inner rel isn't needed by any PlaceHolderVars
* that will be used above the join. * that will be used above the join. We only need to fail if such a PHV
* actually references some inner-rel attributes; but the correct check
* for that is relatively expensive, so we first check against ph_eval_at,
* which must mention the inner rel if the PHV uses any inner-rel attrs.
*/ */
foreach(l, root->placeholder_list) foreach(l, root->placeholder_list)
{ {
PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(l); PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(l);
if (bms_is_subset(phinfo->ph_eval_at, innerrel->relids) && if (bms_is_subset(phinfo->ph_needed, joinrelids))
!bms_is_subset(phinfo->ph_needed, joinrelids)) continue; /* PHV is not used above the join */
return false; if (!bms_overlap(phinfo->ph_eval_at, innerrel->relids))
continue; /* it definitely doesn't reference innerrel */
if (bms_overlap(pull_varnos((Node *) phinfo->ph_var),
innerrel->relids))
return false; /* it does reference innerrel */
} }
/* /*
......
...@@ -2644,3 +2644,24 @@ SELECT b.* FROM b LEFT JOIN a ON (b.a_id = a.id) WHERE (a.id IS NULL OR a.id > 0 ...@@ -2644,3 +2644,24 @@ SELECT b.* FROM b LEFT JOIN a ON (b.a_id = a.id) WHERE (a.id IS NULL OR a.id > 0
(1 row) (1 row)
rollback; rollback;
-- another join removal bug: this is not optimizable, either
begin;
create temp table innertab (id int8 primary key, dat1 int8);
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "innertab_pkey" for table "innertab"
insert into innertab values(123, 42);
SELECT * FROM
(SELECT 1 AS x) ss1
LEFT JOIN
(SELECT q1, q2, COALESCE(dat1, q1) AS y
FROM int8_tbl LEFT JOIN innertab ON q2 = id) ss2
ON true;
x | q1 | q2 | y
---+------------------+-------------------+------------------
1 | 123 | 456 | 123
1 | 123 | 4567890123456789 | 123
1 | 4567890123456789 | 123 | 42
1 | 4567890123456789 | 4567890123456789 | 4567890123456789
1 | 4567890123456789 | -4567890123456789 | 4567890123456789
(5 rows)
rollback;
...@@ -644,3 +644,18 @@ SELECT * FROM b LEFT JOIN a ON (b.a_id = a.id) WHERE (a.id IS NULL OR a.id > 0); ...@@ -644,3 +644,18 @@ SELECT * FROM b LEFT JOIN a ON (b.a_id = a.id) WHERE (a.id IS NULL OR a.id > 0);
SELECT b.* FROM b LEFT JOIN a ON (b.a_id = a.id) WHERE (a.id IS NULL OR a.id > 0); SELECT b.* FROM b LEFT JOIN a ON (b.a_id = a.id) WHERE (a.id IS NULL OR a.id > 0);
rollback; rollback;
-- another join removal bug: this is not optimizable, either
begin;
create temp table innertab (id int8 primary key, dat1 int8);
insert into innertab values(123, 42);
SELECT * FROM
(SELECT 1 AS x) ss1
LEFT JOIN
(SELECT q1, q2, COALESCE(dat1, q1) AS y
FROM int8_tbl LEFT JOIN innertab ON q2 = id) ss2
ON true;
rollback;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment