summaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2011-02-09 23:27:07 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2011-02-09 23:27:42 -0500
commite617f0d7e4084f85104361568ef5f865ebfa0005 (patch)
tree396531b6333cbb242b8d9259e70f6e45df3a1364 /src/backend
parent5478f991c9f73066d27d75b31876f9977af61db5 (diff)
downloadpostgresql-e617f0d7e4084f85104361568ef5f865ebfa0005.tar.gz
Fix improper matching of resjunk column names for FOR UPDATE in subselect.
Flattening of subquery range tables during setrefs.c could lead to the rangetable indexes in PlanRowMark nodes not matching up with the column names previously assigned to the corresponding resjunk ctid (resp. tableoid or wholerow) columns. Typical symptom would be either a "cannot extract system attribute from virtual tuple" error or an Assert failure. This wasn't a problem before 9.0 because we didn't support FOR UPDATE below the top query level, and so the final flattening could never renumber an RTE that was relevant to FOR UPDATE. Fix by using a plan-tree-wide unique number for each PlanRowMark to label the associated resjunk columns, so that the number need not change during flattening. Per report from David Johnston (though I'm darned if I can see how this got past initial testing of the relevant code). Back-patch to 9.0.
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/executor/execMain.c13
-rw-r--r--src/backend/nodes/copyfuncs.c1
-rw-r--r--src/backend/nodes/outfuncs.c2
-rw-r--r--src/backend/optimizer/plan/planner.c3
-rw-r--r--src/backend/optimizer/plan/setrefs.c2
-rw-r--r--src/backend/optimizer/prep/preptlist.c6
-rw-r--r--src/backend/optimizer/prep/prepunion.c1
7 files changed, 21 insertions, 7 deletions
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 600f7e0334..7df7f5cdf0 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -742,6 +742,7 @@ InitPlan(QueryDesc *queryDesc, int eflags)
erm->relation = relation;
erm->rti = rc->rti;
erm->prti = rc->prti;
+ erm->rowmarkId = rc->rowmarkId;
erm->markType = rc->markType;
erm->noWait = rc->noWait;
ItemPointerSetInvalid(&(erm->curCtid));
@@ -1425,23 +1426,29 @@ ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist)
/* if child rel, need tableoid */
if (erm->rti != erm->prti)
{
- snprintf(resname, sizeof(resname), "tableoid%u", erm->prti);
+ snprintf(resname, sizeof(resname), "tableoid%u", erm->rowmarkId);
aerm->toidAttNo = ExecFindJunkAttributeInTlist(targetlist,
resname);
+ if (!AttributeNumberIsValid(aerm->toidAttNo))
+ elog(ERROR, "could not find junk %s column", resname);
}
/* always need ctid for real relations */
- snprintf(resname, sizeof(resname), "ctid%u", erm->prti);
+ snprintf(resname, sizeof(resname), "ctid%u", erm->rowmarkId);
aerm->ctidAttNo = ExecFindJunkAttributeInTlist(targetlist,
resname);
+ if (!AttributeNumberIsValid(aerm->ctidAttNo))
+ elog(ERROR, "could not find junk %s column", resname);
}
else
{
Assert(erm->markType == ROW_MARK_COPY);
- snprintf(resname, sizeof(resname), "wholerow%u", erm->prti);
+ snprintf(resname, sizeof(resname), "wholerow%u", erm->rowmarkId);
aerm->wholeAttNo = ExecFindJunkAttributeInTlist(targetlist,
resname);
+ if (!AttributeNumberIsValid(aerm->wholeAttNo))
+ elog(ERROR, "could not find junk %s column", resname);
}
return aerm;
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 3d898326d7..83630924f6 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -906,6 +906,7 @@ _copyPlanRowMark(PlanRowMark *from)
COPY_SCALAR_FIELD(rti);
COPY_SCALAR_FIELD(prti);
+ COPY_SCALAR_FIELD(rowmarkId);
COPY_SCALAR_FIELD(markType);
COPY_SCALAR_FIELD(noWait);
COPY_SCALAR_FIELD(isParent);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 3b3e5448fd..5c943bc254 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -808,6 +808,7 @@ _outPlanRowMark(StringInfo str, PlanRowMark *node)
WRITE_UINT_FIELD(rti);
WRITE_UINT_FIELD(prti);
+ WRITE_UINT_FIELD(rowmarkId);
WRITE_ENUM_FIELD(markType, RowMarkType);
WRITE_BOOL_FIELD(noWait);
WRITE_BOOL_FIELD(isParent);
@@ -1609,6 +1610,7 @@ _outPlannerGlobal(StringInfo str, PlannerGlobal *node)
WRITE_NODE_FIELD(relationOids);
WRITE_NODE_FIELD(invalItems);
WRITE_UINT_FIELD(lastPHId);
+ WRITE_UINT_FIELD(lastRowMarkId);
WRITE_BOOL_FIELD(transientPlan);
}
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index b52eebe2aa..01b90383cf 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -166,6 +166,7 @@ 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 */
@@ -1885,6 +1886,7 @@ preprocess_rowmarks(PlannerInfo *root)
newrc = makeNode(PlanRowMark);
newrc->rti = newrc->prti = rc->rti;
+ newrc->rowmarkId = ++(root->glob->lastRowMarkId);
if (rc->forUpdate)
newrc->markType = ROW_MARK_EXCLUSIVE;
else
@@ -1910,6 +1912,7 @@ preprocess_rowmarks(PlannerInfo *root)
newrc = makeNode(PlanRowMark);
newrc->rti = newrc->prti = i;
+ newrc->rowmarkId = ++(root->glob->lastRowMarkId);
/* real tables support REFERENCE, anything else needs COPY */
if (rte->rtekind == RTE_RELATION)
newrc->markType = ROW_MARK_REFERENCE;
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 867238ecc8..d004f6cf12 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -252,7 +252,7 @@ set_plan_references(PlannerGlobal *glob, Plan *plan,
newrc = (PlanRowMark *) palloc(sizeof(PlanRowMark));
memcpy(newrc, rc, sizeof(PlanRowMark));
- /* adjust indexes */
+ /* adjust indexes ... but *not* the rowmarkId */
newrc->rti += rtoffset;
newrc->prti += rtoffset;
diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c
index 34b38eb329..63447aa536 100644
--- a/src/backend/optimizer/prep/preptlist.c
+++ b/src/backend/optimizer/prep/preptlist.c
@@ -102,7 +102,7 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
-1,
InvalidOid,
0);
- snprintf(resname, sizeof(resname), "ctid%u", rc->rti);
+ snprintf(resname, sizeof(resname), "ctid%u", rc->rowmarkId);
tle = makeTargetEntry((Expr *) var,
list_length(tlist) + 1,
pstrdup(resname),
@@ -118,7 +118,7 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
-1,
InvalidOid,
0);
- snprintf(resname, sizeof(resname), "tableoid%u", rc->rti);
+ snprintf(resname, sizeof(resname), "tableoid%u", rc->rowmarkId);
tle = makeTargetEntry((Expr *) var,
list_length(tlist) + 1,
pstrdup(resname),
@@ -132,7 +132,7 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
var = makeWholeRowVar(rt_fetch(rc->rti, range_table),
rc->rti,
0);
- snprintf(resname, sizeof(resname), "wholerow%u", rc->rti);
+ snprintf(resname, sizeof(resname), "wholerow%u", rc->rowmarkId);
tle = makeTargetEntry((Expr *) var,
list_length(tlist) + 1,
pstrdup(resname),
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index f62af6c37d..606e5fe60a 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -1294,6 +1294,7 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
newrc->rti = childRTindex;
newrc->prti = rti;
+ newrc->rowmarkId = oldrc->rowmarkId;
newrc->markType = oldrc->markType;
newrc->noWait = oldrc->noWait;
newrc->isParent = false;