* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.190 2009/10/28 14:55:43 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.191 2009/10/28 17:36:50 tgl Exp $
*
*-------------------------------------------------------------------------
*/
bool forUpdate, bool noWait, bool pushedDown);
static List *matchLocks(CmdType event, RuleLock *rulelocks,
int varno, Query *parsetree);
-static Query *fireRIRrules(Query *parsetree, List *activeRIRs);
+static Query *fireRIRrules(Query *parsetree, List *activeRIRs,
+ bool forUpdatePushedDown);
/*
* These locks will ensure that the relation schemas don't change under us
* while we are rewriting and planning the query.
*
+ * forUpdatePushedDown indicates that a pushed-down FOR UPDATE/SHARE applies
+ * to the current subquery, requiring all rels to be opened with RowShareLock.
+ * This should always be false at the start of the recursion.
+ *
* A secondary purpose of this routine is to fix up JOIN RTE references to
* dropped columns (see details below). Because the RTEs are modified in
* place, it is generally appropriate for the caller of this routine to have
* construction of a nested join was O(N^2) in the nesting depth.)
*/
void
-AcquireRewriteLocks(Query *parsetree)
+AcquireRewriteLocks(Query *parsetree, bool forUpdatePushedDown)
{
ListCell *l;
int rt_index;
*/
if (rt_index == parsetree->resultRelation)
lockmode = RowExclusiveLock;
- else if (get_parse_rowmark(parsetree, rt_index) != NULL)
+ else if (forUpdatePushedDown ||
+ get_parse_rowmark(parsetree, rt_index) != NULL)
lockmode = RowShareLock;
else
lockmode = AccessShareLock;
* The subquery RTE itself is all right, but we have to
* recurse to process the represented subquery.
*/
- AcquireRewriteLocks(rte->subquery);
+ AcquireRewriteLocks(rte->subquery,
+ (forUpdatePushedDown ||
+ get_parse_rowmark(parsetree, rt_index) != NULL));
break;
default:
{
CommonTableExpr *cte = (CommonTableExpr *) lfirst(l);
- AcquireRewriteLocks((Query *) cte->ctequery);
+ AcquireRewriteLocks((Query *) cte->ctequery, false);
}
/*
SubLink *sub = (SubLink *) node;
/* Do what we came for */
- AcquireRewriteLocks((Query *) sub->subselect);
+ AcquireRewriteLocks((Query *) sub->subselect, false);
/* Fall through to process lefthand args of SubLink */
}
/*
* Acquire necessary locks and fix any deleted JOIN RTE entries.
*/
- AcquireRewriteLocks(rule_action);
+ AcquireRewriteLocks(rule_action, false);
(void) acquireLocksOnSubLinks(rule_qual, NULL);
current_varno = rt_index;
int rt_index,
bool relation_level,
Relation relation,
- List *activeRIRs)
+ List *activeRIRs,
+ bool forUpdatePushedDown)
{
Query *rule_action;
RangeTblEntry *rte,
if (!relation_level)
elog(ERROR, "cannot handle per-attribute ON SELECT rule");
+ /*
+ * If FOR UPDATE/SHARE of view, be sure we get right initial lock on the
+ * relations it references.
+ */
+ rc = get_parse_rowmark(parsetree, rt_index);
+ forUpdatePushedDown |= (rc != NULL);
+
/*
* Make a modifiable copy of the view query, and acquire needed locks on
* the relations it mentions.
*/
rule_action = copyObject(linitial(rule->actions));
- AcquireRewriteLocks(rule_action);
+ AcquireRewriteLocks(rule_action, forUpdatePushedDown);
/*
* Recursively expand any view references inside the view.
*/
- rule_action = fireRIRrules(rule_action, activeRIRs);
+ rule_action = fireRIRrules(rule_action, activeRIRs, forUpdatePushedDown);
/*
* VIEWs are really easy --- just plug the view query in as a subselect,
* If FOR UPDATE/SHARE of view, mark all the contained tables as
* implicit FOR UPDATE/SHARE, the same as the parser would have done
* if the view's subquery had been written out explicitly.
+ *
+ * Note: we don't consider forUpdatePushedDown here; such marks will be
+ * made by recursing from the upper level in markQueryForLocking.
*/
- if ((rc = get_parse_rowmark(parsetree, rt_index)) != NULL)
+ if (rc != NULL)
markQueryForLocking(rule_action, (Node *) rule_action->jointree,
rc->forUpdate, rc->noWait, true);
/* Do what we came for */
sub->subselect = (Node *) fireRIRrules((Query *) sub->subselect,
- activeRIRs);
+ activeRIRs, false);
/* Fall through to process lefthand args of SubLink */
}
* Apply all RIR rules on each rangetable entry in a query
*/
static Query *
-fireRIRrules(Query *parsetree, List *activeRIRs)
+fireRIRrules(Query *parsetree, List *activeRIRs, bool forUpdatePushedDown)
{
int rt_index;
ListCell *lc;
*/
if (rte->rtekind == RTE_SUBQUERY)
{
- rte->subquery = fireRIRrules(rte->subquery, activeRIRs);
+ rte->subquery = fireRIRrules(rte->subquery, activeRIRs,
+ (forUpdatePushedDown ||
+ get_parse_rowmark(parsetree, rt_index) != NULL));
continue;
}
rt_index,
rule->attrno == -1,
rel,
- activeRIRs);
+ activeRIRs,
+ forUpdatePushedDown);
}
activeRIRs = list_delete_first(activeRIRs);
CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
cte->ctequery = (Node *)
- fireRIRrules((Query *) cte->ctequery, activeRIRs);
+ fireRIRrules((Query *) cte->ctequery, activeRIRs, false);
}
/*
{
Query *query = (Query *) lfirst(l);
- query = fireRIRrules(query, NIL);
+ query = fireRIRrules(query, NIL, false);
/*
* If the query target was rewritten as a view, complain.