diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 93dcf69881a..ed5d7899e10 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -37,7 +37,13 @@ typedef struct rewrite_event CmdType event; /* type of rule being fired */ } rewrite_event; -static bool acquireLocksOnSubLinks(Node *node, void *context); +typedef struct acquireLocksOnSubLinks_context +{ + bool for_execute; /* AcquireRewriteLocks' forExecute param */ +} acquireLocksOnSubLinks_context; + +static bool acquireLocksOnSubLinks(Node *node, + acquireLocksOnSubLinks_context *context); static Query *rewriteRuleAction(Query *parsetree, Query *rule_action, Node *rule_qual, @@ -69,9 +75,19 @@ static Query *fireRIRrules(Query *parsetree, List *activeRIRs, * 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. + * forExecute indicates that the query is about to be executed. + * If so, we'll acquire RowExclusiveLock on the query's resultRelation, + * RowShareLock on any relation accessed FOR UPDATE/SHARE, and + * AccessShareLock on all other relations mentioned. + * + * If forExecute is false, AccessShareLock is acquired on all relations. + * This case is suitable for ruleutils.c, for example, where we only need + * schema stability and we don't intend to actually modify any relations. + * + * forUpdatePushedDown indicates that a pushed-down FOR UPDATE/SHARE + * applies to the current subquery, requiring all rels to be opened with at + * least RowShareLock. This should always be false at the top of the + * recursion. This flag is ignored if forExecute is false. * * 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 @@ -99,10 +115,15 @@ static Query *fireRIRrules(Query *parsetree, List *activeRIRs, * construction of a nested join was O(N^2) in the nesting depth.) */ void -AcquireRewriteLocks(Query *parsetree, bool forUpdatePushedDown) +AcquireRewriteLocks(Query *parsetree, + bool forExecute, + bool forUpdatePushedDown) { ListCell *l; int rt_index; + acquireLocksOnSubLinks_context context; + + context.for_execute = forExecute; /* * First, process RTEs of the current query level. @@ -128,14 +149,12 @@ AcquireRewriteLocks(Query *parsetree, bool forUpdatePushedDown) * release it until end of transaction. This protects the * rewriter and planner against schema changes mid-query. * - * If the 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. + * Assuming forExecute is true, this logic must match what the + * executor will do, else we risk lock-upgrade deadlocks. */ - if (rt_index == parsetree->resultRelation) + if (!forExecute) + lockmode = AccessShareLock; + else if (rt_index == parsetree->resultRelation) lockmode = RowExclusiveLock; else if (forUpdatePushedDown || get_parse_rowmark(parsetree, rt_index) != NULL) @@ -223,6 +242,7 @@ AcquireRewriteLocks(Query *parsetree, bool forUpdatePushedDown) * recurse to process the represented subquery. */ AcquireRewriteLocks(rte->subquery, + forExecute, (forUpdatePushedDown || get_parse_rowmark(parsetree, rt_index) != NULL)); break; @@ -238,7 +258,7 @@ AcquireRewriteLocks(Query *parsetree, bool forUpdatePushedDown) { CommonTableExpr *cte = (CommonTableExpr *) lfirst(l); - AcquireRewriteLocks((Query *) cte->ctequery, false); + AcquireRewriteLocks((Query *) cte->ctequery, forExecute, false); } /* @@ -246,7 +266,7 @@ AcquireRewriteLocks(Query *parsetree, bool forUpdatePushedDown) * the rtable and cteList. */ if (parsetree->hasSubLinks) - query_tree_walker(parsetree, acquireLocksOnSubLinks, NULL, + query_tree_walker(parsetree, acquireLocksOnSubLinks, &context, QTW_IGNORE_RC_SUBQUERIES); } @@ -254,7 +274,7 @@ AcquireRewriteLocks(Query *parsetree, bool forUpdatePushedDown) * Walker to find sublink subqueries for AcquireRewriteLocks */ static bool -acquireLocksOnSubLinks(Node *node, void *context) +acquireLocksOnSubLinks(Node *node, acquireLocksOnSubLinks_context *context) { if (node == NULL) return false; @@ -263,7 +283,9 @@ acquireLocksOnSubLinks(Node *node, void *context) SubLink *sub = (SubLink *) node; /* Do what we came for */ - AcquireRewriteLocks((Query *) sub->subselect, false); + AcquireRewriteLocks((Query *) sub->subselect, + context->for_execute, + false); /* Fall through to process lefthand args of SubLink */ } @@ -305,6 +327,9 @@ rewriteRuleAction(Query *parsetree, int rt_length; Query *sub_action; Query **sub_action_ptr; + acquireLocksOnSubLinks_context context; + + context.for_execute = true; /* * Make modifiable copies of rule action and qual (what we're passed are @@ -316,8 +341,8 @@ rewriteRuleAction(Query *parsetree, /* * Acquire necessary locks and fix any deleted JOIN RTE entries. */ - AcquireRewriteLocks(rule_action, false); - (void) acquireLocksOnSubLinks(rule_qual, NULL); + AcquireRewriteLocks(rule_action, true, false); + (void) acquireLocksOnSubLinks(rule_qual, &context); current_varno = rt_index; rt_length = list_length(parsetree->rtable); @@ -1367,7 +1392,7 @@ ApplyRetrieveRule(Query *parsetree, */ rule_action = copyObject(linitial(rule->actions)); - AcquireRewriteLocks(rule_action, forUpdatePushedDown); + AcquireRewriteLocks(rule_action, true, forUpdatePushedDown); /* * Recursively expand any view references inside the view. @@ -1690,6 +1715,9 @@ CopyAndAddInvertedQual(Query *parsetree, { /* Don't scribble on the passed qual (it's in the relcache!) */ Node *new_qual = (Node *) copyObject(rule_qual); + acquireLocksOnSubLinks_context context; + + context.for_execute = true; /* * In case there are subqueries in the qual, acquire necessary locks and @@ -1697,7 +1725,7 @@ CopyAndAddInvertedQual(Query *parsetree, * rewriteRuleAction, but not entirely ... consider restructuring so that * we only need to process the qual this way once.) */ - (void) acquireLocksOnSubLinks(new_qual, NULL); + (void) acquireLocksOnSubLinks(new_qual, &context); /* Fix references to OLD */ ChangeVarNodes(new_qual, PRS2_OLD_VARNO, rt_index, 0); diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index d4b2b8bc190..907e41d39b8 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -2518,7 +2518,7 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc, query = getInsertSelectQuery(query, NULL); /* Must acquire locks right away; see notes in get_query_def() */ - AcquireRewriteLocks(query, false); + AcquireRewriteLocks(query, false, false); context.buf = buf; context.namespaces = list_make1(&dpns); @@ -2666,8 +2666,11 @@ get_query_def(Query *query, StringInfo buf, List *parentnamespace, * relations, and fix up deleted columns in JOIN RTEs. This ensures * consistent results. Note we assume it's OK to scribble on the passed * querytree! + * + * We are only deparsing the query (we are not about to execute it), so we + * only need AccessShareLock on the relations it mentions. */ - AcquireRewriteLocks(query, false); + AcquireRewriteLocks(query, false, false); context.buf = buf; context.namespaces = lcons(&dpns, list_copy(parentnamespace)); diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h index 50625d4c371..a6af5aa44ec 100644 --- a/src/include/rewrite/rewriteHandler.h +++ b/src/include/rewrite/rewriteHandler.h @@ -18,7 +18,9 @@ #include "nodes/parsenodes.h" extern List *QueryRewrite(Query *parsetree); -extern void AcquireRewriteLocks(Query *parsetree, bool forUpdatePushedDown); +extern void AcquireRewriteLocks(Query *parsetree, + bool forExecute, + bool forUpdatePushedDown); extern Node *build_column_default(Relation rel, int attrno); #endif /* REWRITEHANDLER_H */