Skip to content

Commit 6c86564

Browse files
committed
Fix PARAM_EXEC assignment mechanism to be safe in the presence of WITH.
The planner previously assumed that parameter Vars having the same absolute query level, varno, and varattno could safely be assigned the same runtime PARAM_EXEC slot, even though they might be different Vars appearing in different subqueries. This was (probably) safe before the introduction of CTEs, but the lazy-evalution mechanism used for CTEs means that a CTE can be executed during execution of some other subquery, causing the lifespan of Params at the same syntactic nesting level as the CTE to overlap with use of the same slots inside the CTE. In 9.1 we created additional hazards by using the same parameter-assignment technology for nestloop inner scan parameters, but it was broken before that, as illustrated by the added regression test. To fix, restructure the planner's management of PlannerParamItems so that items having different semantic lifespans are kept rigorously separated. This will probably result in complex queries using more runtime PARAM_EXEC slots than before, but the slots are cheap enough that this hardly matters. Also, stop generating PlannerParamItems containing Params for subquery outputs: all we really need to do is reserve the PARAM_EXEC slot number, and that now only takes incrementing a counter. The planning code is simpler and probably faster than before, as well as being more correct. Per report from Vik Reykja. Back-patch of commit 46c508f into all branches that support WITH.
1 parent ef47adb commit 6c86564

File tree

10 files changed

+247
-180
lines changed

10 files changed

+247
-180
lines changed

src/backend/nodes/outfuncs.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1666,14 +1666,14 @@ _outPlannerGlobal(StringInfo str, const PlannerGlobal *node)
16661666
WRITE_NODE_TYPE("PLANNERGLOBAL");
16671667

16681668
/* NB: this isn't a complete set of fields */
1669-
WRITE_NODE_FIELD(paramlist);
16701669
WRITE_NODE_FIELD(subplans);
16711670
WRITE_BITMAPSET_FIELD(rewindPlanIDs);
16721671
WRITE_NODE_FIELD(finalrtable);
16731672
WRITE_NODE_FIELD(finalrowmarks);
16741673
WRITE_NODE_FIELD(resultRelations);
16751674
WRITE_NODE_FIELD(relationOids);
16761675
WRITE_NODE_FIELD(invalItems);
1676+
WRITE_INT_FIELD(nParamExec);
16771677
WRITE_UINT_FIELD(lastPHId);
16781678
WRITE_UINT_FIELD(lastRowMarkId);
16791679
WRITE_BOOL_FIELD(transientPlan);
@@ -1688,6 +1688,7 @@ _outPlannerInfo(StringInfo str, const PlannerInfo *node)
16881688
WRITE_NODE_FIELD(parse);
16891689
WRITE_NODE_FIELD(glob);
16901690
WRITE_UINT_FIELD(query_level);
1691+
WRITE_NODE_FIELD(plan_params);
16911692
WRITE_BITMAPSET_FIELD(all_baserels);
16921693
WRITE_NODE_FIELD(join_rel_list);
16931694
WRITE_INT_FIELD(join_cur_level);
@@ -1936,7 +1937,7 @@ _outPlannerParamItem(StringInfo str, const PlannerParamItem *node)
19361937
WRITE_NODE_TYPE("PLANNERPARAMITEM");
19371938

19381939
WRITE_NODE_FIELD(item);
1939-
WRITE_UINT_FIELD(abslevel);
1940+
WRITE_INT_FIELD(paramId);
19401941
}
19411942

19421943
/*****************************************************************************

src/backend/optimizer/path/allpaths.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1109,13 +1109,23 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
11091109
else
11101110
tuple_fraction = root->tuple_fraction;
11111111

1112+
/* plan_params should not be in use in current query level */
1113+
Assert(root->plan_params == NIL);
1114+
11121115
/* Generate the plan for the subquery */
11131116
rel->subplan = subquery_planner(root->glob, subquery,
11141117
root,
11151118
false, tuple_fraction,
11161119
&subroot);
11171120
rel->subroot = subroot;
11181121

1122+
/*
1123+
* Since we don't yet support LATERAL, it should not be possible for the
1124+
* sub-query to have requested parameters of this level.
1125+
*/
1126+
if (root->plan_params)
1127+
elog(ERROR, "unexpected outer reference in subquery in FROM");
1128+
11191129
/*
11201130
* It's possible that constraint exclusion proved the subquery empty. If
11211131
* so, it's convenient to turn it back into a dummy path so that we will

src/backend/optimizer/plan/createplan.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,9 @@ create_plan(PlannerInfo *root, Path *best_path)
187187
{
188188
Plan *plan;
189189

190+
/* plan_params should not be in use in current query level */
191+
Assert(root->plan_params == NIL);
192+
190193
/* Initialize this module's private workspace in PlannerInfo */
191194
root->curOuterRels = NULL;
192195
root->curOuterParams = NIL;
@@ -198,6 +201,12 @@ create_plan(PlannerInfo *root, Path *best_path)
198201
if (root->curOuterParams != NIL)
199202
elog(ERROR, "failed to assign all NestLoopParams to plan nodes");
200203

204+
/*
205+
* Reset plan_params to ensure param IDs used for nestloop params are not
206+
* re-used later
207+
*/
208+
root->plan_params = NIL;
209+
201210
return plan;
202211
}
203212

src/backend/optimizer/plan/planner.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,6 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
151151
glob = makeNode(PlannerGlobal);
152152

153153
glob->boundParams = boundParams;
154-
glob->paramlist = NIL;
155154
glob->subplans = NIL;
156155
glob->subroots = NIL;
157156
glob->rewindPlanIDs = NULL;
@@ -160,6 +159,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
160159
glob->resultRelations = NIL;
161160
glob->relationOids = NIL;
162161
glob->invalItems = NIL;
162+
glob->nParamExec = 0;
163163
glob->lastPHId = 0;
164164
glob->lastRowMarkId = 0;
165165
glob->transientPlan = false;
@@ -239,7 +239,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
239239
result->rowMarks = glob->finalrowmarks;
240240
result->relationOids = glob->relationOids;
241241
result->invalItems = glob->invalItems;
242-
result->nParamExec = list_length(glob->paramlist);
242+
result->nParamExec = glob->nParamExec;
243243

244244
return result;
245245
}
@@ -291,6 +291,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
291291
root->glob = glob;
292292
root->query_level = parent_root ? parent_root->query_level + 1 : 1;
293293
root->parent_root = parent_root;
294+
root->plan_params = NIL;
294295
root->planner_cxt = CurrentMemoryContext;
295296
root->init_plans = NIL;
296297
root->cte_plan_ids = NIL;
@@ -562,7 +563,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
562563
* and attach the initPlans to the top plan node.
563564
*/
564565
if (list_length(glob->subplans) != num_old_subplans ||
565-
root->glob->paramlist != NIL)
566+
root->glob->nParamExec > 0)
566567
SS_finalize_plan(root, plan, true);
567568

568569
/* Return internal info if caller wants it */

0 commit comments

Comments
 (0)