Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit7af96a6

Browse files
committed
Apply band-aid fix for an oversight in reparameterize_path_by_child.
The path we wish to reparameterize is not a standalone object:in particular, it implicitly references baserestrictinfo clausesin the associated RelOptInfo, and if it's a SampleScan path thenthere is also the TableSampleClause in the RTE to worry about.Both of those could contain lateral references to the join partnerrelation, which would need to be modified to refer to its child.Since we aren't doing that, affected queries can give wrong answers,or odd failures such as "variable not found in subplan target list",or executor crashes. But we can't just summarily modify thoseexpressions, because they are shared with other paths for the rel.We'd break things if we modify them and then end up using somenon-partitioned-join path.In HEAD, we plan to fix this by postponing reparameterizationuntil create_plan(), when we know that those other paths areno longer of interest, and then adjusting those expressions alongwith the ones in the path itself. That seems like too big a changefor stable branches however. In the back branches, let's just detectwhether any troublesome lateral references actually exist in thoseexpressions, and fail reparameterization if so. This will result innot performing a partitioned join in such cases. Given the lack offield complaints, nobody's likely to miss the optimization.Report and patch by Richard Guo. Apply to 12-16 only, sincethe intended fix for HEAD looks quite different. We're not quiteready to push the HEAD fix, but with back-branch releases comingup soon, it seems wise to get this stopgap fix in place there.Discussion:https://postgr.es/m/CAMbWs496+N=UAjOc=rcD3P7B6oJe4rZw08e_TZRUsWbPxZW3Tw@mail.gmail.com
1 parenta0cffcf commit7af96a6

File tree

3 files changed

+390
-0
lines changed

3 files changed

+390
-0
lines changed

‎src/backend/optimizer/util/pathnode.c

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include"optimizer/optimizer.h"
2727
#include"optimizer/pathnode.h"
2828
#include"optimizer/paths.h"
29+
#include"optimizer/placeholder.h"
2930
#include"optimizer/planmain.h"
3031
#include"optimizer/prep.h"
3132
#include"optimizer/restrictinfo.h"
@@ -56,6 +57,10 @@ static intappend_startup_cost_compare(const ListCell *a, const ListCell *b);
5657
staticList*reparameterize_pathlist_by_child(PlannerInfo*root,
5758
List*pathlist,
5859
RelOptInfo*child_rel);
60+
staticboolcontain_references_to(PlannerInfo*root,Node*clause,
61+
Relidsrelids);
62+
staticboolris_contain_references_to(PlannerInfo*root,List*rinfos,
63+
Relidsrelids);
5964

6065

6166
/*****************************************************************************
@@ -3949,13 +3954,59 @@ do { \
39493954
switch (nodeTag(path))
39503955
{
39513956
caseT_Path:
3957+
3958+
/*
3959+
* If the path's restriction clauses contain lateral references to
3960+
* the other relation, we can't reparameterize, because we must
3961+
* not change the RelOptInfo's contents here. (Doing so would
3962+
* break things if we end up using a non-partitionwise join.)
3963+
*/
3964+
if (ris_contain_references_to(root,
3965+
path->parent->baserestrictinfo,
3966+
child_rel->top_parent_relids))
3967+
returnNULL;
3968+
3969+
/*
3970+
* If it's a SampleScan with tablesample parameters referencing
3971+
* the other relation, we can't reparameterize, because we must
3972+
* not change the RTE's contents here. (Doing so would break
3973+
* things if we end up using a non-partitionwise join.)
3974+
*/
3975+
if (path->pathtype==T_SampleScan)
3976+
{
3977+
Indexscan_relid=path->parent->relid;
3978+
RangeTblEntry*rte;
3979+
3980+
/* it should be a base rel with a tablesample clause... */
3981+
Assert(scan_relid>0);
3982+
rte=planner_rt_fetch(scan_relid,root);
3983+
Assert(rte->rtekind==RTE_RELATION);
3984+
Assert(rte->tablesample!=NULL);
3985+
3986+
if (contain_references_to(root, (Node*)rte->tablesample,
3987+
child_rel->top_parent_relids))
3988+
returnNULL;
3989+
}
3990+
39523991
FLAT_COPY_PATH(new_path,path,Path);
39533992
break;
39543993

39553994
caseT_IndexPath:
39563995
{
39573996
IndexPath*ipath;
39583997

3998+
/*
3999+
* If the path's restriction clauses contain lateral
4000+
* references to the other relation, we can't reparameterize,
4001+
* because we must not change the IndexOptInfo's contents
4002+
* here. (Doing so would break things if we end up using a
4003+
* non-partitionwise join.)
4004+
*/
4005+
if (ris_contain_references_to(root,
4006+
path->parent->baserestrictinfo,
4007+
child_rel->top_parent_relids))
4008+
returnNULL;
4009+
39594010
FLAT_COPY_PATH(ipath,path,IndexPath);
39604011
ADJUST_CHILD_ATTRS(ipath->indexclauses);
39614012
new_path= (Path*)ipath;
@@ -3966,6 +4017,18 @@ do { \
39664017
{
39674018
BitmapHeapPath*bhpath;
39684019

4020+
/*
4021+
* If the path's restriction clauses contain lateral
4022+
* references to the other relation, we can't reparameterize,
4023+
* because we must not change the RelOptInfo's contents here.
4024+
* (Doing so would break things if we end up using a
4025+
* non-partitionwise join.)
4026+
*/
4027+
if (ris_contain_references_to(root,
4028+
path->parent->baserestrictinfo,
4029+
child_rel->top_parent_relids))
4030+
returnNULL;
4031+
39694032
FLAT_COPY_PATH(bhpath,path,BitmapHeapPath);
39704033
REPARAMETERIZE_CHILD_PATH(bhpath->bitmapqual);
39714034
new_path= (Path*)bhpath;
@@ -3997,6 +4060,18 @@ do { \
39974060
ForeignPath*fpath;
39984061
ReparameterizeForeignPathByChild_functionrfpc_func;
39994062

4063+
/*
4064+
* If the path's restriction clauses contain lateral
4065+
* references to the other relation, we can't reparameterize,
4066+
* because we must not change the RelOptInfo's contents here.
4067+
* (Doing so would break things if we end up using a
4068+
* non-partitionwise join.)
4069+
*/
4070+
if (ris_contain_references_to(root,
4071+
path->parent->baserestrictinfo,
4072+
child_rel->top_parent_relids))
4073+
returnNULL;
4074+
40004075
FLAT_COPY_PATH(fpath,path,ForeignPath);
40014076
if (fpath->fdw_outerpath)
40024077
REPARAMETERIZE_CHILD_PATH(fpath->fdw_outerpath);
@@ -4015,6 +4090,18 @@ do { \
40154090
{
40164091
CustomPath*cpath;
40174092

4093+
/*
4094+
* If the path's restriction clauses contain lateral
4095+
* references to the other relation, we can't reparameterize,
4096+
* because we must not change the RelOptInfo's contents here.
4097+
* (Doing so would break things if we end up using a
4098+
* non-partitionwise join.)
4099+
*/
4100+
if (ris_contain_references_to(root,
4101+
path->parent->baserestrictinfo,
4102+
child_rel->top_parent_relids))
4103+
returnNULL;
4104+
40184105
FLAT_COPY_PATH(cpath,path,CustomPath);
40194106
REPARAMETERIZE_CHILD_PATH_LIST(cpath->custom_paths);
40204107
if (cpath->methods&&
@@ -4180,3 +4267,91 @@ reparameterize_pathlist_by_child(PlannerInfo *root,
41804267

41814268
returnresult;
41824269
}
4270+
4271+
/*
4272+
* contain_references_to
4273+
*Detect whether any Vars or PlaceHolderVars in the given clause contain
4274+
*lateral references to the given 'relids'.
4275+
*/
4276+
staticbool
4277+
contain_references_to(PlannerInfo*root,Node*clause,Relidsrelids)
4278+
{
4279+
boolret= false;
4280+
List*vars;
4281+
ListCell*lc;
4282+
4283+
/*
4284+
* Examine all Vars and PlaceHolderVars used in the clause.
4285+
*
4286+
* By omitting the relevant flags, this also gives us a cheap sanity check
4287+
* that no aggregates or window functions appear in the clause. We don't
4288+
* expect any of those in scan-level restrictions or tablesamples.
4289+
*/
4290+
vars=pull_var_clause(clause,PVC_INCLUDE_PLACEHOLDERS);
4291+
foreach(lc,vars)
4292+
{
4293+
Node*node= (Node*)lfirst(lc);
4294+
4295+
if (IsA(node,Var))
4296+
{
4297+
Var*var= (Var*)node;
4298+
4299+
if (bms_is_member(var->varno,relids))
4300+
{
4301+
ret= true;
4302+
break;
4303+
}
4304+
}
4305+
elseif (IsA(node,PlaceHolderVar))
4306+
{
4307+
PlaceHolderVar*phv= (PlaceHolderVar*)node;
4308+
PlaceHolderInfo*phinfo=find_placeholder_info(root,phv, false);
4309+
4310+
/*
4311+
* We should check both ph_eval_at (in case the PHV is to be
4312+
* computed at the other relation and then laterally referenced
4313+
* here) and ph_lateral (in case the PHV is to be evaluated here
4314+
* but contains lateral references to the other relation). The
4315+
* former case should not occur in baserestrictinfo clauses, but
4316+
* it can occur in tablesample clauses.
4317+
*/
4318+
if (bms_overlap(phinfo->ph_eval_at,relids)||
4319+
bms_overlap(phinfo->ph_lateral,relids))
4320+
{
4321+
ret= true;
4322+
break;
4323+
}
4324+
}
4325+
else
4326+
Assert(false);
4327+
}
4328+
4329+
list_free(vars);
4330+
4331+
returnret;
4332+
}
4333+
4334+
/*
4335+
* ris_contain_references_to
4336+
*Apply contain_references_to() to a list of RestrictInfos.
4337+
*
4338+
* We need extra code for this because pull_var_clause() can't descend
4339+
* through RestrictInfos.
4340+
*/
4341+
staticbool
4342+
ris_contain_references_to(PlannerInfo*root,List*rinfos,Relidsrelids)
4343+
{
4344+
ListCell*lc;
4345+
4346+
foreach(lc,rinfos)
4347+
{
4348+
RestrictInfo*rinfo=lfirst_node(RestrictInfo,lc);
4349+
4350+
/* Pseudoconstant clauses can't contain any Vars or PHVs */
4351+
if (rinfo->pseudoconstant)
4352+
continue;
4353+
if (contain_references_to(root, (Node*)rinfo->clause,relids))
4354+
return true;
4355+
}
4356+
return false;
4357+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp