diff options
-rw-r--r-- | src/backend/commands/matview.c | 2 | ||||
-rw-r--r-- | src/backend/rewrite/rewriteHandler.c | 68 | ||||
-rw-r--r-- | src/backend/utils/adt/ruleutils.c | 7 | ||||
-rw-r--r-- | src/include/rewrite/rewriteHandler.h | 4 |
4 files changed, 57 insertions, 24 deletions
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c index acf9564c336..93648a7f4cc 100644 --- a/src/backend/commands/matview.c +++ b/src/backend/commands/matview.c @@ -246,7 +246,7 @@ refresh_matview_datafill(DestReceiver *dest, Query *query, /* Lock and rewrite, using a copy to preserve the original query. */ copied_query = copyObject(query); - AcquireRewriteLocks(copied_query, false); + AcquireRewriteLocks(copied_query, true, false); rewritten = QueryRewrite(copied_query); /* SELECT should never rewrite to more or less than one SELECT query */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 9bcb51f0916..1ac048d78e5 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -38,7 +38,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, @@ -70,9 +76,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 [KEY] 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 [KEY] 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 [KEY] 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 @@ -100,10 +116,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. @@ -129,14 +150,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 [KEY] 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) @@ -224,6 +243,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; @@ -239,7 +259,7 @@ AcquireRewriteLocks(Query *parsetree, bool forUpdatePushedDown) { CommonTableExpr *cte = (CommonTableExpr *) lfirst(l); - AcquireRewriteLocks((Query *) cte->ctequery, false); + AcquireRewriteLocks((Query *) cte->ctequery, forExecute, false); } /* @@ -247,7 +267,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); } @@ -255,7 +275,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; @@ -264,7 +284,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 */ } @@ -306,6 +328,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 @@ -317,8 +342,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); @@ -1387,7 +1412,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. @@ -1719,6 +1744,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 @@ -1726,7 +1754,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 ff983cca5d8..71798efc436 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -3858,7 +3858,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); @@ -4004,8 +4004,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 1831de46406..1c0a3e02ffb 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); extern int relation_is_updatable(Oid reloid, bool include_triggers); |