1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-20 05:03:10 +03:00

Re-implement EvalPlanQual processing to improve its performance and eliminate

a lot of strange behaviors that occurred in join cases.  We now identify the
"current" row for every joined relation in UPDATE, DELETE, and SELECT FOR
UPDATE/SHARE queries.  If an EvalPlanQual recheck is necessary, we jam the
appropriate row into each scan node in the rechecking plan, forcing it to emit
only that one row.  The former behavior could rescan the whole of each joined
relation for each recheck, which was terrible for performance, and what's much
worse could result in duplicated output tuples.

Also, the original implementation of EvalPlanQual could not re-use the recheck
execution tree --- it had to go through a full executor init and shutdown for
every row to be tested.  To avoid this overhead, I've associated a special
runtime Param with each LockRows or ModifyTable plan node, and arranged to
make every scan node below such a node depend on that Param.  Thus, by
signaling a change in that Param, the EPQ machinery can just rescan the
already-built test plan.

This patch also adds a prohibition on set-returning functions in the
targetlist of SELECT FOR UPDATE/SHARE.  This is needed to avoid the
duplicate-output-tuple problem.  It seems fairly reasonable since the
other restrictions on SELECT FOR UPDATE are meant to ensure that there
is a unique correspondence between source tuples and result tuples,
which an output SRF destroys as much as anything else does.
This commit is contained in:
Tom Lane
2009-10-26 02:26:45 +00:00
parent 76d8883c8e
commit 9f2ee8f287
50 changed files with 1547 additions and 1018 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.187 2009/10/12 18:10:45 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.188 2009/10/26 02:26:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -632,7 +632,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
false, tuple_fraction,
&subroot);
rel->subrtable = subroot->parse->rtable;
rel->subrowmark = subroot->parse->rowMarks;
rel->subrowmark = subroot->rowMarks;
/* Copy number of output rows from subplan */
rel->tuples = rel->subplan->plan_rows;

View File

@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.265 2009/10/12 18:10:45 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.266 2009/10/26 02:26:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -951,7 +951,7 @@ create_indexscan_plan(PlannerInfo *root,
if (best_path->indexinfo->indpred)
{
if (baserelid != root->parse->resultRelation &&
get_rowmark(root->parse, baserelid) == NULL)
get_parse_rowmark(root->parse, baserelid) == NULL)
if (predicate_implied_by(clausel,
best_path->indexinfo->indpred))
continue;
@ -3598,7 +3598,7 @@ make_setop(SetOpCmd cmd, SetOpStrategy strategy, Plan *lefttree,
* Build a LockRows plan node
*/
LockRows *
make_lockrows(Plan *lefttree, List *rowMarks)
make_lockrows(Plan *lefttree, List *rowMarks, int epqParam)
{
LockRows *node = makeNode(LockRows);
Plan *plan = &node->plan;
@ -3614,6 +3614,7 @@ make_lockrows(Plan *lefttree, List *rowMarks)
plan->righttree = NULL;
node->rowMarks = rowMarks;
node->epqParam = epqParam;
return node;
}
@ -3750,7 +3751,8 @@ make_result(PlannerInfo *root,
*/
ModifyTable *
make_modifytable(CmdType operation, List *resultRelations,
List *subplans, List *returningLists)
List *subplans, List *returningLists,
List *rowMarks, int epqParam)
{
ModifyTable *node = makeNode(ModifyTable);
Plan *plan = &node->plan;
@ -3801,6 +3803,8 @@ make_modifytable(CmdType operation, List *resultRelations,
node->resultRelations = resultRelations;
node->plans = subplans;
node->returningLists = returningLists;
node->rowMarks = rowMarks;
node->epqParam = epqParam;
return node;
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.155 2009/07/21 02:02:44 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.156 2009/10/26 02:26:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -559,6 +559,9 @@ make_outerjoininfo(PlannerInfo *root,
* parser. It's because the parser hasn't got enough info --- consider
* FOR UPDATE applied to a view. Only after rewriting and flattening do
* we know whether the view contains an outer join.
*
* We use the original RowMarkClause list here; the PlanRowMark list
* would list everything.
*/
foreach(l, root->parse->rowMarks)
{

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.259 2009/10/12 18:10:48 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.260 2009/10/26 02:26:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -35,6 +35,7 @@
#ifdef OPTIMIZER_DEBUG
#include "nodes/print.h"
#endif
#include "parser/analyze.h"
#include "parser/parse_expr.h"
#include "parser/parse_oper.h"
#include "parser/parsetree.h"
@ -63,6 +64,7 @@ static void preprocess_qual_conditions(PlannerInfo *root, Node *jtnode);
static Plan *inheritance_planner(PlannerInfo *root);
static Plan *grouping_planner(PlannerInfo *root, double tuple_fraction);
static bool is_dummy_plan(Plan *plan);
static void preprocess_rowmarks(PlannerInfo *root);
static double preprocess_limit(PlannerInfo *root,
double tuple_fraction,
int64 *offset_est, int64 *count_est);
@ -159,7 +161,6 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
glob->relationOids = NIL;
glob->invalItems = NIL;
glob->lastPHId = 0;
glob->lastRowmarkId = 0;
glob->transientPlan = false;
/* Determine what fraction of the plan is likely to be scanned */
@ -209,7 +210,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
Assert(glob->finalrowmarks == NIL);
top_plan = set_plan_references(glob, top_plan,
root->parse->rtable,
root->parse->rowMarks);
root->rowMarks);
/* ... and the subplans (both regular subplans and initplans) */
Assert(list_length(glob->subplans) == list_length(glob->subrtables));
Assert(list_length(glob->subplans) == list_length(glob->subrowmarks));
@ -301,10 +302,11 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
root->cte_plan_ids = NIL;
root->eq_classes = NIL;
root->append_rel_list = NIL;
root->rowMarks = NIL;
root->hasRecursion = hasRecursion;
if (hasRecursion)
root->wt_param_id = SS_assign_worktable_param(root);
root->wt_param_id = SS_assign_special_param(root);
else
root->wt_param_id = -1;
root->non_recursive_plan = NULL;
@ -364,19 +366,12 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
}
/*
* Assign unique IDs (unique within this planner run) to RowMarkClauses.
* We can't identify them just by RT index because that will change
* during final rtable flattening, and we don't want to have to go back
* and change the resnames assigned to junk CTID tlist entries at that
* point. Do it now before expanding inheritance sets, because child
* relations should inherit their parents' rowmarkId.
* Preprocess RowMark information. We need to do this after subquery
* pullup (so that all non-inherited RTEs are present) and before
* inheritance expansion (so that the info is available for
* expand_inherited_tables to examine and modify).
*/
foreach(l, parse->rowMarks)
{
RowMarkClause *rc = (RowMarkClause *) lfirst(l);
rc->rowmarkId = ++(root->glob->lastRowmarkId);
}
preprocess_rowmarks(root);
/*
* Expand any rangetable entries that are inheritance sets into "append
@ -512,14 +507,15 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
/* If it's not SELECT, we need a ModifyTable node */
if (parse->commandType != CMD_SELECT)
{
List *returningLists;
List *rowMarks;
/*
* Deal with the RETURNING clause if any. It's convenient to pass
* the returningList through setrefs.c now rather than at top
* level (if we waited, handling inherited UPDATE/DELETE would be
* much harder).
*/
List *returningLists;
if (parse->returningList)
{
List *rlist;
@ -534,20 +530,32 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
else
returningLists = NIL;
/*
* If there was a FOR UPDATE/SHARE clause, the LockRows node will
* have dealt with fetching non-locked marked rows, else we need
* to have ModifyTable do that.
*/
if (parse->rowMarks)
rowMarks = NIL;
else
rowMarks = root->rowMarks;
plan = (Plan *) make_modifytable(parse->commandType,
copyObject(root->resultRelations),
list_make1(plan),
returningLists);
returningLists,
rowMarks,
SS_assign_special_param(root));
}
}
/*
* If any subplans were generated, or if we're inside a subplan, build
* initPlan list and extParam/allParam sets for plan nodes, and attach the
* initPlans to the top plan node.
* If any subplans were generated, or if there are any parameters to worry
* about, build initPlan list and extParam/allParam sets for plan nodes,
* and attach the initPlans to the top plan node.
*/
if (list_length(glob->subplans) != num_old_subplans ||
root->query_level > 1)
root->glob->paramlist != NIL)
SS_finalize_plan(root, plan, true);
/* Return internal info if caller wants it */
@ -701,6 +709,7 @@ inheritance_planner(PlannerInfo *root)
List *resultRelations = NIL;
List *returningLists = NIL;
List *rtable = NIL;
List *rowMarks;
List *tlist;
PlannerInfo subroot;
ListCell *l;
@ -797,11 +806,23 @@ inheritance_planner(PlannerInfo *root)
*/
parse->rtable = rtable;
/*
* If there was a FOR UPDATE/SHARE clause, the LockRows node will
* have dealt with fetching non-locked marked rows, else we need
* to have ModifyTable do that.
*/
if (parse->rowMarks)
rowMarks = NIL;
else
rowMarks = root->rowMarks;
/* And last, tack on a ModifyTable node to do the UPDATE/DELETE work */
return (Plan *) make_modifytable(parse->commandType,
copyObject(root->resultRelations),
subplans,
returningLists);
returningLists,
rowMarks,
SS_assign_special_param(root));
}
/*--------------------
@ -1630,11 +1651,15 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
/*
* Finally, if there is a FOR UPDATE/SHARE clause, add the LockRows node.
* (Note: we intentionally test parse->rowMarks not root->rowMarks here.
* If there are only non-locking rowmarks, they should be handled by
* the ModifyTable node instead.)
*/
if (parse->rowMarks)
{
result_plan = (Plan *) make_lockrows(result_plan,
parse->rowMarks);
root->rowMarks,
SS_assign_special_param(root));
}
/* Compute result-relations list if needed */
@ -1681,6 +1706,158 @@ is_dummy_plan(Plan *plan)
return false;
}
/*
* Create a bitmapset of the RT indexes of live base relations
*
* Helper for preprocess_rowmarks ... at this point in the proceedings,
* the only good way to distinguish baserels from appendrel children
* is to see what is in the join tree.
*/
static Bitmapset *
get_base_rel_indexes(Node *jtnode)
{
Bitmapset *result;
if (jtnode == NULL)
return NULL;
if (IsA(jtnode, RangeTblRef))
{
int varno = ((RangeTblRef *) jtnode)->rtindex;
result = bms_make_singleton(varno);
}
else if (IsA(jtnode, FromExpr))
{
FromExpr *f = (FromExpr *) jtnode;
ListCell *l;
result = NULL;
foreach(l, f->fromlist)
result = bms_join(result,
get_base_rel_indexes(lfirst(l)));
}
else if (IsA(jtnode, JoinExpr))
{
JoinExpr *j = (JoinExpr *) jtnode;
result = bms_join(get_base_rel_indexes(j->larg),
get_base_rel_indexes(j->rarg));
}
else
{
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(jtnode));
result = NULL; /* keep compiler quiet */
}
return result;
}
/*
* preprocess_rowmarks - set up PlanRowMarks if needed
*/
static void
preprocess_rowmarks(PlannerInfo *root)
{
Query *parse = root->parse;
Bitmapset *rels;
List *prowmarks;
ListCell *l;
int i;
if (parse->rowMarks)
{
/*
* We've got trouble if FOR UPDATE/SHARE appears inside grouping,
* since grouping renders a reference to individual tuple CTIDs
* invalid. This is also checked at parse time, but that's
* insufficient because of rule substitution, query pullup, etc.
*/
CheckSelectLocking(parse);
}
else
{
/*
* We only need rowmarks for UPDATE, DELETE, or FOR UPDATE/SHARE.
*/
if (parse->commandType != CMD_UPDATE &&
parse->commandType != CMD_DELETE)
return;
}
/*
* We need to have rowmarks for all base relations except the target.
* We make a bitmapset of all base rels and then remove the items we
* don't need or have FOR UPDATE/SHARE marks for.
*/
rels = get_base_rel_indexes((Node *) parse->jointree);
if (parse->resultRelation)
rels = bms_del_member(rels, parse->resultRelation);
/*
* Convert RowMarkClauses to PlanRowMark representation.
*
* Note: currently, it is syntactically impossible to have FOR UPDATE
* applied to an update/delete target rel. If that ever becomes
* possible, we should drop the target from the PlanRowMark list.
*/
prowmarks = NIL;
foreach(l, parse->rowMarks)
{
RowMarkClause *rc = (RowMarkClause *) lfirst(l);
PlanRowMark *newrc = makeNode(PlanRowMark);
Assert(rc->rti != parse->resultRelation);
rels = bms_del_member(rels, rc->rti);
newrc->rti = newrc->prti = rc->rti;
if (rc->forUpdate)
newrc->markType = ROW_MARK_EXCLUSIVE;
else
newrc->markType = ROW_MARK_SHARE;
newrc->noWait = rc->noWait;
newrc->isParent = false;
/* attnos will be assigned in preprocess_targetlist */
newrc->ctidAttNo = InvalidAttrNumber;
newrc->toidAttNo = InvalidAttrNumber;
newrc->wholeAttNo = InvalidAttrNumber;
prowmarks = lappend(prowmarks, newrc);
}
/*
* Now, add rowmarks for any non-target, non-locked base relations.
*/
i = 0;
foreach(l, parse->rtable)
{
RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
PlanRowMark *newrc;
i++;
if (!bms_is_member(i, rels))
continue;
newrc = makeNode(PlanRowMark);
newrc->rti = newrc->prti = i;
/* real tables support REFERENCE, anything else needs COPY */
if (rte->rtekind == RTE_RELATION)
newrc->markType = ROW_MARK_REFERENCE;
else
newrc->markType = ROW_MARK_COPY;
newrc->noWait = false; /* doesn't matter */
newrc->isParent = false;
/* attnos will be assigned in preprocess_targetlist */
newrc->ctidAttNo = InvalidAttrNumber;
newrc->toidAttNo = InvalidAttrNumber;
newrc->wholeAttNo = InvalidAttrNumber;
prowmarks = lappend(prowmarks, newrc);
}
root->rowMarks = prowmarks;
}
/*
* preprocess_limit - do pre-estimation for LIMIT and/or OFFSET clauses
*

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.153 2009/10/14 22:14:22 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.154 2009/10/26 02:26:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -166,7 +166,7 @@ static bool extract_query_dependencies_walker(Node *node,
* glob: global data for planner run
* plan: the topmost node of the plan
* rtable: the rangetable for the current subquery
* rowmarks: the RowMarkClause list for the current subquery
* rowmarks: the PlanRowMark list for the current subquery
*
* The return value is normally the same Plan node passed in, but can be
* different when the passed-in Plan is a SubqueryScan we decide isn't needed.
@ -235,21 +235,22 @@ set_plan_references(PlannerGlobal *glob, Plan *plan,
}
/*
* Adjust RT indexes of RowMarkClauses and add to final rowmarks list
* Adjust RT indexes of PlanRowMarks and add to final rowmarks list
*/
foreach(lc, rowmarks)
{
RowMarkClause *rc = (RowMarkClause *) lfirst(lc);
RowMarkClause *newrc;
PlanRowMark *rc = (PlanRowMark *) lfirst(lc);
PlanRowMark *newrc;
/* flat copy to duplicate all the scalar fields */
newrc = (RowMarkClause *) palloc(sizeof(RowMarkClause));
memcpy(newrc, rc, sizeof(RowMarkClause));
Assert(IsA(rc, PlanRowMark));
/* flat copy is enough since all fields are scalars */
newrc = (PlanRowMark *) palloc(sizeof(PlanRowMark));
memcpy(newrc, rc, sizeof(PlanRowMark));
/* adjust indexes */
newrc->rti += rtoffset;
newrc->prti += rtoffset;
/* rowmarkId must NOT be adjusted */
glob->finalrowmarks = lappend(glob->finalrowmarks, newrc);
}
@ -434,7 +435,7 @@ set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset)
foreach(l, splan->rowMarks)
{
RowMarkClause *rc = (RowMarkClause *) lfirst(l);
PlanRowMark *rc = (PlanRowMark *) lfirst(l);
rc->rti += rtoffset;
rc->prti += rtoffset;
@ -502,6 +503,13 @@ set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset)
{
lfirst_int(l) += rtoffset;
}
foreach(l, splan->rowMarks)
{
PlanRowMark *rc = (PlanRowMark *) lfirst(l);
rc->rti += rtoffset;
rc->prti += rtoffset;
}
foreach(l, splan->plans)
{
lfirst(l) = set_plan_refs(glob,

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.155 2009/10/12 18:10:48 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.156 2009/10/26 02:26:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -77,7 +77,8 @@ static Node *process_sublinks_mutator(Node *node,
process_sublinks_context *context);
static Bitmapset *finalize_plan(PlannerInfo *root,
Plan *plan,
Bitmapset *valid_params);
Bitmapset *valid_params,
Bitmapset *scan_params);
static bool finalize_primnode(Node *node, finalize_primnode_context *context);
@ -215,10 +216,14 @@ generate_new_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod)
}
/*
* Assign a (nonnegative) PARAM_EXEC ID for a recursive query's worktable.
* Assign a (nonnegative) PARAM_EXEC ID for a special parameter (one that
* is not actually used to carry a value at runtime). Such parameters are
* used for special runtime signaling purposes, such as connecting a
* recursive union node to its worktable scan node or forcing plan
* re-evaluation within the EvalPlanQual mechanism.
*/
int
SS_assign_worktable_param(PlannerInfo *root)
SS_assign_special_param(PlannerInfo *root)
{
Param *param;
@ -335,7 +340,7 @@ make_subplan(PlannerInfo *root, Query *orig_subquery, SubLinkType subLinkType,
/* And convert to SubPlan or InitPlan format. */
result = build_subplan(root, plan,
subroot->parse->rtable, subroot->parse->rowMarks,
subroot->parse->rtable, subroot->rowMarks,
subLinkType, testexpr, true, isTopQual);
/*
@ -377,7 +382,7 @@ make_subplan(PlannerInfo *root, Query *orig_subquery, SubLinkType subLinkType,
/* OK, convert to SubPlan format. */
hashplan = (SubPlan *) build_subplan(root, plan,
subroot->parse->rtable,
subroot->parse->rowMarks,
subroot->rowMarks,
ANY_SUBLINK, newtestexpr,
false, true);
/* Check we got what we expected */
@ -949,7 +954,7 @@ SS_process_ctes(PlannerInfo *root)
root->glob->subrtables = lappend(root->glob->subrtables,
subroot->parse->rtable);
root->glob->subrowmarks = lappend(root->glob->subrowmarks,
subroot->parse->rowMarks);
subroot->rowMarks);
splan->plan_id = list_length(root->glob->subplans);
root->init_plans = lappend(root->init_plans, splan);
@ -1702,7 +1707,8 @@ process_sublinks_mutator(Node *node, process_sublinks_context *context)
}
/*
* SS_finalize_plan - do final sublink processing for a completed Plan.
* SS_finalize_plan - do final sublink and parameter processing for a
* completed Plan.
*
* This recursively computes the extParam and allParam sets for every Plan
* node in the given plan tree. It also optionally attaches any previously
@ -1751,7 +1757,8 @@ SS_finalize_plan(PlannerInfo *root, Plan *plan, bool attach_initplans)
* output parameters of any initPlans. (We do not include output
* parameters of regular subplans. Those should only appear within the
* testexpr of SubPlan nodes, and are taken care of locally within
* finalize_primnode.)
* finalize_primnode. Likewise, special parameters that are generated
* by nodes such as ModifyTable are handled within finalize_plan.)
*
* Note: this is a bit overly generous since some parameters of upper
* query levels might belong to query subtrees that don't include this
@ -1772,14 +1779,11 @@ SS_finalize_plan(PlannerInfo *root, Plan *plan, bool attach_initplans)
paramid++;
}
/* Also include the recursion working table, if any */
if (root->wt_param_id >= 0)
valid_params = bms_add_member(valid_params, root->wt_param_id);
/*
* Now recurse through plan tree.
*/
(void) finalize_plan(root, plan, valid_params);
(void) finalize_plan(root, plan, valid_params, NULL);
bms_free(valid_params);
@ -1819,19 +1823,28 @@ SS_finalize_plan(PlannerInfo *root, Plan *plan, bool attach_initplans)
/*
* Recursive processing of all nodes in the plan tree
*
* valid_params is the set of param IDs considered valid to reference in
* this plan node or its children.
* scan_params is a set of param IDs to force scan plan nodes to reference.
* This is for EvalPlanQual support, and is always NULL at the top of the
* recursion.
*
* The return value is the computed allParam set for the given Plan node.
* This is just an internal notational convenience.
*/
static Bitmapset *
finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params)
finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
Bitmapset *scan_params)
{
finalize_primnode_context context;
int locally_added_param;
if (plan == NULL)
return NULL;
context.root = root;
context.paramids = NULL; /* initialize set to empty */
locally_added_param = -1; /* there isn't one */
/*
* When we call finalize_primnode, context.paramids sets are automatically
@ -1852,6 +1865,10 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params)
&context);
break;
case T_SeqScan:
context.paramids = bms_add_members(context.paramids, scan_params);
break;
case T_IndexScan:
finalize_primnode((Node *) ((IndexScan *) plan)->indexqual,
&context);
@ -1860,6 +1877,7 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params)
* we need not look at indexqualorig, since it will have the same
* param references as indexqual.
*/
context.paramids = bms_add_members(context.paramids, scan_params);
break;
case T_BitmapIndexScan:
@ -1875,11 +1893,13 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params)
case T_BitmapHeapScan:
finalize_primnode((Node *) ((BitmapHeapScan *) plan)->bitmapqualorig,
&context);
context.paramids = bms_add_members(context.paramids, scan_params);
break;
case T_TidScan:
finalize_primnode((Node *) ((TidScan *) plan)->tidquals,
&context);
context.paramids = bms_add_members(context.paramids, scan_params);
break;
case T_SubqueryScan:
@ -1893,16 +1913,20 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params)
*/
context.paramids = bms_add_members(context.paramids,
((SubqueryScan *) plan)->subplan->extParam);
/* We need scan_params too, though */
context.paramids = bms_add_members(context.paramids, scan_params);
break;
case T_FunctionScan:
finalize_primnode(((FunctionScan *) plan)->funcexpr,
&context);
context.paramids = bms_add_members(context.paramids, scan_params);
break;
case T_ValuesScan:
finalize_primnode((Node *) ((ValuesScan *) plan)->values_lists,
&context);
context.paramids = bms_add_members(context.paramids, scan_params);
break;
case T_CteScan:
@ -1934,6 +1958,9 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params)
bms_add_member(context.paramids,
((CteScan *) plan)->cteParam);
#endif
context.paramids = bms_add_members(context.paramids,
scan_params);
}
break;
@ -1941,21 +1968,30 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params)
context.paramids =
bms_add_member(context.paramids,
((WorkTableScan *) plan)->wtParam);
context.paramids = bms_add_members(context.paramids, scan_params);
break;
case T_ModifyTable:
{
ModifyTable *mtplan = (ModifyTable *) plan;
ListCell *l;
finalize_primnode((Node *) ((ModifyTable *) plan)->returningLists,
/* Force descendant scan nodes to reference epqParam */
locally_added_param = mtplan->epqParam;
valid_params = bms_add_member(bms_copy(valid_params),
locally_added_param);
scan_params = bms_add_member(bms_copy(scan_params),
locally_added_param);
finalize_primnode((Node *) mtplan->returningLists,
&context);
foreach(l, ((ModifyTable *) plan)->plans)
foreach(l, mtplan->plans)
{
context.paramids =
bms_add_members(context.paramids,
finalize_plan(root,
(Plan *) lfirst(l),
valid_params));
valid_params,
scan_params));
}
}
break;
@ -1970,7 +2006,8 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params)
bms_add_members(context.paramids,
finalize_plan(root,
(Plan *) lfirst(l),
valid_params));
valid_params,
scan_params));
}
}
break;
@ -1985,7 +2022,8 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params)
bms_add_members(context.paramids,
finalize_plan(root,
(Plan *) lfirst(l),
valid_params));
valid_params,
scan_params));
}
}
break;
@ -2000,7 +2038,8 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params)
bms_add_members(context.paramids,
finalize_plan(root,
(Plan *) lfirst(l),
valid_params));
valid_params,
scan_params));
}
}
break;
@ -2032,16 +2071,30 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params)
break;
case T_RecursiveUnion:
/* child nodes are allowed to reference wtParam */
locally_added_param = ((RecursiveUnion *) plan)->wtParam;
valid_params = bms_add_member(bms_copy(valid_params),
locally_added_param);
/* wtParam does *not* get added to scan_params */
break;
case T_LockRows:
/* Force descendant scan nodes to reference epqParam */
locally_added_param = ((LockRows *) plan)->epqParam;
valid_params = bms_add_member(bms_copy(valid_params),
locally_added_param);
scan_params = bms_add_member(bms_copy(scan_params),
locally_added_param);
break;
case T_Hash:
case T_Agg:
case T_WindowAgg:
case T_SeqScan:
case T_Material:
case T_Sort:
case T_Unique:
case T_SetOp:
case T_Group:
case T_LockRows:
break;
default:
@ -2053,20 +2106,25 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params)
context.paramids = bms_add_members(context.paramids,
finalize_plan(root,
plan->lefttree,
valid_params));
valid_params,
scan_params));
context.paramids = bms_add_members(context.paramids,
finalize_plan(root,
plan->righttree,
valid_params));
valid_params,
scan_params));
/*
* RecursiveUnion *generates* its worktable param, so don't bubble that up
* Any locally generated parameter doesn't count towards its generating
* plan node's external dependencies. (Note: if we changed valid_params
* and/or scan_params, we leak those bitmapsets; not worth the notational
* trouble to clean them up.)
*/
if (IsA(plan, RecursiveUnion))
if (locally_added_param >= 0)
{
context.paramids = bms_del_member(context.paramids,
((RecursiveUnion *) plan)->wtParam);
locally_added_param);
}
/* Now we have all the paramids */
@ -2199,7 +2257,7 @@ SS_make_initplan_from_plan(PlannerInfo *root, Plan *plan,
root->glob->subrtables = lappend(root->glob->subrtables,
root->parse->rtable);
root->glob->subrowmarks = lappend(root->glob->subrowmarks,
root->parse->rowMarks);
root->rowMarks);
/*
* Create a SubPlan node and add it to the outer list of InitPlans. Note

View File

@ -16,7 +16,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.67 2009/09/02 17:52:24 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.68 2009/10/26 02:26:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -628,6 +628,7 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
subroot->cte_plan_ids = NIL;
subroot->eq_classes = NIL;
subroot->append_rel_list = NIL;
subroot->rowMarks = NIL;
subroot->hasRecursion = false;
subroot->wt_param_id = -1;
subroot->non_recursive_plan = NULL;

View File

@ -9,14 +9,15 @@
* relation in the correct order. For both UPDATE and DELETE queries,
* we need a junk targetlist entry holding the CTID attribute --- the
* executor relies on this to find the tuple to be replaced/deleted.
* We may also need junk tlist entries for Vars used in the RETURNING list.
* We may also need junk tlist entries for Vars used in the RETURNING list
* and row ID information needed for EvalPlanQual checking.
*
*
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.97 2009/10/12 18:10:48 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.98 2009/10/26 02:26:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -31,7 +32,6 @@
#include "optimizer/subselect.h"
#include "optimizer/tlist.h"
#include "optimizer/var.h"
#include "parser/analyze.h"
#include "parser/parsetree.h"
#include "parser/parse_coerce.h"
#include "utils/rel.h"
@ -54,6 +54,7 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
int result_relation = parse->resultRelation;
List *range_table = parse->rtable;
CmdType command_type = parse->commandType;
ListCell *lc;
/*
* Sanity check: if there is a result relation, it'd better be a real
@ -108,51 +109,47 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
}
/*
* Add TID targets for rels selected FOR UPDATE/SHARE. The executor uses
* the TID to know which rows to lock, much as for UPDATE or DELETE.
* Add necessary junk columns for rowmarked rels. These values are
* needed for locking of rels selected FOR UPDATE/SHARE, and to do
* EvalPlanQual rechecking. While we are at it, store these junk attnos
* in the PlanRowMark list so that we don't have to redetermine them
* at runtime.
*/
if (parse->rowMarks)
foreach(lc, root->rowMarks)
{
ListCell *l;
PlanRowMark *rc = (PlanRowMark *) lfirst(lc);
Var *var;
char resname[32];
TargetEntry *tle;
/*
* We've got trouble if the FOR UPDATE/SHARE appears inside grouping,
* since grouping renders a reference to individual tuple CTIDs
* invalid. This is also checked at parse time, but that's
* insufficient because of rule substitution, query pullup, etc.
*/
CheckSelectLocking(parse);
foreach(l, parse->rowMarks)
/* child rels should just use the same junk attrs as their parents */
if (rc->rti != rc->prti)
{
RowMarkClause *rc = (RowMarkClause *) lfirst(l);
Var *var;
char resname[32];
TargetEntry *tle;
PlanRowMark *prc = get_plan_rowmark(root->rowMarks, rc->prti);
/* ignore child rels */
if (rc->rti != rc->prti)
continue;
/* parent should have appeared earlier in list */
if (prc == NULL || prc->toidAttNo == InvalidAttrNumber)
elog(ERROR, "parent PlanRowMark not processed yet");
rc->ctidAttNo = prc->ctidAttNo;
rc->toidAttNo = prc->toidAttNo;
continue;
}
/* we should have an ID for the RowMarkClause */
Assert(rc->rowmarkId != 0);
/* always need the ctid */
if (rc->markType != ROW_MARK_COPY)
{
/* It's a regular table, so fetch its TID */
var = makeVar(rc->rti,
SelfItemPointerAttributeNumber,
TIDOID,
-1,
0);
snprintf(resname, sizeof(resname),
"ctid%u", rc->rowmarkId);
snprintf(resname, sizeof(resname), "ctid%u", rc->rti);
tle = makeTargetEntry((Expr *) var,
list_length(tlist) + 1,
pstrdup(resname),
true);
tlist = lappend(tlist, tle);
rc->ctidAttNo = tle->resno;
/* if parent of inheritance tree, need the tableoid too */
if (rc->isParent)
@ -162,18 +159,31 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
OIDOID,
-1,
0);
snprintf(resname, sizeof(resname),
"tableoid%u", rc->rowmarkId);
snprintf(resname, sizeof(resname), "tableoid%u", rc->rti);
tle = makeTargetEntry((Expr *) var,
list_length(tlist) + 1,
pstrdup(resname),
true);
tlist = lappend(tlist, tle);
rc->toidAttNo = tle->resno;
}
}
else
{
/* Not a table, so we need the whole row as a junk var */
var = makeVar(rc->rti,
InvalidAttrNumber,
RECORDOID,
-1,
0);
snprintf(resname, sizeof(resname), "wholerow%u", rc->rti);
tle = makeTargetEntry((Expr *) var,
list_length(tlist) + 1,
pstrdup(resname),
true);
tlist = lappend(tlist, tle);
rc->wholeAttNo = tle->resno;
}
}
/*
@ -394,3 +404,24 @@ expand_targetlist(List *tlist, int command_type,
return new_tlist;
}
/*
* Locate PlanRowMark for given RT index, or return NULL if none
*
* This probably ought to be elsewhere, but there's no very good place
*/
PlanRowMark *
get_plan_rowmark(List *rowmarks, Index rtindex)
{
ListCell *l;
foreach(l, rowmarks)
{
PlanRowMark *rc = (PlanRowMark *) lfirst(l);
if (rc->rti == rtindex)
return rc;
}
return NULL;
}

View File

@ -22,7 +22,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.177 2009/10/23 05:24:52 petere Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.178 2009/10/26 02:26:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -248,7 +248,7 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,
rtr->rtindex,
subplan,
subroot->parse->rtable,
subroot->parse->rowMarks);
subroot->rowMarks);
/*
* We don't bother to determine the subquery's output ordering since
@ -1133,7 +1133,7 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
{
Query *parse = root->parse;
Oid parentOID;
RowMarkClause *oldrc;
PlanRowMark *oldrc;
Relation oldrelation;
LOCKMODE lockmode;
List *inhOIDs;
@ -1171,10 +1171,10 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
* the lock, leading to possible deadlocks. (This code should match the
* parser and rewriter.)
*/
oldrc = get_rowmark(parse, rti);
oldrc = get_plan_rowmark(root->rowMarks, rti);
if (rti == parse->resultRelation)
lockmode = RowExclusiveLock;
else if (oldrc)
else if (oldrc && RowMarkRequiresRowShareLock(oldrc->markType))
lockmode = RowShareLock;
else
lockmode = AccessShareLock;
@ -1196,7 +1196,7 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
/*
* If parent relation is selected FOR UPDATE/SHARE, we need to mark its
* RowMarkClause as isParent = true, and generate a new RowMarkClause for
* PlanRowMark as isParent = true, and generate a new PlanRowMark for
* each child.
*/
if (oldrc)
@ -1275,21 +1275,23 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
}
/*
* Build a RowMarkClause if parent is marked FOR UPDATE/SHARE.
* Build a PlanRowMark if parent is marked FOR UPDATE/SHARE.
*/
if (oldrc)
{
RowMarkClause *newrc = makeNode(RowMarkClause);
PlanRowMark *newrc = makeNode(PlanRowMark);
newrc->rti = childRTindex;
newrc->prti = rti;
/* children use the same rowmarkId as their parent */
newrc->rowmarkId = oldrc->rowmarkId;
newrc->forUpdate = oldrc->forUpdate;
newrc->markType = oldrc->markType;
newrc->noWait = oldrc->noWait;
newrc->isParent = false;
/* junk attrs for children are not identified yet */
newrc->ctidAttNo = InvalidAttrNumber;
newrc->toidAttNo = InvalidAttrNumber;
newrc->wholeAttNo = InvalidAttrNumber;
parse->rowMarks = lappend(parse->rowMarks, newrc);
root->rowMarks = lappend(root->rowMarks, newrc);
}
/* Close child relations, but keep locks */