Commit 1db5667b authored by Tom Lane's avatar Tom Lane

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 46c508fb, 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
parent 8b89a886
...@@ -19,7 +19,6 @@ ...@@ -19,7 +19,6 @@
#include <limits.h> #include <limits.h>
#include <math.h> #include <math.h>
#include "access/stratnum.h"
#include "access/sysattr.h" #include "access/sysattr.h"
#include "catalog/pg_class.h" #include "catalog/pg_class.h"
#include "foreign/fdwapi.h" #include "foreign/fdwapi.h"
...@@ -29,11 +28,11 @@ ...@@ -29,11 +28,11 @@
#include "nodes/nodeFuncs.h" #include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h" #include "optimizer/clauses.h"
#include "optimizer/cost.h" #include "optimizer/cost.h"
#include "optimizer/paramassign.h"
#include "optimizer/paths.h" #include "optimizer/paths.h"
#include "optimizer/placeholder.h" #include "optimizer/placeholder.h"
#include "optimizer/plancat.h" #include "optimizer/plancat.h"
#include "optimizer/planmain.h" #include "optimizer/planmain.h"
#include "optimizer/planner.h"
#include "optimizer/predtest.h" #include "optimizer/predtest.h"
#include "optimizer/restrictinfo.h" #include "optimizer/restrictinfo.h"
#include "optimizer/subselect.h" #include "optimizer/subselect.h"
...@@ -151,8 +150,6 @@ static MergeJoin *create_mergejoin_plan(PlannerInfo *root, MergePath *best_path) ...@@ -151,8 +150,6 @@ static MergeJoin *create_mergejoin_plan(PlannerInfo *root, MergePath *best_path)
static HashJoin *create_hashjoin_plan(PlannerInfo *root, HashPath *best_path); static HashJoin *create_hashjoin_plan(PlannerInfo *root, HashPath *best_path);
static Node *replace_nestloop_params(PlannerInfo *root, Node *expr); static Node *replace_nestloop_params(PlannerInfo *root, Node *expr);
static Node *replace_nestloop_params_mutator(Node *node, PlannerInfo *root); static Node *replace_nestloop_params_mutator(Node *node, PlannerInfo *root);
static void process_subquery_nestloop_params(PlannerInfo *root,
List *subplan_params);
static List *fix_indexqual_references(PlannerInfo *root, IndexPath *index_path); static List *fix_indexqual_references(PlannerInfo *root, IndexPath *index_path);
static List *fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path); static List *fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path);
static Node *fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol); static Node *fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol);
...@@ -310,7 +307,7 @@ create_plan(PlannerInfo *root, Path *best_path) ...@@ -310,7 +307,7 @@ create_plan(PlannerInfo *root, Path *best_path)
/* plan_params should not be in use in current query level */ /* plan_params should not be in use in current query level */
Assert(root->plan_params == NIL); Assert(root->plan_params == NIL);
/* Initialize this module's private workspace in PlannerInfo */ /* Initialize this module's workspace in PlannerInfo */
root->curOuterRels = NULL; root->curOuterRels = NULL;
root->curOuterParams = NIL; root->curOuterParams = NIL;
...@@ -1554,7 +1551,7 @@ create_gather_plan(PlannerInfo *root, GatherPath *best_path) ...@@ -1554,7 +1551,7 @@ create_gather_plan(PlannerInfo *root, GatherPath *best_path)
gather_plan = make_gather(tlist, gather_plan = make_gather(tlist,
NIL, NIL,
best_path->num_workers, best_path->num_workers,
SS_assign_special_param(root), assign_special_exec_param(root),
best_path->single_copy, best_path->single_copy,
subplan); subplan);
...@@ -1590,7 +1587,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path) ...@@ -1590,7 +1587,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
copy_generic_path_info(&gm_plan->plan, &best_path->path); copy_generic_path_info(&gm_plan->plan, &best_path->path);
/* Assign the rescan Param. */ /* Assign the rescan Param. */
gm_plan->rescan_param = SS_assign_special_param(root); gm_plan->rescan_param = assign_special_exec_param(root);
/* Gather Merge is pointless with no pathkeys; use Gather instead. */ /* Gather Merge is pointless with no pathkeys; use Gather instead. */
Assert(pathkeys != NIL); Assert(pathkeys != NIL);
...@@ -3774,9 +3771,6 @@ create_nestloop_plan(PlannerInfo *root, ...@@ -3774,9 +3771,6 @@ create_nestloop_plan(PlannerInfo *root,
Relids outerrelids; Relids outerrelids;
List *nestParams; List *nestParams;
Relids saveOuterRels = root->curOuterRels; Relids saveOuterRels = root->curOuterRels;
ListCell *cell;
ListCell *prev;
ListCell *next;
/* NestLoop can project, so no need to be picky about child tlists */ /* NestLoop can project, so no need to be picky about child tlists */
outer_plan = create_plan_recurse(root, best_path->outerjoinpath, 0); outer_plan = create_plan_recurse(root, best_path->outerjoinpath, 0);
...@@ -3820,38 +3814,10 @@ create_nestloop_plan(PlannerInfo *root, ...@@ -3820,38 +3814,10 @@ create_nestloop_plan(PlannerInfo *root,
/* /*
* Identify any nestloop parameters that should be supplied by this join * Identify any nestloop parameters that should be supplied by this join
* node, and move them from root->curOuterParams to the nestParams list. * node, and remove them from root->curOuterParams.
*/ */
outerrelids = best_path->outerjoinpath->parent->relids; outerrelids = best_path->outerjoinpath->parent->relids;
nestParams = NIL; nestParams = identify_current_nestloop_params(root, outerrelids);
prev = NULL;
for (cell = list_head(root->curOuterParams); cell; cell = next)
{
NestLoopParam *nlp = (NestLoopParam *) lfirst(cell);
next = lnext(cell);
if (IsA(nlp->paramval, Var) &&
bms_is_member(nlp->paramval->varno, outerrelids))
{
root->curOuterParams = list_delete_cell(root->curOuterParams,
cell, prev);
nestParams = lappend(nestParams, nlp);
}
else if (IsA(nlp->paramval, PlaceHolderVar) &&
bms_overlap(((PlaceHolderVar *) nlp->paramval)->phrels,
outerrelids) &&
bms_is_subset(find_placeholder_info(root,
(PlaceHolderVar *) nlp->paramval,
false)->ph_eval_at,
outerrelids))
{
root->curOuterParams = list_delete_cell(root->curOuterParams,
cell, prev);
nestParams = lappend(nestParams, nlp);
}
else
prev = cell;
}
join_plan = make_nestloop(tlist, join_plan = make_nestloop(tlist,
joinclauses, joinclauses,
...@@ -4351,42 +4317,18 @@ replace_nestloop_params_mutator(Node *node, PlannerInfo *root) ...@@ -4351,42 +4317,18 @@ replace_nestloop_params_mutator(Node *node, PlannerInfo *root)
if (IsA(node, Var)) if (IsA(node, Var))
{ {
Var *var = (Var *) node; Var *var = (Var *) node;
Param *param;
NestLoopParam *nlp;
ListCell *lc;
/* Upper-level Vars should be long gone at this point */ /* Upper-level Vars should be long gone at this point */
Assert(var->varlevelsup == 0); Assert(var->varlevelsup == 0);
/* If not to be replaced, we can just return the Var unmodified */ /* If not to be replaced, we can just return the Var unmodified */
if (!bms_is_member(var->varno, root->curOuterRels)) if (!bms_is_member(var->varno, root->curOuterRels))
return node; return node;
/* Create a Param representing the Var */ /* Replace the Var with a nestloop Param */
param = assign_nestloop_param_var(root, var); return (Node *) replace_nestloop_param_var(root, var);
/* Is this param already listed in root->curOuterParams? */
foreach(lc, root->curOuterParams)
{
nlp = (NestLoopParam *) lfirst(lc);
if (nlp->paramno == param->paramid)
{
Assert(equal(var, nlp->paramval));
/* Present, so we can just return the Param */
return (Node *) param;
}
}
/* No, so add it */
nlp = makeNode(NestLoopParam);
nlp->paramno = param->paramid;
nlp->paramval = var;
root->curOuterParams = lappend(root->curOuterParams, nlp);
/* And return the replacement Param */
return (Node *) param;
} }
if (IsA(node, PlaceHolderVar)) if (IsA(node, PlaceHolderVar))
{ {
PlaceHolderVar *phv = (PlaceHolderVar *) node; PlaceHolderVar *phv = (PlaceHolderVar *) node;
Param *param;
NestLoopParam *nlp;
ListCell *lc;
/* Upper-level PlaceHolderVars should be long gone at this point */ /* Upper-level PlaceHolderVars should be long gone at this point */
Assert(phv->phlevelsup == 0); Assert(phv->phlevelsup == 0);
...@@ -4423,118 +4365,14 @@ replace_nestloop_params_mutator(Node *node, PlannerInfo *root) ...@@ -4423,118 +4365,14 @@ replace_nestloop_params_mutator(Node *node, PlannerInfo *root)
root); root);
return (Node *) newphv; return (Node *) newphv;
} }
/* Create a Param representing the PlaceHolderVar */ /* Replace the PlaceHolderVar with a nestloop Param */
param = assign_nestloop_param_placeholdervar(root, phv); return (Node *) replace_nestloop_param_placeholdervar(root, phv);
/* Is this param already listed in root->curOuterParams? */
foreach(lc, root->curOuterParams)
{
nlp = (NestLoopParam *) lfirst(lc);
if (nlp->paramno == param->paramid)
{
Assert(equal(phv, nlp->paramval));
/* Present, so we can just return the Param */
return (Node *) param;
}
}
/* No, so add it */
nlp = makeNode(NestLoopParam);
nlp->paramno = param->paramid;
nlp->paramval = (Var *) phv;
root->curOuterParams = lappend(root->curOuterParams, nlp);
/* And return the replacement Param */
return (Node *) param;
} }
return expression_tree_mutator(node, return expression_tree_mutator(node,
replace_nestloop_params_mutator, replace_nestloop_params_mutator,
(void *) root); (void *) root);
} }
/*
* process_subquery_nestloop_params
* Handle params of a parameterized subquery that need to be fed
* from an outer nestloop.
*
* Currently, that would be *all* params that a subquery in FROM has demanded
* from the current query level, since they must be LATERAL references.
*
* The subplan's references to the outer variables are already represented
* as PARAM_EXEC Params, so we need not modify the subplan here. What we
* do need to do is add entries to root->curOuterParams to signal the parent
* nestloop plan node that it must provide these values.
*/
static void
process_subquery_nestloop_params(PlannerInfo *root, List *subplan_params)
{
ListCell *ppl;
foreach(ppl, subplan_params)
{
PlannerParamItem *pitem = (PlannerParamItem *) lfirst(ppl);
if (IsA(pitem->item, Var))
{
Var *var = (Var *) pitem->item;
NestLoopParam *nlp;
ListCell *lc;
/* If not from a nestloop outer rel, complain */
if (!bms_is_member(var->varno, root->curOuterRels))
elog(ERROR, "non-LATERAL parameter required by subquery");
/* Is this param already listed in root->curOuterParams? */
foreach(lc, root->curOuterParams)
{
nlp = (NestLoopParam *) lfirst(lc);
if (nlp->paramno == pitem->paramId)
{
Assert(equal(var, nlp->paramval));
/* Present, so nothing to do */
break;
}
}
if (lc == NULL)
{
/* No, so add it */
nlp = makeNode(NestLoopParam);
nlp->paramno = pitem->paramId;
nlp->paramval = copyObject(var);
root->curOuterParams = lappend(root->curOuterParams, nlp);
}
}
else if (IsA(pitem->item, PlaceHolderVar))
{
PlaceHolderVar *phv = (PlaceHolderVar *) pitem->item;
NestLoopParam *nlp;
ListCell *lc;
/* If not from a nestloop outer rel, complain */
if (!bms_is_subset(find_placeholder_info(root, phv, false)->ph_eval_at,
root->curOuterRels))
elog(ERROR, "non-LATERAL parameter required by subquery");
/* Is this param already listed in root->curOuterParams? */
foreach(lc, root->curOuterParams)
{
nlp = (NestLoopParam *) lfirst(lc);
if (nlp->paramno == pitem->paramId)
{
Assert(equal(phv, nlp->paramval));
/* Present, so nothing to do */
break;
}
}
if (lc == NULL)
{
/* No, so add it */
nlp = makeNode(NestLoopParam);
nlp->paramno = pitem->paramId;
nlp->paramval = (Var *) copyObject(phv);
root->curOuterParams = lappend(root->curOuterParams, nlp);
}
}
else
elog(ERROR, "unexpected type of subquery parameter");
}
}
/* /*
* fix_indexqual_references * fix_indexqual_references
* Adjust indexqual clauses to the form the executor's indexqual * Adjust indexqual clauses to the form the executor's indexqual
......
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
#include "optimizer/clauses.h" #include "optimizer/clauses.h"
#include "optimizer/cost.h" #include "optimizer/cost.h"
#include "optimizer/inherit.h" #include "optimizer/inherit.h"
#include "optimizer/paramassign.h"
#include "optimizer/pathnode.h" #include "optimizer/pathnode.h"
#include "optimizer/paths.h" #include "optimizer/paths.h"
#include "optimizer/plancat.h" #include "optimizer/plancat.h"
...@@ -635,7 +636,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse, ...@@ -635,7 +636,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
root->inhTargetKind = INHKIND_NONE; root->inhTargetKind = INHKIND_NONE;
root->hasRecursion = hasRecursion; root->hasRecursion = hasRecursion;
if (hasRecursion) if (hasRecursion)
root->wt_param_id = SS_assign_special_param(root); root->wt_param_id = assign_special_exec_param(root);
else else
root->wt_param_id = -1; root->wt_param_id = -1;
root->non_recursive_path = NULL; root->non_recursive_path = NULL;
...@@ -1616,7 +1617,7 @@ inheritance_planner(PlannerInfo *root) ...@@ -1616,7 +1617,7 @@ inheritance_planner(PlannerInfo *root)
returningLists, returningLists,
rowMarks, rowMarks,
NULL, NULL,
SS_assign_special_param(root))); assign_special_exec_param(root)));
} }
/*-------------------- /*--------------------
...@@ -2131,7 +2132,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update, ...@@ -2131,7 +2132,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
{ {
path = (Path *) create_lockrows_path(root, final_rel, path, path = (Path *) create_lockrows_path(root, final_rel, path,
root->rowMarks, root->rowMarks,
SS_assign_special_param(root)); assign_special_exec_param(root));
} }
/* /*
...@@ -2204,7 +2205,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update, ...@@ -2204,7 +2205,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
returningLists, returningLists,
rowMarks, rowMarks,
parse->onConflict, parse->onConflict,
SS_assign_special_param(root)); assign_special_exec_param(root));
} }
/* And shove it into final_rel */ /* And shove it into final_rel */
......
This diff is collapsed.
...@@ -12,8 +12,8 @@ subdir = src/backend/optimizer/util ...@@ -12,8 +12,8 @@ subdir = src/backend/optimizer/util
top_builddir = ../../../.. top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global include $(top_builddir)/src/Makefile.global
OBJS = appendinfo.o clauses.o inherit.o joininfo.o orclauses.o pathnode.o \ OBJS = appendinfo.o clauses.o inherit.o joininfo.o orclauses.o \
placeholder.o plancat.o predtest.o relnode.o restrictinfo.o tlist.o \ paramassign.o pathnode.o placeholder.o plancat.o predtest.o \
var.o relnode.o restrictinfo.o tlist.o var.o
include $(top_srcdir)/src/backend/common.mk include $(top_srcdir)/src/backend/common.mk
This diff is collapsed.
/*-------------------------------------------------------------------------
*
* paramassign.h
* Functions for assigning PARAM_EXEC slots during planning.
*
* Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* src/include/optimizer/paramassign.h
*
*-------------------------------------------------------------------------
*/
#ifndef PARAMASSIGN_H
#define PARAMASSIGN_H
#include "nodes/relation.h"
extern Param *replace_outer_var(PlannerInfo *root, Var *var);
extern Param *replace_outer_placeholdervar(PlannerInfo *root,
PlaceHolderVar *phv);
extern Param *replace_outer_agg(PlannerInfo *root, Aggref *agg);
extern Param *replace_outer_grouping(PlannerInfo *root, GroupingFunc *grp);
extern Param *replace_nestloop_param_var(PlannerInfo *root, Var *var);
extern Param *replace_nestloop_param_placeholdervar(PlannerInfo *root,
PlaceHolderVar *phv);
extern void process_subquery_nestloop_params(PlannerInfo *root,
List *subplan_params);
extern List *identify_current_nestloop_params(PlannerInfo *root,
Relids leftrelids);
extern Param *generate_new_exec_param(PlannerInfo *root, Oid paramtype,
int32 paramtypmod, Oid paramcollation);
extern int assign_special_exec_param(PlannerInfo *root);
#endif /* PARAMASSIGN_H */
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* subselect.h * subselect.h
* Planning routines for subselects.
* *
* Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
...@@ -35,9 +36,5 @@ extern Param *SS_make_initplan_output_param(PlannerInfo *root, ...@@ -35,9 +36,5 @@ extern Param *SS_make_initplan_output_param(PlannerInfo *root,
extern void SS_make_initplan_from_plan(PlannerInfo *root, extern void SS_make_initplan_from_plan(PlannerInfo *root,
PlannerInfo *subroot, Plan *plan, PlannerInfo *subroot, Plan *plan,
Param *prm); Param *prm);
extern Param *assign_nestloop_param_var(PlannerInfo *root, Var *var);
extern Param *assign_nestloop_param_placeholdervar(PlannerInfo *root,
PlaceHolderVar *phv);
extern int SS_assign_special_param(PlannerInfo *root);
#endif /* SUBSELECT_H */ #endif /* SUBSELECT_H */
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