Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 4f80974

Browse files
committed
Avoid sharing PARAM_EXEC slots between different levels of NestLoop.
Up to now, createplan.c attempted to share PARAM_EXEC slots for NestLoopParams across different plan levels, if the same underlying Var was being fed down to different righthand-side subplan trees by different NestLoops. This was, I think, more of an artifact of using subselect.c's PlannerParamItem infrastructure than an explicit design goal, but anyway that was the end result. This works well enough as long as the plan tree is executing synchronously, but the feature whereby Gather can execute the parallelized subplan locally breaks it. An upper NestLoop node might execute for a row retrieved from a parallel worker, and assign a value for a PARAM_EXEC slot from that row, while the leader's copy of the parallelized subplan is suspended with a different active value of the row the Var comes from. When control eventually returns to the leader's subplan, it gets the wrong answers if the same PARAM_EXEC slot is being used within the subplan, as reported in bug #15577 from Bartosz Polnik. This is pretty reminiscent of the problem fixed in commit 46c508f, and the proper fix seems to be the same: don't try to share PARAM_EXEC slots across different levels of controlling NestLoop nodes. This requires decoupling NestLoopParam handling from PlannerParamItem handling, although the logic remains somewhat similar. To avoid bizarre division of labor between subselect.c and createplan.c, I decided to move all the param-slot-assignment logic for both cases out of those files and put it into a new file paramassign.c. Hopefully it's a bit better documented now, too. A regression test case for this might be nice, but we don't know a test case that triggers the problem with a suitably small amount of data. Back-patch to 9.6 where we added Gather nodes. It's conceivable that related problems exist in older branches; but without some evidence for that, I'll leave the older branches alone. Discussion: https://postgr.es/m/15577-ca61ab18904af852@postgresql.org
1 parent 2763cc4 commit 4f80974

File tree

7 files changed

+664
-518
lines changed

7 files changed

+664
-518
lines changed

src/backend/optimizer/plan/createplan.c

Lines changed: 8 additions & 170 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
#include <limits.h>
2020
#include <math.h>
2121

22-
#include "access/stratnum.h"
2322
#include "access/sysattr.h"
2423
#include "catalog/pg_class.h"
2524
#include "foreign/fdwapi.h"
@@ -29,11 +28,11 @@
2928
#include "nodes/nodeFuncs.h"
3029
#include "optimizer/clauses.h"
3130
#include "optimizer/cost.h"
31+
#include "optimizer/paramassign.h"
3232
#include "optimizer/paths.h"
3333
#include "optimizer/placeholder.h"
3434
#include "optimizer/plancat.h"
3535
#include "optimizer/planmain.h"
36-
#include "optimizer/planner.h"
3736
#include "optimizer/predtest.h"
3837
#include "optimizer/restrictinfo.h"
3938
#include "optimizer/subselect.h"
@@ -147,8 +146,6 @@ static MergeJoin *create_mergejoin_plan(PlannerInfo *root, MergePath *best_path)
147146
static HashJoin *create_hashjoin_plan(PlannerInfo *root, HashPath *best_path);
148147
static Node *replace_nestloop_params(PlannerInfo *root, Node *expr);
149148
static Node *replace_nestloop_params_mutator(Node *node, PlannerInfo *root);
150-
static void process_subquery_nestloop_params(PlannerInfo *root,
151-
List *subplan_params);
152149
static List *fix_indexqual_references(PlannerInfo *root, IndexPath *index_path);
153150
static List *fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path);
154151
static Node *fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol);
@@ -295,7 +292,7 @@ create_plan(PlannerInfo *root, Path *best_path)
295292
/* plan_params should not be in use in current query level */
296293
Assert(root->plan_params == NIL);
297294

298-
/* Initialize this module's private workspace in PlannerInfo */
295+
/* Initialize this module's workspace in PlannerInfo */
299296
root->curOuterRels = NULL;
300297
root->curOuterParams = NIL;
301298

@@ -3444,9 +3441,6 @@ create_nestloop_plan(PlannerInfo *root,
34443441
Relids outerrelids;
34453442
List *nestParams;
34463443
Relids saveOuterRels = root->curOuterRels;
3447-
ListCell *cell;
3448-
ListCell *prev;
3449-
ListCell *next;
34503444

34513445
/* NestLoop can project, so no need to be picky about child tlists */
34523446
outer_plan = create_plan_recurse(root, best_path->outerjoinpath, 0);
@@ -3490,38 +3484,10 @@ create_nestloop_plan(PlannerInfo *root,
34903484

34913485
/*
34923486
* Identify any nestloop parameters that should be supplied by this join
3493-
* node, and move them from root->curOuterParams to the nestParams list.
3487+
* node, and remove them from root->curOuterParams.
34943488
*/
34953489
outerrelids = best_path->outerjoinpath->parent->relids;
3496-
nestParams = NIL;
3497-
prev = NULL;
3498-
for (cell = list_head(root->curOuterParams); cell; cell = next)
3499-
{
3500-
NestLoopParam *nlp = (NestLoopParam *) lfirst(cell);
3501-
3502-
next = lnext(cell);
3503-
if (IsA(nlp->paramval, Var) &&
3504-
bms_is_member(nlp->paramval->varno, outerrelids))
3505-
{
3506-
root->curOuterParams = list_delete_cell(root->curOuterParams,
3507-
cell, prev);
3508-
nestParams = lappend(nestParams, nlp);
3509-
}
3510-
else if (IsA(nlp->paramval, PlaceHolderVar) &&
3511-
bms_overlap(((PlaceHolderVar *) nlp->paramval)->phrels,
3512-
outerrelids) &&
3513-
bms_is_subset(find_placeholder_info(root,
3514-
(PlaceHolderVar *) nlp->paramval,
3515-
false)->ph_eval_at,
3516-
outerrelids))
3517-
{
3518-
root->curOuterParams = list_delete_cell(root->curOuterParams,
3519-
cell, prev);
3520-
nestParams = lappend(nestParams, nlp);
3521-
}
3522-
else
3523-
prev = cell;
3524-
}
3490+
nestParams = identify_current_nestloop_params(root, outerrelids);
35253491

35263492
join_plan = make_nestloop(tlist,
35273493
joinclauses,
@@ -4007,42 +3973,18 @@ replace_nestloop_params_mutator(Node *node, PlannerInfo *root)
40073973
if (IsA(node, Var))
40083974
{
40093975
Var *var = (Var *) node;
4010-
Param *param;
4011-
NestLoopParam *nlp;
4012-
ListCell *lc;
40133976

40143977
/* Upper-level Vars should be long gone at this point */
40153978
Assert(var->varlevelsup == 0);
40163979
/* If not to be replaced, we can just return the Var unmodified */
40173980
if (!bms_is_member(var->varno, root->curOuterRels))
40183981
return node;
4019-
/* Create a Param representing the Var */
4020-
param = assign_nestloop_param_var(root, var);
4021-
/* Is this param already listed in root->curOuterParams? */
4022-
foreach(lc, root->curOuterParams)
4023-
{
4024-
nlp = (NestLoopParam *) lfirst(lc);
4025-
if (nlp->paramno == param->paramid)
4026-
{
4027-
Assert(equal(var, nlp->paramval));
4028-
/* Present, so we can just return the Param */
4029-
return (Node *) param;
4030-
}
4031-
}
4032-
/* No, so add it */
4033-
nlp = makeNode(NestLoopParam);
4034-
nlp->paramno = param->paramid;
4035-
nlp->paramval = var;
4036-
root->curOuterParams = lappend(root->curOuterParams, nlp);
4037-
/* And return the replacement Param */
4038-
return (Node *) param;
3982+
/* Replace the Var with a nestloop Param */
3983+
return (Node *) replace_nestloop_param_var(root, var);
40393984
}
40403985
if (IsA(node, PlaceHolderVar))
40413986
{
40423987
PlaceHolderVar *phv = (PlaceHolderVar *) node;
4043-
Param *param;
4044-
NestLoopParam *nlp;
4045-
ListCell *lc;
40463988

40473989
/* Upper-level PlaceHolderVars should be long gone at this point */
40483990
Assert(phv->phlevelsup == 0);
@@ -4079,118 +4021,14 @@ replace_nestloop_params_mutator(Node *node, PlannerInfo *root)
40794021
root);
40804022
return (Node *) newphv;
40814023
}
4082-
/* Create a Param representing the PlaceHolderVar */
4083-
param = assign_nestloop_param_placeholdervar(root, phv);
4084-
/* Is this param already listed in root->curOuterParams? */
4085-
foreach(lc, root->curOuterParams)
4086-
{
4087-
nlp = (NestLoopParam *) lfirst(lc);
4088-
if (nlp->paramno == param->paramid)
4089-
{
4090-
Assert(equal(phv, nlp->paramval));
4091-
/* Present, so we can just return the Param */
4092-
return (Node *) param;
4093-
}
4094-
}
4095-
/* No, so add it */
4096-
nlp = makeNode(NestLoopParam);
4097-
nlp->paramno = param->paramid;
4098-
nlp->paramval = (Var *) phv;
4099-
root->curOuterParams = lappend(root->curOuterParams, nlp);
4100-
/* And return the replacement Param */
4101-
return (Node *) param;
4024+
/* Replace the PlaceHolderVar with a nestloop Param */
4025+
return (Node *) replace_nestloop_param_placeholdervar(root, phv);
41024026
}
41034027
return expression_tree_mutator(node,
41044028
replace_nestloop_params_mutator,
41054029
(void *) root);
41064030
}
41074031

4108-
/*
4109-
* process_subquery_nestloop_params
4110-
* Handle params of a parameterized subquery that need to be fed
4111-
* from an outer nestloop.
4112-
*
4113-
* Currently, that would be *all* params that a subquery in FROM has demanded
4114-
* from the current query level, since they must be LATERAL references.
4115-
*
4116-
* The subplan's references to the outer variables are already represented
4117-
* as PARAM_EXEC Params, so we need not modify the subplan here. What we
4118-
* do need to do is add entries to root->curOuterParams to signal the parent
4119-
* nestloop plan node that it must provide these values.
4120-
*/
4121-
static void
4122-
process_subquery_nestloop_params(PlannerInfo *root, List *subplan_params)
4123-
{
4124-
ListCell *ppl;
4125-
4126-
foreach(ppl, subplan_params)
4127-
{
4128-
PlannerParamItem *pitem = (PlannerParamItem *) lfirst(ppl);
4129-
4130-
if (IsA(pitem->item, Var))
4131-
{
4132-
Var *var = (Var *) pitem->item;
4133-
NestLoopParam *nlp;
4134-
ListCell *lc;
4135-
4136-
/* If not from a nestloop outer rel, complain */
4137-
if (!bms_is_member(var->varno, root->curOuterRels))
4138-
elog(ERROR, "non-LATERAL parameter required by subquery");
4139-
/* Is this param already listed in root->curOuterParams? */
4140-
foreach(lc, root->curOuterParams)
4141-
{
4142-
nlp = (NestLoopParam *) lfirst(lc);
4143-
if (nlp->paramno == pitem->paramId)
4144-
{
4145-
Assert(equal(var, nlp->paramval));
4146-
/* Present, so nothing to do */
4147-
break;
4148-
}
4149-
}
4150-
if (lc == NULL)
4151-
{
4152-
/* No, so add it */
4153-
nlp = makeNode(NestLoopParam);
4154-
nlp->paramno = pitem->paramId;
4155-
nlp->paramval = copyObject(var);
4156-
root->curOuterParams = lappend(root->curOuterParams, nlp);
4157-
}
4158-
}
4159-
else if (IsA(pitem->item, PlaceHolderVar))
4160-
{
4161-
PlaceHolderVar *phv = (PlaceHolderVar *) pitem->item;
4162-
NestLoopParam *nlp;
4163-
ListCell *lc;
4164-
4165-
/* If not from a nestloop outer rel, complain */
4166-
if (!bms_is_subset(find_placeholder_info(root, phv, false)->ph_eval_at,
4167-
root->curOuterRels))
4168-
elog(ERROR, "non-LATERAL parameter required by subquery");
4169-
/* Is this param already listed in root->curOuterParams? */
4170-
foreach(lc, root->curOuterParams)
4171-
{
4172-
nlp = (NestLoopParam *) lfirst(lc);
4173-
if (nlp->paramno == pitem->paramId)
4174-
{
4175-
Assert(equal(phv, nlp->paramval));
4176-
/* Present, so nothing to do */
4177-
break;
4178-
}
4179-
}
4180-
if (lc == NULL)
4181-
{
4182-
/* No, so add it */
4183-
nlp = makeNode(NestLoopParam);
4184-
nlp->paramno = pitem->paramId;
4185-
nlp->paramval = copyObject(phv);
4186-
root->curOuterParams = lappend(root->curOuterParams, nlp);
4187-
}
4188-
}
4189-
else
4190-
elog(ERROR, "unexpected type of subquery parameter");
4191-
}
4192-
}
4193-
41944032
/*
41954033
* fix_indexqual_references
41964034
* Adjust indexqual clauses to the form the executor's indexqual

src/backend/optimizer/plan/planner.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#endif
3737
#include "optimizer/clauses.h"
3838
#include "optimizer/cost.h"
39+
#include "optimizer/paramassign.h"
3940
#include "optimizer/pathnode.h"
4041
#include "optimizer/paths.h"
4142
#include "optimizer/plancat.h"
@@ -466,7 +467,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
466467
root->hasInheritedTarget = false;
467468
root->hasRecursion = hasRecursion;
468469
if (hasRecursion)
469-
root->wt_param_id = SS_assign_special_param(root);
470+
root->wt_param_id = assign_special_exec_param(root);
470471
else
471472
root->wt_param_id = -1;
472473
root->non_recursive_path = NULL;
@@ -1362,7 +1363,7 @@ inheritance_planner(PlannerInfo *root)
13621363
returningLists,
13631364
rowMarks,
13641365
NULL,
1365-
SS_assign_special_param(root)));
1366+
assign_special_exec_param(root)));
13661367
}
13671368

13681369
/*--------------------
@@ -1955,7 +1956,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
19551956
{
19561957
path = (Path *) create_lockrows_path(root, final_rel, path,
19571958
root->rowMarks,
1958-
SS_assign_special_param(root));
1959+
assign_special_exec_param(root));
19591960
}
19601961

19611962
/*
@@ -2015,7 +2016,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
20152016
returningLists,
20162017
rowMarks,
20172018
parse->onConflict,
2018-
SS_assign_special_param(root));
2019+
assign_special_exec_param(root));
20192020
}
20202021

20212022
/* And shove it into final_rel */

0 commit comments

Comments
 (0)