mirror of
https://github.com/postgres/postgres.git
synced 2025-07-27 12:41:57 +03:00
Make SELECT FOR UPDATE/SHARE work on inheritance trees, by having the plan
return the tableoid as well as the ctid for any FOR UPDATE targets that have child tables. All child tables are listed in the ExecRowMark list, but the executor just skips the ones that didn't produce the current row. Curiously, this longstanding restriction doesn't seem to have been documented anywhere; so no doc changes.
This commit is contained in:
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.176 2008/11/11 18:13:32 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.177 2008/11/15 19:43:46 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -283,17 +283,6 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
|
||||
int nattrs;
|
||||
ListCell *l;
|
||||
|
||||
/*
|
||||
* XXX for now, can't handle inherited expansion of FOR UPDATE/SHARE; can
|
||||
* we do better? (This will take some redesign because the executor
|
||||
* currently supposes that every rowMark relation is involved in every row
|
||||
* returned by the query.)
|
||||
*/
|
||||
if (get_rowmark(root->parse, parentRTindex))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("SELECT FOR UPDATE/SHARE is not supported for inheritance queries")));
|
||||
|
||||
/*
|
||||
* Initialize to compute size estimates for whole append relation.
|
||||
*
|
||||
|
@ -16,7 +16,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.93 2008/11/02 01:45:28 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.94 2008/11/15 19:43:46 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -138,6 +138,11 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
|
||||
char *resname;
|
||||
TargetEntry *tle;
|
||||
|
||||
/* ignore child rels */
|
||||
if (rc->rti != rc->prti)
|
||||
continue;
|
||||
|
||||
/* always need the ctid */
|
||||
var = makeVar(rc->rti,
|
||||
SelfItemPointerAttributeNumber,
|
||||
TIDOID,
|
||||
@ -153,6 +158,26 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
|
||||
true);
|
||||
|
||||
tlist = lappend(tlist, tle);
|
||||
|
||||
/* if parent of inheritance tree, need the tableoid too */
|
||||
if (rc->isParent)
|
||||
{
|
||||
var = makeVar(rc->rti,
|
||||
TableOidAttributeNumber,
|
||||
OIDOID,
|
||||
-1,
|
||||
0);
|
||||
|
||||
resname = (char *) palloc(32);
|
||||
snprintf(resname, 32, "tableoid%u", rc->rti);
|
||||
|
||||
tle = makeTargetEntry((Expr *) var,
|
||||
list_length(tlist) + 1,
|
||||
resname,
|
||||
true);
|
||||
|
||||
tlist = lappend(tlist, tle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.161 2008/11/11 18:13:32 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.162 2008/11/15 19:43:46 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -1169,6 +1169,7 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
|
||||
{
|
||||
Query *parse = root->parse;
|
||||
Oid parentOID;
|
||||
RowMarkClause *oldrc;
|
||||
Relation oldrelation;
|
||||
LOCKMODE lockmode;
|
||||
List *inhOIDs;
|
||||
@ -1208,6 +1209,15 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find out if parent relation is selected FOR UPDATE/SHARE. If so,
|
||||
* we need to mark its RowMarkClause as isParent = true, and generate
|
||||
* a new RowMarkClause for each child.
|
||||
*/
|
||||
oldrc = get_rowmark(parse, rti);
|
||||
if (oldrc)
|
||||
oldrc->isParent = true;
|
||||
|
||||
/*
|
||||
* Must open the parent relation to examine its tupdesc. We need not lock
|
||||
* it since the rewriter already obtained at least AccessShareLock on each
|
||||
@ -1221,14 +1231,15 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
|
||||
* in the parse/rewrite/plan pipeline.
|
||||
*
|
||||
* If the parent relation is the query's result relation, then we need
|
||||
* RowExclusiveLock. Otherwise, check to see if the relation is accessed
|
||||
* FOR UPDATE/SHARE or not. We can't just grab AccessShareLock because
|
||||
* then the executor would be trying to upgrade the lock, leading to
|
||||
* possible deadlocks. (This code should match the parser and rewriter.)
|
||||
* RowExclusiveLock. Otherwise, if it's accessed FOR UPDATE/SHARE, we
|
||||
* need RowShareLock; otherwise AccessShareLock. We can't just grab
|
||||
* AccessShareLock because then the executor would be trying to upgrade
|
||||
* the lock, leading to possible deadlocks. (This code should match the
|
||||
* parser and rewriter.)
|
||||
*/
|
||||
if (rti == parse->resultRelation)
|
||||
lockmode = RowExclusiveLock;
|
||||
else if (get_rowmark(parse, rti))
|
||||
else if (oldrc)
|
||||
lockmode = RowShareLock;
|
||||
else
|
||||
lockmode = AccessShareLock;
|
||||
@ -1283,6 +1294,22 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
|
||||
appinfo->parent_reloid = parentOID;
|
||||
appinfos = lappend(appinfos, appinfo);
|
||||
|
||||
/*
|
||||
* Build a RowMarkClause if parent is marked FOR UPDATE/SHARE.
|
||||
*/
|
||||
if (oldrc)
|
||||
{
|
||||
RowMarkClause *newrc = makeNode(RowMarkClause);
|
||||
|
||||
newrc->rti = childRTindex;
|
||||
newrc->prti = rti;
|
||||
newrc->forUpdate = oldrc->forUpdate;
|
||||
newrc->noWait = oldrc->noWait;
|
||||
newrc->isParent = false;
|
||||
|
||||
parse->rowMarks = lappend(parse->rowMarks, newrc);
|
||||
}
|
||||
|
||||
/* Close child relations, but keep locks */
|
||||
if (childOID != parentOID)
|
||||
heap_close(newrelation, NoLock);
|
||||
|
Reference in New Issue
Block a user